001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.client.classic; 018 019import static java.lang.Character.isWhitespace; 020import static java.util.logging.Level.*; 021import static org.apache.juneau.commons.httppart.HttpPartType.*; 022import static org.apache.juneau.commons.lang.StateEnum.*; 023import static org.apache.juneau.commons.utils.CollectionUtils.*; 024import static org.apache.juneau.commons.utils.CollectionUtils.isEmpty; 025import static org.apache.juneau.commons.utils.Shorts.*; 026import static org.apache.juneau.commons.utils.StringUtils.*; 027import static org.apache.juneau.commons.utils.StringUtils.isEmpty; 028import static org.apache.juneau.commons.utils.ThrowableUtils.*; 029import static org.apache.juneau.http.HttpMethod.*; 030import static org.apache.juneau.http.classic.HttpEntities.*; 031import static org.apache.juneau.http.classic.HttpHeaders.*; 032import static org.apache.juneau.http.classic.HttpParts.*; 033import static org.apache.juneau.rest.RestSharedConstants.*; 034import static org.apache.juneau.rest.client.classic.RestOperation.*; 035 036import java.io.*; 037import java.lang.reflect.*; 038import java.lang.reflect.Proxy; 039import java.net.*; 040import java.nio.charset.*; 041import java.util.*; 042import java.util.concurrent.*; 043import java.util.concurrent.atomic.*; 044import java.util.function.*; 045import java.util.logging.*; 046import java.util.regex.*; 047 048import javax.net.ssl.*; 049 050import org.apache.http.*; 051import org.apache.http.NameValuePair; 052import org.apache.http.auth.*; 053import org.apache.http.client.*; 054import org.apache.http.client.CookieStore; 055import org.apache.http.client.config.*; 056import org.apache.http.client.entity.*; 057import org.apache.http.client.methods.*; 058import org.apache.http.client.utils.*; 059import org.apache.http.config.*; 060import org.apache.http.conn.*; 061import org.apache.http.conn.routing.*; 062import org.apache.http.conn.socket.*; 063import org.apache.http.conn.ssl.*; 064import org.apache.http.conn.util.*; 065import org.apache.http.cookie.*; 066import org.apache.http.impl.client.*; 067import org.apache.http.impl.conn.*; 068import org.apache.http.params.*; 069import org.apache.http.protocol.*; 070import org.apache.http.ssl.SSLContexts; 071import org.apache.juneau.commons.*; 072import org.apache.juneau.commons.collections.*; 073import org.apache.juneau.commons.function.*; 074import org.apache.juneau.commons.http.*; 075import org.apache.juneau.commons.httppart.*; 076import org.apache.juneau.commons.inject.*; 077import org.apache.juneau.commons.reflect.*; 078import org.apache.juneau.http.classic.entity.*; 079import org.apache.juneau.http.classic.header.*; 080import org.apache.juneau.http.classic.part.*; 081import org.apache.juneau.http.classic.remote.RrpcInterfaceMeta; 082import org.apache.juneau.http.classic.resource.*; 083import org.apache.juneau.http.remote.*; 084import org.apache.juneau.marshall.*; 085import org.apache.juneau.marshall.bson.*; 086import org.apache.juneau.marshall.cbor.*; 087import org.apache.juneau.marshall.hjson.*; 088import org.apache.juneau.marshall.hocon.*; 089import org.apache.juneau.marshall.html.*; 090import org.apache.juneau.marshall.httppart.*; 091import org.apache.juneau.marshall.ini.*; 092import org.apache.juneau.marshall.jcs.*; 093import org.apache.juneau.marshall.json.*; 094import org.apache.juneau.marshall.json5.*; 095import org.apache.juneau.marshall.json5l.*; 096import org.apache.juneau.marshall.jsonl.*; 097import org.apache.juneau.marshall.markdown.*; 098import org.apache.juneau.marshall.marshaller.*; 099import org.apache.juneau.marshall.marshaller.Uon; 100import org.apache.juneau.marshall.msgpack.*; 101import org.apache.juneau.marshall.oapi.*; 102import org.apache.juneau.marshall.parquet.*; 103import org.apache.juneau.marshall.parser.*; 104import org.apache.juneau.marshall.parser.ParseException; 105import org.apache.juneau.marshall.plaintext.*; 106import org.apache.juneau.marshall.prototext.*; 107import org.apache.juneau.marshall.serializer.*; 108import org.apache.juneau.marshall.toml.*; 109import org.apache.juneau.marshall.uon.*; 110import org.apache.juneau.marshall.urlencoding.*; 111import org.apache.juneau.marshall.xml.*; 112import org.apache.juneau.rest.*; 113import org.apache.juneau.rest.client.classic.assertion.*; 114import org.apache.juneau.rest.client.classic.remote.*; 115 116/** 117 * Utility class for interfacing with remote REST interfaces. 118 * 119 * <p> 120 * Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style 121 * REST calls and the ability to perform marshalling of POJOs to and from HTTP parts. 122 * 123 * <h5 class='figure'>Example:</h5> 124 * <p class='bjava'> 125 * <jc>// Create a basic REST client with JSON support and download a bean.</jc> 126 * MyBean <jv>bean</jv> = RestClient.<jsm>create</jsm>() 127 * .json5() 128 * .build() 129 * .get(<jsf>URI</jsf>) 130 * .run() 131 * .assertStatus().asCode().is(200) 132 * .assertHeader(<js>"Content-Type"</js>).matchesSimple(<js>"application/json*"</js>) 133 * .getContent().as(MyBean.<jk>class</jk>); 134 * </p> 135 * 136 * <p> 137 * Breaking apart the fluent call, we can see the classes being used: 138 * <p class='bjava'> 139 * RestClient.Builder<?> <jv>builder</jv> = RestClient.<jsm>create</jsm>().json5(); 140 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 141 * RestRequest <jv>req</jv> = <jv>client</jv>.get(<jsf>URI</jsf>); 142 * RestResponse <jv>res</jv> = <jv>req</jv>.run(); 143 * RestResponseStatusLineAssertion <jv>statusLineAssertion</jv> = <jv>res</jv>.assertStatus(); 144 * FluentIntegerAssertion<RestResponse> <jv>codeAssertion</jv> = <jv>statusLineAssertion</jv>.asCode(); 145 * <jv>res</jv> = <jv>codeAssertion</jv>.is(200); 146 * FluentStringAssertion<RestResponse> <jv>headerAssertion</jv> = <jv>res</jv>.assertHeader(<js>"Content-Type"</js>); 147 * <jv>res</jv> = <jv>headerAssertion</jv>.matchesSimple(<js>"application/json*"</js>); 148 * RestResponseBody <jv>content</jv> = <jv>res</jv>.getContent(); 149 * MyBean <jv>bean</jv> = <jv>content</jv>.as(MyBean.<jk>class</jk>); 150 * </p> 151 * 152 * <p> 153 * It additionally provides support for creating remote proxy interfaces using REST as the transport medium. 154 * 155 * <h5 class='figure'>Example:</h5> 156 * <p class='bjava'> 157 * <jc>// Define a Remote proxy for interacting with a REST interface.</jc> 158 * <ja>@Remote</ja>(path=<js>"/petstore"</js>) 159 * <jk>public interface</jk> PetStore { 160 * 161 * <ja>@RemotePost</ja>(<js>"/pets"</js>) 162 * Pet addPet( 163 * <ja>@Content</ja> CreatePet <jv>pet</jv>, 164 * <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, 165 * <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> 166 * ); 167 * } 168 * 169 * <jc>// Use a RestClient with default JSON 5 support.</jc> 170 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 171 * 172 * PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); 173 * CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); 174 * Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); 175 * </p> 176 * 177 * <p> 178 * The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality: 179 * <ul class='javatree'> 180 * <li class='jc'>{@link RestClient} <jk>extends</jk> {@link HttpClient}, creates {@link RestRequest} objects. 181 * <li class='jc'>{@link RestRequest} <jk>extends</jk> {@link HttpUriRequest}, creates {@link RestResponse} objects. 182 * <li class='jc'>{@link RestResponse} creates {@link ResponseContent} and {@link ResponseHeader} objects. 183 * <li class='jc'>{@link ResponseContent} <jk>extends</jk> {@link HttpEntity} 184 * <li class='jc'>{@link ResponseHeader} <jk>extends</jk> {@link Header} 185 * </ul> 186 * 187 * 188 * <p> 189 * Instances of this class are built using the {@link Builder} class which can be constructed using 190 * the {@link #create() RestClient.create()} method as shown above. 191 * 192 * <p> 193 * Clients are typically created with a root URI so that relative URIs can be used when making requests. 194 * This is done using the {@link Builder#rootUrl(Object)} method. 195 * 196 * <h5 class='figure'>Example:</h5> 197 * <p class='bjava'> 198 * <jc>// Create a client where all URIs are relative to localhost.</jc> 199 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().rootUrl(<js>"http://localhost:5000"</js>).build(); 200 * 201 * <jc>// Use relative paths.</jc> 202 * String <jv>body</jv> = <jv>client</jv>.get(<js>"/subpath"</js>).run().getContent().asString(); 203 * </p> 204 * 205 * <p> 206 * The {@link RestClient} class creates {@link RestRequest} objects using the following methods: 207 * 208 * <ul class='javatree'> 209 * <li class='jc'>{@link RestClient} 210 * <ul> 211 * <li class='jm'>{@link RestClient#get(Object) get(uri)} / {@link RestClient#get() get()} 212 * <li class='jm'>{@link RestClient#put(Object,Object) put(uri,body)} / {@link RestClient#put(Object) put(uri)} 213 * <li class='jm'>{@link RestClient#post(Object) post(uri,body)} / {@link RestClient#post(Object) post(uri)} 214 * <li class='jm'>{@link RestClient#patch(Object,Object) patch(uri,body)} / {@link RestClient#patch(Object) patch(uri)} 215 * <li class='jm'>{@link RestClient#delete(Object) delete(uri)} 216 * <li class='jm'>{@link RestClient#head(Object) head(uri)} 217 * <li class='jm'>{@link RestClient#options(Object) options(uri)} 218 * <li class='jm'>{@link RestClient#formPost(Object,Object) formPost(uri,body)} / {@link RestClient#formPost(Object) formPost(uri)} 219 * <li class='jm'>{@link RestClient#formPostPairs(Object,String...) formPostPairs(uri,parameters...)} 220 * <li class='jm'>{@link RestClient#request(String,Object,Object) request(method,uri,body)} 221 * </ul> 222 * </ul> 223 * 224 * <p> 225 * The {@link RestRequest} class creates {@link RestResponse} objects using the following methods: 226 * 227 * <ul class='javatree'> 228 * <li class='jc'>{@link RestRequest} 229 * <ul> 230 * <li class='jm'>{@link RestRequest#run() run()} 231 * <li class='jm'>{@link RestRequest#complete() complete()} 232 * </ul> 233 * </ul> 234 * 235 * <p> 236 * The distinction between the two methods is that {@link RestRequest#complete() complete()} automatically consumes the response body and 237 * {@link RestRequest#run() run()} does not. Note that you must consume response bodies in order for HTTP connections to be freed up 238 * for reuse! The {@link InputStream InputStreams} returned by the {@link ResponseContent} object are auto-closing once 239 * they are exhausted, so it is often not necessary to explicitly close them. 240 * 241 * <p> 242 * The following examples show the distinction between the two calls: 243 * 244 * <p class='bjava'> 245 * <jc>// Consuming the response, so use run().</jc> 246 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().asString(); 247 * 248 * <jc>// Only interested in response status code, so use complete().</jc> 249 * <jk>int</jk> <jv>status</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); 250 * </p> 251 * 252 * 253 * <h4 class='topic'>POJO Marshalling</h4> 254 * 255 * <p> 256 * By default, JSON support is provided for HTTP request and response bodies. 257 * Other languages can be specified using any of the following builder methods: 258 * <ul class='javatree'> 259 * <li class='jc'>{@link Builder} 260 * <ul> 261 * <li class='jm'>{@link Builder#json() json()} 262 * <li class='jm'>{@link Builder#json5() json5()} 263 * <li class='jm'>{@link Builder#xml() xml()} 264 * <li class='jm'>{@link Builder#html() html()} 265 * <li class='jm'>{@link Builder#plainText() plainText()} 266 * <li class='jm'>{@link Builder#bson() bson()} 267 * <li class='jm'>{@link Builder#msgPack() msgPack()} 268 * <li class='jm'>{@link Builder#uon() uon()} 269 * <li class='jm'>{@link Builder#urlEnc() urlEnc()} 270 * <li class='jm'>{@link Builder#openApi() openApi()} 271 * </ul> 272 * </ul> 273 * 274 * <h5 class='figure'>Example:</h5> 275 * <p class='bjava'> 276 * <jc>// Create a basic REST client with JSON 5 support.</jc> 277 * <jc>// Typically easier to use when performing unit tests.</jc> 278 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 279 * </p> 280 * 281 * <p> 282 * Clients can also support multiple languages: 283 * 284 * <h5 class='figure'>Example:</h5> 285 * <p class='bjava'> 286 * <jc>// Create a REST client with support for multiple languages.</jc> 287 * RestClient <jv>client1</jv> = RestClient.<jsm>create</jsm>().json().xml().openApi().build(); 288 * 289 * <jc>// Create a REST client with support for all supported languages.</jc> 290 * RestClient <jv>client2</jv> = RestClient.<jsm>create</jsm>().universal().build(); 291 * </p> 292 * 293 * <p> 294 * When using clients with multiple language support, you must specify the <c>Content-Type</c> header on requests 295 * with bodies to specify which serializer should be selected. 296 * 297 * <p class='bjava'> 298 * <jc>// Create a REST client with support for multiple languages.</jc> 299 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().universal().build(); 300 * 301 * <jv>client</jv> 302 * .post(<jsf>URI</jsf>, <jv>myBean</jv>) 303 * .contentType(<js>"application/json"</js>) 304 * .complete() 305 * .assertStatus().is(200); 306 * </p> 307 * 308 * <p> 309 * Languages can also be specified per-request. 310 * 311 * <p class='bjava'> 312 * <jc>// Create a REST client with no default languages supported.</jc> 313 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().build(); 314 * 315 * <jc>// Use JSON for this request.</jc> 316 * <jv>client</jv> 317 * .post(<jsf>URI</jsf>, <jv>myBean</jv>) 318 * .json() 319 * .complete() 320 * .assertStatus().is(200); 321 * </p> 322 * 323 * 324 * <p> 325 * The {@link Builder} class provides convenience methods for setting common serializer and parser 326 * settings. 327 * 328 * <h5 class='figure'>Example:</h5> 329 * <p class='bjava'> 330 * <jc>// Create a basic REST client with JSON support.</jc> 331 * <jc>// Use single-quotes and whitespace.</jc> 332 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().sq().ws().build(); 333 * </p> 334 * 335 * <p> 336 * Other methods are also provided for specifying the serializers and parsers used for lower-level marshalling support: 337 * <ul class='javatree'> 338 * <li class='jc'>{@link Builder} 339 * <ul> 340 * <li class='jm'>{@link Builder#serializer(Serializer) serializer(Serializer)} 341 * <li class='jm'>{@link Builder#parser(Parser) parser(Parser)} 342 * <li class='jm'>{@link Builder#marshaller(Marshaller) marshaller(Marshaller)} 343 * </ul> 344 * </ul> 345 * 346 * <p> 347 * HTTP parts (headers, query parameters, form data...) are serialized and parsed using the {@link HttpPartSerializer} 348 * and {@link HttpPartParser} APIs. By default, clients are configured to use {@link OpenApiSerializer} and 349 * {@link OpenApiParser}. These can be overridden using the following methods: 350 * <ul class='javatree'> 351 * <li class='jc'>{@link Builder} 352 * <ul> 353 * <li class='jm'>{@link Builder#partSerializer(Class) partSerializer(Class<? extends HttpPartSerializer>)} 354 * <li class='jm'>{@link Builder#partParser(Class) partParser(Class<? extends HttpPartParser>)} 355 * </ul> 356 * </ul> 357 * 358 * 359 * <h4 class='topic'>Request Headers</h4> 360 * <p> 361 * Per-client or per-request headers can be specified using the following methods: 362 * <ul class='javatree'> 363 * <li class='jc'>{@link Builder} 364 * <ul> 365 * <li class='jm'>{@link Builder#headers() headerData()} 366 * <li class='jm'>{@link Builder#header(String,String) header(String,Object)} 367 * <li class='jm'>{@link Builder#header(String,Supplier) header(String,Supplier<?>)} 368 * <li class='jm'>{@link Builder#headers(Header...) headers(Header...)} 369 * <li class='jm'>{@link Builder#headersDefault(Header...) defaultHeaders(Header...)} 370 * </ul> 371 * <li class='jc'>{@link RestRequest} 372 * <ul> 373 * <li class='jm'>{@link RestRequest#header(String,Object) header(String,Object)} 374 * <li class='jm'>{@link RestRequest#headers(Header...) headers(Header...)} 375 * <li class='jm'>{@link RestRequest#headersBean(Object) headersBean(Object)} 376 * <li class='jm'>{@link RestRequest#headerPairs(String...) headerPairs(String...)} 377 * </ul> 378 * </ul> 379 * 380 * <p> 381 * The supplier methods are particularly useful for header values whose values may change over time (such as <c>Authorization</c> headers 382 * which may need to change every few minutes). 383 * </p> 384 * 385 * <h5 class='figure'>Example:</h5> 386 * <p class='bjava'> 387 * <jc>// Create a client that adds a dynamic Authorization header to every request.</jc> 388 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().header(<js>"Authorization"</js>, ()->getMyAuthToken()).build(); 389 * </p> 390 * 391 * <p> 392 * The {@link HttpPartSchema} API allows you to define OpenAPI schemas to POJO data structures on both requests 393 * and responses. 394 * 395 * <h5 class='figure'>Example:</h5> 396 * <p class='bjava'> 397 * <jc>// Create a client that adds a header "Foo: bar|baz" to every request.</jc> 398 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>() 399 * .header(<js>"Foo"</js>, AList.<jsm>of</jsm>(<js>"bar"</js>,<js>"baz"</js>), <jsf>T_ARRAY_PIPES</jsf>) 400 * .build(); 401 * </p> 402 * 403 * <p> 404 * The methods with {@link ListOperation} parameters allow you to control whether new headers get appended, prepended, or 405 * replace existing headers with the same name. 406 * 407 * <h5 class='section'>Notes:</h5><ul> 408 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>Header</c> or 409 * <c>NameValuePair</c> objects use the values returned by that bean directly. 410 * </ul> 411 * 412 * 413 * <h4 class='topic'>Request Query Parameters</h4> 414 * <p> 415 * Per-client or per-request query parameters can be specified using the following methods: 416 * <ul class='javatree'> 417 * <li class='jc'>{@link Builder} 418 * <ul> 419 * <li class='jm'>{@link Builder#queryData() queryData()} 420 * <li class='jm'>{@link Builder#queryData(String,String) queryData(String,String)} 421 * <li class='jm'>{@link Builder#queryData(String,Supplier) queryData(String,Supplier<?>)} 422 * <li class='jm'>{@link Builder#queryData(NameValuePair...) queryData(NameValuePair...)} 423 * <li class='jm'>{@link Builder#queryDataDefault(NameValuePair...) defaultQueryData(NameValuePair...)} 424 * </ul> 425 * <li class='jc'>{@link RestRequest} 426 * <ul> 427 * <li class='jm'>{@link RestRequest#queryData(String,Object) queryData(String,Object)} 428 * <li class='jm'>{@link RestRequest#queryData(NameValuePair...) queryData(NameValuePair...)} 429 * <li class='jm'>{@link RestRequest#queryDataBean(Object) queryDataBean(Object)} 430 * <li class='jm'>{@link RestRequest#queryCustom(Object) queryCustom(Object)} 431 * <li class='jm'>{@link RestRequest#queryDataPairs(String...) queryDataPairs(String...)} 432 * </ul> 433 * </ul> 434 * 435 * <h5 class='figure'>Example:</h5> 436 * <p class='bjava'> 437 * <jc>// Create a client that adds a ?foo=bar query parameter to every request.</jc> 438 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().query(<js>"foo"</js>, <js>"bar"</js>).build(); 439 * 440 * <jc>// Or do it on every request.</jc> 441 * String <jv>response</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).query(<js>"foo"</js>, <js>"bar"</js>).run().getContent().asString(); 442 * </p> 443 * 444 * <h5 class='section'>Notes:</h5><ul> 445 * <li class='note'>Like header values, dynamic values and OpenAPI schemas are supported. 446 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> 447 * objects use the values returned by that bean directly. 448 * </ul> 449 * 450 * 451 * <h4 class='topic'>Request Form Data</h4> 452 * 453 * <p> 454 * Per-client or per-request form-data parameters can be specified using the following methods: 455 * <ul class='javatree'> 456 * <li class='jc'>{@link Builder} 457 * <ul> 458 * <li class='jm'>{@link Builder#formData() formData()} 459 * <li class='jm'>{@link Builder#formData(String,String) formData(String,String)} 460 * <li class='jm'>{@link Builder#formData(String,Supplier) formData(String,Supplier<?>)} 461 * <li class='jm'>{@link Builder#formData(NameValuePair...) formDatas(NameValuePair...)} 462 * <li class='jm'>{@link Builder#formDataDefault(NameValuePair...) defaultFormData(NameValuePair...)} 463 * </ul> 464 * <li class='jc'>{@link RestRequest} 465 * <ul> 466 * <li class='jm'>{@link RestRequest#formData(String,Object) formData(String,Object)} 467 * <li class='jm'>{@link RestRequest#formData(NameValuePair...) formData(NameValuePair...)} 468 * <li class='jm'>{@link RestRequest#formDataBean(Object) formDataBean(Object)} 469 * <li class='jm'>{@link RestRequest#formDataCustom(Object) formDataCustom(Object)} 470 * <li class='jm'>{@link RestRequest#formDataPairs(String...) formDataPairs(String...)} 471 * </ul> 472 * </ul> 473 * 474 * <h5 class='section'>Notes:</h5><ul> 475 * <li class='note'>Like header values, dynamic values and OpenAPI schemas are supported. 476 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> 477 * objects use the values returned by that bean directly. 478 * </ul> 479 * 480 * 481 * <h4 class='topic'>Request Body</h4> 482 * 483 * <p> 484 * The request body can either be passed in with the client creator method (e.g. {@link RestClient#post(Object,Object) post(uri,body)}), 485 * or can be specified via the following methods: 486 * 487 * <ul class='javatree'> 488 * <li class='jc'>{@link RestRequest} 489 * <ul> 490 * <li class='jm'>{@link RestRequest#content(Object) body(Object)} 491 * <li class='jm'>{@link RestRequest#content(Object,HttpPartSchema) body(Object,HttpPartSchema)} 492 * </ul> 493 * </ul> 494 * 495 * <p> 496 * The request body can be any of the following types: 497 * <ul class='javatree'> 498 * <li class='jc'> 499 * {@link Object} - POJO to be converted to text using the {@link Serializer} defined on the client or request. 500 * <li class='jc'> 501 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 502 * <li class='jc'> 503 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 504 * <li class='jc'> 505 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 506 * <li class='jc'> 507 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 508 * <li class='jc'> 509 * {@link PartList} - Converted to a URL-encoded FORM post. 510 * <li class='jc'> 511 * {@link Supplier} - A supplier of anything on this list. 512 * </ul> 513 * 514 * <h5 class='section'>Notes:</h5><ul> 515 * <li class='note'>If the serializer on the client or request is explicitly set to <jk>null</jk>, POJOs will be converted to strings 516 * using the registered part serializer as content type <js>"text/plain</js>. If the part serializer is also <jk>null</jk>, 517 * POJOs will be converted to strings using {@link ClassMeta#toString(Object)} which typically just calls {@link Object#toString()}. 518 * </ul> 519 * 520 * 521 * <h4 class='topic'>Response Status</h4> 522 * 523 * <p> 524 * After execution using {@link RestRequest#run()} or {@link RestRequest#complete()}, the following methods can be used 525 * to get the response status: 526 * 527 * <ul class='javatree'> 528 * <li class='jc'>{@link RestResponse} 529 * <ul> 530 * <li class='jm'><c>{@link RestResponse#getStatusLine() getStatusLine()} <jk>returns</jk> {@link StatusLine}</c> 531 * <li class='jm'><c>{@link RestResponse#getStatusCode() getStatusCode()} <jk>returns</jk> <jk>int</jk></c> 532 * <li class='jm'><c>{@link RestResponse#getReasonPhrase() getReasonPhrase()} <jk>returns</jk> String</c> 533 * <li class='jm'><c>{@link RestResponse#assertStatus() assertStatus()} <jk>returns</jk> {@link FluentResponseStatusLineAssertion}</c> 534 * </ul> 535 * </ul> 536 * 537 * <h5 class='figure'>Example:</h5> 538 * <p class='bjava'> 539 * <jc>// Only interested in status code.</jc> 540 * <jk>int</jk> <jv>statusCode</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); 541 * </p> 542 * 543 * <p> 544 * Equivalent methods with mutable parameters are provided to allow access to status values without breaking fluent call chains. 545 * 546 * <h5 class='figure'>Example:</h5> 547 * <p class='bjava'> 548 * <jc>// Interested in multiple values.</jc> 549 * Holder<Integer> <jv>statusCode</jv> = Holder.<jsm>create</jsm>(); 550 * Holder<String> <jv>reasonPhrase</jv> = Holder.<jsm>create</jsm>(); 551 * 552 * <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(<jv>statusCode</jv>).getReasonPhrase(<jv>reasonPhrase</jv>); 553 * System.<jsf>err</jsf>.println(<js>"statusCode="</js>+<jv>statusCode</jv>.get()+<js>", reasonPhrase="</js>+<jv>reasonPhrase</jv>.get()); 554 * </p> 555 * 556 * <h5 class='section'>Notes:</h5><ul> 557 * <li class='note'>If you are only interested in the response status and not the response body, be sure to use {@link RestRequest#complete()} instead 558 * of {@link RestRequest#run()} to make sure the response body gets automatically cleaned up. Otherwise you must 559 * consume the response yourself. 560 * </ul> 561 * 562 * <p> 563 * The assertion method is provided for quickly asserting status codes in fluent calls. 564 * 565 * <h5 class='figure'>Example:</h5> 566 * <p class='bjava'> 567 * <jc>// Status assertion using a static value.</jc> 568 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 569 * .run() 570 * .assertStatus().asCode().isBetween(200,399) 571 * .getContent().asString(); 572 * 573 * <jc>// Status assertion using a predicate.</jc> 574 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 575 * .run() 576 * .assertStatus().asCode().is(<jv>x</jv> -> <jv>x</jv><400) 577 * .getContent().asString(); 578 * </p> 579 * 580 * 581 * <h4 class='topic'>Response Headers</h4> 582 * 583 * <p> 584 * Response headers are accessed through the following methods: 585 * 586 * <ul class='javatree'> 587 * <li class='jc'>{@link RestResponse} 588 * <ul> 589 * <li class='jm'><c>{@link RestResponse#getHeaders(String) getHeaders(String)} <jk>returns</jk> {@link ResponseHeader}[]</c> 590 * <li class='jm'><c>{@link RestResponse#getFirstHeader(String) getFirstHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> 591 * <li class='jm'><c>{@link RestResponse#getLastHeader(String) getLastHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> 592 * <li class='jm'><c>{@link RestResponse#getAllHeaders() getAllHeaders()} <jk>returns</jk> {@link ResponseHeader}[]</c> 593 * <li class='jm'><c>{@link RestResponse#getStringHeader(String) getStringHeader(String)} <jk>returns</jk> String</c> 594 * <li class='jm'><c>{@link RestResponse#containsHeader(String) containsHeader(String)} <jk>returns</jk> <jk>boolean</jk></c> 595 * </ul> 596 * </ul> 597 * 598 * <p> 599 * The {@link RestResponse#getFirstHeader(String)} and {@link RestResponse#getLastHeader(String)} methods return an empty {@link ResponseHeader} object instead of<jk>null</jk>. 600 * This allows it to be used more easily in fluent calls. 601 * 602 * <h5 class='figure'>Example:</h5> 603 * <p class='bjava'> 604 * <jc>// See if response contains Location header.</jc> 605 * <jk>boolean</jk> <jv>hasLocationHeader</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getLastHeader(<js>"Location"</js>).exists(); 606 * </p> 607 * 608 * <p> 609 * The {@link ResponseHeader} class extends from the HttpClient {@link Header} class and provides several convenience 610 * methods: 611 * 612 * <ul class='javatree'> 613 * <li class='jc'>{@link ResponseHeader} 614 * <ul> 615 * <li class='jm'><c>{@link ResponseHeader#isPresent() isPresent()} <jk>returns</jk> <jk>boolean</jk></c> 616 * <li class='jm'><c>{@link ResponseHeader#asString() asString()} <jk>returns</jk> String</c> 617 * <li class='jm'><c>{@link ResponseHeader#as(Type,Type...) as(Type,Type...)} <jk>returns</jk> T</c> 618 * <li class='jm'><c>{@link ResponseHeader#as(Class) as(Class<T>)} <jk>returns</jk> T</c> 619 * <li class='jm'><c>{@link ResponseHeader#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> 620 * <li class='jm'><c>{@link ResponseHeader#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> 621 * <li class='jm'><c>{@link ResponseHeader#asHeader(Class) asHeader(Class<T <jk>extends</jk> BasicHeader> c)} <jk>returns</jk> {@link BasicHeader}</c> 622 * <li class='jm'><c>{@link ResponseHeader#asStringHeader() asStringHeader()} <jk>returns</jk> {@link BasicStringHeader}</c> 623 * <li class='jm'><c>{@link ResponseHeader#asIntegerHeader() asIntegerHeader()} <jk>returns</jk> {@link BasicIntegerHeader}</c> 624 * <li class='jm'><c>{@link ResponseHeader#asLongHeader() asLongHeader()} <jk>returns</jk> {@link BasicLongHeader}</c> 625 * <li class='jm'><c>{@link ResponseHeader#asDateHeader() asDateHeader()} <jk>returns</jk> {@link BasicDateHeader}</c> 626 * <li class='jm'><c>{@link ResponseHeader#asCsvHeader() asCsvHeader()} <jk>returns</jk> {@link BasicCsvHeader}</c> 627 * <li class='jm'><c>{@link ResponseHeader#asEntityTagsHeader() asEntityTagsHeader()} <jk>returns</jk> {@link BasicEntityTagsHeader}</c> 628 * <li class='jm'><c>{@link ResponseHeader#asStringRangesHeader() asStringRangesHeader()} <jk>returns</jk> {@link BasicStringRangesHeader}</c> 629 * <li class='jm'><c>{@link ResponseHeader#asUriHeader() asUriHeader()} <jk>returns</jk> {@link BasicUriHeader}</c> 630 * </ul> 631 * </ul> 632 * 633 * <p> 634 * The {@link ResponseHeader#schema(HttpPartSchema)} method allows you to perform parsing of OpenAPI formats for 635 * header parts. 636 * 637 * <h5 class='figure'>Example:</h5> 638 * <p class='bjava'> 639 * <jc>// Parse the header "Foo: bar|baz".</jc> 640 * List<String> <jv>fooHeader</jv> = <jv>client</jv> 641 * .get(<jsf>URI</jsf>) 642 * .complete() 643 * .getHeader(<js>"Foo"</js>).schema(<jsf>T_ARRAY_PIPES</jsf>).as(List.<jk>class</jk>, String.<jk>class</jk>); 644 * </p> 645 * 646 * <p> 647 * Assertion methods are also provided for fluent-style calls: 648 * 649 * <ul class='javatree'> 650 * <li class='jc'>{@link ResponseHeader} 651 * <ul> 652 * <li class='jm'><c>{@link ResponseHeader#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseHeaderAssertion}</c> 653 * </ul> 654 * </ul> 655 * 656 * <p> 657 * Note how in the following example, the fluent assertion returns control to the {@link RestResponse} object after 658 * the assertion has been completed: 659 * 660 * <h5 class='figure'>Example:</h5> 661 * <p class='bjava'> 662 * <jc>// Assert the response content type is any sort of JSON.</jc> 663 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 664 * .run() 665 * .getHeader(<js>"Content-Type"</js>).assertValue().matchesSimple(<js>"application/json*"</js>) 666 * .getContent().asString(); 667 * </p> 668 * 669 * 670 * <h4 class='topic'>Response Body</h4> 671 * 672 * <p> 673 * The response body is accessed through the following method: 674 * 675 * <ul class='javatree'> 676 * <li class='jc'>{@link RestResponse} 677 * <ul> 678 * <li class='jm'><c>{@link RestResponse#getContent() getContent()} <jk>returns</jk> {@link ResponseContent}</c> 679 * </ul> 680 * </ul> 681 * 682 * <p> 683 * The {@link ResponseContent} class extends from the HttpClient {@link HttpEntity} class and provides several convenience 684 * methods: 685 * 686 * <ul class='javatree'> 687 * <li class='jc'>{@link ResponseContent} 688 * <ul> 689 * <li class='jm'><c>{@link ResponseContent#asInputStream() asInputStream()} <jk>returns</jk> InputStream</c> 690 * <li class='jm'><c>{@link ResponseContent#asReader() asReader()} <jk>returns</jk> Reader</c> 691 * <li class='jm'><c>{@link ResponseContent#asReader(Charset) asReader(Charset)} <jk>returns</jk> Reader</c> 692 * <li class='jm'><c>{@link ResponseContent#pipeTo(OutputStream) pipeTo(OutputStream)} <jk>returns</jk> {@link RestResponse}</c> 693 * <li class='jm'><c>{@link ResponseContent#pipeTo(Writer) pipeTo(Writer)} <jk>returns</jk> {@link RestResponse}</c> 694 * <li class='jm'><c>{@link ResponseContent#as(Type,Type...) as(Type,Type...)} <jk>returns</jk> T</c> 695 * <li class='jm'><c>{@link ResponseContent#as(Class) as(Class<T>)} <jk>returns</jk> T</c> 696 * <li class='jm'><c>{@link ResponseContent#asFuture(Class) asFuture(Class<T>)} <jk>returns</jk> Future<T></c> 697 * <li class='jm'><c>{@link ResponseContent#asFuture(Type,Type...) asFuture(Type,Type...)} <jk>returns</jk> Future<T></c> 698 * <li class='jm'><c>{@link ResponseContent#asString() asString()} <jk>returns</jk> String</c> 699 * <li class='jm'><c>{@link ResponseContent#asStringFuture() asStringFuture()} <jk>returns</jk> Future<String></c> 700 * <li class='jm'><c>{@link ResponseContent#asAbbreviatedString(int) asAbbreviatedString(int)} <jk>returns</jk> String</c> 701 * <li class='jm'><c>{@link ResponseContent#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> 702 * <li class='jm'><c>{@link ResponseContent#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> 703 * </ul> 704 * </ul> 705 * 706 * <br> 707 * 708 * <h5 class='figure'>Examples:</h5> 709 * <p class='bjava'> 710 * <jc>// Parse into a linked-list of strings.</jc> 711 * List<String> <jv>list1</jv> = <jv>client</jv> 712 * .get(<jsf>URI</jsf>) 713 * .run() 714 * .getContent().as(LinkedList.<jk>class</jk>, String.<jk>class</jk>); 715 * 716 * <jc>// Parse into a linked-list of beans.</jc> 717 * List<MyBean> <jv>list2</jv> = <jv>client</jv> 718 * .get(<jsf>URI</jsf>) 719 * .run() 720 * .getContent().as(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 721 * 722 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 723 * List<List<String>> <jv>list3</jv> = <jv>client</jv> 724 * .get(<jsf>URI</jsf>) 725 * .run() 726 * .getContent().as(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 727 * 728 * <jc>// Parse into a map of string keys/values.</jc> 729 * Map<String,String> <jv>map1</jv> = <jv>client</jv> 730 * .get(<jsf>URI</jsf>) 731 * .run() 732 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 733 * 734 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 735 * Map<String,List<MyBean>> <jv>map2</jv> = <jv>client</jv> 736 * .get(<jsf>URI</jsf>) 737 * .run() 738 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 739 * </p> 740 * 741 * <p> 742 * The response body can only be consumed once unless it has been cached into memory. In many cases, the body is 743 * automatically cached when using the assertions methods or methods such as {@link ResponseContent#asString()}. 744 * However, methods that involve reading directly from the input stream cannot be called twice. 745 * In these cases, the {@link RestResponse#cacheContent()} and {@link ResponseContent#cache()} methods are provided 746 * to cache the response body in memory so that you can perform several operations against it. 747 * 748 * <p class='bjava'> 749 * <jc>// Cache the response body so we can access it twice.</jc> 750 * InputStream <jv>inputStream</jv> = <jv>client</jv> 751 * .get(<jsf>URI</jsf>) 752 * .run() 753 * .cacheBody() 754 * .getContent().pipeTo(<jv>someOtherStream</jv>) 755 * .getContent().asInputStream(); 756 * </p> 757 * 758 * <p> 759 * Assertion methods are also provided for fluent-style calls: 760 * 761 * <ul class='javatree'> 762 * <li class='jc'>{@link ResponseContent} 763 * <ul> 764 * <li class='jm'><c>{@link ResponseContent#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseBodyAssertion}</c> 765 * </ul> 766 * </ul> 767 * 768 * <br> 769 * 770 * <h5 class='figure'>Example:</h5> 771 * <p class='bjava'> 772 * <jc>// Assert that the body contains the string "Success".</jc> 773 * String <jv>body</jv> = <jv>client</jv> 774 * .get(<jsf>URI</jsf>) 775 * .run() 776 * .getContent().assertString().contains(<js>"Success"</js>) 777 * .getContent().asString(); 778 * </p> 779 * 780 * <p> 781 * Object assertions allow you to parse the response body into a POJO and then perform various tests on that resulting 782 * POJO. 783 * 784 * <h5 class='figure'>Example:</h5> 785 * <p class='bjava'> 786 * <jc>// Parse bean into POJO and then validate that it was parsed correctly.</jc> 787 * MyBean <jv>bean</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 788 * .run() 789 * .getContent().assertObject(MyBean.<jk>class</jk>).asJson().is(<js>"{foo:'bar'}"</js>) 790 * .getContent().as(MyBean.<jk>class</jk>); 791 * </p> 792 * 793 * 794 * <h4 class='topic'>Custom Call Handlers</h4> 795 * 796 * <p> 797 * The {@link RestCallHandler} interface provides the ability to provide custom handling of requests. 798 * 799 * <ul class='javatree'> 800 * <li class='jc'>{@link Builder} 801 * <ul> 802 * <li class='jm'>{@link Builder#callHandler() callHandler()} 803 * </ul> 804 * <li class='jic'>{@link RestCallHandler} 805 * <ul> 806 * <li class='jm'><c>{@link RestCallHandler#run(HttpHost,HttpRequest,HttpContext) run(HttpHost,HttpRequest,HttpContext)} <jk>returns</jk> HttpResponse</c> 807 * </ul> 808 * </ul> 809 * 810 * <p> 811 * Note that there are other ways of accomplishing this such as extending the {@link RestClient} class and overriding 812 * the {@link #run(HttpHost,HttpRequest,HttpContext)} method 813 * or by defining your own {@link HttpRequestExecutor}. Using this interface is often simpler though. 814 * 815 * 816 * <h4 class='topic'>Interceptors</h4> 817 * 818 * <p> 819 * The {@link RestCallInterceptor} API provides a quick way of intercepting and manipulating requests and responses beyond 820 * the existing {@link HttpRequestInterceptor} and {@link HttpResponseInterceptor} APIs. 821 * 822 * <ul class='javatree'> 823 * <li class='jc'>{@link Builder} 824 * <ul> 825 * <li class='jm'>{@link Builder#interceptors(Object...) interceptors(Object...)} 826 * </ul> 827 * <li class='jc'>{@link RestRequest} 828 * <ul> 829 * <li class='jm'>{@link RestRequest#interceptors(RestCallInterceptor...) interceptors(RestCallInterceptor...)} 830 * </ul> 831 * <li class='jic'>{@link RestCallInterceptor} 832 * <ul> 833 * <li class='jm'>{@link RestCallInterceptor#onInit(RestRequest) onInit(RestRequest)} 834 * <li class='jm'>{@link RestCallInterceptor#onConnect(RestRequest,RestResponse) onConnect(RestRequest,RestResponse)} 835 * <li class='jm'>{@link RestCallInterceptor#onClose(RestRequest,RestResponse) onClose(RestRequest,RestResponse)} 836 * </ul> 837 * </ul> 838 * 839 * 840 * <h4 class='topic'>Logging / Debugging</h4> 841 * 842 * <p> 843 * The following methods provide logging of requests and responses: 844 * 845 * <ul class='javatree'> 846 * <li class='jc'>{@link Builder} 847 * <ul> 848 * <li class='jm'>{@link Builder#logger(Logger) logger(Logger)} 849 * <li class='jm'>{@link Builder#logToConsole() logToConsole()} 850 * <li class='jm'>{@link Builder#logRequests(DetailLevel,Level,BiPredicate) logRequests(DetailLevel,Level,BiPredicate)} 851 * </ul> 852 * </ul> 853 * 854 * <p> 855 * The following example shows the results of logging all requests that end with <c>/bean</c>. 856 * 857 * <h5 class='figure'>Examples:</h5> 858 * <p class='bjava'> 859 * MyBean <jv>bean</jv> = RestClient 860 * .<jsm>create</jsm>() 861 * .json5() 862 * .logRequests(DetailLevel.<jsf>FULL</jsf>, Level.<jsf>SEVERE</jsf>, (<jv>req</jv>,<jv>res</jv>)-><jv>req</jv>.getUri().endsWith(<js>"/bean"</js>)) 863 * .logToConsole() 864 * .build() 865 * .post(<js>"http://localhost/bean"</js>, <jv>anotherBean</jv>) 866 * .run() 867 * .getContent().as(MyBean.<jk>class</jk>); 868 * </p> 869 * 870 * <p> 871 * This produces the following console output: 872 * 873 * <p class='bconsole'> 874 * === HTTP Call (outgoing) ====================================================== 875 * === REQUEST === 876 * POST http://localhost/bean 877 * ---request headers--- 878 * Accept: application/json5 879 * ---request entity--- 880 * Content-Type: application/json5 881 * ---request content--- 882 * {f:1} 883 * === RESPONSE === 884 * HTTP/1.1 200 885 * ---response headers--- 886 * Content-Type: application/json 887 * ---response content--- 888 * {f:1} 889 * === END =======================================================================", 890 * </p> 891 * 892 * 893 * <p class='notes'> 894 * It should be noted that if you enable request logging detail level {@link DetailLevel#FULL}, response bodies will be cached by default which may introduce 895 * a performance penalty. 896 * 897 * <p> 898 * Additionally, the following method is also provided for enabling debug mode: 899 * 900 * <ul class='javatree'> 901 * <li class='jc'>{@link Builder} 902 * <ul> 903 * <li class='jm'>{@link Builder#debug() debug()} 904 * </ul> 905 * </ul> 906 * 907 * <p> 908 * Enabling debug mode has the following effects: 909 * <ul> 910 * <li>{@link org.apache.juneau.marshall.Context.Builder#debug() Context.Builder#debug()} is enabled. 911 * <li>{@link Builder#detectLeaks()} is enabled. 912 * <li>{@link Builder#logToConsole()} is called. 913 * </ul> 914 * 915 * 916 * <h4 class='topic'>REST Proxies</h4> 917 * 918 * <p> 919 * One of the more powerful features of the REST client class is the ability to produce Java interface proxies against 920 * arbitrary remote REST resources. 921 * 922 * <h5 class='figure'>Example:</h5> 923 * <p class='bjava'> 924 * <jc>// Define a Remote proxy for interacting with a REST interface.</jc> 925 * <ja>@Remote</ja>(path=<js>"/petstore"</js>) 926 * <jk>public interface</jk> PetStore { 927 * 928 * <ja>@RemotePost</ja>(<js>"/pets"</js>) 929 * Pet addPet( 930 * <ja>@Content</ja> CreatePet <jv>pet</jv>, 931 * <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, 932 * <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> 933 * ); 934 * } 935 * 936 * <jc>// Use a RestClient with default JSON 5 support.</jc> 937 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build()) 938 * 939 * PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); 940 * CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); 941 * Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); 942 * </p> 943 * 944 * <p> 945 * The methods to retrieve remote interfaces are: 946 * 947 * <ul class='javatree'> 948 * <li class='jc'>{@link RestClient} 949 * <ul> 950 * <li class='jm'><c>{@link RestClient#getRemote(Class) getRemote(Class<T>)} <jk>returns</jk> T</c> 951 * <li class='jm'><c>{@link RestClient#getRemote(Class,Object) getRemote(Class<T>,Object)} <jk>returns</jk> T</c> 952 * <li class='jm'><c>{@link RestClient#getRemote(Class,Object,Serializer,Parser) getRemote(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> 953 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class) getRrpcInterface(Class<T>)} <jk>returns</jk> T</c> 954 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object) getRrpcInterface(Class<T>,Object)} <jk>returns</jk> T</c> 955 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object,Serializer,Parser) getRrpcInterface(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> 956 * </ul> 957 * </ul> 958 * 959 * <p> 960 * Two basic types of remote interfaces are provided: 961 * 962 * <ul class='spaced-list'> 963 * <li>{@link Remote @Remote}-annotated interfaces. These can be defined against arbitrary external REST resources. 964 * <li>RPC-over-REST interfaces. These are Java interfaces that allow you to make method calls on server-side POJOs. 965 * </ul> 966 * 967 * <p> 968 * Refer to the following documentation on both flavors: 969 * 970 * <ul class='doctree'> 971 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxies">REST Proxies</a> 972 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 973 * </ul> 974 * 975 * <br> 976 * <hr> 977 * <h4 class='topic'>Customizing Apache HttpClient</h4> 978 * 979 * <p> 980 * Several methods are provided for customizing the underlying HTTP client and client builder classes: 981 * <ul class='javatree'> 982 * <li class='jc'>{@link Builder} 983 * <ul> 984 * <li class='jm'>{@link Builder#httpClientBuilder(HttpClientBuilder) httpClientBuilder(HttpClientBuilder)} - Set the client builder yourself. 985 * <li class='jm'>{@link Builder#createHttpClientBuilder() createHttpClientBuilder()} - Override to create the client builder. 986 * <li class='jm'>{@link Builder#createHttpClient() createHttpClient()} - Override to create the client. 987 * <li class='jm'>{@link Builder#createConnectionManager() createConnectionManager()} - Override to create the connection management. 988 * </ul> 989 * </ul> 990 * 991 * <p> 992 * Additionally, all methods on the <c>HttpClientBuilder</c> class have been extended with fluent setters. 993 * 994 * <h5 class='figure'>Example:</h5> 995 * <p class='bjava'> 996 * <jc>// Create a client with customized HttpClient settings.</jc> 997 * MyBean <jv>bean</jv> = RestClient 998 * .<jsm>create</jsm>() 999 * .disableRedirectHandling() 1000 * .connectionManager(<jv>myConnectionManager</jv>) 1001 * .addInterceptorFirst(<jv>myHttpRequestInterceptor</jv>) 1002 * .build(); 1003 * </p> 1004 * 1005 * <p> 1006 * Refer to the {@link HttpClientBuilder HTTP Client Builder API} for more information. 1007 * 1008 * 1009 * <h4 class='topic'>Extending RestClient</h4> 1010 * 1011 * <p> 1012 * The <c>RestClient</c> API has been designed to allow for the ability to be easily extended. 1013 * The following example that overrides the primary run method shows how this can be done. 1014 * 1015 * <h5 class='figure'>Example:</h5> 1016 * <p class='bjava'> 1017 * <jk>public class</jk> MyRestClient <jk>extends</jk> RestClient { 1018 * 1019 * <jc>// Must provide this constructor!</jc> 1020 * <jk>public</jk> MyRestClient(RestClient.Builder<?> <jv>builder</jv>) { 1021 * <jk>super</jk>(<jv>builder</jv>); 1022 * } 1023 * 1024 * <jd>/** Optionally override to customize builder settings before initialization. </jd> 1025 * <ja>@Override</ja> 1026 * <jk>protected void</jk> init(RestClient.Builder<?>) {...} 1027 * 1028 * <jd>/** Optionally override to provide post-initialization (e.g. setting up SAML handshakes, etc...). </jd> 1029 * <ja>@Override</ja> 1030 * <jk>protected void</jk> init() {...} 1031 * 1032 * <jd>/** Optionally override to customize requests when they're created (e.g. add headers to each request). </jd> 1033 * <ja>@Override</ja> 1034 * <jk>protected</jk> RestRequest request(RestOperation) {...} 1035 * 1036 * <jd>/** Optionally override to implement your own call handling. </jd> 1037 * <ja>@Override</ja> 1038 * <jk>protected</jk> HttpResponse run(HttpHost, HttpRequest, HttpContext) {...} 1039 * 1040 * <jd>/** Optionally override to customize requests before they're executed. </jd> 1041 * <ja>@Override</ja> 1042 * <jk>protected void</jk> onCallInit(RestRequest) {...} 1043 * 1044 * <jd>/** Optionally override to customize responses as soon as a connection is made. </jd> 1045 * <ja>@Override</ja> 1046 * <jk>protected void</jk> onCallConnect(RestRequest, RestResponse) {...} 1047 * 1048 * <jd>/** Optionally override to perform any call cleanup. </jd> 1049 * <ja>@Override</ja> 1050 * <jk>protected void</jk> onCallClose(RestRequest, RestResponse) {...} 1051 * } 1052 * 1053 * <jc>// Instantiate your client.</jc> 1054 * MyRestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().build(MyRestClient.<jk>class</jk>); 1055 * </p> 1056 * 1057 * <p> 1058 * The {@link RestRequest} and {@link RestResponse} objects can also be extended and integrated by overriding the 1059 * {@link RestClient#createRequest(URI,String,boolean)} and {@link RestClient#createResponse(RestRequest,HttpResponse,Parser)} methods. 1060 * 1061 * <h5 class='section'>Notes:</h5><ul> 1062 * <li class='note'>This class is thread safe and reusable. 1063 * </ul> 1064 * 1065 * <h5 class='section'>See Also:</h5><ul> 1066 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClient">juneau-rest-client-classic</a> 1067 * </ul> 1068 */ 1069@SuppressWarnings({ 1070 "java:S106", // System.err is the intentional default fallback for the configurable console PrintStream 1071 "java:S112", // Builder.interceptors(Class...), executeRemoteWithRetry(), materializeRemote() and throwRemoteError() declare throws Exception because remote-proxy calls surface arbitrary checked exceptions 1072 "java:S115", // Constants use UPPER_snakeCase naming convention 1073 "java:S1133", // Deprecated override required by HttpClient interface 1074 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 1075 "java:S3776", // buildRemoteRequest(), executeRemoteWithRetry(), callback() and getRrpcInterface() are branch-heavy remote-call orchestration; splitting them would obscure the flow 1076 "java:S6539", // Monster class; RestClient is intentionally a single fluent builder/client aggregating config, call orchestration, and remote-proxy wiring 1077 "rawtypes", // Raw types necessary for generic type handling. 1078 "resource", // Resource management handled externally 1079 "unchecked" // Casts in getInstance() (Class<? extends Serializer/Parser>) and in the getRemote()/getRrpcInterface() Proxy.newProxyInstance results are guarded by isAssignableFrom checks and the proxied interface type 1080}) 1081public class RestClient extends MarshallingContextable implements HttpClient, Closeable { 1082 1083 // Argument name constants for reqnn 1084 private static final String HEADER_ACCEPT = org.apache.juneau.http.header.Accept.NAME; 1085 private static final String HEADER_CONTENT_TYPE = org.apache.juneau.http.header.ContentType.NAME; 1086 1087 /** 1088 * Builder class. 1089 */ 1090 @SuppressWarnings({ 1091 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 1092 }) 1093 public abstract static class Builder<SELF extends Builder<SELF>> extends MarshallingContextable.Builder<SELF> { 1094 1095 private BeanInstantiator.Builder<RestCallHandler> callHandler; 1096 private final WritableBeanStore beanStore = new BasicBeanStore(); 1097 private BiPredicate<RestRequest,RestResponse> logRequestsPredicate; 1098 private boolean detectLeaks; 1099 private boolean allowPrivateUrls; 1100 private final List<String> allowedAddressRanges = new ArrayList<>(); 1101 private boolean executorServiceShutdownOnClose; 1102 private boolean ignoreErrors; 1103 private boolean keepHttpClientOpen; 1104 private boolean logToConsole; 1105 private boolean pooled; 1106 private boolean skipEmptyFormData; 1107 private boolean skipEmptyHeaderData; 1108 private boolean skipEmptyQueryData; 1109 private CloseableHttpClient httpClient; 1110 private DetailLevel logRequests; 1111 private ExecutorService executorService; 1112 private HeaderList headerData; 1113 private HttpClientBuilder httpClientBuilder; 1114 private HttpClientConnectionManager connectionManager; 1115 // The instance the default createConnectionManager() last returned; only that one can be given a pinned twin. 1116 private HttpClientConnectionManager defaultConnectionManager; 1117 // The socket-factory registry and pool settings of defaultConnectionManager, which its pinned twin shares. 1118 private Registry<ConnectionSocketFactory> defaultRegistry; 1119 private PoolSettings defaultPoolSettings; 1120 // TLS and pool settings, applied to both pools of the connection manager the default createConnectionManager() 1121 // builds (HttpClientBuilder ignores its own copies once a connection manager is set). 1122 private HostnameVerifier sslHostnameVerifier; 1123 private LayeredConnectionSocketFactory sslSocketFactory; 1124 private SSLContext sslContext; 1125 private int maxConnPerRoute; 1126 private int maxConnTotal; 1127 private ConnectionConfig defaultConnectionConfig; 1128 private SocketConfig defaultSocketConfig; 1129 private PublicSuffixMatcher publicSuffixMatcher; 1130 private long connTimeToLive; 1131 private TimeUnit connTimeToLiveTimeUnit; 1132 private boolean systemProperties; 1133 // Set by the default createHttpClient() to the two-pool router it installed for SSRF-guard-active @Remote calls. 1134 private SsrfGuardConnectionManager ssrfGuard; 1135 // Set by the default createHttpClient(): whether the default createConnectionManager() (not an override, which may 1136 // hand the same instance to several clients) created both of ssrfGuard's pools, which no caller can then reach, 1137 // so the client shuts them down on close() even when the connection manager is shared. 1138 private boolean ownsConnectionManager; 1139 private HttpPartParser.Creator partParser; 1140 private HttpPartSerializer.Creator partSerializer; 1141 private Level logRequestsLevel; 1142 private Logger logger; 1143 private PartList formData; 1144 private PartList pathData; 1145 private PartList queryData; 1146 private ParserSet.Builder parsers; 1147 private Predicate<Integer> errorCodes = x -> x <= 0 || x >= 400; 1148 private PrintStream console; 1149 private SerializerSet.Builder serializers; 1150 private Supplier<String> rootUrl; 1151 private UrlEncodingSerializer.Builder urlEncodingSerializer; 1152 List<RestCallInterceptor> interceptors; 1153 1154 /** 1155 * Constructor. 1156 */ 1157 protected Builder() {} 1158 1159 /** 1160 * Appends an <c>Accept</c> header on this request. 1161 * 1162 * <p> 1163 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>))</c>. 1164 * 1165 * @param value 1166 * The new header value. 1167 * <br>Can be <jk>null</jk> (no Accept header will be added). 1168 * @return This object. 1169 * @see #headers() 1170 */ 1171 public SELF accept(String value) { 1172 return headers(Accept.of(value)); 1173 } 1174 1175 /** 1176 * Sets the value for the <c>Accept-Charset</c> request header on all requests. 1177 * 1178 * <p> 1179 * This is a shortcut for calling <c>headerData().append(AcceptCharset.<jsm>of</jsm>(<jv>value</jv>))</c>. 1180 * 1181 * @param value The new header value. 1182 * <br>Can be <jk>null</jk> (no Accept-Charset header will be added). 1183 * @return This object. 1184 * @see #headers() 1185 */ 1186 public SELF acceptCharset(String value) { 1187 return headers(AcceptCharset.of(value)); 1188 } 1189 1190 /** 1191 * <i><l>Serializer</l> configuration property: </i> Add <js>"_type"</js> properties when needed. 1192 * 1193 * <p> 1194 * When enabled, <js>"_type"</js> properties will be added to beans if their type cannot be inferred 1195 * through reflection. 1196 * 1197 * <p> 1198 * This is used to recreate the correct objects during parsing if the object types cannot be inferred. 1199 * <br>For example, when serializing a <c>Map<String,Object></c> field where the bean class cannot be determined from 1200 * the type of the values. 1201 * 1202 * <p> 1203 * Note the differences between the following settings: 1204 * <ul class='javatree'> 1205 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 1206 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 1207 * </ul> 1208 * 1209 * <h5 class='section'>Example:</h5> 1210 * <p class='bjava'> 1211 * <jc>// Create a JSON client that adds _type to nodes in the request body.</jc> 1212 * RestClient <jv>client</jv> = RestClient 1213 * .<jsm>create</jsm>() 1214 * .json() 1215 * .addBeanTypes() 1216 * .build(); 1217 * 1218 * <jc>// Our map of beans to serialize.</jc> 1219 * <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>) 1220 * <jk>public class</jk> MyBean { 1221 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1222 * } 1223 * 1224 * AMap <jv>map</jv> = AMap.of(<js>"foo"</js>, <jk>new</jk> MyBean()); 1225 * 1226 * <jc>// Request body will contain: {"foo":{"_type":"mybean","foo":"bar"}}</jc> 1227 * <jv>client</jv> 1228 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 1229 * .run(); 1230 * </p> 1231 * 1232 * <h5 class='section'>See Also:</h5><ul> 1233 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#addBeanTypes() Serializer.Builder#addBeanTypes()} 1234 * </ul> 1235 * 1236 * @return This object. 1237 */ 1238 public SELF addBeanTypes() { 1239 serializers().forEach(Serializer.Builder::addBeanTypes); 1240 return self(); 1241 } 1242 1243 /** 1244 * Adds this protocol interceptor to the head of the protocol processing list. 1245 * 1246 * <h5 class='section'>Notes:</h5><ul> 1247 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1248 * </ul> 1249 * 1250 * @param itcp New property value. 1251 * <br>Cannot be <jk>null</jk>. 1252 * @return This object. 1253 * @see HttpClientBuilder#addInterceptorFirst(HttpRequestInterceptor) 1254 */ 1255 public SELF addInterceptorFirst(HttpRequestInterceptor itcp) { 1256 httpClientBuilder().addInterceptorFirst(reqnn("itcp", itcp)); 1257 return self(); 1258 } 1259 1260 /** 1261 * Adds this protocol interceptor to the head of the protocol processing list. 1262 * 1263 * <h5 class='section'>Notes:</h5><ul> 1264 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1265 * </ul> 1266 * 1267 * @param itcp New property value. 1268 * <br>Cannot be <jk>null</jk>. 1269 * @return This object. 1270 * @see HttpClientBuilder#addInterceptorFirst(HttpResponseInterceptor) 1271 */ 1272 public SELF addInterceptorFirst(HttpResponseInterceptor itcp) { 1273 httpClientBuilder().addInterceptorFirst(reqnn("itcp", itcp)); 1274 return self(); 1275 } 1276 1277 /** 1278 * Adds this protocol interceptor to the tail of the protocol processing list. 1279 * 1280 * <h5 class='section'>Notes:</h5><ul> 1281 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1282 * </ul> 1283 * 1284 * @param itcp New property value. 1285 * <br>Cannot be <jk>null</jk>. 1286 * @return This object. 1287 * @see HttpClientBuilder#addInterceptorLast(HttpRequestInterceptor) 1288 */ 1289 public SELF addInterceptorLast(HttpRequestInterceptor itcp) { 1290 httpClientBuilder().addInterceptorLast(reqnn("itcp", itcp)); 1291 return self(); 1292 } 1293 1294 /** 1295 * Adds this protocol interceptor to the tail of the protocol processing list. 1296 * 1297 * <h5 class='section'>Notes:</h5><ul> 1298 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1299 * </ul> 1300 * 1301 * @param itcp New property value. 1302 * <br>Cannot be <jk>null</jk>. 1303 * @return This object. 1304 * @see HttpClientBuilder#addInterceptorLast(HttpResponseInterceptor) 1305 */ 1306 public SELF addInterceptorLast(HttpResponseInterceptor itcp) { 1307 httpClientBuilder().addInterceptorLast(reqnn("itcp", itcp)); 1308 return self(); 1309 } 1310 1311 /** 1312 * <i><l>Serializer</l> configuration property: </i> Add type attribute to root nodes. 1313 * 1314 * <p> 1315 * When enabled, <js>"_type"</js> properties will be added to top-level beans. 1316 * 1317 * <p> 1318 * When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level 1319 * type information that might normally be included to determine the data type will not be serialized. 1320 * 1321 * <p> 1322 * For example, when serializing a top-level POJO with a {@link Marshalled#typeName() @Marshalled(typeName)} value, a 1323 * <js>'_type'</js> attribute will only be added when this setting is enabled. 1324 * 1325 * <p> 1326 * Note the differences between the following settings: 1327 * <ul class='javatree'> 1328 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 1329 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 1330 * </ul> 1331 * 1332 * <h5 class='section'>Example:</h5> 1333 * <p class='bjava'> 1334 * <jc>// Create a JSON client that adds _type to root node.</jc> 1335 * RestClient <jv>client</jv> = RestClient 1336 * .<jsm>create</jsm>() 1337 * .json() 1338 * .addRootType() 1339 * .build(); 1340 * 1341 * <jc>// Our bean to serialize.</jc> 1342 * <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>) 1343 * <jk>public class</jk> MyBean { 1344 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1345 * } 1346 * 1347 * <jc>// Request body will contain: {"_type":"mybean","foo":"bar"}</jc> 1348 * <jv>client</jv> 1349 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 1350 * .run(); 1351 * </p> 1352 * 1353 * <h5 class='section'>See Also:</h5><ul> 1354 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#addRootType() Serializer.Builder#addRootType()} 1355 * </ul> 1356 * 1357 * @return This object. 1358 */ 1359 public SELF addRootType() { 1360 serializers().forEach(Serializer.Builder::addRootType); 1361 return self(); 1362 } 1363 1364 /** 1365 * Assigns {@link BackoffManager} instance. 1366 * 1367 * @param backoffManager New property value. 1368 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1369 * @return This object. 1370 * @see HttpClientBuilder#setBackoffManager(BackoffManager) 1371 */ 1372 public SELF backoffManager(BackoffManager backoffManager) { 1373 httpClientBuilder().setBackoffManager(backoffManager); 1374 return self(); 1375 } 1376 1377 /** 1378 * Set up this client to use BASIC auth. 1379 * 1380 * <h5 class='section'>Example:</h5> 1381 * <p class='bjava'> 1382 * <jc>// Construct a client that uses BASIC authentication.</jc> 1383 * RestClient <jv>client</jv> = RestClient 1384 * .<jsm>create</jsm>() 1385 * .basicAuth(<js>"http://localhost"</js>, 80, <js>"me"</js>, <js>"mypassword"</js>) 1386 * .build(); 1387 * </p> 1388 * 1389 * @param host The auth scope hostname. 1390 * <br>Can be <jk>null</jk> (will use <js>"*"</js> as the hostname). 1391 * @param port The auth scope port. 1392 * @param user The username. 1393 * <br>Can be <jk>null</jk> (will use empty string). 1394 * @param pw The password. 1395 * <br>Can be <jk>null</jk> (will use empty string). 1396 * @return This object. 1397 */ 1398 public SELF basicAuth(String host, int port, String user, String pw) { 1399 var scope = new AuthScope(host, port); 1400 var up = new UsernamePasswordCredentials(user, pw); 1401 var p = new BasicCredentialsProvider(); 1402 p.setCredentials(scope, up); 1403 defaultCredentialsProvider(p); 1404 return self(); 1405 } 1406 1407 @Override /* Overridden from Context.Builder<?> */ 1408 public RestClient build() { 1409 // Validated here, ahead of the reflective construction, so a bad entry surfaces as the IllegalArgumentException itself. 1410 RemoteUrlPolicy.AllowedRanges.of(allowedAddressRanges.toArray(new String[0])); 1411 return build(RestClient.class); 1412 } 1413 1414 /** 1415 * Returns the creator for the rest call handler. 1416 * 1417 * <p> 1418 * Allows you to provide a custom handler for making HTTP calls. 1419 * 1420 * <h5 class='section'>Example:</h5> 1421 * <p class='bjava'> 1422 * <jc>// Create a client that handles processing of requests using a custom handler.</jc> 1423 * <jk>public class</jk> MyRestCallHandler <jk>implements</jk> RestCallHandler { 1424 * 1425 * <ja>@Override</ja> 1426 * <jk>public</jk> HttpResponse run(HttpHost <jv>target</jv>, HttpRequest <jv>request</jv>, HttpContext <jv>context</jv>) <jk>throws</jk> IOException { 1427 * <jc>// Custom handle requests.</jc> 1428 * } 1429 * } 1430 * 1431 * RestClient <jv>client</jv> = RestClient 1432 * .<jsm>create</jsm>() 1433 * .callHandler(MyRestCallHandler.<jk>class</jk>) 1434 * .build(); 1435 * </p> 1436 * 1437 * <h5 class='section'>Notes:</h5><ul> 1438 * <li class='warn'>The SSRF guard of {@code @Remote} calls travels on the request's {@link HttpContext}, and a 1439 * custom handler may copy the request, or send it with another context, without it. So a client with any handler other than 1440 * {@link BasicRestCallHandler} itself, or with a {@code BasicRestCallHandler} bound to a different client 1441 * (through {@code impl(...)}), refuses every SSRF-guard-active {@code @Remote} call (one without the 1442 * {@link #allowPrivateUrls(boolean) allowPrivateUrls} opt-in). Ordinary requests are unaffected. 1443 * <li class='note'> 1444 * The {@link RestClient#run(HttpHost, HttpRequest, HttpContext)} method can also be overridden to produce the same results. 1445 * The same SSRF restriction applies: a client whose class overrides that method or either 1446 * {@code execute(...)} overload it dispatches to refuses SSRF-guard-active {@code @Remote} calls too. 1447 * <li class='note'> 1448 * Use {@link org.apache.juneau.commons.inject.BeanInstantiator.Builder BeanInstantiator.Builder} <c>impl(Object)</c> to specify an already instantiated instance. 1449 * <li class='note'> 1450 * Use {@link org.apache.juneau.commons.inject.BeanInstantiator.Builder BeanInstantiator.Builder} <c>type(Class)</c> to specify a subtype to instantiate. 1451 * <br>Subclass must have a public constructor that takes in any args available 1452 * in the bean store of this builder (including {@link RestClient} itself). 1453 * </ul> 1454 * 1455 * <h5 class='section'>See Also:</h5><ul> 1456 * <li class='jic'>{@link RestCallHandler} 1457 * </ul> 1458 * 1459 * @return The instantiator for the rest call handler. 1460 */ 1461 public final BeanInstantiator.Builder<RestCallHandler> callHandler() { 1462 if (callHandler == null) 1463 callHandler = createCallHandler(); 1464 return callHandler; 1465 } 1466 1467 /** 1468 * REST call handler class. 1469 * 1470 * <p> 1471 * Specifies a custom handler for making HTTP calls. 1472 * 1473 * <p> 1474 * This is a shortcut for <c>callHandler().type(<jv>value</jv>)</c>. 1475 * 1476 * @param value 1477 * The new value for this setting. 1478 * <br>Cannot be <jk>null</jk>. 1479 * @return This object. 1480 * @see #callHandler() 1481 */ 1482 public SELF callHandler(Class<? extends RestCallHandler> value) { 1483 callHandler().type(reqnn("value", value)); 1484 return self(); 1485 } 1486 1487 /** 1488 * Sets the client version by setting the value for the <js>"Client-Version"</js> header. 1489 * 1490 * <p> 1491 * This is a shortcut for calling <c>headerData().append(ClientVersion.<jsm>of</jsm>(<jv>value</jv>))</c>. 1492 * 1493 * @param value The version string (e.g. <js>"1.2.3"</js>) 1494 * <br>Can be <jk>null</jk> (no Client-Version header will be added). 1495 * @return This object. 1496 * @see #headers() 1497 */ 1498 public SELF clientVersion(String value) { 1499 return headers(ClientVersion.of(value)); 1500 } 1501 1502 /** 1503 * Assigns {@link ConnectionBackoffStrategy} instance. 1504 * 1505 * @param connectionBackoffStrategy New property value. 1506 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1507 * @return This object. 1508 * @see HttpClientBuilder#setConnectionBackoffStrategy(ConnectionBackoffStrategy) 1509 */ 1510 public SELF connectionBackoffStrategy(ConnectionBackoffStrategy connectionBackoffStrategy) { 1511 httpClientBuilder().setConnectionBackoffStrategy(connectionBackoffStrategy); 1512 return self(); 1513 } 1514 1515 /** 1516 * Assigns {@link HttpClientConnectionManager} instance. 1517 * 1518 * <h5 class='section'>Notes:</h5><ul> 1519 * <li class='warn'>This client cannot pin the connections of a connection manager it did not create, so a 1520 * client built with one refuses every SSRF-guard-active {@code @Remote} call (one without the 1521 * {@link #allowPrivateUrls(boolean) allowPrivateUrls} opt-in). Ordinary requests are unaffected. 1522 * </ul> 1523 * 1524 * @param value New property value. 1525 * <br>Can be <jk>null</jk> (value will not be set, a default connection manager will be created). 1526 * @return This object. 1527 * @see HttpClientBuilder#setConnectionManager(HttpClientConnectionManager) 1528 */ 1529 public SELF connectionManager(HttpClientConnectionManager value) { 1530 connectionManager = value; 1531 httpClientBuilder().setConnectionManager(value); 1532 return self(); 1533 } 1534 1535 /** 1536 * Defines the connection manager is to be shared by multiple client instances. 1537 * 1538 * <h5 class='section'>Notes:</h5><ul> 1539 * <li class='note'>If the connection manager is shared its life-cycle is expected to be managed by the caller and it will not be shut down if the client is closed. 1540 * This applies to a connection manager supplied through {@link #connectionManager(HttpClientConnectionManager)} 1541 * or returned by an overridden {@link #createConnectionManager()} (even one that calls {@code super}, since the 1542 * override may hand the same instance to several clients). The connection manager the default 1543 * {@link #createConnectionManager()} builds for a client cannot be reached by the caller, so it is never 1544 * shared: {@link RestClient#close()} shuts it down. 1545 * <li class='note'>The client's internal pool for SSRF-guard-active {@code @Remote} calls (see {@link #createConnectionManager()}) 1546 * is never shared, and {@link RestClient#close()} still shuts it down. Combined with {@link #keepHttpClientOpen()}, 1547 * though, nothing shuts that internal pool (or the default connection manager) down; their connections stay 1548 * open until they are closed as idle or the JVM exits. 1549 * </ul> 1550 * 1551 * @param shared New property value. 1552 * @return This object. 1553 * @see HttpClientBuilder#setConnectionManagerShared(boolean) 1554 */ 1555 public SELF connectionManagerShared(boolean shared) { 1556 httpClientBuilder().setConnectionManagerShared(shared); 1557 return self(); 1558 } 1559 1560 /** 1561 * Assigns {@link ConnectionReuseStrategy} instance. 1562 * 1563 * @param reuseStrategy New property value. 1564 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1565 * @return This object. 1566 * @see HttpClientBuilder#setConnectionReuseStrategy(ConnectionReuseStrategy) 1567 */ 1568 public SELF connectionReuseStrategy(ConnectionReuseStrategy reuseStrategy) { 1569 httpClientBuilder().setConnectionReuseStrategy(reuseStrategy); 1570 return self(); 1571 } 1572 1573 /** 1574 * Sets maximum time to live for persistent connections. 1575 * 1576 * <h5 class='section'>Notes:</h5><ul> 1577 * <li class='note'>It is applied to both pools of the connection manager the default {@link #createConnectionManager()} 1578 * builds. It has no effect on a connection manager supplied through 1579 * {@link #connectionManager(HttpClientConnectionManager)} or an overridden {@link #createConnectionManager()}. 1580 * <li class='note'>A positive value makes that connection manager a {@link PoolingHttpClientConnectionManager}, as 1581 * {@link #pooled()} does: {@link BasicHttpClientConnectionManager} has no time-to-live. 1582 * </ul> 1583 * 1584 * @param connTimeToLive New property value. 1585 * @param connTimeToLiveTimeUnit New property value. 1586 * <br>Cannot be <jk>null</jk>. 1587 * @return This object. 1588 * @see HttpClientBuilder#setConnectionTimeToLive(long,TimeUnit) 1589 */ 1590 public SELF connectionTimeToLive(long connTimeToLive, TimeUnit connTimeToLiveTimeUnit) { 1591 this.connTimeToLive = connTimeToLive; 1592 this.connTimeToLiveTimeUnit = connTimeToLiveTimeUnit; 1593 httpClientBuilder().setConnectionTimeToLive(connTimeToLive, connTimeToLiveTimeUnit); 1594 return self(); 1595 } 1596 1597 /** 1598 * Console print stream 1599 * 1600 * <p> 1601 * Allows you to redirect the console output to a different print stream. 1602 * 1603 * @param value 1604 * The new value for this setting. 1605 * <br>Can be <jk>null</jk> (defaults to <c>System.err</c>). 1606 * @return This object. 1607 */ 1608 public SELF console(PrintStream value) { 1609 console = value; 1610 return self(); 1611 } 1612 1613 /** 1614 * Assigns a map of {@link org.apache.http.client.entity.InputStreamFactory InputStreamFactories} to be used for automatic content decompression. 1615 * 1616 * @param contentDecoderMap New property value. 1617 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1618 * @return This object. 1619 * @see HttpClientBuilder#setContentDecoderRegistry(Map) 1620 */ 1621 public SELF contentDecoderRegistry(Map<String,InputStreamFactory> contentDecoderMap) { 1622 httpClientBuilder().setContentDecoderRegistry(contentDecoderMap); 1623 return self(); 1624 } 1625 1626 /** 1627 * Sets the value for the <c>Content-Type</c> request header on all requests. 1628 * 1629 * <p> 1630 * This is a shortcut for calling <c>headerData().append(ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 1631 * 1632 * <p> 1633 * This overrides the media type specified on the serializer. 1634 * 1635 * @param value The new header value. 1636 * <br>Can be <jk>null</jk> (no Content-Type header will be added). 1637 * @return This object. 1638 * @see #headers() 1639 */ 1640 public SELF contentType(String value) { 1641 return headers(ContentType.of(value)); 1642 } 1643 1644 @Override /* Overridden from Context.Builder<?> */ 1645 public SELF copy() { 1646 throw new NoSuchMethodError("Not implemented."); 1647 } 1648 1649 /** 1650 * Sets the value for the <c>Debug</c> request header on all requests. 1651 * 1652 * <p> 1653 * This is a shortcut for calling <c>headerData().append(Debug.<jsm>of</jsm>(<jv>value</jv>))</c>. 1654 * 1655 * @return This object. 1656 * @see #headers() 1657 */ 1658 @Override 1659 public SELF debug() { 1660 super.debug(); 1661 serializers().forEach(Serializer.Builder::debug); 1662 return headers(Debug.TRUE); 1663 } 1664 1665 /** 1666 * <i><l>Parser</l> configuration property: </i> Debug output lines. 1667 * 1668 * <p> 1669 * When parse errors occur, this specifies the number of lines of input before and after the 1670 * error location to be printed as part of the exception message. 1671 * 1672 * <h5 class='section'>Example:</h5> 1673 * <p class='bjava'> 1674 * <jc>// Create a parser whose exceptions print out 100 lines before and after the parse error location.</jc> 1675 * RestClient <jv>client</jv> = RestClient 1676 * .<jsm>create</jsm>() 1677 * .json() 1678 * .debug() <jc>// Enable debug mode to capture Reader contents as strings.</jc> 1679 * .debugOuputLines(100) 1680 * .build(); 1681 * 1682 * <jc>// Try to parse some bad JSON.</jc> 1683 * <jk>try</jk> { 1684 * <jv>client</jv> 1685 * .get(<js>"/pathToBadJson"</js>) 1686 * .run() 1687 * .getContent().as(Object.<jk>class</jk>); <jc>// Try to parse it.</jc> 1688 * } <jk>catch</jk> (RestCallException <jv>e</jv>) { 1689 * System.<jsf>err</jsf>.println(<jv>e</jv>.getMessage()); <jc>// Will display 200 lines of the output.</jc> 1690 * } 1691 * </p> 1692 * 1693 * <h5 class='section'>See Also:</h5><ul> 1694 * <li class='jm'>{@link org.apache.juneau.marshall.parser.Parser.Builder#debugOutputLines(int) Parser.Builder#debugOutputLines(int)} 1695 * </ul> 1696 * 1697 * @param value 1698 * The new value for this property. 1699 * <br>The default value is <c>5</c>. 1700 * @return This object. 1701 */ 1702 public SELF debugOutputLines(int value) { 1703 parsers().forEach(x -> x.debugOutputLines(value)); 1704 return self(); 1705 } 1706 1707 /** 1708 * Assigns default {@link org.apache.http.auth.AuthScheme} registry which will be used for request execution if not explicitly set in the client execution context. 1709 * 1710 * @param authSchemeRegistry New property value. 1711 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1712 * @return This object. 1713 * @see HttpClientBuilder#setDefaultAuthSchemeRegistry(Lookup) 1714 */ 1715 public SELF defaultAuthSchemeRegistry(Lookup<AuthSchemeProvider> authSchemeRegistry) { 1716 httpClientBuilder().setDefaultAuthSchemeRegistry(authSchemeRegistry); 1717 return self(); 1718 } 1719 1720 /** 1721 * Assigns default {@link ConnectionConfig}. 1722 * 1723 * <h5 class='section'>Notes:</h5><ul> 1724 * <li class='note'>It is applied to both pools of the connection manager the default {@link #createConnectionManager()} 1725 * builds. It has no effect on a connection manager supplied through 1726 * {@link #connectionManager(HttpClientConnectionManager)} or an overridden {@link #createConnectionManager()}. 1727 * </ul> 1728 * 1729 * @param config New property value. 1730 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1731 * @return This object. 1732 * @see HttpClientBuilder#setDefaultConnectionConfig(ConnectionConfig) 1733 */ 1734 public SELF defaultConnectionConfig(ConnectionConfig config) { 1735 this.defaultConnectionConfig = config; 1736 httpClientBuilder().setDefaultConnectionConfig(config); 1737 return self(); 1738 } 1739 1740 /** 1741 * Assigns default {@link CookieSpec} registry which will be used for request execution if not explicitly set in the client execution context. 1742 * 1743 * @param cookieSpecRegistry New property value. 1744 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1745 * @return This object. 1746 * @see HttpClientBuilder#setDefaultCookieSpecRegistry(Lookup) 1747 */ 1748 public SELF defaultCookieSpecRegistry(Lookup<CookieSpecProvider> cookieSpecRegistry) { 1749 httpClientBuilder().setDefaultCookieSpecRegistry(cookieSpecRegistry); 1750 return self(); 1751 } 1752 1753 /** 1754 * Assigns default {@link CookieStore} instance which will be used for request execution if not explicitly set in the client execution context. 1755 * 1756 * @param cookieStore New property value. 1757 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1758 * @return This object. 1759 * @see HttpClientBuilder#setDefaultCookieStore(CookieStore) 1760 */ 1761 public SELF defaultCookieStore(CookieStore cookieStore) { 1762 httpClientBuilder().setDefaultCookieStore(cookieStore); 1763 return self(); 1764 } 1765 1766 /** 1767 * Assigns default {@link CredentialsProvider} instance which will be used for request execution if not explicitly set in the client execution context. 1768 * 1769 * @param credentialsProvider New property value. 1770 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1771 * @return This object. 1772 * @see HttpClientBuilder#setDefaultCredentialsProvider(CredentialsProvider) 1773 */ 1774 public SELF defaultCredentialsProvider(CredentialsProvider credentialsProvider) { 1775 httpClientBuilder().setDefaultCredentialsProvider(credentialsProvider); 1776 return self(); 1777 } 1778 1779 /** 1780 * Assigns default {@link RequestConfig} instance which will be used for request execution if not explicitly set in the client execution context. 1781 * 1782 * @param config New property value. 1783 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1784 * @return This object. 1785 * @see HttpClientBuilder#setDefaultRequestConfig(RequestConfig) 1786 */ 1787 public SELF defaultRequestConfig(RequestConfig config) { 1788 httpClientBuilder().setDefaultRequestConfig(config); 1789 return self(); 1790 } 1791 1792 /** 1793 * Assigns default {@link SocketConfig}. 1794 * 1795 * <h5 class='section'>Notes:</h5><ul> 1796 * <li class='note'>It is applied to both pools of the connection manager the default {@link #createConnectionManager()} 1797 * builds. It has no effect on a connection manager supplied through 1798 * {@link #connectionManager(HttpClientConnectionManager)} or an overridden {@link #createConnectionManager()}. 1799 * </ul> 1800 * 1801 * @param config New property value. 1802 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1803 * @return This object. 1804 * @see HttpClientBuilder#setDefaultSocketConfig(SocketConfig) 1805 */ 1806 public SELF defaultSocketConfig(SocketConfig config) { 1807 this.defaultSocketConfig = config; 1808 httpClientBuilder().setDefaultSocketConfig(config); 1809 return self(); 1810 } 1811 1812 /** 1813 * <i><l>RestClient</l> configuration property: </i> Enable leak detection. 1814 * 1815 * <p> 1816 * Enable client and request/response leak detection. 1817 * 1818 * <p> 1819 * Causes messages to be logged to the console if clients or request/response objects are not properly closed 1820 * when the <c>finalize</c> methods are invoked. 1821 * 1822 * <p> 1823 * Automatically enabled with {@link org.apache.juneau.marshall.Context.Builder#debug() Context.Builder#debug()}. 1824 * 1825 * <h5 class='section'>Example:</h5> 1826 * <p class='bjava'> 1827 * <jc>// Create a client that logs a message if </jc> 1828 * RestClient <jv>client</jv> = RestClient 1829 * .<jsm>create</jsm>() 1830 * .detectLeaks() 1831 * .logToConsole() <jc>// Also log the error message to System.err</jc> 1832 * .build(); 1833 * 1834 * <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> 1835 * </p> 1836 * 1837 * @return This object. 1838 */ 1839 public SELF detectLeaks() { 1840 detectLeaks = true; 1841 return self(); 1842 } 1843 1844 /** 1845 * Opts every {@code @Remote}/{@code @Url} call made through this client out of the default deny-private SSRF 1846 * guardrail (see {@code org.apache.juneau.http.remote.RemoteUrlPolicy}), for local-dev/intranet targets that 1847 * are intentionally private. 1848 * 1849 * <p> 1850 * The effective per-call value is the logical OR of this setting, the 1851 * {@code RestClient.allowPrivateUrls} system property, and the {@code @Remote}/{@code @RemoteOp} 1852 * {@code allowPrivateUrls} annotation attribute: any one of the three opting in is sufficient. The {@code http}/ 1853 * {@code https} scheme requirement is unaffected. 1854 * 1855 * @param value {@code true} to allow private/loopback/link-local/metadata {@code @Remote} targets. Default {@code false}. 1856 * @return This object. 1857 */ 1858 public SELF allowPrivateUrls(boolean value) { 1859 allowPrivateUrls = value; 1860 return self(); 1861 } 1862 1863 /** 1864 * Allows this client's SSRF-guarded {@code @Remote}/{@code @Url} calls to reach hosts inside the given 1865 * special-purpose address ranges that the default policy denies (see {@code org.apache.juneau.http.remote.RemoteUrlPolicy}). 1866 * 1867 * <p> 1868 * Same narrow semantics and validation as the JVM-wide {@code RestClient.allowAddressRanges} system property: each 1869 * entry is a CIDR range or single address (parsed without DNS) that must lie inside {@code 192.0.0.0/24}, 1870 * {@code 198.18.0.0/15}, {@code 224.0.0.0/4}, {@code 240.0.0.0/4} or {@code ff00::/8}. It never re-opens 1871 * loopback, link-local/cloud-metadata, private, unspecified or any other deny. 1872 * 1873 * <p> 1874 * The effective allow-list is the union of this setting and the system property. Repeated calls accumulate. 1875 * An invalid entry fails {@link #build()} with an {@link IllegalArgumentException}. 1876 * 1877 * @param value The CIDR ranges or addresses to allow. Can be <jk>null</jk> or empty (no effect). 1878 * @return This object. 1879 */ 1880 public SELF allowAddressRanges(String...value) { 1881 if (nn(value)) 1882 Collections.addAll(allowedAddressRanges, value); 1883 return self(); 1884 } 1885 1886 /** 1887 * <i><l>BeanTraverse</l> configuration property: </i> Automatically detect POJO recursions. 1888 * 1889 * <p> 1890 * When enabled, specifies that recursions should be checked for during traversal. 1891 * 1892 * <p> 1893 * Recursions can occur when traversing models that aren't true trees but rather contain loops. 1894 * <br>In general, unchecked recursions cause stack-overflow-errors. 1895 * <br>These show up as {@link MarshallingRecursionException MarshallingRecursionException} with the message <js>"Depth too deep. Stack overflow occurred."</js>. 1896 * 1897 * <h5 class='section'>Notes:</h5><ul> 1898 * <li class='note'> 1899 * Checking for recursion can cause a small performance penalty. 1900 * </ul> 1901 * 1902 * <h5 class='section'>Example:</h5> 1903 * <p class='bjava'> 1904 * <jc>// Create a JSON client that automatically checks for recursions.</jc> 1905 * RestClient <jv>client</jv> = RestClient 1906 * .<jsm>create</jsm>() 1907 * .json() 1908 * .detectRecursions() 1909 * .build(); 1910 * 1911 * <jc>// Create a POJO model with a recursive loop.</jc> 1912 * <jk>public class</jk> A { 1913 * <jk>public</jk> Object <jf>f</jf>; 1914 * } 1915 * A <jv>a</jv> = <jk>new</jk> A(); 1916 * <jv>a</jv>.<jf>f</jf> = <jv>a</jv>; 1917 * 1918 * <jk>try</jk> { 1919 * <jc>// Throws a RestCallException with an inner SerializeException and not a StackOverflowError</jc> 1920 * <jv>client</jv> 1921 * .post(<js>"http://localhost:10000/foo"</js>, <jv>a</jv>) 1922 * .run(); 1923 * } <jk>catch</jk> (RestCallException <jv>e</jv>} { 1924 * <jc>// Handle exception.</jc> 1925 * } 1926 * </p> 1927 * 1928 * <h5 class='section'>See Also:</h5><ul> 1929 * <li class='jm'>{@link org.apache.juneau.marshall.MarshallingTraverseContext.Builder#detectRecursions() MarshallingTraverseContext.Builder#detectRecursions()} 1930 * </ul> 1931 * 1932 * @return This object. 1933 */ 1934 public SELF detectRecursions() { 1935 serializers().forEach(Serializer.Builder::detectRecursions); 1936 return self(); 1937 } 1938 1939 /** 1940 * Disables authentication scheme caching. 1941 * 1942 * <h5 class='section'>Notes:</h5><ul> 1943 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1944 * </ul> 1945 * 1946 * @return This object. 1947 * @see HttpClientBuilder#disableAuthCaching() 1948 */ 1949 public SELF disableAuthCaching() { 1950 httpClientBuilder().disableAuthCaching(); 1951 return self(); 1952 } 1953 1954 /** 1955 * Disables automatic request recovery and re-execution. 1956 * 1957 * @return This object. 1958 * @see HttpClientBuilder#disableAutomaticRetries() 1959 */ 1960 public SELF disableAutomaticRetries() { 1961 httpClientBuilder().disableAutomaticRetries(); 1962 return self(); 1963 } 1964 1965 /** 1966 * Disables connection state tracking. 1967 * 1968 * @return This object. 1969 * @see HttpClientBuilder#disableConnectionState() 1970 */ 1971 public SELF disableConnectionState() { 1972 httpClientBuilder().disableConnectionState(); 1973 return self(); 1974 } 1975 1976 /** 1977 * Disables automatic content decompression. 1978 * 1979 * <h5 class='section'>Notes:</h5><ul> 1980 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1981 * </ul> 1982 * 1983 * @return This object. 1984 * @see HttpClientBuilder#disableContentCompression() 1985 */ 1986 public SELF disableContentCompression() { 1987 httpClientBuilder().disableContentCompression(); 1988 return self(); 1989 } 1990 1991 /** 1992 * Disables state (cookie) management. 1993 * 1994 * <h5 class='section'>Notes:</h5><ul> 1995 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1996 * </ul> 1997 * 1998 * @return This object. 1999 * @see HttpClientBuilder#disableCookieManagement() 2000 */ 2001 public SELF disableCookieManagement() { 2002 httpClientBuilder().disableCookieManagement(); 2003 return self(); 2004 } 2005 2006 /** 2007 * Disables automatic redirect handling. 2008 * 2009 * @return This object. 2010 * @see HttpClientBuilder#disableRedirectHandling() 2011 */ 2012 public SELF disableRedirectHandling() { 2013 httpClientBuilder().disableRedirectHandling(); 2014 return self(); 2015 } 2016 2017 /** 2018 * Errors codes predicate. 2019 * 2020 * <p> 2021 * Defines a predicate to test for error codes. 2022 * 2023 * <h5 class='section'>Example:</h5> 2024 * <p class='bjava'> 2025 * <jc>// Create a client that considers any 300+ responses to be errors.</jc> 2026 * RestClient <jv>client</jv> = RestClient 2027 * .<jsm>create</jsm>() 2028 * .errorCodes(<jv>x</jv> -> <jv>x</jv>>=300) 2029 * .build(); 2030 * </p> 2031 * 2032 * @param value 2033 * The new value for this setting. 2034 * <br>The default value is <code>x -> x >= 400</code>. 2035 * <br>Cannot be <jk>null</jk>. 2036 * @return This object. 2037 */ 2038 public SELF errorCodes(Predicate<Integer> value) { 2039 errorCodes = reqnn("value", value); 2040 return self(); 2041 } 2042 2043 /** 2044 * Makes this instance of {@link HttpClient} proactively evict expired connections from the connection pool using a background thread. 2045 * 2046 * <h5 class='section'>Notes:</h5><ul> 2047 * <li class='note'>One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. 2048 * <li class='note'>This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. 2049 * <li class='note'>This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. 2050 * </ul> 2051 * 2052 * @return This object. 2053 * @see HttpClientBuilder#evictExpiredConnections() 2054 */ 2055 public SELF evictExpiredConnections() { 2056 httpClientBuilder().evictExpiredConnections(); 2057 return self(); 2058 } 2059 2060 /** 2061 * Makes this instance of {@link HttpClient} proactively evict idle connections from the connection pool using a background thread. 2062 * 2063 * <h5 class='section'>Notes:</h5><ul> 2064 * <li class='note'>One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. 2065 * <li class='note'>This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. 2066 * <li class='note'>This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. 2067 * </ul> 2068 * 2069 * @param maxIdleTime New property value. 2070 * @param maxIdleTimeUnit New property value. 2071 * <br>Cannot be <jk>null</jk>. 2072 * @return This object. 2073 * @see HttpClientBuilder#evictIdleConnections(long,TimeUnit) 2074 */ 2075 public SELF evictIdleConnections(long maxIdleTime, TimeUnit maxIdleTimeUnit) { 2076 httpClientBuilder().evictIdleConnections(maxIdleTime, maxIdleTimeUnit); 2077 return self(); 2078 } 2079 2080 /** 2081 * <i><l>RestClient</l> configuration property: </i> Executor service. 2082 * 2083 * <p> 2084 * Defines the executor service to use when calling future methods on the {@link RestRequest} class. 2085 * 2086 * <p> 2087 * This executor service is used to create {@link Future} objects on the following methods: 2088 * <ul> 2089 * <li class='jm'>{@link RestRequest#runFuture()} 2090 * <li class='jm'>{@link RestRequest#completeFuture()} 2091 * <li class='jm'>{@link ResponseContent#asFuture(Class)} (and similar methods) 2092 * </ul> 2093 * 2094 * <p> 2095 * The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout 2096 * and a queue size of 10. 2097 * 2098 * <h5 class='section'>Example:</h5> 2099 * <p class='bjava'> 2100 * <jc>// Create a client with a customized executor service.</jc> 2101 * RestClient <jv>client</jv> = RestClient 2102 * .<jsm>create</jsm>() 2103 * .executorService(<jk>new</jk> ThreadPoolExecutor(1, 1, 30, TimeUnit.<jsf>SECONDS</jsf>, <jk>new</jk> ArrayBlockingQueue<Runnable>(10)), <jk>true</jk>) 2104 * .build(); 2105 * 2106 * <jc>// Use it to asynchronously run a request.</jc> 2107 * Future<RestResponse> <jv>responseFuture</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).runFuture(); 2108 * 2109 * <jc>// Do some other stuff.</jc> 2110 * 2111 * <jc>// Now read the response.</jc> 2112 * String <jv>body</jv> = <jv>responseFuture</jv>.get().getContent().asString(); 2113 * 2114 * <jc>// Use it to asynchronously retrieve a response.</jc> 2115 * Future<MyBean> <jv>myBeanFuture</jv> = <jv>client</jv> 2116 * .get(<jsf>URI</jsf>) 2117 * .run() 2118 * .getContent().asFuture(MyBean.<jk>class</jk>); 2119 * 2120 * <jc>// Do some other stuff.</jc> 2121 * 2122 * <jc>// Now read the response.</jc> 2123 * MyBean <jv>bean</jv> = <jv>myBeanFuture</jv>.get(); 2124 * </p> 2125 * 2126 * @param executorService The executor service. 2127 * <br>Can be <jk>null</jk> (a default executor service will be created if needed). 2128 * @param shutdownOnClose Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called. 2129 * @return This object. 2130 */ 2131 public SELF executorService(ExecutorService executorService, boolean shutdownOnClose) { 2132 this.executorService = executorService; 2133 this.executorServiceShutdownOnClose = shutdownOnClose; 2134 return self(); 2135 } 2136 2137 /** 2138 * Returns the builder for the list of form data parameters that get applied to all requests created by this builder. 2139 * 2140 * <p> 2141 * This is the primary method for accessing the form data parameter list. 2142 * On first call, the builder is created via the method {@link #createFormData()}. 2143 * 2144 * <h5 class='section'>Example:</h5> 2145 * <p class='bjava'> 2146 * <jc>// Create a client that adds a "foo=bar" form-data parameter on every request.</jc> 2147 * RestClient.Builder<?> <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 2148 * <jv>builder</jv>.formData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 2149 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 2150 * </p> 2151 * 2152 * <p> 2153 * The following convenience methods are also provided for updating the parameters: 2154 * <ul> 2155 * <li class='jm'>{@link #formData(NameValuePair...)} 2156 * <li class='jm'>{@link #formDataDefault(NameValuePair...)} 2157 * <li class='jm'>{@link #formData(String,String)} 2158 * <li class='jm'>{@link #formData(String,Supplier)} 2159 * </ul> 2160 * 2161 * @return The form data list builder. 2162 */ 2163 public final PartList formData() { 2164 if (formData == null) 2165 formData = createFormData(); 2166 return formData; 2167 } 2168 2169 /** 2170 * Appends multiple form-data parameters to the request bodies of all URL-encoded form posts. 2171 * 2172 * <h5 class='section'>Example:</h5> 2173 * <p class='bjava'> 2174 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 2175 * 2176 * RestClient <jv>client</jv> = RestClient 2177 * .<jsm>create</jsm>() 2178 * .formData( 2179 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 2180 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 2181 * ) 2182 * .build(); 2183 * </p> 2184 * 2185 * <p> 2186 * This is a shortcut for calling <c>formData().append(<jv>parts</jv>)</c>. 2187 * 2188 * @param parts 2189 * The form-data parameters. 2190 * <br>Can contain <jk>null</jk> values (ignored). 2191 * @return This object. 2192 * @see #formData() 2193 */ 2194 public SELF formData(NameValuePair...parts) { 2195 formData().append(parts); 2196 return self(); 2197 } 2198 2199 /** 2200 * Appends a form-data parameter to all request bodies. 2201 * 2202 * <h5 class='section'>Example:</h5> 2203 * <p class='bjava'> 2204 * RestClient <jv>client</jv> = RestClient 2205 * .<jsm>create</jsm>() 2206 * .formData(<js>"foo"</js>, <js>"bar"</js>) 2207 * .build(); 2208 * </p> 2209 * 2210 * <p> 2211 * This is a shortcut for calling <c>formData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2212 * 2213 * @param name The parameter name. 2214 * <br>Cannot be <jk>null</jk>. 2215 * @param value The parameter value. 2216 * <br>Can be <jk>null</jk> (null value will be serialized). 2217 * @return This object. 2218 * @see #formData() 2219 */ 2220 public SELF formData(String name, String value) { 2221 formData().append(name, value); 2222 return self(); 2223 } 2224 2225 /** 2226 * Appends a form-data parameter with a dynamic value to all request bodies. 2227 * 2228 * <h5 class='section'>Example:</h5> 2229 * <p class='bjava'> 2230 * RestClient <jv>client</jv> = RestClient 2231 * .<jsm>create</jsm>() 2232 * .formData(<js>"foo"</js>, ()-><js>"bar"</js>) 2233 * .build(); 2234 * </p> 2235 * 2236 * <p> 2237 * This is a shortcut for calling <c>formData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2238 * 2239 * @param name The parameter name. 2240 * <br>Cannot be <jk>null</jk>. 2241 * @param value The parameter value supplier. 2242 * <br>Can be <jk>null</jk> (null value will be serialized). 2243 * @return This object. 2244 * @see #formData() 2245 */ 2246 public SELF formData(String name, Supplier<String> value) { 2247 formData().append(name, value); 2248 return self(); 2249 } 2250 2251 /** 2252 * Sets default form-data parameter values. 2253 * 2254 * <p> 2255 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 2256 * 2257 * <h5 class='section'>Example:</h5> 2258 * <p class='bjava'> 2259 * RestClient <jv>client</jv> = RestClient 2260 * .<jsm>create</jsm>() 2261 * .formDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 2262 * .build(); 2263 * </p> 2264 * 2265 * <p> 2266 * This is a shortcut for calling <c>formData().setDefault(<jv>parts</jv>)</c>. 2267 * 2268 * @param parts The parts. 2269 * <br>Can contain <jk>null</jk> values (ignored). 2270 * @return This object. 2271 * @see #formData() 2272 */ 2273 public SELF formDataDefault(NameValuePair...parts) { 2274 formData().setDefault(parts); 2275 return self(); 2276 } 2277 2278 /** 2279 * Returns the root URI defined for this client. 2280 * 2281 * <p> 2282 * Returns <jk>null</jk> in lieu of an empty string. 2283 * Trailing slashes are trimmed. 2284 * 2285 * @return The root URI defined for this client. 2286 */ 2287 public String getRootUri() { return rootUrl != null ? rootUrl.get() : null; } 2288 2289 /** 2290 * Returns the root URL supplier set on this builder. 2291 * 2292 * @return The root URL supplier, or <jk>null</jk> if not set. 2293 */ 2294 public Supplier<String> getRootUrlSupplier() { return rootUrl; } 2295 2296 /** 2297 * Appends a header to all requests. 2298 * 2299 * <h5 class='section'>Example:</h5> 2300 * <p class='bjava'> 2301 * RestClient <jv>client</jv> = RestClient 2302 * .<jsm>create</jsm>() 2303 * .header(<js>"Foo"</js>, <js>"bar"</js>); 2304 * .build(); 2305 * </p> 2306 * 2307 * <p> 2308 * This is a shortcut for calling <c>headerData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2309 * 2310 * @param name The header name. 2311 * <br>Cannot be <jk>null</jk>. 2312 * @param value The header value. 2313 * <br>Can be <jk>null</jk> (null value will be serialized). 2314 * @return This object. 2315 * @see #headers() 2316 */ 2317 public SELF header(String name, String value) { 2318 headers().append(name, value); 2319 return self(); 2320 } 2321 2322 /** 2323 * Appends a header to all requests using a dynamic value. 2324 * 2325 * <h5 class='section'>Example:</h5> 2326 * <p class='bjava'> 2327 * RestClient <jv>client</jv> = RestClient 2328 * .<jsm>create</jsm>() 2329 * .header(<js>"Foo"</js>, ()-><js>"bar"</js>); 2330 * .build(); 2331 * </p> 2332 * 2333 * <p> 2334 * This is a shortcut for calling <c>headerData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2335 * 2336 * @param name The header name. 2337 * <br>Cannot be <jk>null</jk>. 2338 * @param value The header value supplier. 2339 * <br>Can be <jk>null</jk> (null value will be serialized). 2340 * @return This object. 2341 * @see #headers() 2342 */ 2343 public SELF header(String name, Supplier<String> value) { 2344 headers().append(name, value); 2345 return self(); 2346 } 2347 2348 /** 2349 * Returns the builder for the list of headers that get applied to all requests created by this builder. 2350 * 2351 * <p> 2352 * This is the primary method for accessing the request header list. 2353 * On first call, the builder is created via the method {@link #createHeaderData()}. 2354 * 2355 * <h5 class='section'>Example:</h5> 2356 * <p class='bjava'> 2357 * <jc>// Create a client that adds a "Foo: bar" header on every request.</jc> 2358 * RestClient.Builder<?> <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 2359 * <jv>builder</jv>.headerData().setDefault(<js>"Foo"</js>, <js>"bar"</js>)); 2360 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 2361 * </p> 2362 * 2363 * <p> 2364 * The following convenience methods are also provided for updating the headers: 2365 * <ul> 2366 * <li class='jm'>{@link #headers(Header...)} 2367 * <li class='jm'>{@link #headersDefault(Header...)} 2368 * <li class='jm'>{@link #header(String,String)} 2369 * <li class='jm'>{@link #header(String,Supplier)} 2370 * <li class='jm'>{@link #mediaType(String)} 2371 * <li class='jm'>{@link #mediaType(MediaType)} 2372 * <li class='jm'>{@link #accept(String)} 2373 * <li class='jm'>{@link #acceptCharset(String)} 2374 * <li class='jm'>{@link #clientVersion(String)} 2375 * <li class='jm'>{@link #contentType(String)} 2376 * <li class='jm'>{@link #debug()} 2377 * <li class='jm'>{@link #noTrace()} 2378 * </ul> 2379 * 2380 * @return The header list builder. 2381 */ 2382 public final HeaderList headers() { 2383 if (headerData == null) 2384 headerData = createHeaderData(); 2385 return headerData; 2386 } 2387 2388 /** 2389 * Appends multiple headers to all requests. 2390 * 2391 * <h5 class='section'>Example:</h5> 2392 * <p class='bjava'> 2393 * <jk>import static</jk> org.apache.juneau.http.HttpHeaders.*; 2394 * 2395 * RestClient <jv>client</jv> = RestClient 2396 * .<jsm>create</jsm>() 2397 * .headers( 2398 * <jsf>ACCEPT_TEXT_XML</jsf>, 2399 * <jsm>stringHeader</jsm>(<js>"Foo"</js>, <js>"bar"</js>) 2400 * ) 2401 * .build(); 2402 * </p> 2403 * 2404 * <p> 2405 * This is a shortcut for calling <c>headerData().append(<jv>parts</jv>)</c>. 2406 * 2407 * @param parts 2408 * The header to set. 2409 * <br>Can contain <jk>null</jk> values (ignored). 2410 * @return This object. 2411 * @see #headers() 2412 */ 2413 public SELF headers(Header...parts) { 2414 headers().append(parts); 2415 return self(); 2416 } 2417 2418 /** 2419 * Sets default header values. 2420 * 2421 * <p> 2422 * Uses default values for specified headers if not otherwise specified on the outgoing requests. 2423 * 2424 * <h5 class='section'>Example:</h5> 2425 * <p class='bjava'> 2426 * RestClient <jv>client</jv> = RestClient 2427 * .<jsm>create</jsm>() 2428 * .headersDefault(<jsm>stringHeader</jsm>(<js>"Foo"</js>, ()-><js>"bar"</js>)); 2429 * .build(); 2430 * </p> 2431 * 2432 * <p> 2433 * This is a shortcut for calling <c>headerData().setDefault(<jv>parts</jv>)</c>. 2434 * 2435 * @param parts The header values. 2436 * <br>Can contain <jk>null</jk> values (ignored). 2437 * @return This object. 2438 * @see #headers() 2439 */ 2440 public SELF headersDefault(Header...parts) { 2441 headers().setDefault(parts); 2442 return self(); 2443 } 2444 2445 /** 2446 * Sets a default request header with serializer session options (JSON5 map) for all requests. 2447 * 2448 * <p> 2449 * This is a shortcut for <c>headersDefault(basicHeader({@link RestSharedConstants#HEADER_JuneauSerializerOptions}, <jv>json5</jv>))</c>. 2450 * 2451 * @param json5 2452 * Serializer session options in JSON5 form. 2453 * <br>Can be <jk>null</jk> (default is not set by this method). 2454 * @return This object. 2455 */ 2456 public SELF serializerSessionOptionsHeader(String json5) { 2457 if (json5 != null) 2458 headersDefault(basicHeader(HEADER_JuneauSerializerOptions, json5)); 2459 return self(); 2460 } 2461 2462 /** 2463 * Sets a default request header with parser session options (JSON5 map) for all requests. 2464 * 2465 * <p> 2466 * This is a shortcut for <c>headersDefault(basicHeader({@link RestSharedConstants#HEADER_JuneauParserOptions}, <jv>json5</jv>))</c>. 2467 * 2468 * @param json5 2469 * Parser session options in JSON5 form. 2470 * <br>Can be <jk>null</jk> (default is not set by this method). 2471 * @return This object. 2472 */ 2473 public SELF parserSessionOptionsHeader(String json5) { 2474 if (json5 != null) 2475 headersDefault(basicHeader(HEADER_JuneauParserOptions, json5)); 2476 return self(); 2477 } 2478 2479 /** 2480 * Convenience for {@link #serializerSessionOptionsHeader(String)} using a map serialized with JSON5. 2481 * 2482 * @param properties 2483 * Property names and values for serializer session options. 2484 * <br>Can be <jk>null</jk> (treated as an empty map). 2485 * @return This object. 2486 */ 2487 public SELF serializerSessionOptionsHeader(Map<String,?> properties) { 2488 try { 2489 return serializerSessionOptionsHeader(isEmpty(properties) ? null : Json5.of(properties)); 2490 } catch (SerializeException e) { 2491 throw rex(e, "Could not serialize serializer session options header"); 2492 } 2493 } 2494 2495 /** 2496 * Convenience for {@link #parserSessionOptionsHeader(String)} using a map serialized with JSON5. 2497 * 2498 * @param properties 2499 * Property names and values for parser session options. 2500 * <br>Can be <jk>null</jk> (treated as an empty map). 2501 * @return This object. 2502 */ 2503 public SELF parserSessionOptionsHeader(Map<String,?> properties) { 2504 try { 2505 return parserSessionOptionsHeader(isEmpty(properties) ? null : Json5.of(properties)); 2506 } catch (SerializeException e) { 2507 throw rex(e, "Could not serialize parser session options header"); 2508 } 2509 } 2510 2511 /** 2512 * Convenience method for specifying HTML as the marshalling transmission media type. 2513 * 2514 * <p> 2515 * POJOs are converted to HTML without any sort of doc wrappers. 2516 * 2517 * <p> 2518 * {@link HtmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2519 * <ul> 2520 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 2521 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2522 * </ul> 2523 * <p> 2524 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2525 * <ul> 2526 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 2527 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2528 * </ul> 2529 * <p> 2530 * <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden 2531 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2532 * <p> 2533 * <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden 2534 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2535 * <p> 2536 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2537 * <ul> 2538 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2539 * last-enabled language if the headers are not set. 2540 * </ul> 2541 * <p> 2542 * Identical to calling <c>serializer(HtmlSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2543 * 2544 * <h5 class='section'>Example:</h5> 2545 * <p class='bjava'> 2546 * <jc>// Construct a client that uses HTML marshalling.</jc> 2547 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().html().build(); 2548 * </p> 2549 * 2550 * @return This object. 2551 */ 2552 public SELF html() { 2553 return serializer(HtmlSerializer.class).parser(HtmlParser.class); 2554 } 2555 2556 /** 2557 * Convenience method for specifying HTML DOC as the marshalling transmission media type. 2558 * 2559 * <p> 2560 * POJOs are converted to fully renderable HTML pages. 2561 * 2562 * <p> 2563 * {@link HtmlDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2564 * <ul> 2565 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()} or 2566 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2567 * </ul> 2568 * <p> 2569 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2570 * <ul> 2571 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 2572 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2573 * </ul> 2574 * <p> 2575 * <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden 2576 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2577 * <p> 2578 * <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden 2579 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2580 * <p> 2581 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2582 * <ul> 2583 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2584 * last-enabled language if the headers are not set. 2585 * </ul> 2586 * <p> 2587 * Identical to calling <c>serializer(HtmlDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2588 * 2589 * <h5 class='section'>Example:</h5> 2590 * <p class='bjava'> 2591 * <jc>// Construct a client that uses HTML Doc marshalling.</jc> 2592 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().htmlDoc().build(); 2593 * </p> 2594 * 2595 * @return This object. 2596 */ 2597 public SELF htmlDoc() { 2598 return serializer(HtmlDocSerializer.class).parser(HtmlParser.class); 2599 } 2600 2601 /** 2602 * Convenience method for specifying Stripped HTML DOC as the marshalling transmission media type. 2603 * 2604 * <p> 2605 * Same as {@link #htmlDoc()} but without the header and body tags and page title and description. 2606 * 2607 * <p> 2608 * {@link HtmlStrippedDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2609 * <ul> 2610 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 2611 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2612 * </ul> 2613 * <p> 2614 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2615 * <ul> 2616 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 2617 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2618 * </ul> 2619 * <p> 2620 * <c>Accept</c> request header will be set to <js>"text/html+stripped"</js> unless overridden 2621 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2622 * <p> 2623 * <c>Content-Type</c> request header will be set to <js>"text/html+stripped"</js> unless overridden 2624 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2625 * <p> 2626 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2627 * <ul> 2628 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2629 * last-enabled language if the headers are not set. 2630 * </ul> 2631 * <p> 2632 * Identical to calling <c>serializer(HtmlStrippedDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2633 * 2634 * <h5 class='section'>Example:</h5> 2635 * <p class='bjava'> 2636 * <jc>// Construct a client that uses HTML Stripped Doc marshalling.</jc> 2637 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().htmlStrippedDoc().build(); 2638 * </p> 2639 * 2640 * @return This object. 2641 */ 2642 public SELF htmlStrippedDoc() { 2643 return serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class); 2644 } 2645 2646 /** 2647 * Sets the {@link HttpClient} to be used to handle all HTTP communications with the target server. 2648 * 2649 * <p> 2650 * This can be used to bypass the client created by {@link #createHttpClient()} method. 2651 * 2652 * <h5 class='section'>Example:</h5> 2653 * <p class='bjava'> 2654 * <jc>// Construct a client that uses a customized HttpClient.</jc> 2655 * RestClient <jv>client</jv> = RestClient 2656 * .<jsm>create</jsm>() 2657 * .httpClient(HttpClientBuilder.<jsm>create</jsm>().build()) 2658 * .build(); 2659 * </p> 2660 * 2661 * @param value The {@link HttpClient} to be used to handle all HTTP communications with the target server. 2662 * <br>Can be <jk>null</jk> (a default client will be created). 2663 * @return This object. 2664 */ 2665 public SELF httpClient(CloseableHttpClient value) { 2666 httpClient = value; 2667 return self(); 2668 } 2669 2670 /** 2671 * Returns the HTTP client builder. 2672 * 2673 * @return The HTTP client builder. 2674 */ 2675 public final HttpClientBuilder httpClientBuilder() { 2676 if (httpClientBuilder == null) 2677 httpClientBuilder = createHttpClientBuilder(); 2678 return httpClientBuilder; 2679 } 2680 2681 /** 2682 * Sets the {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. 2683 * 2684 * <p> 2685 * This can be used to bypass the builder created by {@link #createHttpClientBuilder()} method. 2686 * 2687 * <h5 class='section'>Example:</h5> 2688 * <p class='bjava'> 2689 * <jc>// Construct a client that uses a customized HttpClientBuilder.</jc> 2690 * RestClient <jv>client</jv> = RestClient 2691 * .<jsm>create</jsm>() 2692 * .httpClientBuilder(HttpClientBuilder.<jsm>create</jsm>()) 2693 * .build(); 2694 * </p> 2695 * 2696 * <h5 class='section'>Notes:</h5><ul> 2697 * <li class='note'>The TLS and connection-pool settings Apache {@link HttpClientBuilder} applies only to a 2698 * connection manager it builds itself 2699 * ({@link HttpClientBuilder#setSSLContext(SSLContext) setSSLContext}, 2700 * {@link HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) setSSLSocketFactory}, 2701 * {@link HttpClientBuilder#setSSLHostnameVerifier(HostnameVerifier) setSSLHostnameVerifier}, 2702 * {@link HttpClientBuilder#setPublicSuffixMatcher(PublicSuffixMatcher) setPublicSuffixMatcher}, 2703 * {@link HttpClientBuilder#setMaxConnTotal(int) setMaxConnTotal}, 2704 * {@link HttpClientBuilder#setMaxConnPerRoute(int) setMaxConnPerRoute}, 2705 * {@link HttpClientBuilder#setConnectionTimeToLive(long, TimeUnit) setConnectionTimeToLive}, 2706 * {@link HttpClientBuilder#setDefaultSocketConfig(SocketConfig) setDefaultSocketConfig}, 2707 * {@link HttpClientBuilder#setDefaultConnectionConfig(ConnectionConfig) setDefaultConnectionConfig}, 2708 * {@link HttpClientBuilder#setDnsResolver(DnsResolver) setDnsResolver} and 2709 * {@link HttpClientBuilder#useSystemProperties() useSystemProperties}) are read from the given builder and 2710 * applied to the connection manager the default {@link #createConnectionManager()} builds (see there). 2711 * <li class='note'>These connection-manager settings, made through a method of this builder before this method is 2712 * called, are applied too, and win over the same setting on the given builder: 2713 * {@link #sslContext(SSLContext) sslContext}, 2714 * {@link #sslSocketFactory(LayeredConnectionSocketFactory) sslSocketFactory}, 2715 * {@link #sslHostnameVerifier(HostnameVerifier) sslHostnameVerifier}, 2716 * {@link #publicSuffixMatcher(PublicSuffixMatcher) publicSuffixMatcher} (for the default hostname verifier only: 2717 * its use for cookies is lost with the replaced builder), 2718 * {@link #maxConnTotal(int) maxConnTotal}, {@link #maxConnPerRoute(int) maxConnPerRoute}, {@link #pooled() pooled}, 2719 * {@link #connectionTimeToLive(long, TimeUnit) connectionTimeToLive}, 2720 * {@link #defaultSocketConfig(SocketConfig) defaultSocketConfig}, 2721 * {@link #defaultConnectionConfig(ConnectionConfig) defaultConnectionConfig} and the TLS and 2722 * {@code http.maxConnections} system properties of {@link #useSystemProperties()}. Every other setting made 2723 * through this builder before this method is called (interceptors, proxy, the rest of 2724 * {@code useSystemProperties()}, and so on) was made on the replaced builder and is lost. 2725 * <li class='note'>For these connection-manager settings, a value set through a method of this builder always wins 2726 * over the same setting on the {@code HttpClientBuilder}, whichever was set last: unlike Apache's own 2727 * last-write-wins, a later {@code httpClientBuilder().setDefaultSocketConfig(...)} (and so on) does not 2728 * override an earlier {@link #defaultSocketConfig(SocketConfig) defaultSocketConfig(...)}. 2729 * <li class='note'>As in Apache {@code HttpClientBuilder}, an SSL socket factory wins over an SSL context wherever 2730 * each was set: a {@link HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) setSSLSocketFactory} 2731 * on the given builder wins over an earlier {@link #sslContext(SSLContext) sslContext}. 2732 * <li class='warn'>A socket factory set with {@code setSSLSocketFactory} on the given builder is used for 2733 * SSRF-guard-active {@code @Remote} calls too, and must connect to exactly the address it is given (see 2734 * {@link #sslSocketFactory(LayeredConnectionSocketFactory)}). 2735 * <li class='warn'>A client built by an {@code HttpClientBuilder} subclass that overrides (or inherits an override 2736 * of) {@code build()}, {@code createMainExec(...)}, {@code decorateMainExec(...)} or 2737 * {@code decorateProtocolExec(...)} refuses every SSRF-guard-active {@code @Remote} call: the override may 2738 * build the client around another connection manager or request executor. 2739 * </ul> 2740 * 2741 * @param value The {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. 2742 * <br>Can be <jk>null</jk> (a default builder will be created). 2743 * @return This object. 2744 */ 2745 public SELF httpClientBuilder(HttpClientBuilder value) { 2746 httpClientBuilder = value; 2747 return self(); 2748 } 2749 2750 /** 2751 * Assigns {@link HttpProcessor} instance. 2752 * 2753 * <h5 class='section'>Notes:</h5><ul> 2754 * <li class='warn'>The processor replaces all of the client's request interceptors, among them the ones that 2755 * enforce the SSRF guard and strip credentials on cross-origin redirects. So a client with one refuses every 2756 * SSRF-guard-active {@code @Remote} call (one without the {@link #allowPrivateUrls(boolean) allowPrivateUrls} 2757 * opt-in). Add interceptors with {@link HttpClientBuilder#addInterceptorLast(HttpRequestInterceptor)} on the 2758 * {@link #httpClientBuilder() HttpClientBuilder} instead. 2759 * </ul> 2760 * 2761 * @param httpprocessor New property value. 2762 * @return This object. 2763 * @see HttpClientBuilder#setHttpProcessor(HttpProcessor) 2764 */ 2765 public SELF httpProcessor(HttpProcessor httpprocessor) { 2766 httpClientBuilder().setHttpProcessor(httpprocessor); 2767 return self(); 2768 } 2769 2770 /** 2771 * Ignore errors. 2772 * 2773 * <p> 2774 * When enabled, HTTP error response codes (e.g. <l>>=400</l>) will not cause a {@link RestCallException} to 2775 * be thrown. 2776 * <p> 2777 * Note that this is equivalent to <c>builder.errorCodes(x -> <jk>false</jk>);</c> 2778 * 2779 * <h5 class='section'>Example:</h5> 2780 * <p class='bjava'> 2781 * <jc>// Create a client that doesn't throws a RestCallException when a 500 error occurs.</jc> 2782 * RestClient 2783 * .<jsm>create</jsm>() 2784 * .ignoreErrors() 2785 * .build() 2786 * .get(<js>"/error"</js>) <jc>// Throws a 500 error</jc> 2787 * .run() 2788 * .assertStatus().is(500); 2789 * </p> 2790 * 2791 * @return This object. 2792 */ 2793 public SELF ignoreErrors() { 2794 ignoreErrors = true; 2795 return self(); 2796 } 2797 2798 /** 2799 * <i><l>BeanTraverse</l> configuration property: </i> Ignore recursion errors. 2800 * 2801 * <p> 2802 * When enabled, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>. 2803 * 2804 * <p> 2805 * For example, if a model contains the links A->B->C->A, then the JSON generated will look like 2806 * the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>... 2807 * 2808 * <p class='bjson'> 2809 * {A:{B:{C:<jk>null</jk>}}} 2810 * </p> 2811 * 2812 * <h5 class='section'>Notes:</h5><ul> 2813 * <li class='note'> 2814 * Checking for recursion can cause a small performance penalty. 2815 * </ul> 2816 * 2817 * <h5 class='section'>Example:</h5> 2818 * <p class='bjava'> 2819 * <jc>// Create a JSON client that ignores recursions.</jc> 2820 * RestClient <jv>client</jv> = RestClient 2821 * .<jsm>create</jsm>() 2822 * .json() 2823 * .ignoreRecursions() 2824 * .build(); 2825 * 2826 * <jc>// Create a POJO model with a recursive loop.</jc> 2827 * <jk>public class</jk> A { 2828 * <jk>public</jk> Object <jf>f</jf>; 2829 * } 2830 * A <jv>a</jv> = <jk>new</jk> A(); 2831 * <jv>a</jv>.<jf>f</jf> = <jv>a</jv>; 2832 * 2833 * <jc>// Produces request body "{f:null}"</jc> 2834 * <jv>client</jv> 2835 * .post(<js>"http://localhost:10000/foo"</js>, <jv>a</jv>) 2836 * .run(); 2837 * </p> 2838 * 2839 * <h5 class='section'>See Also:</h5><ul> 2840 * <li class='jm'>{@link org.apache.juneau.marshall.MarshallingTraverseContext.Builder#ignoreRecursions() MarshallingTraverseContext.Builder#ignoreRecursions()} 2841 * </ul> 2842 * 2843 * @return This object. 2844 */ 2845 public SELF ignoreRecursions() { 2846 serializers().forEach(Serializer.Builder::ignoreRecursions); 2847 return self(); 2848 } 2849 2850 /** 2851 * <i><l>BeanTraverse</l> configuration property: </i> Initial depth. 2852 * 2853 * <p> 2854 * The initial indentation level at the root. 2855 * 2856 * <p> 2857 * Useful when constructing document fragments that need to be indented at a certain level when whitespace is enabled. 2858 * 2859 * <h5 class='section'>Example:</h5> 2860 * <p class='bjava'> 2861 * <jc>// Create a REST client with JSON serializer with whitespace enabled and an initial depth of 2.</jc> 2862 * RestClient <jv>client</jv> = RestClient 2863 * .<jsm>create</jsm>() 2864 * .json() 2865 * .ws() 2866 * .initialDepth(2) 2867 * .build(); 2868 * 2869 * <jc>// Our bean to serialize.</jc> 2870 * <jk>public class</jk> MyBean { 2871 * <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>; 2872 * } 2873 * 2874 * <jc>// Produces request body "\t\t{\n\t\t\t'foo':'bar'\n\t\t}\n"</jc> 2875 * <jv>client</jv> 2876 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 2877 * .run(); 2878 * </p> 2879 * 2880 * <h5 class='section'>See Also:</h5><ul> 2881 * <li class='jm'>{@link org.apache.juneau.marshall.MarshallingTraverseContext.Builder#initialDepth(int) MarshallingTraverseContext.Builder#initialDepth(int)} 2882 * </ul> 2883 * 2884 * @param value 2885 * The new value for this property. 2886 * <br>The default is <c>0</c>. 2887 * @return This object. 2888 */ 2889 public SELF initialDepth(int value) { 2890 serializers().forEach(x -> x.initialDepth(value)); 2891 return self(); 2892 } 2893 2894 /** 2895 * <i><l>RestClient</l> configuration property: </i> Call interceptors. 2896 * 2897 * <p> 2898 * Adds an interceptor that can be called to hook into specified events in the lifecycle of a single request. 2899 * 2900 * <h5 class='section'>Example:</h5> 2901 * <p class='bjava'> 2902 * <jc>// Customized interceptor (note you can also extend from BasicRestCallInterceptor as well.</jc> 2903 * <jk>public class</jk> MyRestCallInterceptor <jk>implements</jk> RestCallInterceptor { 2904 * 2905 * <ja>@Override</ja> 2906 * <jk>public void</jk> onInit(RestRequest <jv>req</jv>) <jk>throws</jk> Exception { 2907 * <jc>// Intercept immediately after RestRequest object is created and all headers/query/form-data has been 2908 * // set on the request from the client.</jc> 2909 * } 2910 * 2911 * <ja>@Override</ja> 2912 * <jk>public void</jk> onConnect(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2913 * <jc>// Intercept immediately after an HTTP response has been received.</jc> 2914 * } 2915 * 2916 * <ja>@Override</ja> 2917 * <jk>public void</jk> onClose(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2918 * <jc>// Intercept when the response body is consumed.</jc> 2919 * } 2920 * } 2921 * 2922 * <jc>// Create a client with a customized interceptor.</jc> 2923 * RestClient <jv>client</jv> = RestClient 2924 * .<jsm>create</jsm>() 2925 * .interceptors(MyRestCallInterceptor.<jk>class</jk>) 2926 * .build(); 2927 * </p> 2928 * 2929 * <h5 class='section'>Notes:</h5><ul> 2930 * <li class='note'>The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and 2931 * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. 2932 * </ul> 2933 * 2934 * @param values 2935 * The values to add to this setting. 2936 * <br>Can be implementations of any of the following: 2937 * <ul> 2938 * <li class='jic'>{@link RestCallInterceptor} 2939 * <li class='jic'>{@link HttpRequestInterceptor} 2940 * <li class='jic'>{@link HttpResponseInterceptor} 2941 * </ul> 2942 * <br>Can contain <jk>null</jk> values (ignored). 2943 * @return This object. 2944 * @throws Exception If one or more interceptors could not be created. 2945 */ 2946 public SELF interceptors(Class<?>...values) throws Exception { 2947 for (var c : values) { 2948 if (c == null) 2949 continue; 2950 var ci = ClassInfo.of(c); 2951 if (nn(ci)) { 2952 if (ci.isAssignableToAny(RestCallInterceptor.class, HttpRequestInterceptor.class, HttpResponseInterceptor.class)) 2953 interceptors(ci.newInstance()); 2954 else 2955 throw new ConfigException("Invalid class of type '%s' passed to interceptors().", ci.getName()); 2956 } 2957 } 2958 return self(); 2959 } 2960 2961 /** 2962 * Call interceptors. 2963 * 2964 * <p> 2965 * Adds an interceptor that gets called immediately after a connection is made. 2966 * 2967 * <h5 class='section'>Example:</h5> 2968 * <p class='bjava'> 2969 * <jc>// Create a client with a customized interceptor.</jc> 2970 * RestClient <jv>client</jv> = RestClient 2971 * .<jsm>create</jsm>() 2972 * .interceptors( 2973 * <jk>new</jk> RestCallInterceptor() { 2974 * 2975 * <ja>@Override</ja> 2976 * <jk>public void</jk> onInit(RestRequest <jv>req</jv>) <jk>throws</jk> Exception { 2977 * <jc>// Intercept immediately after RestRequest object is created and all headers/query/form-data has been 2978 * // set on the request from the client.</jc> 2979 * } 2980 * 2981 * <ja>@Override</ja> 2982 * <jk>public void</jk> onConnect(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2983 * <jc>// Intercept immediately after an HTTP response has been received.</jc> 2984 * } 2985 * 2986 * <ja>@Override</ja> 2987 * <jk>public void</jk> onClose(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2988 * <jc>// Intercept when the response body is consumed.</jc> 2989 * } 2990 * } 2991 * ) 2992 * .build(); 2993 * </p> 2994 * 2995 * <h5 class='section'>Notes:</h5><ul> 2996 * <li class='note'>The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and 2997 * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. 2998 * </ul> 2999 * 3000 * @param value 3001 * The values to add to this setting. 3002 * <br>Can be implementations of any of the following: 3003 * <ul> 3004 * <li class='jic'>{@link RestCallInterceptor} 3005 * <li class='jic'>{@link HttpRequestInterceptor} 3006 * <li class='jic'>{@link HttpResponseInterceptor} 3007 * </ul> 3008 * <br>Can contain <jk>null</jk> values (ignored). 3009 * @return This object. 3010 */ 3011 public SELF interceptors(Object...value) { 3012 List<RestCallInterceptor> l = list(); 3013 for (var o : value) { 3014 if (o == null) 3015 continue; 3016 ClassInfo ci = ClassInfo.of(o); 3017 if (nn(ci)) { 3018 if (! ci.isAssignableToAny(HttpRequestInterceptor.class, HttpResponseInterceptor.class, RestCallInterceptor.class)) 3019 throw new ConfigException("Invalid object of type '%s' passed to interceptors().", ci.getName()); 3020 if (o instanceof HttpRequestInterceptor o2) 3021 addInterceptorLast(o2); 3022 if (o instanceof HttpResponseInterceptor o2) 3023 addInterceptorLast(o2); 3024 if (o instanceof RestCallInterceptor o2) 3025 l.add(o2); 3026 } 3027 } 3028 if (interceptors == null) 3029 interceptors = l; 3030 else 3031 interceptors.addAll(0, l); 3032 return self(); 3033 } 3034 3035 /** 3036 * Convenience method for specifying JSON as the marshalling transmission media type. 3037 * 3038 * <p> 3039 * {@link JsonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3040 * <ul> 3041 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3042 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3043 * </ul> 3044 * <p> 3045 * {@link JsonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3046 * <ul> 3047 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3048 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3049 * </ul> 3050 * <p> 3051 * <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden 3052 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}}. 3053 * <p> 3054 * <c>Content-Type</c> request header will be set to <js>"application/json"</js> unless overridden 3055 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3056 * <p> 3057 * Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. 3058 * <ul> 3059 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3060 * last-enabled language if the headers are not set. 3061 * </ul> 3062 * <p> 3063 * Identical to calling <c>serializer(JsonSerializer.<jk>class</jk>).parser(JsonParser.<jk>class</jk>)</c>. 3064 * 3065 * <h5 class='section'>Example:</h5> 3066 * <p class='bjava'> 3067 * <jc>// Construct a client that uses JSON marshalling.</jc> 3068 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().build(); 3069 * </p> 3070 * 3071 * @return This object. 3072 */ 3073 public SELF json() { 3074 return serializer(JsonSerializer.class).parser(JsonParser.class); 3075 } 3076 3077 /** 3078 * Convenience method for specifying Simplified JSON as the marshalling transmission media type. 3079 * 3080 * <p> 3081 * Simplified JSON is typically useful for automated tests because you can do simple string comparison of results 3082 * without having to escape lots of quotes. 3083 * 3084 * <p> 3085 * {@link Json5Serializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3086 * <ul> 3087 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3088 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3089 * </ul> 3090 * <p> 3091 * {@link Json5Parser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3092 * <ul> 3093 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3094 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3095 * </ul> 3096 * <p> 3097 * <c>Accept</c> request header will be set to <js>"application/json5"</js> unless overridden 3098 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3099 * <p> 3100 * <c>Content-Type</c> request header will be set to <js>"application/json5"</js> unless overridden 3101 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3102 * <p> 3103 * A server that only handles JSON may reply <c>406 Not Acceptable</c> or <c>415 Unsupported Media Type</c> to these 3104 * JSON5 media types. 3105 * <p> 3106 * Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. 3107 * <ul> 3108 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3109 * last-enabled language if the headers are not set. 3110 * </ul> 3111 * <p> 3112 * Identical to calling <c>serializer(Json5Serializer.<jk>class</jk>).parser(Json5Parser.<jk>class</jk>)</c>. 3113 * 3114 * <h5 class='section'>Example:</h5> 3115 * <p class='bjava'> 3116 * <jc>// Construct a client that uses Simplified JSON marshalling.</jc> 3117 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 3118 * </p> 3119 * 3120 * @return This object. 3121 */ 3122 public SELF json5() { 3123 return serializer(Json5Serializer.class).parser(Json5Parser.class); 3124 } 3125 3126 /** 3127 * Convenience method for specifying JCS (JSON Canonicalization Scheme, RFC 8785) as the marshalling transmission media type. 3128 * 3129 * <p> 3130 * {@link JcsSerializer} will be used to serialize POJOs to request bodies. 3131 * {@link JsonParser} will be used to parse POJOs from response bodies (JCS output is valid JSON). 3132 * <p> 3133 * <c>Accept</c> and <c>Content-Type</c> will be set to <js>"application/jcs+json"</js>. 3134 * <p> 3135 * Identical to calling <c>serializer(JcsSerializer.<jk>class</jk>).parser(JsonParser.<jk>class</jk>)</c>. 3136 * 3137 * @return This object. 3138 */ 3139 public SELF jcs() { 3140 return serializer(JcsSerializer.class).parser(JsonParser.class); 3141 } 3142 3143 /** 3144 * Assigns {@link ConnectionKeepAliveStrategy} instance. 3145 * 3146 * @param keepAliveStrategy New property value. 3147 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 3148 * @return This object. 3149 * @see HttpClientBuilder#setKeepAliveStrategy(ConnectionKeepAliveStrategy) 3150 */ 3151 public SELF keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { 3152 httpClientBuilder().setKeepAliveStrategy(keepAliveStrategy); 3153 return self(); 3154 } 3155 3156 /** 3157 * <i><l>RestClient</l> configuration property: </i> Keep HttpClient open. 3158 * 3159 * <p> 3160 * Don't close this client when the {@link RestClient#close()} method is called. 3161 * 3162 * <h5 class='section'>Example:</h5> 3163 * <p class='bjava'> 3164 * <jc>// Create a client with a customized client and don't close the client service.</jc> 3165 * RestClient <jv>client</jv> = RestClient 3166 * .<jsm>create</jsm>() 3167 * .httpClient(<jv>myHttpClient</jv>) 3168 * .keepHttpClientOpen() 3169 * .build(); 3170 * 3171 * <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> 3172 * </p> 3173 * 3174 * <h5 class='section'>Notes:</h5><ul> 3175 * <li class='note'>When combined with {@link #connectionManagerShared(boolean) connectionManagerShared(true)}, the 3176 * client's internal pool for SSRF-guard-active {@code @Remote} calls (see {@link #createConnectionManager()}) and 3177 * the connection manager the default {@code createConnectionManager()} built are not shut down either, and 3178 * nothing else shuts them down; their connections stay open until they are closed as idle or the JVM exits. 3179 * </ul> 3180 * 3181 * @return This object. 3182 */ 3183 public SELF keepHttpClientOpen() { 3184 keepHttpClientOpen = true; 3185 return self(); 3186 } 3187 3188 /** 3189 * <i><l>Serializer</l> configuration property: </i> Don't trim null bean property values. 3190 * 3191 * <p> 3192 * When enabled, null bean values will be serialized to the output. 3193 * 3194 * <h5 class='section'>Notes:</h5><ul> 3195 * <li class='note'>Not enabling this setting will cause <c>Map</c>s with <jk>null</jk> values to be lost during parsing. 3196 * </ul> 3197 * 3198 * <h5 class='section'>Example:</h5> 3199 * <p class='bjava'> 3200 * <jc>// Create a REST client with JSON serializer that serializes null properties.</jc> 3201 * RestClient <jv>client</jv> = RestClient 3202 * .<jsm>create</jsm>() 3203 * .json() 3204 * .keepNullProperties() 3205 * .build(); 3206 * 3207 * <jc>// Our bean to serialize.</jc> 3208 * <jk>public class</jk> MyBean { 3209 * <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>; 3210 * } 3211 * 3212 * <jc>// Request body will contain: {foo:null}</jc> 3213 * <jv>client</jv> 3214 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 3215 * .run(); 3216 * </p> 3217 * 3218 * <h5 class='section'>See Also:</h5><ul> 3219 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#keepNullProperties() Serializer.Builder#keepNullProperties()} 3220 * </ul> 3221 * 3222 * @return This object. 3223 */ 3224 public SELF keepNullProperties() { 3225 serializers().forEach(Serializer.Builder::keepNullProperties); 3226 return self(); 3227 } 3228 3229 /** 3230 * Logger. 3231 * 3232 * <p> 3233 * Specifies the logger to use for logging. 3234 * 3235 * <p> 3236 * If not specified, uses the following logger: 3237 * <p class='bjava'> 3238 * Logger.<jsm>getLogger</jsm>(RestClient.<jk>class</jk>.getName()); 3239 * </p> 3240 * 3241 * <h5 class='section'>Example:</h5> 3242 * <p class='bjava'> 3243 * <jc>// Construct a client that logs messages to a special logger.</jc> 3244 * RestClient <jv>client</jv> = RestClient 3245 * .<jsm>create</jsm>() 3246 * .logger(Logger.<jsm>getLogger</jsm>(<js>"MyLogger"</js>)) <jc>// Log to MyLogger logger.</jc> 3247 * .logToConsole() <jc>// Also log to console.</jc> 3248 * .logRequests(<jsf>FULL</jsf>, <jsf>WARNING</jsf>) <jc>// Log requests with full detail at WARNING level.</jc> 3249 * .build(); 3250 * </p> 3251 * 3252 * @param value The logger to use for logging. 3253 * <br>Can be <jk>null</jk> (defaults to <c>Logger.getLogger(RestClient.class.getName())</c>). 3254 * @return This object. 3255 */ 3256 public SELF logger(Logger value) { 3257 logger = value; 3258 return self(); 3259 } 3260 3261 /** 3262 * Log requests. 3263 * 3264 * <p> 3265 * Causes requests/responses to be logged at the specified log level at the end of the request. 3266 * 3267 * <p> 3268 * <jsf>SIMPLE</jsf> detail produces a log message like the following: 3269 * <p class='bconsole'> 3270 * POST http://localhost:10000/testUrl, HTTP/1.1 200 OK 3271 * </p> 3272 * 3273 * <p> 3274 * <jsf>FULL</jsf> detail produces a log message like the following: 3275 * <p class='bconsole'> 3276 * === HTTP Call (outgoing) ======================================================= 3277 * === REQUEST === 3278 * POST http://localhost:10000/testUrl 3279 * ---request headers--- 3280 * Debug: true 3281 * No-Trace: true 3282 * Accept: application/json 3283 * ---request entity--- 3284 * Content-Type: application/json 3285 * ---request content--- 3286 * {"foo":"bar","baz":123} 3287 * === RESPONSE === 3288 * HTTP/1.1 200 OK 3289 * ---response headers--- 3290 * Content-Type: application/json;charset=utf-8 3291 * Content-Length: 21 3292 * Server: Jetty(8.1.0.v20120127) 3293 * ---response content--- 3294 * {"message":"OK then"} 3295 * === END ======================================================================== 3296 * </p> 3297 * 3298 * <p> 3299 * By default, the message is logged to the default logger. It can be logged to a different logger via the 3300 * {@link #logger(Logger)} method or logged to the console using the 3301 * {@link #logToConsole()} method. 3302 * 3303 * @param detail The detail level of logging. 3304 * @param level The log level. 3305 * @param test A predicate to use per-request to see if the request should be logged. If <jk>null</jk>, always logs. 3306 * @return This object. 3307 */ 3308 public SELF logRequests(DetailLevel detail, Level level, BiPredicate<RestRequest,RestResponse> test) { 3309 logRequests = detail; 3310 logRequestsLevel = level; 3311 logRequestsPredicate = test; 3312 return self(); 3313 } 3314 3315 /** 3316 * Log to console. 3317 * 3318 * <p> 3319 * Specifies to log messages to the console. 3320 * 3321 * <h5 class='section'>Example:</h5> 3322 * <p class='bjava'> 3323 * <jc>// Construct a client that logs messages to a special logger.</jc> 3324 * RestClient <jv>client</jv> = RestClient 3325 * .<jsm>create</jsm>() 3326 * .logToConsole() 3327 * .logRequests(<jsf>FULL</jsf>, <jsf>INFO</jsf>) <jc>// Level is ignored when logging to console.</jc> 3328 * .build(); 3329 * </p> 3330 * 3331 * @return This object. 3332 */ 3333 public SELF logToConsole() { 3334 logToConsole = true; 3335 return self(); 3336 } 3337 3338 /** 3339 * <i><l>RestClient</l> configuration property: </i> Marshaller 3340 * 3341 * <p> 3342 * Shortcut for specifying the serializers and parsers 3343 * using the serializer and parser defined in a marshaller. 3344 * 3345 * <h5 class='section'>Notes:</h5><ul> 3346 * <li class='note'>When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), 3347 * parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. 3348 * </ul> 3349 * 3350 * <h5 class='section'>Example:</h5> 3351 * <p class='bjava'> 3352 * <jc>// Create a client that uses Simplified-JSON transport using an existing marshaller.</jc> 3353 * RestClient <jv>client</jv> = RestClient 3354 * .<jsm>create</jsm>() 3355 * .marshaller(Json5.<jsf>DEFAULT_READABLE</jsf>) 3356 * .build(); 3357 * </p> 3358 * 3359 * @param value The values to add to this setting. 3360 * <br>Can be <jk>null</jk> (value will not be set, existing serializers and parsers will be kept). 3361 * @return This object. 3362 */ 3363 public SELF marshaller(Marshaller value) { 3364 if (nn(value)) 3365 serializer(value.getSerializer()).parser(value.getParser()); 3366 return self(); 3367 } 3368 3369 /** 3370 * <i><l>RestClient</l> configuration property: </i> Marshalls 3371 * 3372 * <p> 3373 * Shortcut for specifying the serializers and parsers 3374 * using the serializer and parser defined in a marshaller. 3375 * 3376 * <h5 class='section'>Notes:</h5><ul> 3377 * <li class='note'>When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), 3378 * parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. 3379 * </ul> 3380 * 3381 * <h5 class='section'>Example:</h5> 3382 * <p class='bjava'> 3383 * <jc>// Create a client that uses JSON and XML transport using existing marshalls.</jc> 3384 * RestClient <jv>client</jv> = RestClient 3385 * .<jsm>create</jsm>() 3386 * .marshaller(Json.<jsf>DEFAULT_READABLE</jsf>, Xml.<jsf>DEFAULT_READABLE</jsf>) 3387 * .build(); 3388 * </p> 3389 * 3390 * @param value The values to add to this setting. 3391 * <br>Can contain <jk>null</jk> values (ignored). 3392 * @return This object. 3393 */ 3394 public SELF marshallers(Marshaller...value) { 3395 for (var m : value) 3396 if (nn(m)) 3397 serializer(m.getSerializer()).parser(m.getParser()); 3398 return self(); 3399 } 3400 3401 /** 3402 * Assigns maximum connection per route value. 3403 * 3404 * <h5 class='section'>Notes:</h5><ul> 3405 * <li class='note'>A positive value is applied to the connection manager the default 3406 * {@link #createConnectionManager()} builds, which is then always a {@link PoolingHttpClientConnectionManager} 3407 * (as if {@link #pooled()} had been called), and to its internal pool for SSRF-guard-active {@code @Remote} calls. 3408 * <li class='note'>It has no effect on a connection manager supplied through 3409 * {@link #connectionManager(HttpClientConnectionManager)} or an overridden {@link #createConnectionManager()}. 3410 * </ul> 3411 * 3412 * @param maxConnPerRoute New property value. 3413 * @return This object. 3414 * @see HttpClientBuilder#setMaxConnPerRoute(int) 3415 */ 3416 public SELF maxConnPerRoute(int maxConnPerRoute) { 3417 this.maxConnPerRoute = maxConnPerRoute; 3418 httpClientBuilder().setMaxConnPerRoute(maxConnPerRoute); 3419 return self(); 3420 } 3421 3422 /** 3423 * Assigns maximum total connection value. 3424 * 3425 * <h5 class='section'>Notes:</h5><ul> 3426 * <li class='note'>A positive value is applied to the connection manager the default 3427 * {@link #createConnectionManager()} builds, which is then always a {@link PoolingHttpClientConnectionManager} 3428 * (as if {@link #pooled()} had been called), and to its internal pool for SSRF-guard-active {@code @Remote} calls. 3429 * Each pool applies the limit on its own (see {@link #createConnectionManager()}). 3430 * <li class='note'>It has no effect on a connection manager supplied through 3431 * {@link #connectionManager(HttpClientConnectionManager)} or an overridden {@link #createConnectionManager()}. 3432 * </ul> 3433 * 3434 * @param maxConnTotal New property value. 3435 * @return This object. 3436 * @see HttpClientBuilder#setMaxConnTotal(int) 3437 */ 3438 public SELF maxConnTotal(int maxConnTotal) { 3439 this.maxConnTotal = maxConnTotal; 3440 httpClientBuilder().setMaxConnTotal(maxConnTotal); 3441 return self(); 3442 } 3443 3444 /** 3445 * <i><l>BeanTraverse</l> configuration property: </i> Max serialization depth. 3446 * 3447 * <p> 3448 * When enabled, abort traversal if specified depth is reached in the POJO tree. 3449 * 3450 * <p> 3451 * If this depth is exceeded, an exception is thrown. 3452 * 3453 * <p> 3454 * This prevents stack overflows from occurring when trying to traverse models with recursive references. 3455 * 3456 * <h5 class='section'>Example:</h5> 3457 * <p class='bjava'> 3458 * <jc>// Create a REST client with JSON serializer that throws an exception if the depth reaches greater than 20.</jc> 3459 * RestClient <jv>client</jv> = RestClient 3460 * .<jsm>create</jsm>() 3461 * .json() 3462 * .maxDepth(20) 3463 * .build(); 3464 * </p> 3465 * 3466 * <h5 class='section'>See Also:</h5><ul> 3467 * <li class='jm'>{@link org.apache.juneau.marshall.MarshallingTraverseContext.Builder#maxDepth(int) MarshallingTraverseContext.Builder#maxDepth(int)} 3468 * </ul> 3469 * 3470 * @param value 3471 * The new value for this property. 3472 * <br>The default is <c>100</c>. 3473 * @return This object. 3474 */ 3475 public SELF maxDepth(int value) { 3476 serializers().forEach(x -> x.maxDepth(value)); 3477 return self(); 3478 } 3479 3480 /** 3481 * <i><l>WriterSerializer</l> configuration property: </i> Maximum indentation. 3482 * 3483 * <p> 3484 * Specifies the maximum indentation level in the serialized document. 3485 * 3486 * <h5 class='section'>Notes:</h5><ul> 3487 * <li class='note'>This setting does not apply to the RDF serializers. 3488 * </ul> 3489 * 3490 * <h5 class='section'>Example:</h5> 3491 * <p class='bjava'> 3492 * <jc>// Create a REST client with JSON serializer that indents a maximum of 20 tabs.</jc> 3493 * RestClient <jv>client</jv> = RestClient 3494 * .<jsm>create</jsm>() 3495 * .json() 3496 * .ws() <jc>// Enable whitespace</jc> 3497 * .maxIndent(20) 3498 * .build(); 3499 * </p> 3500 * 3501 * <h5 class='section'>See Also:</h5><ul> 3502 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#maxIndent(int) WriterSerializer.Builder#maxIndent(int)} 3503 * </ul> 3504 * 3505 * @param value 3506 * The new value for this property. 3507 * <br>The default is <c>100</c>. 3508 * @return This object. 3509 */ 3510 public SELF maxIndent(int value) { 3511 serializers().forEachWS(x -> x.maxIndent(value)); 3512 return self(); 3513 } 3514 3515 /** 3516 * Appends the <c>Accept</c> and <c>Content-Type</c> headers on all requests made by this client. 3517 * 3518 * <p> 3519 * Headers are appended to the end of the current header list. 3520 * 3521 * <p> 3522 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>), ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 3523 * 3524 * @param value The new header values. 3525 * <br>Can be <jk>null</jk> (no Accept or Content-Type headers will be added). 3526 * @return This object. 3527 * @see #headers() 3528 */ 3529 @Override 3530 public SELF mediaType(MediaType value) { 3531 super.mediaType(value); 3532 return headers(Accept.of(value), ContentType.of(value)); 3533 } 3534 3535 /** 3536 * Appends the <c>Accept</c> and <c>Content-Type</c> headers on all requests made by this client. 3537 * 3538 * <p> 3539 * Headers are appended to the end of the current header list. 3540 * 3541 * <p> 3542 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>), ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 3543 * 3544 * @param value The new header values. 3545 * <br>Can be <jk>null</jk> (no Accept or Content-Type headers will be added). 3546 * @return This object. 3547 * @see #headers() 3548 */ 3549 public SELF mediaType(String value) { 3550 super.mediaType(MediaType.of(value)); 3551 return headers(Accept.of(value), ContentType.of(value)); 3552 } 3553 3554 /** 3555 * Convenience method for specifying BSON as the marshalling transmission media type. 3556 * 3557 * <p> 3558 * BSON (Binary JSON) is the format used by MongoDB. Identical to calling 3559 * <c>serializer(BsonSerializer.<jk>class</jk>).parser(BsonParser.<jk>class</jk>)</c>. 3560 * 3561 * @return This object. 3562 */ 3563 public SELF bson() { 3564 return serializer(BsonSerializer.class).parser(BsonParser.class); 3565 } 3566 3567 /** 3568 * Convenience method for specifying MessagePack as the marshalling transmission media type. 3569 * 3570 * <p> 3571 * MessagePack is a binary equivalent to JSON that takes up considerably less space than JSON. 3572 * 3573 * <p> 3574 * {@link MsgPackSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3575 * <ul> 3576 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3577 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3578 * </ul> 3579 * <p> 3580 * {@link MsgPackParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3581 * <ul> 3582 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3583 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3584 * </ul> 3585 * <p> 3586 * <c>Accept</c> request header will be set to <js>"application/msgpack"</js> unless overridden 3587 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3588 * <p> 3589 * <c>Content-Type</c> request header will be set to <js>"application/msgpack"</js> unless overridden 3590 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3591 * <p> 3592 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 3593 * <ul> 3594 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3595 * last-enabled language if the headers are not set. 3596 * </ul> 3597 * <p> 3598 * Identical to calling <c>serializer(MsgPackSerializer.<jk>class</jk>).parser(MsgPackParser.<jk>class</jk>)</c>. 3599 * 3600 * <h5 class='section'>Example:</h5> 3601 * <p class='bjava'> 3602 * <jc>// Construct a client that uses MessagePack marshalling.</jc> 3603 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().msgPack().build(); 3604 * </p> 3605 * 3606 * @return This object. 3607 */ 3608 public SELF msgPack() { 3609 return serializer(MsgPackSerializer.class).parser(MsgPackParser.class); 3610 } 3611 3612 /** 3613 * When called, <c>No-Trace: true</c> is added to requests. 3614 * 3615 * <p> 3616 * This gives the opportunity for the servlet to not log errors on invalid requests. 3617 * This is useful for testing purposes when you don't want your log file to show lots of errors that are simply the 3618 * results of testing. 3619 * 3620 * <p> 3621 * It's up to the server to decide whether to allow for this. 3622 * The <c>BasicTestRestLogger</c> class watches for this header and prevents logging of status 400+ responses to 3623 * prevent needless logging of test scenarios. 3624 * 3625 * @return This object. 3626 * @see #headers() 3627 */ 3628 public SELF noTrace() { 3629 return headers(NoTrace.of(true)); 3630 } 3631 3632 /** 3633 * <i><l>OpenApiCommon</l> configuration property: </i> Default collection format for HTTP parts. 3634 * 3635 * <p> 3636 * Specifies the collection format to use for HTTP parts when not otherwise specified via {@link Schema#collectionFormat()} for the 3637 * OpenAPI serializer and parser on this client. 3638 * 3639 * <h5 class='section'>Example:</h5> 3640 * <p class='bjava'> 3641 * <jc>// Create a REST client with CSV format for http parts.</jc> 3642 * RestClient <jv>client</jv> = RestClient 3643 * .<jsm>create</jsm>() 3644 * .collectionFormat(<jsf>CSV</jsf>) 3645 * .build(); 3646 * 3647 * <jc>// An arbitrary data structure.</jc> 3648 * AList <jv>list</jv> = AList.<jsm>of</jsm>( 3649 * <js>"foo"</js>, 3650 * <js>"bar"</js>, 3651 * AMap.<jsm>of</jsm>( 3652 * <js>"baz"</js>, AList.<jsm>of</jsm>(<js>"qux"</js>,<js>"true"</js>,<js>"123"</js>) 3653 * ) 3654 * ); 3655 * 3656 * <jc>// Set a header with a comma-separated list.</jc> 3657 * <jv>client</jv> 3658 * .get(<js>"/uri"</js>) 3659 * .header(<js>"Foo"</js>, <jv>list</jv>) <jc>// Will be serialized as: foo=bar,baz=qux\,true\,123</jc> 3660 * .run(); 3661 * </p> 3662 * 3663 * <ul class='values javatree'> 3664 * <li class='jc'>{@link HttpPartCollectionFormat} 3665 * <ul> 3666 * <li class='jf'>{@link HttpPartCollectionFormat#CSV CSV} - (default) Comma-separated values (e.g. <js>"foo,bar"</js>). 3667 * <li class='jf'>{@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. <js>"foo bar"</js>). 3668 * <li class='jf'>{@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. <js>"foo\tbar"</js>). 3669 * <li class='jf'>{@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. <js>"foo|bar"</js>). 3670 * <li class='jf'>{@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. <js>"foo=bar&foo=baz"</js>). 3671 * <li class='jf'>{@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. <js>"@(foo,bar)"</js>). 3672 * </ul> 3673 * </ul> 3674 * 3675 * <h5 class='section'>See Also:</h5><ul> 3676 * <li class='jm'>{@link org.apache.juneau.marshall.oapi.OpenApiSerializer.Builder#collectionFormat(HttpPartCollectionFormat) OpenApiSerializer.Builder#collectionFormat(HttpPartCollectionFormat)} 3677 * <li class='jm'>{@link org.apache.juneau.marshall.oapi.OpenApiParser.Builder#collectionFormat(HttpPartCollectionFormat) OpenApiParser.Builder#collectionFormat(HttpPartCollectionFormat)} 3678 * </ul> 3679 * 3680 * @param value 3681 * The new value for this property. 3682 * <br>The default value is {@link HttpPartCollectionFormat#NO_COLLECTION_FORMAT}. 3683 * <br>Cannot be <jk>null</jk>. 3684 * @return This object. 3685 */ 3686 public SELF oapiCollectionFormat(HttpPartCollectionFormat value) { 3687 serializers().forEach(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); 3688 parsers().forEach(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); 3689 partSerializer().builder(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); 3690 partParser().builder(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); 3691 return self(); 3692 } 3693 3694 /** 3695 * <i><l>OpenApiCommon</l> configuration property: </i> Default OpenAPI format for HTTP parts. 3696 * 3697 * <p> 3698 * Specifies the format to use for HTTP parts when not otherwise specified via {@link Schema#format()} for 3699 * the OpenAPI serializer and parser on this client. 3700 * 3701 * <h5 class='section'>Example:</h5> 3702 * <p class='bjava'> 3703 * <jc>// Create a REST client with UON part serialization and parsing.</jc> 3704 * RestClient <jv>client</jv> = RestClient 3705 * .<jsm>create</jsm>() 3706 * .oapiFormat(<jsf>UON</jsf>) 3707 * .build(); 3708 * 3709 * <jc>// Set a header with a value in UON format.</jc> 3710 * <jv>client</jv> 3711 * .get(<js>"/uri"</js>) 3712 * .header(<js>"Foo"</js>, <js>"bar baz"</js>) <jc>// Will be serialized as: 'bar baz'</jc> 3713 * .run(); 3714 * </p> 3715 * 3716 * <ul class='values javatree'> 3717 * <li class='jc'>{@link HttpPartFormat} 3718 * <ul> 3719 * <li class='jf'>{@link HttpPartFormat#UON UON} - UON notation (e.g. <js>"'foo bar'"</js>). 3720 * <li class='jf'>{@link HttpPartFormat#INT32 INT32} - Signed 32 bits. 3721 * <li class='jf'>{@link HttpPartFormat#INT64 INT64} - Signed 64 bits. 3722 * <li class='jf'>{@link HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number. 3723 * <li class='jf'>{@link HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number. 3724 * <li class='jf'>{@link HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters. 3725 * <li class='jf'>{@link HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. <js>"00FF"</js>). 3726 * <li class='jf'>{@link HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Spaced-separated hexadecimal encoded octets (e.g. <js>"00 FF"</js>). 3727 * <li class='jf'>{@link HttpPartFormat#DATE DATE} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>. 3728 * <li class='jf'>{@link HttpPartFormat#DATE_TIME DATE_TIME} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>. 3729 * <li class='jf'>{@link HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured. 3730 * <li class='jf'>{@link HttpPartFormat#NO_FORMAT NO_FORMAT} - (default) Not specified. 3731 * </ul> 3732 * </ul> 3733 * 3734 * <h5 class='section'>See Also:</h5><ul> 3735 * <li class='jm'>{@link org.apache.juneau.marshall.oapi.OpenApiSerializer.Builder#format(HttpPartFormat) OpenApiSerializer.Builder#format(HttpPartFormat)} 3736 * <li class='jm'>{@link org.apache.juneau.marshall.oapi.OpenApiParser.Builder#format(HttpPartFormat) OpenApiParser.Builder#format(HttpPartFormat)} 3737 * </ul> 3738 * 3739 * @param value 3740 * The new value for this property. 3741 * <br>The default value is {@link HttpPartFormat#NO_FORMAT}. 3742 * <br>Cannot be <jk>null</jk>. 3743 * @return This object. 3744 */ 3745 public SELF oapiFormat(HttpPartFormat value) { 3746 serializers().forEach(OpenApiSerializer.Builder.class, x -> x.format(value)); 3747 parsers().forEach(OpenApiParser.Builder.class, x -> x.format(value)); 3748 partSerializer().builder(OpenApiSerializer.Builder.class).ifPresent(x -> x.format(value)); 3749 partParser().builder(OpenApiParser.Builder.class).ifPresent(x -> x.format(value)); 3750 return self(); 3751 } 3752 3753 /** 3754 * Convenience method for specifying OpenAPI as the marshalling transmission media type. 3755 * 3756 * <p> 3757 * OpenAPI is a language that allows serialization to formats that use {@link HttpPartSchema} objects to describe their structure. 3758 * 3759 * <p> 3760 * {@link OpenApiSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3761 * <ul> 3762 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3763 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3764 * <li>Typically the {@link RestRequest#content(Object, HttpPartSchema)} method will be used to specify the body of the request with the 3765 * schema describing it's structure. 3766 * </ul> 3767 * <p> 3768 * {@link OpenApiParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3769 * <ul> 3770 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3771 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3772 * <li>Typically the {@link ResponseContent#schema(HttpPartSchema)} method will be used to specify the structure of the response body. 3773 * </ul> 3774 * <p> 3775 * <c>Accept</c> request header will be set to <js>"text/openapi"</js> unless overridden 3776 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3777 * <p> 3778 * <c>Content-Type</c> request header will be set to <js>"text/openapi"</js> unless overridden 3779 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3780 * <p> 3781 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 3782 * <ul> 3783 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3784 * last-enabled language if the headers are not set. 3785 * </ul> 3786 * <p> 3787 * Identical to calling <c>serializer(OpenApiSerializer.<jk>class</jk>).parser(OpenApiParser.<jk>class</jk>)</c>. 3788 * 3789 * <h5 class='section'>Example:</h5> 3790 * <p class='bjava'> 3791 * <jc>// Construct a client that uses OpenAPI marshalling.</jc> 3792 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().openApi().build(); 3793 * </p> 3794 * 3795 * @return This object. 3796 */ 3797 public SELF openApi() { 3798 return serializer(OpenApiSerializer.class).parser(OpenApiParser.class); 3799 } 3800 3801 /** 3802 * <i><l>UonSerializer</l> configuration property: </i> Parameter format. 3803 * 3804 * <p> 3805 * Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. 3806 * 3807 * <p> 3808 * Specifies the format to use for GET parameter keys and values. 3809 * 3810 * <h5 class='section'>Example:</h5> 3811 * <p class='bjava'> 3812 * <jc>// Create a REST client with URL-Encoded serializer that serializes values in plain-text format.</jc> 3813 * RestClient <jv>client</jv> = RestClient 3814 * .<jsm>create</jsm>() 3815 * .urlEnc() 3816 * .paramFormat(<jsf>PLAINTEXT</jsf>) 3817 * .build(); 3818 * 3819 * <jc>// An arbitrary data structure.</jc> 3820 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>( 3821 * <js>"foo"</js>, <js>"bar"</js>, 3822 * <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>} 3823 * ); 3824 * 3825 * <jc>// Request body will be serialized as: foo=bar,baz=qux,true,123</jc> 3826 * <jv>client</jv> 3827 * .post(<js>"/uri"</js>, <jv>map</jv>) 3828 * .run(); 3829 * </p> 3830 * 3831 * <ul class='values javatree'> 3832 * <li class='jf'>{@link ParamFormat#UON} (default) - Use UON notation for parameters. 3833 * <li class='jf'>{@link ParamFormat#PLAINTEXT} - Use plain text for parameters. 3834 * </ul> 3835 * 3836 * <h5 class='section'>See Also:</h5><ul> 3837 * <li class='jm'>{@link org.apache.juneau.marshall.uon.UonSerializer.Builder#paramFormat(ParamFormat) UonSerializer.Builder#paramFormat(ParamFormat)} 3838 * </ul> 3839 * 3840 * @param value The new value for this property. 3841 * <br>Cannot be <jk>null</jk>. 3842 * @return This object. 3843 */ 3844 public SELF paramFormat(ParamFormat value) { 3845 serializers().forEach(UonSerializer.Builder.class, x -> x.paramFormat(value)); 3846 return self(); 3847 } 3848 3849 /** 3850 * <i><l>UonSerializer</l> configuration property: </i> Parameter format. 3851 * 3852 * <p> 3853 * Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. 3854 * 3855 * <p> 3856 * Specifies plaintext as the format to use for GET parameter keys and values. 3857 * 3858 * <h5 class='section'>Example:</h5> 3859 * <p class='bjava'> 3860 * <jc>// Create a REST client with URL-Encoded serializer that serializes values in plain-text format.</jc> 3861 * RestClient <jv>client</jv> = RestClient 3862 * .<jsm>create</jsm>() 3863 * .urlEnc() 3864 * .build(); 3865 * 3866 * <jc>// An arbitrary data structure.</jc> 3867 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>( 3868 * <js>"foo"</js>, <js>"bar"</js>, 3869 * <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>} 3870 * ); 3871 * 3872 * <jc>// Request body will be serialized as: foo=bar,baz=qux,true,123</jc> 3873 * <jv>client</jv> 3874 * .post(<js>"/uri"</js>, <jv>map</jv>) 3875 * .run(); 3876 * </p> 3877 * 3878 * <h5 class='section'>See Also:</h5><ul> 3879 * <li class='jm'>{@link org.apache.juneau.marshall.uon.UonSerializer.Builder#paramFormatPlain() UonSerializer.Builder#paramFormatPlain()} 3880 * </ul> 3881 * 3882 * @return This object. 3883 */ 3884 public SELF paramFormatPlain() { 3885 serializers().forEach(UonSerializer.Builder.class, UonSerializer.Builder::paramFormatPlain); 3886 return self(); 3887 } 3888 3889 /** 3890 * Parser. 3891 * 3892 * <p> 3893 * Associates the specified {@link Parser Parser} with the HTTP client. 3894 * 3895 * <p> 3896 * The parser is used to parse the HTTP response body into a POJO. 3897 * 3898 * <h5 class='section'>Notes:</h5><ul> 3899 * <li class='note'>When using this method that takes in a class, the parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3900 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3901 * </ul> 3902 * 3903 * <h5 class='section'>Example:</h5> 3904 * <p class='bjava'> 3905 * <jc>// Create a client that uses JSON transport for response bodies.</jc> 3906 * RestClient <jv>client</jv> = RestClient 3907 * .<jsm>create</jsm>() 3908 * .parser(JsonParser.<jk>class</jk>) 3909 * .build(); 3910 * </p> 3911 * 3912 * @param value 3913 * The new value for this setting. 3914 * <br>The default value is {@link JsonParser#DEFAULT}. 3915 * @return This object. 3916 */ 3917 public SELF parser(Class<? extends Parser> value) { 3918 return parsers(value); 3919 } 3920 3921 /** 3922 * Parser. 3923 * 3924 * <p> 3925 * Associates the specified {@link Parser Parser} with the HTTP client. 3926 * 3927 * <p> 3928 * The parser is used to parse the HTTP response body into a POJO. 3929 * 3930 * <h5 class='section'>Notes:</h5><ul> 3931 * <li class='note'>When using this method that takes in a pre-instantiated parser, the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3932 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 3933 * on this builder class have no effect. 3934 * </ul> 3935 * 3936 * <h5 class='section'>Example:</h5> 3937 * <p class='bjava'> 3938 * <jc>// Create a client that uses a predefined JSON parser for response bodies.</jc> 3939 * RestClient <jv>client</jv> = RestClient 3940 * .<jsm>create</jsm>() 3941 * .parser(JsonParser.<jsf>DEFAULT</jsf>) 3942 * .build(); 3943 * </p> 3944 * 3945 * @param value 3946 * The new value for this setting. 3947 * <br>The default value is {@link JsonParser#DEFAULT}. 3948 * <br>Cannot be <jk>null</jk>. 3949 * @return This object. 3950 */ 3951 public SELF parser(Parser value) { 3952 return parsers(value); 3953 } 3954 3955 /** 3956 * Returns the parser group sub-builder. 3957 * 3958 * @return The parser group sub-builder. 3959 */ 3960 public final ParserSet.Builder parsers() { 3961 if (parsers == null) 3962 parsers = createParsers(); 3963 return parsers; 3964 } 3965 3966 /** 3967 * Parsers. 3968 * 3969 * <p> 3970 * Associates the specified {@link Parser Parsers} with the HTTP client. 3971 * 3972 * <p> 3973 * The parsers are used to parse the HTTP response body into a POJO. 3974 * 3975 * <p> 3976 * The parser that best matches the <c>Accept</c> header will be used to parse the response body. 3977 * <br>If no <c>Accept</c> header is specified, the first parser in the list will be used. 3978 * 3979 * <h5 class='section'>Notes:</h5><ul> 3980 * <li class='note'>When using this method that takes in classes, the parsers can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 3981 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3982 * </ul> 3983 * 3984 * <h5 class='section'>Example:</h5> 3985 * <p class='bjava'> 3986 * <jc>// Create a client that uses JSON and XML transport for response bodies.</jc> 3987 * RestClient <jv>client</jv> = RestClient 3988 * .<jsm>create</jsm>() 3989 * .parser(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>) 3990 * .build(); 3991 * </p> 3992 * 3993 * @param value 3994 * The new value for this setting. 3995 * <br>The default value is {@link JsonParser#DEFAULT}. 3996 * @return This object. 3997 */ 3998 public SELF parsers(Class<? extends Parser>...value) { 3999 reqnns("value", value); 4000 parsers().add(value); 4001 return self(); 4002 } 4003 4004 /** 4005 * Parsers. 4006 * 4007 * <p> 4008 * Associates the specified {@link Parser Parsers} with the HTTP client. 4009 * 4010 * <p> 4011 * The parsers are used to parse the HTTP response body into a POJO. 4012 * 4013 * <p> 4014 * The parser that best matches the <c>Accept</c> header will be used to parse the response body. 4015 * <br>If no <c>Accept</c> header is specified, the first parser in the list will be used. 4016 * 4017 * <h5 class='section'>Notes:</h5><ul> 4018 * <li class='note'>When using this method that takes in pre-instantiated parsers, the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 4019 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 4020 * on this builder class have no effect. 4021 * </ul> 4022 * 4023 * <h5 class='section'>Example:</h5> 4024 * <p class='bjava'> 4025 * <jc>// Create a client that uses JSON and XML transport for response bodies.</jc> 4026 * RestClient <jv>client</jv> = RestClient 4027 * .<jsm>create</jsm>() 4028 * .parser(JsonParser.<jsf>DEFAULT</jsf>, XmlParser.<jsf>DEFAULT</jsf>) 4029 * .build(); 4030 * </p> 4031 * 4032 * @param value 4033 * The new value for this setting. 4034 * <br>The default value is {@link JsonParser#DEFAULT}. 4035 * @return This object. 4036 */ 4037 public SELF parsers(Parser...value) { 4038 reqnns("value", value); 4039 parsers().add(value); 4040 return self(); 4041 } 4042 4043 /** 4044 * Returns the part parser sub-builder. 4045 * 4046 * @return The part parser sub-builder. 4047 */ 4048 public final HttpPartParser.Creator partParser() { 4049 if (partParser == null) 4050 partParser = createPartParser(); 4051 return partParser; 4052 } 4053 4054 /** 4055 * Part parser. 4056 * 4057 * <p> 4058 * The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. 4059 * 4060 * <p> 4061 * The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. 4062 * 4063 * <h5 class='section'>Example:</h5> 4064 * <p class='bjava'> 4065 * <jc>// Create a client that uses UON format by default for incoming HTTP parts.</jc> 4066 * RestClient <jv>client</jv> = RestClient 4067 * .<jsm>create</jsm>() 4068 * .partParser(UonParser.<jk>class</jk>) 4069 * .build(); 4070 * </p> 4071 * 4072 * @param value 4073 * The new value for this setting. 4074 * <br>The default value is {@link OpenApiParser}. 4075 * <br>Cannot be <jk>null</jk>. 4076 * @return This object. 4077 */ 4078 public SELF partParser(Class<? extends HttpPartParser> value) { 4079 partParser().type(reqnn("value", value)); 4080 return self(); 4081 } 4082 4083 /** 4084 * Part parser. 4085 * 4086 * <p> 4087 * The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. 4088 * 4089 * <p> 4090 * The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. 4091 * 4092 * <h5 class='section'>Example:</h5> 4093 * <p class='bjava'> 4094 * <jc>// Create a client that uses UON format by default for incoming HTTP parts.</jc> 4095 * RestClient <jv>client</jv> = RestClient 4096 * .<jsm>create</jsm>() 4097 * .partParser(UonParser.<jsf>DEFAULT</jsf>) 4098 * .build(); 4099 * </p> 4100 * 4101 * @param value 4102 * The new value for this setting. 4103 * <br>The default value is {@link OpenApiParser}. 4104 * <br>Cannot be <jk>null</jk>. 4105 * @return This object. 4106 */ 4107 public SELF partParser(HttpPartParser value) { 4108 partParser().impl(reqnn("value", value)); 4109 return self(); 4110 } 4111 4112 /** 4113 * Returns the part serializer sub-builder. 4114 * 4115 * @return The part serializer sub-builder. 4116 */ 4117 public final HttpPartSerializer.Creator partSerializer() { 4118 if (partSerializer == null) 4119 partSerializer = createPartSerializer(); 4120 return partSerializer; 4121 } 4122 4123 /** 4124 * Part serializer. 4125 * 4126 * <p> 4127 * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. 4128 * 4129 * <p> 4130 * The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. 4131 * 4132 * <h5 class='section'>Example:</h5> 4133 * <p class='bjava'> 4134 * <jc>// Create a client that uses UON format by default for outgoing HTTP parts.</jc> 4135 * RestClient <jv>client</jv> = RestClient 4136 * .<jsm>create</jsm>() 4137 * .partSerializer(UonSerializer.<jk>class</jk>) 4138 * .build(); 4139 * </p> 4140 * 4141 * @param value 4142 * The new value for this setting. 4143 * <br>The default value is {@link OpenApiSerializer}. 4144 * <br>Cannot be <jk>null</jk>. 4145 * @return This object. 4146 */ 4147 public SELF partSerializer(Class<? extends HttpPartSerializer> value) { 4148 partSerializer().type(reqnn("value", value)); 4149 return self(); 4150 } 4151 4152 /** 4153 * Part serializer. 4154 * 4155 * <p> 4156 * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. 4157 * 4158 * <p> 4159 * The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. 4160 * 4161 * <h5 class='section'>Example:</h5> 4162 * <p class='bjava'> 4163 * <jc>// Create a client that uses UON format by default for outgoing HTTP parts.</jc> 4164 * RestClient <jv>client</jv> = RestClient 4165 * .<jsm>create</jsm>() 4166 * .partSerializer(UonSerializer.<jsf>DEFAULT</jsf>) 4167 * .build(); 4168 * </p> 4169 * 4170 * @param value 4171 * The new value for this setting. 4172 * <br>The default value is {@link OpenApiSerializer}. 4173 * <br>Cannot be <jk>null</jk>. 4174 * @return This object. 4175 */ 4176 public SELF partSerializer(HttpPartSerializer value) { 4177 partSerializer().impl(reqnn("value", value)); 4178 return self(); 4179 } 4180 4181 /** 4182 * Returns the builder for the list of path data parameters that get applied to all requests created by this builder. 4183 * 4184 * <p> 4185 * This is the primary method for accessing the path data parameter list. 4186 * On first call, the builder is created via the method {@link #createFormData()}. 4187 * 4188 * <h5 class='section'>Example:</h5> 4189 * <p class='bjava'> 4190 * <jc>// Create a client that uses "bar" for the "{foo}" path variable on every request.</jc> 4191 * RestClient.Builder<?> <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 4192 * <jv>builder</jv>.pathData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 4193 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 4194 * </p> 4195 * 4196 * <p> 4197 * The following convenience methods are also provided for updating the parameters: 4198 * <ul> 4199 * <li class='jm'>{@link #pathData(NameValuePair...)} 4200 * <li class='jm'>{@link #pathDataDefault(NameValuePair...)} 4201 * <li class='jm'>{@link #pathData(String,String)} 4202 * <li class='jm'>{@link #pathData(String,Supplier)} 4203 * </ul> 4204 * 4205 * @return The form data list builder. 4206 */ 4207 public final PartList pathData() { 4208 if (pathData == null) 4209 pathData = createPathData(); 4210 return pathData; 4211 } 4212 4213 /** 4214 * Sets multiple path parameters on all requests. 4215 * 4216 * <h5 class='section'>Example:</h5> 4217 * <p class='bjava'> 4218 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 4219 * 4220 * RestClient <jv>client</jv> = RestClient 4221 * .<jsm>create</jsm>() 4222 * .pathData( 4223 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 4224 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 4225 * ) 4226 * .build(); 4227 * </p> 4228 * 4229 * <p> 4230 * This is a shortcut for calling <c>pathData().append(<jv>parts</jv>)</c>. 4231 * 4232 * @param parts 4233 * The path parameters. 4234 * <br>Can contain <jk>null</jk> values (ignored). 4235 * @return This object. 4236 * @see #pathData() 4237 */ 4238 public SELF pathData(NameValuePair...parts) { 4239 pathData().append(parts); 4240 return self(); 4241 } 4242 4243 /** 4244 * Appends a path parameter to all request bodies. 4245 * 4246 * <h5 class='section'>Example:</h5> 4247 * <p class='bjava'> 4248 * RestClient <jv>client</jv> = RestClient 4249 * .<jsm>create</jsm>() 4250 * .pathData(<js>"foo"</js>, <js>"bar"</js>) 4251 * .build(); 4252 * </p> 4253 * 4254 * <p> 4255 * This is a shortcut for calling <c>pathData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4256 * 4257 * @param name The parameter name. 4258 * <br>Cannot be <jk>null</jk>. 4259 * @param value The parameter value. 4260 * <br>Can be <jk>null</jk> (null value will be serialized). 4261 * @return This object. 4262 * @see #pathData() 4263 */ 4264 public SELF pathData(String name, String value) { 4265 pathData().append(name, value); 4266 return self(); 4267 } 4268 4269 /** 4270 * Sets a path parameter with a dynamic value to all request bodies. 4271 * 4272 * <h5 class='section'>Example:</h5> 4273 * <p class='bjava'> 4274 * RestClient <jv>client</jv> = RestClient 4275 * .<jsm>create</jsm>() 4276 * .pathData(<js>"foo"</js>, ()-><js>"bar"</js>) 4277 * .build(); 4278 * </p> 4279 * 4280 * <p> 4281 * This is a shortcut for calling <c>pathData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4282 * 4283 * @param name The parameter name. 4284 * <br>Cannot be <jk>null</jk>. 4285 * @param value The parameter value supplier. 4286 * <br>Can be <jk>null</jk> (null value will be serialized). 4287 * @return This object. 4288 * @see #pathData() 4289 */ 4290 public SELF pathData(String name, Supplier<String> value) { 4291 pathData().set(name, value); 4292 return self(); 4293 } 4294 4295 /** 4296 * Sets default path parameter values. 4297 * 4298 * <p> 4299 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 4300 * 4301 * <h5 class='section'>Example:</h5> 4302 * <p class='bjava'> 4303 * RestClient <jv>client</jv> = RestClient 4304 * .<jsm>create</jsm>() 4305 * .pathDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 4306 * .build(); 4307 * </p> 4308 * 4309 * <p> 4310 * This is a shortcut for calling <c>pathData().setDefault(<jv>parts</jv>)</c>. 4311 * 4312 * @param parts The parts. 4313 * <br>Can contain <jk>null</jk> values (ignored). 4314 * @return This object. 4315 * @see #pathData() 4316 */ 4317 public SELF pathDataDefault(NameValuePair...parts) { 4318 pathData().setDefault(parts); 4319 return self(); 4320 } 4321 4322 /** 4323 * Convenience method for specifying Plain Text as the marshalling transmission media type. 4324 * 4325 * <p> 4326 * Plain text marshalling typically only works on simple POJOs that can be converted to and from strings using 4327 * swaps, swap methods, etc... 4328 * 4329 * <p> 4330 * {@link PlainTextSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 4331 * <ul> 4332 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 4333 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4334 * </ul> 4335 * <p> 4336 * {@link PlainTextParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 4337 * <ul> 4338 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 4339 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4340 * </ul> 4341 * <p> 4342 * <c>Accept</c> request header will be set to <js>"text/plain"</js> unless overridden 4343 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 4344 * <p> 4345 * <c>Content-Type</c> request header will be set to <js>"text/plain"</js> unless overridden 4346 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 4347 * <p> 4348 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 4349 * <ul> 4350 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 4351 * last-enabled language if the headers are not set. 4352 * </ul> 4353 * <p> 4354 * Identical to calling <c>serializer(PlainTextSerializer.<jk>class</jk>).parser(PlainTextParser.<jk>class</jk>)</c>. 4355 * 4356 * <h5 class='section'>Example:</h5> 4357 * <p class='bjava'> 4358 * <jc>// Construct a client that uses Plain Text marshalling.</jc> 4359 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().plainText().build(); 4360 * </p> 4361 * 4362 * @return This object. 4363 */ 4364 public SELF plainText() { 4365 return serializer(PlainTextSerializer.class).parser(PlainTextParser.class); 4366 } 4367 4368 /** 4369 * When called, the {@link #createConnectionManager()} method will return a {@link PoolingHttpClientConnectionManager} 4370 * instead of a {@link BasicHttpClientConnectionManager}. 4371 * 4372 * <h5 class='section'>Example:</h5> 4373 * <p class='bjava'> 4374 * <jc>// Construct a client that uses pooled connections.</jc> 4375 * RestClient <jv>client</jv> = RestClient 4376 * .<jsm>create</jsm>() 4377 * .pooled() 4378 * .build(); 4379 * </p> 4380 * 4381 * @return This object. 4382 */ 4383 public SELF pooled() { 4384 this.pooled = true; 4385 return self(); 4386 } 4387 4388 /** 4389 * Assigns default proxy value. 4390 * 4391 * <h5 class='section'>Notes:</h5><ul> 4392 * <li class='note'>This value can be overridden by the {@link #routePlanner(HttpRoutePlanner)} method. 4393 * <li class='warn'>Through a proxy, the SSRF guard's pin-on-connect would check the proxy's address rather than 4394 * the target's, so an SSRF-guard-active {@code @Remote} call (one without the 4395 * {@link #allowPrivateUrls(boolean) allowPrivateUrls} opt-in) on a proxy route is refused. This applies to any 4396 * proxy route, including one chosen by a {@link #routePlanner(HttpRoutePlanner) route planner} or the proxy 4397 * system properties ({@link #useSystemProperties()}). Ordinary requests are unaffected. 4398 * </ul> 4399 * 4400 * @param proxy New property value. 4401 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4402 * @return This object. 4403 * @see HttpClientBuilder#setProxy(HttpHost) 4404 */ 4405 public SELF proxy(HttpHost proxy) { 4406 httpClientBuilder().setProxy(proxy); 4407 return self(); 4408 } 4409 4410 /** 4411 * Assigns {@link AuthenticationStrategy} instance for proxy authentication. 4412 * 4413 * @param proxyAuthStrategy New property value. 4414 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4415 * @return This object. 4416 * @see HttpClientBuilder#setProxyAuthenticationStrategy(AuthenticationStrategy) 4417 */ 4418 public SELF proxyAuthenticationStrategy(AuthenticationStrategy proxyAuthStrategy) { 4419 httpClientBuilder().setProxyAuthenticationStrategy(proxyAuthStrategy); 4420 return self(); 4421 } 4422 4423 /** 4424 * Assigns file containing public suffix matcher. 4425 * 4426 * <h5 class='section'>Notes:</h5><ul> 4427 * <li class='note'>Instances of this class can be created with {@link PublicSuffixMatcherLoader}. 4428 * <li class='note'>It is used for cookies and, in both pools of the connection manager the default 4429 * {@link #createConnectionManager()} builds, by the default hostname verifier (one is used unless 4430 * {@link #sslHostnameVerifier(HostnameVerifier)} or {@link #sslSocketFactory(LayeredConnectionSocketFactory)} 4431 * is set). 4432 * </ul> 4433 * 4434 * @param publicSuffixMatcher New property value. 4435 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4436 * @return This object. 4437 * @see HttpClientBuilder#setPublicSuffixMatcher(PublicSuffixMatcher) 4438 */ 4439 public SELF publicSuffixMatcher(PublicSuffixMatcher publicSuffixMatcher) { 4440 this.publicSuffixMatcher = publicSuffixMatcher; 4441 httpClientBuilder().setPublicSuffixMatcher(publicSuffixMatcher); 4442 return self(); 4443 } 4444 4445 /** 4446 * Returns the builder for the list of query parameters that get applied to all requests created by this builder. 4447 * 4448 * <p> 4449 * This is the primary method for accessing the query parameter list. 4450 * On first call, the builder is created via the method {@link #createQueryData()}. 4451 * 4452 * <h5 class='section'>Example:</h5> 4453 * <p class='bjava'> 4454 * <jc>// Create a client that adds a "foo=bar" query parameter on every request.</jc> 4455 * RestClient.Builder<?> <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 4456 * <jv>builder</jv>.queryData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 4457 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 4458 * </p> 4459 * 4460 * <p> 4461 * The following convenience methods are also provided for updating the parameters: 4462 * <ul> 4463 * <li class='jm'>{@link #queryData(NameValuePair...)} 4464 * <li class='jm'>{@link #queryDataDefault(NameValuePair...)} 4465 * <li class='jm'>{@link #queryData(String,String)} 4466 * <li class='jm'>{@link #queryData(String,Supplier)} 4467 * </ul> 4468 * 4469 * @return The query data list builder. 4470 */ 4471 public final PartList queryData() { 4472 if (queryData == null) 4473 queryData = createQueryData(); 4474 return queryData; 4475 } 4476 4477 /** 4478 * Appends multiple query parameters to the URI of all requests. 4479 * 4480 * <h5 class='section'>Example:</h5> 4481 * <p class='bjava'> 4482 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 4483 * 4484 * RestClient <jv>client</jv> = RestClient 4485 * .<jsm>create</jsm>() 4486 * .queryData( 4487 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 4488 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 4489 * ) 4490 * .build(); 4491 * </p> 4492 * 4493 * <p> 4494 * This is a shortcut for calling <c>queryData().append(<jv>parts</jv>)</c>. 4495 * 4496 * @param parts 4497 * The query parameters. 4498 * <br>Can contain <jk>null</jk> values (ignored). 4499 * @return This object. 4500 * @see #queryData() 4501 */ 4502 public SELF queryData(NameValuePair...parts) { 4503 queryData().append(parts); 4504 return self(); 4505 } 4506 4507 /** 4508 * Appends a query parameter to the URI. 4509 * 4510 * <h5 class='section'>Example:</h5> 4511 * <p class='bjava'> 4512 * RestClient <jv>client</jv> = RestClient 4513 * .<jsm>create</jsm>() 4514 * .queryData(<js>"foo"</js>, <js>"bar"</js>) 4515 * .build(); 4516 * </p> 4517 * 4518 * <p> 4519 * This is a shortcut for calling <c>queryData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4520 * 4521 * @param name The parameter name. 4522 * <br>Cannot be <jk>null</jk>. 4523 * @param value The parameter value. 4524 * <br>Can be <jk>null</jk> (null value will be serialized). 4525 * @return This object. 4526 * @see #queryData() 4527 */ 4528 public SELF queryData(String name, String value) { 4529 queryData().append(name, value); 4530 return self(); 4531 } 4532 4533 /** 4534 * Appends a query parameter with a dynamic value to the URI. 4535 * 4536 * <h5 class='section'>Example:</h5> 4537 * <p class='bjava'> 4538 * RestClient <jv>client</jv> = RestClient 4539 * .<jsm>create</jsm>() 4540 * .queryData(<js>"foo"</js>, ()-><js>"bar"</js>) 4541 * .build(); 4542 * </p> 4543 * 4544 * <p> 4545 * This is a shortcut for calling <c>queryData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4546 * 4547 * @param name The parameter name. 4548 * <br>Cannot be <jk>null</jk>. 4549 * @param value The parameter value supplier. 4550 * <br>Can be <jk>null</jk> (null value will be serialized). 4551 * @return This object. 4552 * @see #queryData() 4553 */ 4554 public SELF queryData(String name, Supplier<String> value) { 4555 queryData().append(name, value); 4556 return self(); 4557 } 4558 4559 /** 4560 * Sets default query parameter values. 4561 * 4562 * <p> 4563 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 4564 * 4565 * <h5 class='section'>Example:</h5> 4566 * <p class='bjava'> 4567 * RestClient <jv>client</jv> = RestClient 4568 * .<jsm>create</jsm>() 4569 * .queryDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 4570 * .build(); 4571 * </p> 4572 * 4573 * <p> 4574 * This is a shortcut for calling <c>queryData().setDefault(<jv>parts</jv>)</c>. 4575 * 4576 * @param parts The parts. 4577 * <br>Can contain <jk>null</jk> values (ignored). 4578 * @return This object. 4579 * @see #queryData() 4580 */ 4581 public SELF queryDataDefault(NameValuePair...parts) { 4582 queryData().setDefault(parts); 4583 return self(); 4584 } 4585 4586 /** 4587 * Sets a default query parameter with serializer session options (UON map) for all requests. 4588 * 4589 * <p> 4590 * This is a shortcut for <c>queryDataDefault(stringPart({@link RestSharedConstants#QUERY_juneauSerializerOptions}, <jv>uon</jv>))</c>. 4591 * 4592 * @param uon 4593 * Serializer session options in UON form. 4594 * <br>Can be <jk>null</jk> (default is not set by this method). 4595 * @return This object. 4596 */ 4597 public SELF serializerSessionOptionsQueryDefault(String uon) { 4598 if (uon != null) 4599 queryDataDefault(stringPart(QUERY_juneauSerializerOptions, uon)); 4600 return self(); 4601 } 4602 4603 /** 4604 * Sets a default query parameter with parser session options (UON map) for all requests. 4605 * 4606 * <p> 4607 * This is a shortcut for <c>queryDataDefault(stringPart({@link RestSharedConstants#QUERY_juneauParserOptions}, <jv>uon</jv>))</c>. 4608 * 4609 * @param uon 4610 * Parser session options in UON form. 4611 * <br>Can be <jk>null</jk> (default is not set by this method). 4612 * @return This object. 4613 */ 4614 public SELF parserSessionOptionsQueryDefault(String uon) { 4615 if (uon != null) 4616 queryDataDefault(stringPart(QUERY_juneauParserOptions, uon)); 4617 return self(); 4618 } 4619 4620 /** 4621 * Convenience for {@link #serializerSessionOptionsQueryDefault(String)} using {@link Uon#of(Object)}. 4622 * 4623 * @param properties 4624 * Property names and values for serializer session options. 4625 * <br>Can be <jk>null</jk> (treated as an empty map). 4626 * @return This object. 4627 */ 4628 public SELF serializerSessionOptionsQueryDefault(Map<String,?> properties) { 4629 try { 4630 return serializerSessionOptionsQueryDefault(isEmpty(properties) ? null : Uon.of(properties)); 4631 } catch (SerializeException e) { 4632 throw rex(e, "Could not serialize serializer session options query default"); 4633 } 4634 } 4635 4636 /** 4637 * Convenience for {@link #parserSessionOptionsQueryDefault(String)} using {@link Uon#of(Object)}. 4638 * 4639 * @param properties 4640 * Property names and values for parser session options. 4641 * <br>Can be <jk>null</jk> (treated as an empty map). 4642 * @return This object. 4643 */ 4644 public SELF parserSessionOptionsQueryDefault(Map<String,?> properties) { 4645 try { 4646 return parserSessionOptionsQueryDefault(isEmpty(properties) ? null : Uon.of(properties)); 4647 } catch (SerializeException e) { 4648 throw rex(e, "Could not serialize parser session options query default"); 4649 } 4650 } 4651 4652 /** 4653 * <i><l>WriterSerializer</l> configuration property: </i> Quote character. 4654 * 4655 * <p> 4656 * Specifies the character to use for quoting attributes and values. 4657 * 4658 * <h5 class='section'>Notes:</h5><ul> 4659 * <li class='note'>This setting does not apply to the RDF serializers. 4660 * </ul> 4661 * 4662 * <h5 class='section'>Example:</h5> 4663 * <p class='bjava'> 4664 * <jc>// Create a REST client with JSON serializer that uses single quotes.</jc> 4665 * RestClient <jv>client</jv> = RestClient 4666 * .<jsm>create</jsm>() 4667 * .json() 4668 * .quoteChar(<js>'\''</js>) 4669 * .build(); 4670 * 4671 * <jc>// A bean with a single property</jc> 4672 * <jk>public class</jk> MyBean { 4673 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 4674 * } 4675 * 4676 * <jc>// Request body will contain: {'foo':'bar'}</jc> 4677 * <jv>client</jv> 4678 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 4679 * .run(); 4680 * </p> 4681 * 4682 * <h5 class='section'>See Also:</h5><ul> 4683 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#quoteChar(char) WriterSerializer.Builder#quoteChar(char)} 4684 * </ul> 4685 * 4686 * @param value 4687 * The new value for this property. 4688 * <br>The default is <js>'"'</js>. 4689 * @return This object. 4690 */ 4691 public SELF quoteChar(char value) { 4692 serializers().forEachWS(x -> x.quoteChar(value)); 4693 return self(); 4694 } 4695 4696 /** 4697 * Assigns {@link RedirectStrategy} instance. 4698 * 4699 * <h5 class='section'>Notes:</h5><ul> 4700 * <li class='note'>This value can be overridden by the {@link #disableRedirectHandling()} method. 4701 * </ul> 4702 * 4703 * @param redirectStrategy New property value. 4704 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4705 * @return This object. 4706 * @see HttpClientBuilder#setRedirectStrategy(RedirectStrategy) 4707 */ 4708 public SELF redirectStrategy(RedirectStrategy redirectStrategy) { 4709 httpClientBuilder().setRedirectStrategy(redirectStrategy); 4710 return self(); 4711 } 4712 4713 /** 4714 * Assigns {@link HttpRequestExecutor} instance. 4715 * 4716 * @param requestExec New property value. 4717 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4718 * @return This object. 4719 * @see HttpClientBuilder#setRequestExecutor(HttpRequestExecutor) 4720 */ 4721 public SELF requestExecutor(HttpRequestExecutor requestExec) { 4722 httpClientBuilder().setRequestExecutor(requestExec); 4723 return self(); 4724 } 4725 4726 /** 4727 * Assigns {@link HttpRequestRetryHandler} instance. 4728 * 4729 * <h5 class='section'>Notes:</h5><ul> 4730 * <li class='note'>This value can be overridden by the {@link #disableAutomaticRetries()} method. 4731 * </ul> 4732 * 4733 * @param retryHandler New property value. 4734 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4735 * @return This object. 4736 * @see HttpClientBuilder#setRetryHandler(HttpRequestRetryHandler) 4737 */ 4738 public SELF retryHandler(HttpRequestRetryHandler retryHandler) { 4739 httpClientBuilder().setRetryHandler(retryHandler); 4740 return self(); 4741 } 4742 4743 /** 4744 * <i><l>RestClient</l> configuration property: </i> Root URI. 4745 * 4746 * <p> 4747 * When set, relative URI strings passed in through the various rest call methods (e.g. {@link RestClient#get(Object)} 4748 * will be prefixed with the specified root. 4749 * <br>This root URI is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URI string. 4750 * 4751 * <h5 class='section'>Example:</h5> 4752 * <p class='bjava'> 4753 * <jc>// Create a client that uses UON format by default for HTTP parts.</jc> 4754 * RestClient <jv>client</jv> = RestClient 4755 * .<jsm>create</jsm>() 4756 * .rootUrl(<js>"http://localhost:10000/foo"</js>) 4757 * .build(); 4758 * 4759 * Bar <jv>bar</jv> = <jv>client</jv> 4760 * .get(<js>"/bar"</js>) <jc>// Relative to http://localhost:10000/foo</jc> 4761 * .run() 4762 * .getContent().as(Bar.<jk>class</jk>); 4763 * </p> 4764 * 4765 * @param value 4766 * The root URI to prefix to relative URI strings. 4767 * <br>Trailing slashes are trimmed. 4768 * <br>Usually a <c>String</c> but you can also pass in <c>URI</c> and <c>URL</c> objects as well. 4769 * <br>Can be <jk>null</jk> (no root URL will be set). 4770 * @return This object. 4771 */ 4772 public SELF rootUrl(Object value) { 4773 var s = s(value); 4774 if (ine(s)) 4775 s = s.replaceAll("\\/$", ""); 4776 if (isEmpty(s)) 4777 rootUrl = null; 4778 else if (s.indexOf("://") == -1) 4779 throw rex("Invalid rootUrl value: '%s'. Must be a valid absolute URL.", value); 4780 else { 4781 final var url = s; 4782 rootUrl = () -> url; 4783 } 4784 return self(); 4785 } 4786 4787 /** 4788 * Sets the root URL using a supplier, allowing the URL to be computed dynamically at request time. 4789 * 4790 * <p> 4791 * The supplier is called on every request, so the URL can change between requests. 4792 * No build-time validation is performed; invalid values will surface as errors when requests are made. 4793 * 4794 * <p> 4795 * Three common patterns are supported: 4796 * <ul> 4797 * <li> 4798 * <b>Swappable reference</b> — Use an {@link AtomicReference AtomicReference} 4799 * when you need to point the client at a different host at any time: 4800 * <p class='bjava'> 4801 * AtomicReference<String> <jv>urlRef</jv> = <jk>new</jk> AtomicReference<>(<js>"https://host1"</js>); 4802 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().rootUrl(<jv>urlRef</jv>::get).build(); 4803 * 4804 * <jc>// Switch to a different host at runtime.</jc> 4805 * <jv>urlRef</jv>.set(<js>"https://host2"</js>); 4806 * </p> 4807 * <li> 4808 * <b>Refreshable cached value</b> — Use {@link Memoizer Memoizer} 4809 * (via {@link Suppliers#memoizer(Supplier) Suppliers.memoizer()}) 4810 * when computing the URL is expensive (e.g. service discovery) and you want it cached until explicitly refreshed: 4811 * <p class='bjava'> 4812 * Memoizer<String> <jv>url</jv> = <jsm>memoizer</jsm>(() -> serviceDiscovery.<jsm>findUrl</jsm>(<js>"my-service"</js>)); 4813 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().rootUrl(<jv>url</jv>).build(); 4814 * 4815 * <jc>// Force re-evaluation on the next request (e.g. after a failover).</jc> 4816 * <jv>url</jv>.reset(); 4817 * </p> 4818 * <li> 4819 * <b>Purely dynamic</b> — Use a plain lambda when the URL must be re-evaluated on every request: 4820 * <p class='bjava'> 4821 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().rootUrl(() -> config.<jsm>getRootUrl</jsm>()).build(); 4822 * </p> 4823 * </ul> 4824 * 4825 * @param value 4826 * A supplier that returns the root URI to prefix to relative URI strings. 4827 * <br>Can be <jk>null</jk> (no root URL will be set). 4828 * @return This object. 4829 */ 4830 public SELF rootUrl(Supplier<String> value) { 4831 rootUrl = value; 4832 return self(); 4833 } 4834 4835 /** 4836 * Assigns {@link HttpRoutePlanner} instance. 4837 * 4838 * @param routePlanner New property value. 4839 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4840 * @return This object. 4841 * @see HttpClientBuilder#setRoutePlanner(HttpRoutePlanner) 4842 */ 4843 public SELF routePlanner(HttpRoutePlanner routePlanner) { 4844 httpClientBuilder().setRoutePlanner(routePlanner); 4845 return self(); 4846 } 4847 4848 /** 4849 * Assigns {@link SchemePortResolver} instance. 4850 * 4851 * @param schemePortResolver New property value. 4852 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4853 * @return This object. 4854 * @see HttpClientBuilder#setSchemePortResolver(SchemePortResolver) 4855 */ 4856 public SELF schemePortResolver(SchemePortResolver schemePortResolver) { 4857 httpClientBuilder().setSchemePortResolver(schemePortResolver); 4858 return self(); 4859 } 4860 4861 /** 4862 * Serializer. 4863 * 4864 * <p> 4865 * Associates the specified {@link Serializer Serializer} with the HTTP client. 4866 * 4867 * <p> 4868 * The serializer is used to serialize POJOs into the HTTP request body. 4869 * 4870 * <h5 class='section'>Notes:</h5><ul> 4871 * <li class='note'>When using this method that takes in a class, the serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 4872 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4873 * </ul> 4874 * 4875 * <h5 class='section'>Example:</h5> 4876 * <p class='bjava'> 4877 * <jc>// Create a client that uses JSON transport for request bodies.</jc> 4878 * RestClient <jv>client</jv> = RestClient 4879 * .<jsm>create</jsm>() 4880 * .serializer(JsonSerializer.<jk>class</jk>) 4881 * .sortCollections() <jc>// Sort any collections being serialized.</jc> 4882 * .build(); 4883 * </p> 4884 * 4885 * @param value 4886 * The new value for this setting. 4887 * <br>The default is {@link JsonSerializer}. 4888 * @return This object. 4889 */ 4890 public SELF serializer(Class<? extends Serializer> value) { 4891 return serializers(value); 4892 } 4893 4894 /** 4895 * Serializer. 4896 * 4897 * <p> 4898 * Associates the specified {@link Serializer Serializer} with the HTTP client. 4899 * 4900 * <p> 4901 * The serializer is used to serialize POJOs into the HTTP request body. 4902 * 4903 * <h5 class='section'>Notes:</h5><ul> 4904 * <li class='note'>When using this method that takes in a pre-instantiated serializer, the serializer property setters (e.g. {@link #sortCollections()}) or 4905 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 4906 * on this builder class have no effect. 4907 * </ul> 4908 * 4909 * <h5 class='section'>Example:</h5> 4910 * <p class='bjava'> 4911 * <jc>// Create a client that uses a predefined JSON serializer request bodies.</jc> 4912 * RestClient <jv>client</jv> = RestClient 4913 * .<jsm>create</jsm>() 4914 * .serializer(JsonSerializer.<jsf>DEFAULT_READABLE</jsf>) 4915 * .build(); 4916 * </p> 4917 * 4918 * @param value 4919 * The new value for this setting. 4920 * <br>The default is {@link JsonSerializer}. 4921 * <br>Cannot be <jk>null</jk>. 4922 * @return This object. 4923 */ 4924 public SELF serializer(Serializer value) { 4925 return serializers(value); 4926 } 4927 4928 /** 4929 * Returns the serializer group sub-builder. 4930 * 4931 * @return The serializer group sub-builder. 4932 */ 4933 public final SerializerSet.Builder serializers() { 4934 if (serializers == null) 4935 serializers = createSerializers(); 4936 return serializers; 4937 } 4938 4939 /** 4940 * Serializers. 4941 * 4942 * <p> 4943 * Associates the specified {@link Serializer Serializers} with the HTTP client. 4944 * 4945 * <p> 4946 * The serializer is used to serialize POJOs into the HTTP request body. 4947 * 4948 * <p> 4949 * The serializer that best matches the <c>Content-Type</c> header will be used to serialize the request body. 4950 * <br>If no <c>Content-Type</c> header is specified, the first serializer in the list will be used. 4951 * 4952 * <h5 class='section'>Notes:</h5><ul> 4953 * <li class='note'>When using this method that takes in classes, the serializers can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 4954 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4955 * </ul> 4956 * 4957 * <h5 class='section'>Example:</h5> 4958 * <p class='bjava'> 4959 * <jc>// Create a client that uses JSON and XML transport for request bodies.</jc> 4960 * RestClient <jv>client</jv> = RestClient 4961 * .<jsm>create</jsm>() 4962 * .serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>) 4963 * .sortCollections() <jc>// Sort any collections being serialized.</jc> 4964 * .build(); 4965 * </p> 4966 * 4967 * @param value 4968 * The new value for this setting. 4969 * <br>The default is {@link JsonSerializer}. 4970 * @return This object. 4971 */ 4972 public SELF serializers(Class<? extends Serializer>...value) { 4973 reqnns("value", value); 4974 serializers().add(value); 4975 return self(); 4976 } 4977 4978 /** 4979 * Serializers. 4980 * 4981 * <p> 4982 * Associates the specified {@link Serializer Serializers} with the HTTP client. 4983 * 4984 * <p> 4985 * The serializer is used to serialize POJOs into the HTTP request body. 4986 * 4987 * <p> 4988 * The serializer that best matches the <c>Content-Type</c> header will be used to serialize the request body. 4989 * <br>If no <c>Content-Type</c> header is specified, the first serializer in the list will be used. 4990 * 4991 * <h5 class='section'>Notes:</h5><ul> 4992 * <li class='note'>When using this method that takes in a pre-instantiated serializers, the serializer property setters (e.g. {@link #sortCollections()}) or 4993 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 4994 * on this builder class have no effect. 4995 * </ul> 4996 * 4997 * <h5 class='section'>Example:</h5> 4998 * <p class='bjava'> 4999 * <jc>// Create a client that uses predefined JSON and XML serializers for request bodies.</jc> 5000 * RestClient <jv>client</jv> = RestClient 5001 * .<jsm>create</jsm>() 5002 * .serializers(JsonSerializer.<jsf>DEFAULT_READABLE</jsf>, XmlSerializer.<jsf>DEFAULT_READABLE</jsf>) 5003 * .build(); 5004 * </p> 5005 * 5006 * @param value 5007 * The new value for this setting. 5008 * <br>The default is {@link JsonSerializer}. 5009 * @return This object. 5010 */ 5011 public SELF serializers(Serializer...value) { 5012 reqnns("value", value); 5013 serializers().add(value); 5014 return self(); 5015 } 5016 5017 /** 5018 * Assigns {@link ServiceUnavailableRetryStrategy} instance. 5019 * 5020 * @param serviceUnavailStrategy New property value. 5021 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5022 * @return This object. 5023 * @see HttpClientBuilder#setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy) 5024 */ 5025 public SELF serviceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy serviceUnavailStrategy) { 5026 httpClientBuilder().setServiceUnavailableRetryStrategy(serviceUnavailStrategy); 5027 return self(); 5028 } 5029 5030 /** 5031 * Skip empty form data. 5032 * 5033 * <p> 5034 * When enabled, form data consisting of empty strings will be skipped on requests. 5035 * Note that <jk>null</jk> values are already skipped. 5036 * 5037 * <p> 5038 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5039 * 5040 * @return This object. 5041 */ 5042 public SELF skipEmptyFormData() { 5043 return skipEmptyFormData(true); 5044 } 5045 5046 /** 5047 * Skip empty form data. 5048 * 5049 * <p> 5050 * When enabled, form data consisting of empty strings will be skipped on requests. 5051 * Note that <jk>null</jk> values are already skipped. 5052 * 5053 * <p> 5054 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5055 * 5056 * @param value 5057 * The new value for this setting. 5058 * <br>The default is <jk>false</jk>. 5059 * @return This object. 5060 */ 5061 public SELF skipEmptyFormData(boolean value) { 5062 skipEmptyFormData = value; 5063 return self(); 5064 } 5065 5066 /** 5067 * Skip empty header data. 5068 * 5069 * <p> 5070 * When enabled, headers consisting of empty strings will be skipped on requests. 5071 * Note that <jk>null</jk> values are already skipped. 5072 * 5073 * <p> 5074 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5075 * 5076 * @return This object. 5077 */ 5078 public SELF skipEmptyHeaderData() { 5079 return skipEmptyHeaderData(true); 5080 } 5081 5082 /** 5083 * Skip empty header data. 5084 * 5085 * <p> 5086 * When enabled, headers consisting of empty strings will be skipped on requests. 5087 * Note that <jk>null</jk> values are already skipped. 5088 * 5089 * <p> 5090 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5091 * 5092 * @param value 5093 * The new value for this setting. 5094 * <br>The default is <jk>false</jk>. 5095 * @return This object. 5096 */ 5097 public SELF skipEmptyHeaderData(boolean value) { 5098 skipEmptyHeaderData = value; 5099 return self(); 5100 } 5101 5102 /** 5103 * Skip empty query data. 5104 * 5105 * <p> 5106 * When enabled, query parameters consisting of empty strings will be skipped on requests. 5107 * Note that <jk>null</jk> values are already skipped. 5108 * 5109 * <p> 5110 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5111 * 5112 * @return This object. 5113 */ 5114 public SELF skipEmptyQueryData() { 5115 return skipEmptyQueryData(true); 5116 } 5117 5118 /** 5119 * Skip empty query data. 5120 * 5121 * <p> 5122 * When enabled, query parameters consisting of empty strings will be skipped on requests. 5123 * Note that <jk>null</jk> values are already skipped. 5124 * 5125 * <p> 5126 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 5127 * 5128 * @param value 5129 * The new value for this setting. 5130 * <br>The default is <jk>false</jk>. 5131 * @return This object. 5132 */ 5133 public SELF skipEmptyQueryData(boolean value) { 5134 skipEmptyQueryData = value; 5135 return self(); 5136 } 5137 5138 /** 5139 * <i><l>Serializer</l> configuration property: </i> Sort arrays and collections alphabetically. 5140 * 5141 * <p> 5142 * When enabled, copies and sorts the contents of arrays and collections before serializing them. 5143 * 5144 * <p> 5145 * Note that this introduces a performance penalty since it requires copying the existing collection. 5146 * 5147 * <h5 class='section'>Example:</h5> 5148 * <p class='bjava'> 5149 * <jc>// Create a REST client with JSON serializer that sorts arrays and collections before serialization.</jc> 5150 * RestClient <jv>client</jv> = RestClient 5151 * .<jsm>create</jsm>() 5152 * .json() 5153 * .sortCollections() 5154 * .build(); 5155 * 5156 * <jc>// An unsorted array</jc> 5157 * String[] <jv>array</jv> = {<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>} 5158 * 5159 * <jc>// Request body will contain: ["bar","baz","foo"]</jc> 5160 * <jv>client</jv> 5161 * .post(<js>"http://localhost:10000/foo"</js>, <jv>array</jv>) 5162 * .run(); 5163 * </p> 5164 * 5165 * <h5 class='section'>See Also:</h5><ul> 5166 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#sortCollections() Serializer.Builder#sortCollections()} 5167 * </ul> 5168 * 5169 * @return This object. 5170 */ 5171 public SELF sortCollections() { 5172 serializers().forEach(Serializer.Builder::sortCollections); 5173 return self(); 5174 } 5175 5176 /** 5177 * <i><l>Serializer</l> configuration property: </i> Sort maps alphabetically. 5178 * 5179 * <p> 5180 * When enabled, copies and sorts the contents of maps by their keys before serializing them. 5181 * 5182 * <p> 5183 * Note that this introduces a performance penalty. 5184 * 5185 * <h5 class='section'>Example:</h5> 5186 * <p class='bjava'> 5187 * <jc>// Create a REST client with JSON serializer that sorts maps before serialization.</jc> 5188 * RestClient <jv>client</jv> = RestClient 5189 * .<jsm>create</jsm>() 5190 * .json() 5191 * .sortMaps() 5192 * .build(); 5193 * 5194 * <jc>// An unsorted map.</jc> 5195 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>(<js>"foo"</js>,1,<js>"bar"</js>,2,<js>"baz"</js>,3); 5196 * 5197 * <jc>// Request body will contain: {"bar":2,"baz":3,"foo":1}</jc> 5198 * <jv>client</jv> 5199 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 5200 * .run(); 5201 * </p> 5202 * 5203 * <h5 class='section'>See Also:</h5><ul> 5204 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#sortMaps() Serializer.Builder#sortMaps()} 5205 * </ul> 5206 * 5207 * @return This object. 5208 */ 5209 public SELF sortMaps() { 5210 serializers().forEach(Serializer.Builder::sortMaps); 5211 return self(); 5212 } 5213 5214 /** 5215 * <i><l>WriterSerializer</l> configuration property: </i> Quote character. 5216 * 5217 * <p> 5218 * Specifies to use single quotes for quoting attributes and values. 5219 * 5220 * <h5 class='section'>Notes:</h5><ul> 5221 * <li class='note'>This setting does not apply to the RDF serializers. 5222 * </ul> 5223 * 5224 * <h5 class='section'>Example:</h5> 5225 * <p class='bjava'> 5226 * <jc>// Create a REST client with JSON serializer that uses single quotes.</jc> 5227 * RestClient <jv>client</jv> = RestClient 5228 * .<jsm>create</jsm>() 5229 * .json() 5230 * .sq() 5231 * .build(); 5232 * 5233 * <jc>// A bean with a single property</jc> 5234 * <jk>public class</jk> MyBean { 5235 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5236 * } 5237 * 5238 * <jc>// Request body will contain: {'foo':'bar'}</jc> 5239 * <jv>client</jv> 5240 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5241 * .run(); 5242 * </p> 5243 * 5244 * <h5 class='section'>See Also:</h5><ul> 5245 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#quoteChar(char) WriterSerializer.Builder#quoteChar(char)} 5246 * </ul> 5247 * 5248 * @return This object. 5249 */ 5250 public SELF sq() { 5251 serializers().forEachWS(WriterSerializer.Builder::sq); 5252 return self(); 5253 } 5254 5255 /** 5256 * Assigns {@link SSLContext} instance. 5257 * 5258 * <p> 5259 * The context (for example one initialized with a custom trust store) is used for {@code https} connections, with 5260 * the {@link #sslHostnameVerifier(HostnameVerifier) hostname verifier} if one is set, or the default one. 5261 * 5262 * <h5 class='section'>Notes:</h5><ul> 5263 * <li class='note'>It is applied to the connection manager the default {@link #createConnectionManager()} builds, for 5264 * both ordinary requests and the internal pool for SSRF-guard-active {@code @Remote} calls. It has no effect on 5265 * a connection manager supplied through {@link #connectionManager(HttpClientConnectionManager)} or an overridden 5266 * {@link #createConnectionManager()}, which carries its own socket factories. 5267 * <li class='note'>An {@link #sslSocketFactory(LayeredConnectionSocketFactory) SSL socket factory} takes precedence 5268 * over this value, including one set with 5269 * {@link HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) setSSLSocketFactory} on a builder 5270 * passed to {@link #httpClientBuilder(HttpClientBuilder)} after this value was set (as in Apache 5271 * {@code HttpClientBuilder}). 5272 * </ul> 5273 * 5274 * @param sslContext New property value. 5275 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5276 * @return This object. 5277 * @see HttpClientBuilder#setSSLContext(SSLContext) 5278 */ 5279 public SELF sslContext(SSLContext sslContext) { 5280 this.sslContext = sslContext; 5281 httpClientBuilder().setSSLContext(sslContext); 5282 return self(); 5283 } 5284 5285 /** 5286 * Assigns {@link HostnameVerifier} instance. 5287 * 5288 * <h5 class='section'>Notes:</h5><ul> 5289 * <li class='note'>It is applied to the connection manager the default {@link #createConnectionManager()} builds, for 5290 * both ordinary requests and the internal pool for SSRF-guard-active {@code @Remote} calls. It has no effect on 5291 * a connection manager supplied through {@link #connectionManager(HttpClientConnectionManager)} or an overridden 5292 * {@link #createConnectionManager()}, which carries its own socket factories. 5293 * <li class='note'>An {@link #sslSocketFactory(LayeredConnectionSocketFactory) SSL socket factory} takes precedence 5294 * over this value, including one set with 5295 * {@link HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) setSSLSocketFactory} on a builder 5296 * passed to {@link #httpClientBuilder(HttpClientBuilder)} after this value was set (as in Apache 5297 * {@code HttpClientBuilder}). 5298 * </ul> 5299 * 5300 * @param hostnameVerifier New property value. 5301 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5302 * @return This object. 5303 * @see HttpClientBuilder#setSSLHostnameVerifier(HostnameVerifier) 5304 */ 5305 public SELF sslHostnameVerifier(HostnameVerifier hostnameVerifier) { 5306 this.sslHostnameVerifier = hostnameVerifier; 5307 httpClientBuilder().setSSLHostnameVerifier(hostnameVerifier); 5308 return self(); 5309 } 5310 5311 /** 5312 * Assigns {@link LayeredConnectionSocketFactory} instance. 5313 * 5314 * <p> 5315 * The factory is used for {@code https} connections, in place of one built from the 5316 * {@link #sslContext(SSLContext) SSL context} and {@link #sslHostnameVerifier(HostnameVerifier) hostname verifier}. 5317 * 5318 * <h5 class='section'>Notes:</h5><ul> 5319 * <li class='note'>It is applied to the connection manager the default {@link #createConnectionManager()} builds, for 5320 * both ordinary requests and the internal pool for SSRF-guard-active {@code @Remote} calls. It has no effect on 5321 * a connection manager supplied through {@link #connectionManager(HttpClientConnectionManager)} or an overridden 5322 * {@link #createConnectionManager()}, which carries its own socket factories. 5323 * <li class='warn'>For SSRF-guard-active {@code @Remote} calls, the factory is given the address the SSRF pin has 5324 * already checked (the {@code remoteAddress} of 5325 * {@link ConnectionSocketFactory#connectSocket(int, Socket, HttpHost, InetSocketAddress, InetSocketAddress, HttpContext) connectSocket}). 5326 * It must connect to exactly that address: a factory that ignores it and connects by host name (resolving 5327 * the name again) bypasses the pin. The same holds for a factory set with 5328 * {@link HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) setSSLSocketFactory} on a 5329 * builder passed to {@link #httpClientBuilder(HttpClientBuilder)}. 5330 * </ul> 5331 * 5332 * @param sslSocketFactory New property value. 5333 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5334 * @return This object. 5335 * @see HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) 5336 */ 5337 public SELF sslSocketFactory(LayeredConnectionSocketFactory sslSocketFactory) { 5338 this.sslSocketFactory = sslSocketFactory; 5339 httpClientBuilder().setSSLSocketFactory(sslSocketFactory); 5340 return self(); 5341 } 5342 5343 /** 5344 * Assigns {@link AuthenticationStrategy} instance for target host authentication. 5345 * 5346 * @param targetAuthStrategy New property value. 5347 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5348 * @return This object. 5349 * @see HttpClientBuilder#setTargetAuthenticationStrategy(AuthenticationStrategy) 5350 */ 5351 public SELF targetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy) { 5352 httpClientBuilder().setTargetAuthenticationStrategy(targetAuthStrategy); 5353 return self(); 5354 } 5355 5356 /** 5357 * <i><l>Serializer</l> configuration property: </i> Trim empty lists and arrays. 5358 * 5359 * <p> 5360 * When enabled, empty lists and arrays will not be serialized. 5361 * 5362 * <p> 5363 * Note that enabling this setting has the following effects on parsing: 5364 * <ul class='spaced-list'> 5365 * <li> 5366 * Map entries with empty list values will be lost. 5367 * <li> 5368 * Bean properties with empty list values will not be set. 5369 * </ul> 5370 * 5371 * <h5 class='section'>Example:</h5> 5372 * <p class='bjava'> 5373 * <jc>// Create a serializer that skips empty arrays and collections.</jc> 5374 * WriterSerializer <jv>serializer</jv> = JsonSerializer 5375 * .<jsm>create</jsm>() 5376 * .trimEmptyCollections() 5377 * .build(); 5378 * 5379 * <jc>// A bean with a field with an empty array.</jc> 5380 * <jk>public class</jk> MyBean { 5381 * <jk>public</jk> String[] <jf>foo</jf> = {}; 5382 * } 5383 * 5384 * <jc>// Request body will contain: {}</jc> 5385 * <jv>client</jv> 5386 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5387 * .run(); 5388 * </p> 5389 * 5390 * <h5 class='section'>See Also:</h5><ul> 5391 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#trimEmptyCollections() Serializer.Builder#trimEmptyCollections()} 5392 * </ul> 5393 * 5394 * @return This object. 5395 */ 5396 public SELF trimEmptyCollections() { 5397 serializers().forEach(Serializer.Builder::trimEmptyCollections); 5398 return self(); 5399 } 5400 5401 /** 5402 * <i><l>Serializer</l> configuration property: </i> Trim empty maps. 5403 * 5404 * <p> 5405 * When enabled, empty map values will not be serialized to the output. 5406 * 5407 * <p> 5408 * Note that enabling this setting has the following effects on parsing: 5409 * <ul class='spaced-list'> 5410 * <li> 5411 * Bean properties with empty map values will not be set. 5412 * </ul> 5413 * 5414 * <h5 class='section'>Example:</h5> 5415 * <p class='bjava'> 5416 * <jc>// Create a REST client with JSON serializer that skips empty maps.</jc> 5417 * RestClient <jv>client</jv> = RestClient 5418 * .<jsm>create</jsm>() 5419 * .json() 5420 * .trimEmptyMaps() 5421 * .build(); 5422 * 5423 * <jc>// A bean with a field with an empty map.</jc> 5424 * <jk>public class</jk> MyBean { 5425 * <jk>public</jk> AMap <jf>foo</jf> = AMap.<jsm>of</jsm>(); 5426 * } 5427 * 5428 * <jc>// Request body will contain: {}</jc> 5429 * <jv>client</jv> 5430 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5431 * .run(); 5432 * </p> 5433 * 5434 * <h5 class='section'>See Also:</h5><ul> 5435 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#trimEmptyMaps() Serializer.Builder#trimEmptyMaps()} 5436 * </ul> 5437 * 5438 * @return This object. 5439 */ 5440 public SELF trimEmptyMaps() { 5441 serializers().forEach(Serializer.Builder::trimEmptyMaps); 5442 return self(); 5443 } 5444 5445 /** 5446 * <i><l>Parser</l> configuration property: </i> Trim parsed strings. 5447 * 5448 * <p> 5449 * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being added to 5450 * the POJO. 5451 * 5452 * <h5 class='section'>Example:</h5> 5453 * <p class='bjava'> 5454 * <jc>// Create a REST client with JSON parser with trim-strings enabled.</jc> 5455 * RestClient <jv>client</jv> = RestClient 5456 * .<jsm>create</jsm>() 5457 * .json() 5458 * .trimStringsOnRead() 5459 * .build(); 5460 * 5461 * <jc>// Try to parse JSON containing {" foo ":" bar "}.</jc> 5462 * Map<String,String> <jv>map</jv> = <jv>client</jv> 5463 * .get(<js>"/pathToJson"</js>) 5464 * .run() 5465 * .getContent().as(HashMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 5466 * 5467 * <jc>// Make sure strings are trimmed.</jc> 5468 * <jsm>assertEquals</jsm>(<js>"bar"</js>, <jv>map</jv>.get(<js>"foo"</js>)); 5469 * </p> 5470 * 5471 * <h5 class='section'>See Also:</h5><ul> 5472 * <li class='jm'>{@link org.apache.juneau.marshall.parser.Parser.Builder#trimStrings() Parser.Builder#trimStrings()} 5473 * </ul> 5474 * 5475 * @return This object. 5476 */ 5477 public SELF trimStringsOnRead() { 5478 parsers().forEach(Parser.Builder::trimStrings); 5479 return self(); 5480 } 5481 5482 /** 5483 * <i><l>Serializer</l> configuration property: </i> Trim strings. 5484 * 5485 * <p> 5486 * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized. 5487 * 5488 * <h5 class='section'>Example:</h5> 5489 * <p class='bjava'> 5490 * <jc>// Create a REST client with JSON serializer that trims strings before serialization.</jc> 5491 * RestClient <jv>client</jv> = RestClient 5492 * .<jsm>create</jsm>() 5493 * .json() 5494 * .trimStrings() 5495 * .build(); 5496 * 5497 * <jc>// A map with space-padded keys/values</jc> 5498 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>(<js>" foo "</js>, <js>" bar "</js>); 5499 * 5500 * <jc>// Request body will contain: {"foo":"bar"}</jc> 5501 * <jv>client</jv> 5502 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 5503 * .run(); 5504 * </p> 5505 * 5506 * <h5 class='section'>See Also:</h5><ul> 5507 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#trimStrings() Serializer.Builder#trimStrings()} 5508 * </ul> 5509 * 5510 * @return This object. 5511 */ 5512 public SELF trimStringsOnWrite() { 5513 serializers().forEach(Serializer.Builder::trimStrings); 5514 return self(); 5515 } 5516 5517 /** 5518 * Convenience method for specifying all available transmission types. 5519 * 5520 * <p> 5521 * All basic Juneau serializers will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5522 * <ul> 5523 * <li>The serializers can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5524 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5525 * </ul> 5526 * <p> 5527 * All basic Juneau parsers will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5528 * <ul> 5529 * <li>The parsers can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 5530 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5531 * </ul> 5532 * <p> 5533 * <c>Accept</c> request header must be set via {@link #headers()}, or per-request 5534 * via {@link RestRequest#header(Header)} in order for the correct parser to be selected. 5535 * <p> 5536 * <c>Content-Type</c> request header must be set via {@link #headers()}, 5537 * or per-request via {@link RestRequest#header(Header)} in order for the correct serializer to be selected. 5538 * <p> 5539 * Similar to calling <c>json().json5().html().xml().uon().urlEnc().openApi().msgPack().plainText()</c>. 5540 * 5541 * <h5 class='section'>Example:</h5> 5542 * <p class='bjava'> 5543 * <jc>// Construct a client that uses universal marshalling.</jc> 5544 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().universal().build(); 5545 * </p> 5546 * 5547 * @return This object. 5548 */ 5549 public SELF universal() { 5550 // @formatter:off 5551 return 5552 serializers( 5553 JsonSerializer.class, 5554 JcsSerializer.class, 5555 Json5Serializer.class, 5556 Json5lSerializer.class, 5557 JsonlSerializer.class, 5558 HtmlSerializer.class, 5559 XmlSerializer.class, 5560 UonSerializer.class, 5561 UrlEncodingSerializer.class, 5562 OpenApiSerializer.class, 5563 BsonSerializer.class, 5564 CborSerializer.class, 5565 MsgPackSerializer.class, 5566 ParquetSerializer.class, 5567 PlainTextSerializer.class, 5568 TomlSerializer.class, 5569 HjsonSerializer.class, 5570 HoconSerializer.class, 5571 IniSerializer.class, 5572 PrototextSerializer.class, 5573 MarkdownSerializer.class 5574 ) 5575 .parsers( 5576 JsonParser.class, 5577 Json5Parser.class, 5578 Json5lParser.class, 5579 JsonlParser.class, 5580 XmlParser.class, 5581 HtmlParser.class, 5582 UonParser.class, 5583 UrlEncodingParser.class, 5584 OpenApiParser.class, 5585 BsonParser.class, 5586 CborParser.class, 5587 MsgPackParser.class, 5588 ParquetParser.class, 5589 PlainTextParser.class, 5590 TomlParser.class, 5591 HjsonParser.class, 5592 HoconParser.class, 5593 IniParser.class, 5594 PrototextParser.class, 5595 MarkdownParser.class 5596 ); 5597 // @formatter:on 5598 } 5599 5600 /** 5601 * Convenience method for specifying UON as the marshalling transmission media type. 5602 * 5603 * <p> 5604 * UON is Url-Encoding Object notation that is equivalent to JSON but suitable for transmission as URL-encoded 5605 * query and form post values. 5606 * 5607 * <p> 5608 * {@link UonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5609 * <ul> 5610 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5611 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5612 * </ul> 5613 * <p> 5614 * {@link UonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5615 * <ul> 5616 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 5617 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5618 * </ul> 5619 * <p> 5620 * <c>Accept</c> request header will be set to <js>"text/uon"</js> unless overridden 5621 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5622 * <p> 5623 * <c>Content-Type</c> request header will be set to <js>"text/uon"</js> unless overridden 5624 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5625 * <p> 5626 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5627 * <ul> 5628 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5629 * last-enabled language if the headers are not set. 5630 * </ul> 5631 * <p> 5632 * Identical to calling <c>serializer(UonSerializer.<jk>class</jk>).parser(UonParser.<jk>class</jk>)</c>. 5633 * 5634 * <h5 class='section'>Example:</h5> 5635 * <p class='bjava'> 5636 * <jc>// Construct a client that uses UON marshalling.</jc> 5637 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().uon().build(); 5638 * </p> 5639 * 5640 * @return This object. 5641 */ 5642 public SELF uon() { 5643 return serializer(UonSerializer.class).parser(UonParser.class); 5644 } 5645 5646 /** 5647 * <i><l>Serializer</l> configuration property: </i> URI context bean. 5648 * 5649 * <p> 5650 * Bean used for resolution of URIs to absolute or root-relative form. 5651 * 5652 * <h5 class='section'>Example:</h5> 5653 * <p class='bjava'> 5654 * <jc>// Our URI contextual information.</jc> 5655 * String <jv>authority</jv> = <js>"http://localhost:10000"</js>; 5656 * String <jv>contextRoot</jv> = <js>"/myContext"</js>; 5657 * String <jv>servletPath</jv> = <js>"/myServlet"</js>; 5658 * String <jv>pathInfo</jv> = <js>"/foo"</js>; 5659 * 5660 * <jc>// Create a UriContext object.</jc> 5661 * UriContext <jv>uriContext</jv> = <jk>new</jk> UriContext(<jv>authority</jv>, <jv>contextRoot</jv>, <jv>servletPath</jv>, <jv>pathInfo</jv>); 5662 * 5663 * <jc>// Create a REST client with JSON serializer and associate our context.</jc> 5664 * RestClient <jv>client</jv> = RestClient 5665 * .<jsm>create</jsm>() 5666 * .json() 5667 * .uriContext(<jv>uriContext</jv>) 5668 * .uriRelativity(<jsf>RESOURCE</jsf>) <jc>// Assume relative paths are relative to servlet.</jc> 5669 * .uriResolution(<jsf>ABSOLUTE</jsf>) <jc>// Serialize URIs as absolute paths.</jc> 5670 * .build(); 5671 * 5672 * <jc>// A relative URI</jc> 5673 * URI <jv>uri</jv> = <jk>new</jk> URI(<js>"bar"</js>); 5674 * 5675 * <jc>// Request body will contain: "http://localhost:10000/myContext/myServlet/foo/bar"</jc> 5676 * <jv>client</jv> 5677 * .post(<js>"http://localhost:10000/foo"</js>, <jv>uri</jv>) 5678 * .run(); 5679 * </p> 5680 * 5681 * <h5 class='section'>See Also:</h5><ul> 5682 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#uriContext(UriContext) Serializer.Builder#uriContext(UriContext)} 5683 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5684 * </ul> 5685 * 5686 * @param value The new value for this property. 5687 * <br>Cannot be <jk>null</jk>. 5688 * @return This object. 5689 */ 5690 public SELF uriContext(UriContext value) { 5691 serializers().forEach(x -> x.uriContext(reqnn("value", value))); 5692 return self(); 5693 } 5694 5695 /** 5696 * <i><l>Serializer</l> configuration property: </i> URI relativity. 5697 * 5698 * <p> 5699 * Defines what relative URIs are relative to when serializing any of the following: 5700 * <ul> 5701 * <li>{@link URI} 5702 * <li>{@link URL} 5703 * <li>Properties and classes annotated with {@link Uri @Uri} 5704 * </ul> 5705 * 5706 * <p> 5707 * See {@link #uriContext(UriContext)} for examples. 5708 * 5709 * <ul class='values javatree'> 5710 * <li class='jf'>{@link UriRelativity#RESOURCE} 5711 * - Relative URIs should be considered relative to the servlet URI. 5712 * <li class='jf'>{@link UriRelativity#PATH_INFO} 5713 * - Relative URIs should be considered relative to the request URI. 5714 * </ul> 5715 * 5716 * <h5 class='section'>See Also:</h5><ul> 5717 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#uriRelativity(UriRelativity) Serializer.Builder#uriRelativity(UriRelativity)} 5718 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5719 * </ul> 5720 * 5721 * @param value 5722 * The new value for this property. 5723 * <br>The default is {@link UriRelativity#RESOURCE} 5724 * <br>Cannot be <jk>null</jk>. 5725 * @return This object. 5726 */ 5727 public SELF uriRelativity(UriRelativity value) { 5728 serializers().forEach(x -> x.uriRelativity(reqnn("value", value))); 5729 return self(); 5730 } 5731 5732 /** 5733 * <i><l>Serializer</l> configuration property: </i> URI resolution. 5734 * 5735 * <p> 5736 * Defines the resolution level for URIs when serializing any of the following: 5737 * <ul> 5738 * <li>{@link URI} 5739 * <li>{@link URL} 5740 * <li>Properties and classes annotated with {@link Uri @Uri} 5741 * </ul> 5742 * 5743 * <p> 5744 * See {@link #uriContext(UriContext)} for examples. 5745 * 5746 * <ul class='values'> 5747 * <li class='jf'>{@link UriResolution#ABSOLUTE} 5748 * - Resolve to an absolute URI (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>). 5749 * <li class='jf'>{@link UriResolution#ROOT_RELATIVE} 5750 * - Resolve to a root-relative URI (e.g. <js>"/context-root/servlet-path/path-info"</js>). 5751 * <li class='jf'>{@link UriResolution#NONE} 5752 * - Don't do any URI resolution. 5753 * </ul> 5754 * 5755 * <h5 class='section'>See Also:</h5><ul> 5756 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.Serializer.Builder#uriResolution(UriResolution) Serializer.Builder#uriResolution(UriResolution)} 5757 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5758 * </ul> 5759 * 5760 * @param value 5761 * The new value for this property. 5762 * <br>The default is {@link UriResolution#NONE} 5763 * <br>Cannot be <jk>null</jk>. 5764 * @return This object. 5765 */ 5766 public SELF uriResolution(UriResolution value) { 5767 serializers().forEach(x -> x.uriResolution(reqnn("value", value))); 5768 return self(); 5769 } 5770 5771 /** 5772 * Convenience method for specifying URL-Encoding as the marshalling transmission media type. 5773 * 5774 * <p> 5775 * {@link UrlEncodingSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5776 * <ul> 5777 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5778 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5779 * <li>This serializer is NOT used when using the {@link RestRequest#formData(String, Object)} (and related) methods for constructing 5780 * the request body. Instead, the part serializer specified via {@link #partSerializer(Class)} is used. 5781 * </ul> 5782 * <p> 5783 * {@link UrlEncodingParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5784 * <ul> 5785 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 5786 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5787 * </ul> 5788 * <p> 5789 * <c>Accept</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden 5790 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5791 * <p> 5792 * <c>Content-Type</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden 5793 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5794 * <p> 5795 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5796 * <ul> 5797 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5798 * last-enabled language if the headers are not set. 5799 * </ul> 5800 * <p> 5801 * Identical to calling <c>serializer(UrlEncodingSerializer.<jk>class</jk>).parser(UrlEncodingParser.<jk>class</jk>)</c>. 5802 * 5803 * <h5 class='section'>Example:</h5> 5804 * <p class='bjava'> 5805 * <jc>// Construct a client that uses URL-Encoded marshalling.</jc> 5806 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().urlEnc().build(); 5807 * </p> 5808 * 5809 * @return This object. 5810 */ 5811 public SELF urlEnc() { 5812 return serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class); 5813 } 5814 5815 /** 5816 * Returns the URL-encoding serializer sub-builder. 5817 * 5818 * @return The URL-encoding serializer sub-builder. 5819 */ 5820 public final UrlEncodingSerializer.Builder urlEncodingSerializer() { 5821 if (urlEncodingSerializer == null) 5822 urlEncodingSerializer = createUrlEncodingSerializer(); 5823 return urlEncodingSerializer; 5824 } 5825 5826 5827 /** 5828 * Assigns {@link UserTokenHandler} instance. 5829 * 5830 * <h5 class='section'>Notes:</h5><ul> 5831 * <li class='note'>This value can be overridden by the {@link #disableConnectionState()} method. 5832 * </ul> 5833 * 5834 * @param userTokenHandler New property value. 5835 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5836 * @return This object. 5837 * @see HttpClientBuilder#setUserTokenHandler(UserTokenHandler) 5838 */ 5839 public SELF userTokenHandler(UserTokenHandler userTokenHandler) { 5840 httpClientBuilder().setUserTokenHandler(userTokenHandler); 5841 return self(); 5842 } 5843 5844 /** 5845 * Use system properties when creating and configuring default implementations. 5846 * 5847 * <h5 class='section'>Notes:</h5><ul> 5848 * <li class='note'>The system properties Apache {@link HttpClientBuilder} reads for its own connection manager 5849 * apply to both pools of the connection manager the default {@link #createConnectionManager()} builds, as 5850 * Apache applies them: with no {@link #sslContext(SSLContext) sslContext} or 5851 * {@link #sslSocketFactory(LayeredConnectionSocketFactory) sslSocketFactory}, {@code https} uses the JVM's 5852 * default SSL socket factory (the {@code javax.net.ssl.*} trust and key stores); {@code https.protocols} and 5853 * {@code https.cipherSuites} restrict the TLS versions and cipher suites (unless an {@code sslSocketFactory} 5854 * is set); and unless {@code http.keepAlive} is {@code false}, {@code http.maxConnections} (default 5) limits 5855 * the connections per route, and twice that in total, which makes the connection manager a pooling one. 5856 * {@link #maxConnTotal(int)} and {@link #maxConnPerRoute(int)} win over that limit. 5857 * <li class='note'>The proxy system properties apply too (see {@link #proxy(HttpHost)}). 5858 * </ul> 5859 * 5860 * @return This object. 5861 * @see HttpClientBuilder#useSystemProperties() 5862 */ 5863 public SELF useSystemProperties() { 5864 this.systemProperties = true; 5865 httpClientBuilder().useSystemProperties(); 5866 return self(); 5867 } 5868 5869 /** 5870 * <i><l>WriterSerializer</l> configuration property: </i> Use whitespace. 5871 * 5872 * <p> 5873 * When enabled, whitespace is added to the output to improve readability. 5874 * 5875 * <h5 class='section'>Example:</h5> 5876 * <p class='bjava'> 5877 * <jc>// Create a REST client with JSON serializer with whitespace enabled.</jc> 5878 * RestClient <jv>client</jv> = RestClient 5879 * .<jsm>create</jsm>() 5880 * .json() 5881 * .useWhitespace() 5882 * .build(); 5883 * 5884 * <jc>// A bean with a single property</jc> 5885 * <jk>public class</jk> MyBean { 5886 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5887 * } 5888 * 5889 * <jc>// Request body will contain: {\n\t"foo": "bar"\n\}\n</jc> 5890 * <jv>client</jv> 5891 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5892 * .run(); 5893 * </p> 5894 * 5895 * <h5 class='section'>See Also:</h5><ul> 5896 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#useWhitespace() WriterSerializer.Builder#useWhitespace()} 5897 * </ul> 5898 * @return This object. 5899 */ 5900 public SELF useWhitespace() { 5901 serializers().forEachWS(WriterSerializer.Builder::useWhitespace); 5902 return self(); 5903 } 5904 5905 /** 5906 * <i><l>WriterSerializer</l> configuration property: </i> Use whitespace. 5907 * 5908 * <p> 5909 * When enabled, whitespace is added to the output to improve readability. 5910 * 5911 * <h5 class='section'>Example:</h5> 5912 * <p class='bjava'> 5913 * <jc>// Create a REST client with JSON serializer with whitespace enabled.</jc> 5914 * RestClient <jv>client</jv> = RestClient 5915 * .<jsm>create</jsm>() 5916 * .json() 5917 * .ws() 5918 * .build(); 5919 * 5920 * <jc>// A bean with a single property</jc> 5921 * <jk>public class</jk> MyBean { 5922 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5923 * } 5924 * 5925 * <jc>// Request body will contain: {\n\t"foo": "bar"\n\}\n</jc> 5926 * <jv>client</jv> 5927 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5928 * .run(); 5929 * </p> 5930 * 5931 * <h5 class='section'>See Also:</h5><ul> 5932 * <li class='jm'>{@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#useWhitespace() WriterSerializer.Builder#useWhitespace()} 5933 * </ul> 5934 * 5935 * @return This object. 5936 */ 5937 public SELF ws() { 5938 serializers().forEachWS(WriterSerializer.Builder::ws); 5939 return self(); 5940 } 5941 5942 /** 5943 * Convenience method for specifying XML as the marshalling transmission media type. 5944 * 5945 * <p> 5946 * {@link XmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5947 * <ul> 5948 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5949 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5950 * </ul> 5951 * <p> 5952 * {@link XmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5953 * <ul> 5954 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #ignoreUnknownBeanProperties()}) or 5955 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5956 * </ul> 5957 * <p> 5958 * <c>Accept</c> request header will be set to <js>"text/xml"</js> unless overridden 5959 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5960 * <p> 5961 * <c>Content-Type</c> request header will be set to <js>"text/xml"</js> unless overridden 5962 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5963 * <p> 5964 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5965 * <ul> 5966 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5967 * last-enabled language if the headers are not set. 5968 * </ul> 5969 * <p> 5970 * Identical to calling <c>serializer(XmlSerializer.<jk>class</jk>).parser(XmlParser.<jk>class</jk>)</c>. 5971 * 5972 * <h5 class='section'>Example:</h5> 5973 * <p class='bjava'> 5974 * <jc>// Construct a client that uses XML marshalling.</jc> 5975 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().xml().build(); 5976 * </p> 5977 * 5978 * @return This object. 5979 */ 5980 public SELF xml() { 5981 return serializer(XmlSerializer.class).parser(XmlParser.class); 5982 } 5983 5984 /** 5985 * Creates the creator for the rest call handler. 5986 * 5987 * <p> 5988 * Subclasses can override this method to provide their own implementation. 5989 * 5990 * <p> 5991 * The default behavior creates a bean instantiator initialized to return a {@link BasicRestCallHandler}. 5992 * 5993 * @return The creator for the rest call handler. 5994 * @see #callHandler() 5995 */ 5996 protected BeanInstantiator.Builder<RestCallHandler> createCallHandler() { 5997 return BeanInstantiator.of(RestCallHandler.class, beanStore).type(BasicRestCallHandler.class); 5998 } 5999 6000 /** 6001 * Creates the {@link HttpClientConnectionManager} returned by {@link #createConnectionManager()}. 6002 * 6003 * <p> 6004 * Subclasses can override this method to provide their own connection manager. 6005 * 6006 * <p> 6007 * The default implementation returns a new {@link PoolingHttpClientConnectionManager} if {@link #pooled()}, 6008 * {@link #maxConnTotal(int)}, {@link #maxConnPerRoute(int)} or a positive 6009 * {@link #connectionTimeToLive(long, TimeUnit) connectionTimeToLive} was set, or the {@code http.maxConnections} 6010 * limit of {@link #useSystemProperties()} applies (unless {@code http.keepAlive} is {@code false}), or a new 6011 * {@link BasicHttpClientConnectionManager} if not. It applies the {@link #sslContext(SSLContext) sslContext}, 6012 * {@link #sslSocketFactory(LayeredConnectionSocketFactory) sslSocketFactory}, 6013 * {@link #sslHostnameVerifier(HostnameVerifier) sslHostnameVerifier}, {@code maxConnTotal} and {@code maxConnPerRoute} 6014 * settings, which Apache {@link HttpClientBuilder} ignores once a connection manager is set. It also applies, to 6015 * both pools, the other settings Apache {@code HttpClientBuilder} applies only to a connection manager it builds 6016 * itself, whether made through this builder or directly on the {@link #httpClientBuilder() HttpClientBuilder}: 6017 * {@link #publicSuffixMatcher(PublicSuffixMatcher) publicSuffixMatcher} (for the default hostname verifier), 6018 * {@link #defaultSocketConfig(SocketConfig) defaultSocketConfig}, 6019 * {@link #defaultConnectionConfig(ConnectionConfig) defaultConnectionConfig}, 6020 * {@link #connectionTimeToLive(long, TimeUnit) connectionTimeToLive} (which, like {@code maxConnTotal} and 6021 * {@code maxConnPerRoute}, makes it a {@code PoolingHttpClientConnectionManager}) and the TLS and 6022 * {@code http.maxConnections} system properties of {@link #useSystemProperties()}. A 6023 * {@link HttpClientBuilder#setDnsResolver(DnsResolver) DNS resolver} set on the {@code HttpClientBuilder} is used 6024 * for ordinary requests only: SSRF-guard-active {@code @Remote} calls always resolve through the SSRF pin. 6025 * Each of these settings made through this builder also survives {@link #httpClientBuilder(HttpClientBuilder)} 6026 * replacing the {@code HttpClientBuilder}, and always wins over the same setting on the {@code HttpClientBuilder}, 6027 * whichever was set last (unlike Apache's own last-write-wins). An SSL socket factory wins over any SSL context, 6028 * wherever each was set. The {@code HttpClientBuilder}'s settings are read from its fields (it has no getters); 6029 * if they cannot be read, this method throws an {@link IllegalStateException} rather than ignore them. 6030 * 6031 * <p> 6032 * Each {@link #build()} calls this method again, so a builder reused after {@code build()} gives every client its 6033 * own connection manager (closing one client does not shut down another's). A connection manager supplied through 6034 * {@link #connectionManager(HttpClientConnectionManager)} is used as is by every client built. 6035 * 6036 * <p> 6037 * SSRF-guard-active {@code @Remote} calls do not use this connection manager: the default 6038 * {@link #createHttpClient()} pairs it with a second, dedicated one whose connections are all opened through the 6039 * SSRF pin, so that a guarded call never reuses a connection an ordinary request opened. It can only do that 6040 * for the instance this default implementation returns. If an override returns any other connection manager, 6041 * the client refuses every SSRF-guard-active {@code @Remote} call (one without the 6042 * {@link #allowPrivateUrls(boolean) allowPrivateUrls} opt-in). 6043 * 6044 * <p> 6045 * The dedicated connection manager copies this one's settings before each guarded call, but it is a separate pool: 6046 * limits such as {@link PoolingHttpClientConnectionManager#setMaxTotal(int) maxTotal} apply to each pool on its 6047 * own, so a client that mixes ordinary and guarded calls can hold up to twice as many open connections. 6048 * 6049 * <h5 class='section'>Example:</h5> 6050 * <p class='bjava'> 6051 * <jc>// A Builder that provides it's own customized HttpClientConnectionManager.</jc> 6052 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 6053 * <ja>@Override</ja> 6054 * <jk>protected</jk> HttpClientConnectionManager createConnectionManager() { 6055 * <jk>return new</jk> PoolingHttpClientConnectionManager(); 6056 * } 6057 * } 6058 * 6059 * <jc>// Instantiate.</jc> 6060 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 6061 * </p> 6062 * 6063 * @return The connection manager to use. 6064 */ 6065 protected HttpClientConnectionManager createConnectionManager() { 6066 var s = HttpClientBuilderSettings.of(httpClientBuilder()); 6067 defaultRegistry = socketFactoryRegistry(s); 6068 defaultPoolSettings = poolSettings(s); 6069 defaultConnectionManager = newConnectionManager(defaultRegistry, or(s.dnsResolver(), SystemDefaultDnsResolver.INSTANCE), defaultPoolSettings); 6070 return defaultConnectionManager; 6071 } 6072 6073 // The socket factories for the TLS settings, chosen as Apache HttpClientBuilder chooses them when it builds its own 6074 // connection manager: the SSL socket factory, else one on the SSL context (else the JVM's default socket factory 6075 // under useSystemProperties(), else a default context) with the hostname verifier (else the default verifier on 6076 // the public suffix matcher) and, under useSystemProperties(), the https.protocols and https.cipherSuites. 6077 // A setting made through this builder always wins over the same setting read from the HttpClientBuilder, whichever 6078 // was set last. The two differ when httpClientBuilder(...) replaced the builder after the setting was made, or when 6079 // the setting was later made again directly on httpClientBuilder(). As in Apache, a socket factory wins over any 6080 // SSL context, wherever each was set. 6081 private Registry<ConnectionSocketFactory> socketFactoryRegistry(HttpClientBuilderSettings s) { 6082 var https = or(sslSocketFactory, s.sslSocketFactory()); 6083 if (https == null) { 6084 var sysProps = systemProperties || s.systemProperties(); 6085 var protocols = sysProps ? splitSystemProperty("https.protocols") : null; 6086 var cipherSuites = sysProps ? splitSystemProperty("https.cipherSuites") : null; 6087 var verifier = or(sslHostnameVerifier, s.hostnameVerifier()); 6088 if (verifier == null) 6089 verifier = new DefaultHostnameVerifier(or(publicSuffixMatcher, s.publicSuffixMatcher(), PublicSuffixMatcherLoader.getDefault())); 6090 var context = or(sslContext, s.sslContext()); 6091 if (nn(context)) 6092 https = new SSLConnectionSocketFactory(context, protocols, cipherSuites, verifier); 6093 else if (sysProps) 6094 https = new SSLConnectionSocketFactory((javax.net.ssl.SSLSocketFactory)javax.net.ssl.SSLSocketFactory.getDefault(), protocols, cipherSuites, verifier); 6095 else 6096 https = new SSLConnectionSocketFactory(SSLContexts.createDefault(), verifier); 6097 } 6098 return RegistryBuilder.<ConnectionSocketFactory>create() 6099 .register("http", PlainConnectionSocketFactory.getSocketFactory()) 6100 .register("https", https) 6101 .build(); 6102 } 6103 6104 private static String[] splitSystemProperty(String name) { 6105 var v = System.getProperty(name); 6106 return ib(v) ? null : splita(v); 6107 } 6108 6109 // The pool settings, applied as Apache HttpClientBuilder applies them to its own connection manager. The 6110 // http.maxConnections limit (default 5) of useSystemProperties() applies per route, and twice that in total, 6111 // unless http.keepAlive is off; maxConnTotal and maxConnPerRoute win over it. Any limit or a time-to-live 6112 // makes the manager a pooling one: BasicHttpClientConnectionManager has neither. 6113 private PoolSettings poolSettings(HttpClientBuilderSettings s) { 6114 var systemMax = (systemProperties || s.systemProperties()) && "true".equalsIgnoreCase(System.getProperty("http.keepAlive", "true")) 6115 ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0; 6116 var settingsTotal = s.maxConnTotal() > 0 ? s.maxConnTotal() : 2 * systemMax; 6117 var settingsPerRoute = s.maxConnPerRoute() > 0 ? s.maxConnPerRoute() : systemMax; 6118 var maxTotal = maxConnTotal > 0 ? maxConnTotal : settingsTotal; 6119 var maxPerRoute = maxConnPerRoute > 0 ? maxConnPerRoute : settingsPerRoute; 6120 var ownTtl = connTimeToLive > 0; 6121 var ttl = ownTtl ? connTimeToLive : s.connTimeToLive(); 6122 var ttlUnit = ownTtl ? connTimeToLiveTimeUnit : s.connTimeToLiveTimeUnit(); 6123 var pooling = pooled || maxTotal > 0 || maxPerRoute > 0 || ttl > 0; 6124 return new PoolSettings(pooling, maxTotal, maxPerRoute, ttl, or(ttlUnit, TimeUnit.MILLISECONDS), 6125 or(defaultSocketConfig, s.socketConfig()), or(defaultConnectionConfig, s.connectionConfig())); 6126 } 6127 6128 private static boolean overridesCreateConnectionManager(Class<?> c) { 6129 for (var x = c; x != Builder.class; x = x.getSuperclass()) { 6130 try { 6131 x.getDeclaredMethod("createConnectionManager"); 6132 return true; 6133 } catch (@SuppressWarnings("unused") NoSuchMethodException e) { /* Not declared here. */ } 6134 } 6135 return false; 6136 } 6137 6138 // Why guarded @Remote calls cannot be sent through a client built by b, or null if they can. The checks of the 6139 // client RestClient makes afterwards only see the connection manager the client stores, so they cannot tell: 6140 // - an HttpClientBuilder subclass whose build() or exec-chain factory may build the client around another 6141 // connection manager or request executor, or put a step in front of them that sends the request elsewhere; 6142 // - an HttpProcessor, which replaces the client's request interceptors (the SSRF guard's lease hand-over and the 6143 // redirect credential guard among them). 6144 private static String ssrfBuilderRefusal(HttpClientBuilder b) { 6145 var optOut = "or set allowPrivateUrls(true) if this is an intentional local-dev/intranet target."; 6146 if (! HttpClientBuilderSettings.isReadable()) 6147 // HTT: only an HttpClient release without the HttpClientBuilder fields, or a JVM that denies reflective access 6148 // to them, gets here (and then the default createConnectionManager() has already failed). 6149 return "Refusing to send a policy-covered @Remote request: the settings of " + HttpClientBuilder.class.getName() 6150 + " cannot be read, so whether the client it built honors the SSRF guardrail's connect-time contract " 6151 + "(pin-on-connect + redirect revalidation) cannot be confirmed. Use an HttpClient 4.5.x release, " + optOut; 6152 var override = overriddenBuilderMethod(b.getClass()); 6153 if (nn(override)) 6154 return "Refusing to send a policy-covered @Remote request through a client built by an HttpClientBuilder " 6155 + "subclass (" + override + "): the override may build the client around another connection manager or " 6156 + "request executor, so the SSRF guardrail's connect-time contract (pin-on-connect + redirect " 6157 + "revalidation) cannot be confirmed. Remove the override (configure the builder through its setters " 6158 + "instead), " + optOut; 6159 if (HttpClientBuilderSettings.hasHttpProcessor(b)) 6160 return "Refusing to send a policy-covered @Remote request through a client whose HttpProcessor was set: " 6161 + "httpProcessor(...) replaces the client's request interceptors, among them the SSRF guardrail's own and " 6162 + "the redirect credential guard, so its connect-time contract (pin-on-connect + redirect revalidation) " 6163 + "cannot be confirmed. Remove the httpProcessor(...) setting (add interceptors with " 6164 + "addInterceptorFirst(...) or addInterceptorLast(...) instead), " + optOut; 6165 var change = lateBuilderChange(b); 6166 if (nn(change)) 6167 return "Refusing to send a policy-covered @Remote request through a client whose HttpClientBuilder was changed " 6168 + "after the SSRF guard was wired into it (" + change + "), as an override of createHttpClient() does that " 6169 + "changes the builder after super.createHttpClient() and builds again. The change may add credentials to " 6170 + "a redirect hop on another origin, follow redirects without revalidation or send the request past the " 6171 + "guard's executor, so the SSRF guardrail's connect-time contract (pin-on-connect + redirect revalidation) " 6172 + "cannot be confirmed. Make the change before super.createHttpClient() (on httpClientBuilder() or through " 6173 + "this builder's setters), " + optOut; 6174 return null; 6175 } 6176 6177 // What was changed on b after the default createHttpClient() wired the SSRF guard into it, or null if nothing it 6178 // wired was: the guard's two request interceptors must still be the last ones (the redirect credential guard strips 6179 // what every interceptor before it added), and its redirect strategy and request executor must still be set. 6180 private static String lateBuilderChange(HttpClientBuilder b) { 6181 var last = HttpClientBuilderSettings.lastRequestInterceptors(b); 6182 var n = last.size(); 6183 if (n < 2 || ! (last.get(n - 2) instanceof ClassicRedirectCredentialGuard) || ! (last.get(n - 1) instanceof SsrfGuardConnectionManager.GuardInterceptor)) 6184 return "a request interceptor was added after the SSRF guard's own"; 6185 if (! (HttpClientBuilderSettings.redirectStrategy(b) instanceof PolicyAwareRedirectStrategy)) 6186 return "the redirect strategy was replaced"; 6187 if (! (HttpClientBuilderSettings.requestExecutor(b) instanceof SsrfGuardConnectionManager.GuardedRequestExecutor)) 6188 return "the request executor was replaced"; 6189 return null; 6190 } 6191 6192 // The HttpClientBuilder methods that decide which connection manager and request executor a built client uses. 6193 private static final List<Method> BUILDER_EXEC_METHODS = Arrays.stream(HttpClientBuilder.class.getDeclaredMethods()) 6194 .filter(m -> Set.of("build", "createMainExec", "decorateMainExec", "decorateProtocolExec").contains(m.getName())) 6195 .toList(); 6196 6197 // Returns "<class> overrides <method>(<params>)" for the first of BUILDER_EXEC_METHODS a class between c and 6198 // HttpClientBuilder declares, or null if there is none. 6199 private static String overriddenBuilderMethod(Class<?> c) { 6200 for (var x = c; nn(x) && x != HttpClientBuilder.class; x = x.getSuperclass()) { 6201 for (var m : BUILDER_EXEC_METHODS) { 6202 try { 6203 x.getDeclaredMethod(m.getName(), m.getParameterTypes()); 6204 return cn(x) + " overrides " + m.getName() + "(" + String.join(",", Arrays.stream(m.getParameterTypes()).map(Class::getSimpleName).toList()) + ")"; 6205 } catch (@SuppressWarnings("unused") NoSuchMethodException e) { /* Not declared here. */ } 6206 } 6207 } 6208 return null; 6209 } 6210 6211 private static HttpClientConnectionManager newConnectionManager(Registry<ConnectionSocketFactory> registry, DnsResolver dnsResolver, PoolSettings p) { 6212 if (! p.pooling()) { 6213 var cm = new BasicHttpClientConnectionManager(registry, null, null, dnsResolver); 6214 if (nn(p.socketConfig())) 6215 cm.setSocketConfig(p.socketConfig()); 6216 if (nn(p.connectionConfig())) 6217 cm.setConnectionConfig(p.connectionConfig()); 6218 return cm; 6219 } 6220 var cm = new PoolingHttpClientConnectionManager(registry, null, null, dnsResolver, p.timeToLive(), p.timeToLiveUnit()); 6221 if (nn(p.socketConfig())) 6222 cm.setDefaultSocketConfig(p.socketConfig()); 6223 if (nn(p.connectionConfig())) 6224 cm.setDefaultConnectionConfig(p.connectionConfig()); 6225 if (p.maxTotal() > 0) 6226 cm.setMaxTotal(p.maxTotal()); 6227 if (p.maxPerRoute() > 0) 6228 cm.setDefaultMaxPerRoute(p.maxPerRoute()); 6229 return cm; 6230 } 6231 6232 // The settings both pools of the default connection manager are built with. 6233 private record PoolSettings(boolean pooling, int maxTotal, int maxPerRoute, long timeToLive, TimeUnit timeToLiveUnit, 6234 SocketConfig socketConfig, ConnectionConfig connectionConfig) {} 6235 6236 /** 6237 * Creates the builder for the form data list. 6238 * 6239 * <p> 6240 * Subclasses can override this method to provide their own implementation. 6241 * 6242 * <p> 6243 * The default behavior creates an empty builder. 6244 * 6245 * @return The form data list builder. 6246 * @see #formData() 6247 */ 6248 protected PartList createFormData() { 6249 return PartList.create(); 6250 } 6251 6252 /** 6253 * Creates the builder for the header list. 6254 * 6255 * <p> 6256 * Subclasses can override this method to provide their own implementation. 6257 * 6258 * <p> 6259 * The default behavior creates an empty builder. 6260 * 6261 * @return The header list builder. 6262 * @see #headers() 6263 */ 6264 protected HeaderList createHeaderData() { 6265 return HeaderList.create(); 6266 } 6267 6268 /** 6269 * Creates an instance of an {@link HttpClient} to be used to handle all HTTP communications with the target server. 6270 * 6271 * <p> 6272 * This HTTP client is used when the HTTP client is not specified through one of the constructors or the 6273 * {@link #httpClient(CloseableHttpClient)} method. 6274 * 6275 * <p> 6276 * Subclasses can override this method to provide specially-configured HTTP clients to handle stuff such as 6277 * SSL/TLS certificate handling, authentication, etc. 6278 * 6279 * <p> 6280 * The default implementation returns an instance of {@link HttpClient} using the client builder returned by 6281 * {@link #createHttpClientBuilder()}. 6282 * 6283 * <h5 class='section'>Notes:</h5><ul> 6284 * <li class='warn'>SSRF-guard-active {@code @Remote} calls (ones without the 6285 * {@link #allowPrivateUrls(boolean) allowPrivateUrls} opt-in) are only sent through the client this default 6286 * implementation builds, which it wires to the connection manager that pins them. The client refuses every 6287 * such call if an override returns any other client (including a wrapper around the one it built), or after 6288 * {@code super.createHttpClient()} sets an {@link HttpProcessor} on the {@link HttpClientBuilder}, adds a request 6289 * interceptor with {@code addInterceptorLast(...)}, or replaces its redirect strategy or request executor (make 6290 * such changes before calling {@code super}), or if the builder is a 6291 * subclass that overrides {@link HttpClientBuilder#build()} or one of its exec-chain methods 6292 * ({@code createMainExec}, {@code decorateMainExec}, {@code decorateProtocolExec}), even an override that 6293 * returns or decorates nothing but what {@code super} built. Ordinary requests are unaffected. 6294 * </ul> 6295 * 6296 * <h5 class='section'>Example:</h5> 6297 * <p class='bjava'> 6298 * <jc>// A Builder that provides it's own customized HttpClient.</jc> 6299 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 6300 * <ja>@Override</ja> 6301 * <jk>protected</jk> HttpClientBuilder createHttpClient() { 6302 * <jk>return</jk> HttpClientBuilder.<jsm>create</jsm>().build(); 6303 * } 6304 * } 6305 * 6306 * <jc>// Instantiate.</jc> 6307 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 6308 * </p> 6309 * 6310 * @return The HTTP client to use. 6311 */ 6312 protected CloseableHttpClient createHttpClient() { 6313 // A new connection manager for each client unless the caller supplied one: closing a client shuts its 6314 // connection manager down, so a reused builder must not hand an earlier client's to the next one. 6315 var cm = nn(connectionManager) ? connectionManager : createConnectionManager(); 6316 // Only a connection manager this builder created can be given a pinned twin; for any other one, the client 6317 // reports the SSRF guard as unsupported and refuses guarded remote calls. The twin is built from the same 6318 // socket factories and pool settings as the connection manager, so it is the same kind; settings an 6319 // override applied to that instance afterwards reach it before each guarded lease. 6320 ssrfGuard = nn(cm) && cm == defaultConnectionManager ? new SsrfGuardConnectionManager(cm, newConnectionManager(defaultRegistry, pinningResolver(), defaultPoolSettings)) : null; 6321 ownsConnectionManager = nn(ssrfGuard) && connectionManager == null && ! overridesCreateConnectionManager(getClass()); 6322 if (nn(ssrfGuard)) 6323 cm = ssrfGuard; 6324 httpClientBuilder().setConnectionManager(cm); 6325 // Once per HttpClientBuilder: a builder reused after build() keeps the interceptors earlier builds added. 6326 if (! HttpClientBuilderSettings.hasLastRequestInterceptor(httpClientBuilder(), ClassicRedirectCredentialGuard.class)) 6327 httpClientBuilder().addInterceptorLast(new ClassicRedirectCredentialGuard()); 6328 if (nn(ssrfGuard) && HttpClientBuilderSettings.isReadable()) { 6329 // The guard of a request travels on its context (see SsrfGuardMarker): the interceptor reads it before the 6330 // connection lease, and the executor refuses a guarded request on a connection of the ordinary pool. 6331 if (! HttpClientBuilderSettings.hasLastRequestInterceptor(httpClientBuilder(), SsrfGuardConnectionManager.GuardInterceptor.class)) 6332 httpClientBuilder().addInterceptorLast(new SsrfGuardConnectionManager.GuardInterceptor()); 6333 httpClientBuilder().setRequestExecutor(new SsrfGuardConnectionManager.GuardedRequestExecutor(HttpClientBuilderSettings.requestExecutor(httpClientBuilder()), ssrfGuard)); 6334 } 6335 httpClientBuilder().setRedirectStrategy(new PolicyAwareRedirectStrategy()); 6336 return httpClientBuilder().build(); 6337 } 6338 6339 // The shared pinning resolver unless this builder set per-client allowed address ranges, which need a resolver of their own. 6340 private PolicyPinningDnsResolver pinningResolver() { 6341 var ranges = RemoteUrlPolicy.AllowedRanges.of(allowedAddressRanges.toArray(new String[0])); 6342 return ranges.isEmpty() ? PolicyPinningDnsResolver.INSTANCE : new PolicyPinningDnsResolver(ranges); 6343 } 6344 6345 /** 6346 * Creates an instance of an {@link HttpClientBuilder} to be used to create the {@link HttpClient}. 6347 * 6348 * <p> 6349 * Subclasses can override this method to provide their own client builder. 6350 * The builder can also be specified using the {@link #httpClientBuilder(HttpClientBuilder)} method. 6351 * 6352 * <h5 class='section'>Example:</h5> 6353 * <p class='bjava'> 6354 * <jc>// A Builder that provides it's own customized HttpClientBuilder.</jc> 6355 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 6356 * <ja>@Override</ja> 6357 * <jk>protected</jk> HttpClientBuilder createHttpClientBuilder() { 6358 * <jk>return</jk> HttpClientBuilder.<jsm>create</jsm>(); 6359 * } 6360 * } 6361 * 6362 * <jc>// Instantiate.</jc> 6363 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 6364 * </p> 6365 * 6366 * @return The HTTP client builder to use to create the HTTP client. 6367 */ 6368 protected HttpClientBuilder createHttpClientBuilder() { 6369 return HttpClientBuilder.create(); 6370 } 6371 6372 /** 6373 * Instantiates the parser group sub-builder. 6374 * 6375 * @return A new parser group sub-builder. 6376 */ 6377 protected ParserSet.Builder createParsers() { 6378 return ParserSet.create().marshallingContext(marshallingContext()); 6379 } 6380 6381 /** 6382 * Instantiates the part parser sub-builder. 6383 * 6384 * @return A new part parser sub-builder. 6385 */ 6386 protected HttpPartParser.Creator createPartParser() { 6387 return HttpPartParser.creator().type(OpenApiParser.class).marshallingContext(marshallingContext()); 6388 } 6389 6390 /** 6391 * Instantiates the part serializer sub-builder. 6392 * 6393 * @return A new part serializer sub-builder. 6394 */ 6395 protected HttpPartSerializer.Creator createPartSerializer() { 6396 return HttpPartSerializer.creator().type(OpenApiSerializer.class).marshallingContext(marshallingContext()); 6397 } 6398 6399 /** 6400 * Creates the builder for the path data list. 6401 * 6402 * <p> 6403 * Subclasses can override this method to provide their own implementation. 6404 * 6405 * <p> 6406 * The default behavior creates an empty builder. 6407 * 6408 * @return The path data list builder. 6409 * @see #pathData() 6410 */ 6411 protected PartList createPathData() { 6412 return PartList.create(); 6413 } 6414 6415 /** 6416 * Creates the builder for the query data list. 6417 * 6418 * <p> 6419 * Subclasses can override this method to provide their own implementation. 6420 * 6421 * <p> 6422 * The default behavior creates an empty builder. 6423 * 6424 * @return The query data list builder. 6425 * @see #queryData() 6426 */ 6427 protected PartList createQueryData() { 6428 return PartList.create(); 6429 } 6430 6431 /** 6432 * Instantiates the serializer group sub-builder. 6433 * 6434 * @return A new serializer group sub-builder. 6435 */ 6436 protected SerializerSet.Builder createSerializers() { 6437 return SerializerSet.create().marshallingContext(marshallingContext()); 6438 } 6439 6440 /** 6441 * Instantiates the URL-encoding serializer sub-builder. 6442 * 6443 * @return A new URL-encoding serializer sub-builder. 6444 */ 6445 protected UrlEncodingSerializer.Builder createUrlEncodingSerializer() { 6446 return UrlEncodingSerializer.create().marshallingContext(marshallingContext()); 6447 } 6448 6449 final CloseableHttpClient getHttpClient() { return nn(httpClient) ? httpClient : createHttpClient(); } 6450 } 6451 6452 /** 6453 * Concrete default builder leaf for the non-subclassed {@link RestClient#create()} / {@link RestClient#copy()} path. 6454 */ 6455 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 6456 6457 DefaultBuilder() {} 6458 } 6459 6460 private static final RestCallInterceptor[] EMPTY_REST_CALL_INTERCEPTORS = {}; 6461 private static final ConcurrentHashMap<Class<?>,Context> requestContexts = new ConcurrentHashMap<>(); 6462 private static final BiPredicate<RestRequest,RestResponse> LOG_REQUESTS_PREDICATE_DEFAULT = (req, res) -> true; 6463 6464 /** 6465 * Instantiates a new clean-slate {@link Builder} object. 6466 * 6467 * @return A new {@link Builder} object. 6468 */ 6469 public static <T extends Builder<T>> Builder<T> create() { 6470 return (Builder<T>) new DefaultBuilder(); 6471 } 6472 6473 protected final boolean detectLeaks; 6474 protected final boolean allowPrivateUrls; 6475 private final RemoteUrlPolicy.AllowedRanges allowedAddressRanges; 6476 protected final boolean ignoreErrors; 6477 protected final boolean keepHttpClientOpen; 6478 protected final boolean skipEmptyFormData; 6479 protected final boolean skipEmptyHeaderData; 6480 protected final boolean skipEmptyQueryData; 6481 protected final BiPredicate<RestRequest,RestResponse> logRequestsPredicate; 6482 protected final CloseableHttpClient httpClient; 6483 protected final DetailLevel logRequests; 6484 protected final HeaderList headerData; 6485 protected final HttpPartParser partParser; 6486 protected final HttpPartSerializer partSerializer; 6487 protected final Level logRequestsLevel; 6488 protected final PartList formData; 6489 protected final PartList pathData; 6490 protected final PartList queryData; 6491 protected final ParserSet parsers; 6492 protected final RestCallInterceptor[] interceptors; 6493 protected final SerializerSet serializers; 6494 protected final UrlEncodingSerializer urlEncodingSerializer; // Used for form posts only. 6495 Predicate<Integer> errorCodes; 6496 private final WritableBeanStore beanStore; 6497 private final HttpClientConnectionManager connectionManager; 6498 private final Logger logger; 6499 private final Map<Class<?>,HttpPartParser> partParsers = new ConcurrentHashMap<>(); 6500 private final Map<Class<?>,HttpPartSerializer> partSerializers = new ConcurrentHashMap<>(); 6501 private final Pattern absUrlPattern = Pattern.compile("^\\w++://.*"); 6502 private final PrintStream console; 6503 private final RestCallHandler callHandler; 6504 private StackTraceElement[] closedStack; 6505 private final StackTraceElement[] creationStack; 6506 private final Supplier<String> rootUrl; 6507 private final boolean executorServiceShutdownOnClose; 6508 private final boolean logToConsole; 6509 private final boolean ssrfPolicySupported; 6510 6511 // Why SSRF-guard-active @Remote calls are refused, or null if they are supported. 6512 private final String ssrfRefusal; 6513 // The two-pool router this client's HttpClient uses, or null when the SSRF guard is unsupported. 6514 private final SsrfGuardConnectionManager ssrfGuard; 6515 6516 private final boolean ownsConnectionManager; 6517 private final AtomicBoolean isClosed = new AtomicBoolean(false); 6518 private final AtomicReference<ExecutorService> executorService = new AtomicReference<>(); 6519 6520 /** 6521 * Constructor. 6522 * 6523 * @param builder The builder for this client. 6524 */ 6525 public RestClient(Builder<?> builder) { 6526 super(builder); 6527 6528 beanStore = builder.beanStore.addBean(RestClient.class, this); 6529 6530 callHandler = builder.callHandler().run(); 6531 connectionManager = builder.connectionManager; 6532 console = nn(builder.console) ? builder.console : System.err; 6533 creationStack = isDebug() ? Thread.currentThread().getStackTrace() : null; 6534 detectLeaks = builder.detectLeaks; 6535 allowPrivateUrls = builder.allowPrivateUrls || Boolean.getBoolean(RemoteUrlPolicy.ALLOW_PRIVATE_URLS_PROPERTY); 6536 allowedAddressRanges = RemoteUrlPolicy.AllowedRanges.of(builder.allowedAddressRanges.toArray(new String[0])); 6537 errorCodes = builder.errorCodes; 6538 formData = builder.formData().copy(); 6539 headerData = builder.headers().copy(); 6540 httpClient = builder.getHttpClient(); 6541 // Read after getHttpClient(): the default createHttpClient() sets ssrfGuard. It wires it into the client it 6542 // builds, but an override of createHttpClient() or of HttpClientBuilder.build() may return another client. 6543 var builtGuard = builder.httpClient == null ? builder.ssrfGuard : null; 6544 ssrfGuard = nn(builtGuard) && builtGuard.isConnectionManagerOf(httpClient) ? builtGuard : null; 6545 ownsConnectionManager = nn(ssrfGuard) && builder.ownsConnectionManager; 6546 // The HttpClientBuilder is checked as it is now, not when the default createHttpClient() built from it: an override 6547 // may call super, change the builder and build again. What is checked: a builder subclass override, an 6548 // HttpProcessor, and whether what createHttpClient() wired (the guard's interceptors last, its redirect strategy and 6549 // request executor) is still in place. One of these changes made after the client was built is refused too (fail 6550 // closed): which client it went into cannot be told. A change undone before the override returns is not seen. 6551 ssrfRefusal = ssrfRefusal(builtGuard, nn(builtGuard) ? Builder.ssrfBuilderRefusal(builder.httpClientBuilder()) : null); 6552 ssrfPolicySupported = ssrfRefusal == null; 6553 ignoreErrors = builder.ignoreErrors; 6554 interceptors = nn(builder.interceptors) ? builder.interceptors.toArray(EMPTY_REST_CALL_INTERCEPTORS) : EMPTY_REST_CALL_INTERCEPTORS; 6555 keepHttpClientOpen = builder.keepHttpClientOpen; 6556 logger = nn(builder.logger) ? builder.logger : Logger.getLogger(cn(RestClient.class)); 6557 if (nn(builder.logRequests)) { 6558 logRequests = builder.logRequests; 6559 } else { 6560 logRequests = isDebug() ? DetailLevel.FULL : DetailLevel.NONE; 6561 } 6562 if (nn(builder.logRequestsLevel)) { 6563 logRequestsLevel = builder.logRequestsLevel; 6564 } else { 6565 logRequestsLevel = isDebug() ? Level.WARNING : Level.OFF; 6566 } 6567 logRequestsPredicate = nn(builder.logRequestsPredicate) ? builder.logRequestsPredicate : LOG_REQUESTS_PREDICATE_DEFAULT; 6568 logToConsole = builder.logToConsole || isDebug(); 6569 parsers = builder.parsers().build(); 6570 partParser = builder.partParser().create(); 6571 partSerializer = builder.partSerializer().create(); 6572 pathData = builder.pathData().copy(); 6573 queryData = builder.queryData().copy(); 6574 rootUrl = builder.rootUrl; 6575 serializers = builder.serializers().build(); 6576 skipEmptyFormData = builder.skipEmptyFormData; 6577 skipEmptyHeaderData = builder.skipEmptyHeaderData; 6578 skipEmptyQueryData = builder.skipEmptyQueryData; 6579 urlEncodingSerializer = builder.urlEncodingSerializer().build(); 6580 if (builder.executorService != null) 6581 executorService.set(builder.executorService); 6582 executorServiceShutdownOnClose = builder.executorServiceShutdownOnClose; 6583 6584 init(); 6585 } 6586 6587 /** 6588 * Returns the root URL set on this client. 6589 * 6590 * @return The root URL, or <jk>null</jk> if not set. 6591 */ 6592 public String getRootUrl() { 6593 return rootUrl != null ? rootUrl.get() : null; 6594 } 6595 6596 /** 6597 * Returns the effective builder/settings-level {@code allowPrivateUrls} opt-in for this client (the logical OR 6598 * of {@code Builder#allowPrivateUrls(boolean)} and the {@value RemoteUrlPolicy#ALLOW_PRIVATE_URLS_PROPERTY} 6599 * system property). 6600 * 6601 * <p> 6602 * The fully effective per-call value additionally ORs in the {@code @Remote}/{@code @RemoteOp} 6603 * {@code allowPrivateUrls} annotation attribute (see {@link Remote#allowPrivateUrls()}). 6604 * 6605 * @return {@code true} if this client opts into allowing private/loopback/link-local/metadata {@code @Remote} targets. 6606 */ 6607 public boolean isAllowPrivateUrls() { 6608 return allowPrivateUrls; 6609 } 6610 6611 /** 6612 * Returns the per-client allowed address ranges set through {@link Builder#allowAddressRanges(String...)}. 6613 * 6614 * @return The ranges; never <jk>null</jk>, empty when none were set. 6615 */ 6616 public RemoteUrlPolicy.AllowedRanges getAllowedAddressRanges() { 6617 return allowedAddressRanges; 6618 } 6619 6620 /** 6621 * Performs a REST call where the entire call is specified in a simple string. 6622 * 6623 * <p> 6624 * This method is useful for performing callbacks when the target of a callback is passed in 6625 * on an initial request, for example to signal when a long-running process has completed. 6626 * 6627 * <p> 6628 * The call string can be any of the following formats: 6629 * <ul class='spaced-list'> 6630 * <li> 6631 * <js>"[method] [uri]"</js> - e.g. <js>"GET http://localhost/callback"</js> 6632 * <li> 6633 * <js>"[method] [uri] [payload]"</js> - e.g. <js>"POST http://localhost/callback some text payload"</js> 6634 * <li> 6635 * <js>"[method] [headers] [uri] [payload]"</js> - e.g. <js>"POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}"</js> 6636 * </ul> 6637 * <p> 6638 * The payload will always be sent using a simple {@link StringEntity}. 6639 * 6640 * @param callString The call string. 6641 * <br>Can be <jk>null</jk> or empty (treated as empty string, will result in an invalid request). 6642 * @return 6643 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6644 * as a parsed object. 6645 * @throws RestCallException REST call failed. 6646 */ 6647 @SuppressWarnings({ 6648 "java:S1854", // state = S5 is a necessary state machine transition; SonarLint false positive 6649 "java:S4165" // state already holds S5 in some paths, but the assignment is a deliberate state machine transition 6650 }) 6651 public RestRequest callback(String callString) throws RestCallException { 6652 callString = emptyIfNull(callString); 6653 6654 // S1 - Looking for end of method. 6655 // S2 - Found end of method, looking for beginning of URI or headers. 6656 // S3 - Found beginning of headers, looking for end of headers. 6657 // S4 - Found end of headers, looking for beginning of URI. 6658 // S5 - Found beginning of URI, looking for end of URI. 6659 6660 var state = S1; 6661 6662 var mark = 0; 6663 String method = null; 6664 String headers = null; 6665 String uri = null; 6666 String content = null; 6667 for (var i = 0; i < callString.length(); i++) { 6668 var c = callString.charAt(i); 6669 if (state == S1) { 6670 if (isWhitespace(c)) { 6671 method = callString.substring(mark, i); 6672 state = S2; 6673 } 6674 } else if (state == S2) { 6675 if (! isWhitespace(c)) { 6676 mark = i; 6677 state = (c == '{') ? S3 : S5; 6678 } 6679 } else if (state == S3) { 6680 if (c == '}') { 6681 headers = callString.substring(mark, i + 1); 6682 state = S4; 6683 } 6684 } else if (state == S4) { 6685 if (! isWhitespace(c)) { 6686 mark = i; 6687 state = S5; 6688 } 6689 } else /* (state == S5) */ { 6690 if (isWhitespace(c)) { 6691 uri = callString.substring(mark, i); 6692 content = callString.substring(i).trim(); 6693 break; 6694 } 6695 } 6696 } 6697 6698 if (state != S5) 6699 throw new RestCallException(null, null, "Invalid format for call string. State=%s", state); 6700 6701 try { 6702 var req = request(method, uri, ine(content)); 6703 if (nn(headers)) 6704 Json5Map.ofString(headers).forEach((k, v) -> req.header(stringHeader(k, s(v)))); 6705 if (ine(content)) 6706 req.contentString(content); 6707 return req; 6708 } catch (ParseException e) { 6709 throw new RestCallException(null, e, "Invalid format for call string."); 6710 } 6711 } 6712 6713 /** 6714 * Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}. 6715 * 6716 * <p> 6717 * It's good practice to call this method after the client is no longer used. 6718 * 6719 * @throws IOException Thrown by underlying stream. 6720 */ 6721 @Override 6722 public void close() throws IOException { 6723 isClosed.set(true); 6724 ExecutorService es = executorService.get(); 6725 if (! keepHttpClientOpen) { 6726 try { 6727 httpClient.close(); 6728 } finally { 6729 shutdownPinnedPool(); 6730 } 6731 } 6732 if (nn(es) && executorServiceShutdownOnClose) 6733 es.shutdown(); 6734 // If executorServiceShutdownOnClose is false, executor service remains open for reuse 6735 if (nn(creationStack)) 6736 closedStack = Thread.currentThread().getStackTrace(); 6737 } 6738 6739 // The pinned pool is always this client's own (nothing else can reach it), and so is the ordinary pool when this 6740 // client's build() created it, so they are shut down with the client even when the connection manager is shared 6741 // and the HttpClient therefore leaves it open. 6742 private void shutdownPinnedPool() { 6743 if (ownsConnectionManager) 6744 ssrfGuard.shutdown(); 6745 else if (nn(ssrfGuard)) 6746 ssrfGuard.shutdownPinned(); 6747 } 6748 6749 /** 6750 * Same as {@link #close()}, but ignores any exceptions. 6751 */ 6752 public void closeQuietly() { 6753 isClosed.set(true); 6754 try { 6755 if (! keepHttpClientOpen) { 6756 try { 6757 httpClient.close(); 6758 } finally { 6759 shutdownPinnedPool(); 6760 } 6761 } 6762 ExecutorService es = executorService.get(); 6763 if (nn(es) && executorServiceShutdownOnClose) 6764 es.shutdown(); 6765 } catch (@SuppressWarnings("unused") Exception t) { /* Suppress close/shutdown exceptions during cleanup */ } 6766 if (nn(creationStack)) 6767 closedStack = Thread.currentThread().getStackTrace(); 6768 } 6769 6770 @Override /* Overridden from Context */ 6771 public Builder copy() { 6772 throw new NoSuchMethodError("Not implemented."); 6773 } 6774 6775 /** 6776 * Creates a mutable copy of the form data defined on this client. 6777 * 6778 * <p> 6779 * Used during the construction of {@link RestRequest} objects. 6780 * 6781 * <p> 6782 * Subclasses can override this method to provide their own builder. 6783 * 6784 * @return A new builder. 6785 */ 6786 public PartList createFormData() { 6787 return formData.copy(); 6788 } 6789 6790 /** 6791 * Creates a mutable copy of the header data defined on this client. 6792 * 6793 * <p> 6794 * Used during the construction of {@link RestRequest} objects. 6795 * 6796 * <p> 6797 * Subclasses can override this method to provide their own builder. 6798 * 6799 * @return A new builder. 6800 */ 6801 public HeaderList createHeaderData() { 6802 return headerData.copy(); 6803 } 6804 6805 /** 6806 * Creates a mutable copy of the path data defined on this client. 6807 * 6808 * <p> 6809 * Used during the construction of {@link RestRequest} objects. 6810 * 6811 * <p> 6812 * Subclasses can override this method to provide their own builder. 6813 * 6814 * @return A new builder. 6815 */ 6816 public PartList createPathData() { 6817 return pathData.copy(); 6818 } 6819 6820 /** 6821 * Creates a mutable copy of the query data defined on this client. 6822 * 6823 * <p> 6824 * Used during the construction of {@link RestRequest} objects. 6825 * 6826 * <p> 6827 * Subclasses can override this method to provide their own builder. 6828 * 6829 * @return A new builder. 6830 */ 6831 public PartList createQueryData() { 6832 return queryData.copy(); 6833 } 6834 6835 /** 6836 * Perform a <c>DELETE</c> request against the specified URI. 6837 * 6838 * @param uri 6839 * The URI of the remote REST resource. 6840 * <br>Can be any of the following types: 6841 * <ul> 6842 * <li>{@link URIBuilder} 6843 * <li>{@link URI} 6844 * <li>{@link URL} 6845 * <li>{@link String} 6846 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6847 * </ul> 6848 * @return 6849 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6850 * as a parsed object. 6851 * @throws RestCallException If any authentication errors occurred. 6852 */ 6853 public RestRequest delete(Object uri) throws RestCallException { 6854 return request(op("DELETE", uri, NO_BODY)); 6855 } 6856 6857 /** 6858 * Executes HTTP request using the default context. 6859 * 6860 * <h5 class='section'>Notes:</h5><ul> 6861 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6862 * </ul> 6863 * 6864 * @param target The target host for the request. 6865 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 6866 * target or by inspecting the request. 6867 * @param request The request to execute. 6868 * @return The response to the request. 6869 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6870 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6871 * implementation and configuration of this client. 6872 * @throws IOException In case of a problem or the connection was aborted. 6873 * @throws ClientProtocolException In case of an http protocol error. 6874 */ 6875 @Override /* Overridden from HttpClient */ 6876 public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException { 6877 return httpClient.execute(target, request); 6878 } 6879 6880 /** 6881 * Executes HTTP request using the given context. 6882 * 6883 * <h5 class='section'>Notes:</h5><ul> 6884 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6885 * <li class='note'>The {@link #run(HttpHost,HttpRequest,HttpContext)} method has been provided as a wrapper around this method. 6886 * Subclasses can override these methods for handling requests with and without bodies separately. 6887 * <li class='note'>The {@link RestCallHandler} interface can also be implemented to intercept this method. 6888 * </ul> 6889 * 6890 * @param target The target host for the request. 6891 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 6892 * target or by inspecting the request. 6893 * @param request The request to execute. 6894 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6895 * @return 6896 * The response to the request. 6897 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6898 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6899 * implementation and configuration of this client. 6900 * @throws IOException In case of a problem or the connection was aborted. 6901 * @throws ClientProtocolException In case of an http protocol error. 6902 */ 6903 @Override /* Overridden from HttpClient */ 6904 public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException { 6905 return httpClient.execute(target, request, context); 6906 } 6907 6908 /** 6909 * Executes HTTP request to the target using the default context and processes the response using the given response handler. 6910 * 6911 * <p> 6912 * The content entity associated with the response is fully consumed and the underlying connection is released back 6913 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6914 * from having to manage resource deallocation internally. 6915 * 6916 * <h5 class='section'>Notes:</h5><ul> 6917 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6918 * </ul> 6919 * 6920 * @param target 6921 * The target host for the request. 6922 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default target or by inspecting the request. 6923 * @param request The request to execute. 6924 * @param responseHandler The response handler. 6925 * @return The response object as generated by the response handler. 6926 * @throws IOException In case of a problem or the connection was aborted. 6927 * @throws ClientProtocolException In case of an http protocol error. 6928 */ 6929 @Override /* Overridden from HttpClient */ 6930 public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException { 6931 return httpClient.execute(target, request, responseHandler); 6932 } 6933 6934 /** 6935 * Executes a request using the default context and processes the response using the given response handler. 6936 * 6937 * <p> 6938 * The content entity associated with the response is fully consumed and the underlying connection is released back 6939 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6940 * from having to manage resource deallocation internally. 6941 * 6942 * <h5 class='section'>Notes:</h5><ul> 6943 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6944 * </ul> 6945 * 6946 * @param target 6947 * The target host for the request. 6948 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default target or by inspecting the request. 6949 * @param request The request to execute. 6950 * @param responseHandler The response handler. 6951 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6952 * @return The response object as generated by the response handler. 6953 * @throws IOException In case of a problem or the connection was aborted. 6954 * @throws ClientProtocolException In case of an http protocol error. 6955 */ 6956 @Override /* Overridden from HttpClient */ 6957 public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException { 6958 return httpClient.execute(target, request, responseHandler, context); 6959 } 6960 6961 /** 6962 * Executes HTTP request using the default context. 6963 * 6964 * <h5 class='section'>Notes:</h5><ul> 6965 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6966 * </ul> 6967 * 6968 * @param request The request to execute. 6969 * @return 6970 * The response to the request. 6971 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6972 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6973 * implementation and configuration of this client. 6974 * @throws IOException In case of a problem or the connection was aborted. 6975 * @throws ClientProtocolException In case of an http protocol error. 6976 */ 6977 @Override /* Overridden from HttpClient */ 6978 public HttpResponse execute(HttpUriRequest request) throws IOException { 6979 return httpClient.execute(request); 6980 } 6981 6982 /** 6983 * Executes HTTP request using the given context. 6984 * 6985 * <h5 class='section'>Notes:</h5><ul> 6986 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6987 * </ul> 6988 * 6989 * @param request The request to execute. 6990 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6991 * @return 6992 * The response to the request. 6993 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6994 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6995 * implementation and configuration of this client. 6996 * @throws IOException In case of a problem or the connection was aborted. 6997 * @throws ClientProtocolException In case of an http protocol error. 6998 */ 6999 @Override /* Overridden from HttpClient */ 7000 public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException { 7001 return httpClient.execute(request, context); 7002 } 7003 7004 /** 7005 * Executes HTTP request using the default context and processes the response using the given response handler. 7006 * 7007 * <p> 7008 * The content entity associated with the response is fully consumed and the underlying connection is released back 7009 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 7010 * from having to manage resource deallocation internally. 7011 * 7012 * <h5 class='section'>Notes:</h5><ul> 7013 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 7014 * </ul> 7015 * 7016 * @param request The request to execute. 7017 * @param responseHandler The response handler. 7018 * @return Object returned by response handler. 7019 * @throws IOException In case of a problem or the connection was aborted. 7020 * @throws ClientProtocolException In case of an http protocol error. 7021 */ 7022 @Override /* Overridden from HttpClient */ 7023 public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException { 7024 return httpClient.execute(request, responseHandler); 7025 } 7026 7027 /** 7028 * Executes HTTP request using the given context and processes the response using the given response handler. 7029 * 7030 * <p> 7031 * The content entity associated with the response is fully consumed and the underlying connection is released back 7032 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 7033 * from having to manage resource deallocation internally. 7034 * 7035 * <h5 class='section'>Notes:</h5><ul> 7036 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 7037 * </ul> 7038 * 7039 * @param request The request to execute. 7040 * @param responseHandler The response handler. 7041 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 7042 * @return The response object as generated by the response handler. 7043 * @throws IOException In case of a problem or the connection was aborted. 7044 * @throws ClientProtocolException In case of an http protocol error. 7045 */ 7046 @Override /* Overridden from HttpClient */ 7047 public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException { 7048 return httpClient.execute(request, responseHandler, context); 7049 } 7050 7051 /** 7052 * Same as {@link #formPost(Object, Object)} but doesn't specify the input yet. 7053 * 7054 * @param uri 7055 * The URI of the remote REST resource. 7056 * <br>Can be any of the following types: 7057 * <ul> 7058 * <li>{@link URIBuilder} 7059 * <li>{@link URI} 7060 * <li>{@link URL} 7061 * <li>{@link String} 7062 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7063 * </ul> 7064 * @return 7065 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7066 * as a parsed object. 7067 * @throws RestCallException If any authentication errors occurred. 7068 */ 7069 public RestRequest formPost(Object uri) throws RestCallException { 7070 return request(op("POST", uri, NO_BODY)); 7071 } 7072 7073 /** 7074 * Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> 7075 * against the specified URI. 7076 * 7077 * @param uri 7078 * The URI of the remote REST resource. 7079 * <br>Can be any of the following types: 7080 * <ul> 7081 * <li>{@link URIBuilder} 7082 * <li>{@link URI} 7083 * <li>{@link URL} 7084 * <li>{@link String} 7085 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7086 * </ul> 7087 * @param body 7088 * The object to serialize and transmit to the URI as the body of the request. 7089 * <ul class='spaced-list'> 7090 * <li>{@link NameValuePair} - URL-encoded as a single name-value pair. 7091 * <li>{@link NameValuePair} array - URL-encoded as name value pairs. 7092 * <li>{@link PartList} - URL-encoded as name value pairs. 7093 * <li>{@link Reader}/{@link InputStream}- Streamed directly and <l>Content-Type</l> set to <js>"application/x-www-form-urlencoded"</js> 7094 * <li>{@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 7095 * <li>{@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7096 * <li>{@link Object} - Converted to a {@link SerializedEntity} using {@link UrlEncodingSerializer} to serialize. 7097 * <li>{@link Supplier} - A supplier of anything on this list. 7098 * </ul> 7099 * @return 7100 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7101 * as a parsed object. 7102 * @throws RestCallException If any authentication errors occurred. 7103 */ 7104 public RestRequest formPost(Object uri, Object body) throws RestCallException { 7105 var req = request(op("POST", uri, NO_BODY)); 7106 try { 7107 if (body instanceof Supplier<?> body2) 7108 body = body2.get(); 7109 if (body instanceof NameValuePair body2) 7110 return req.content(new UrlEncodedFormEntity(l(body2))); 7111 if (body instanceof NameValuePair[] namevaluepairArray) 7112 return req.content(new UrlEncodedFormEntity(l(namevaluepairArray))); 7113 if (body instanceof PartList body2) 7114 return req.content(new UrlEncodedFormEntity(body2)); 7115 if (body instanceof HttpResource body2) 7116 body2.getHeaders().forEach(req::header); 7117 if (body instanceof HttpEntity body2) { 7118 if (body2.getContentType() == null) 7119 req.header(ContentType.APPLICATION_FORM_URLENCODED); 7120 return req.content(body2); 7121 } 7122 if (body instanceof Reader || body instanceof InputStream) 7123 return req.header(ContentType.APPLICATION_FORM_URLENCODED).content(body); 7124 return req.content(serializedEntity(body, urlEncodingSerializer, null)); 7125 } catch (IOException e) { 7126 throw new RestCallException(null, e, "Could not read form post body."); 7127 } 7128 } 7129 7130 /** 7131 * Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> 7132 * against the specified URI. 7133 * 7134 * @param uri 7135 * The URI of the remote REST resource. 7136 * <br>Can be any of the following types: 7137 * <ul> 7138 * <li>{@link URIBuilder} 7139 * <li>{@link URI} 7140 * <li>{@link URL} 7141 * <li>{@link String} 7142 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7143 * </ul> 7144 * @param parameters 7145 * The parameters of the form post. 7146 * <br>The parameters represent name/value pairs and must be an even number of arguments. 7147 * <br>Parameters are converted to {@link BasicPart} objects. 7148 * @return 7149 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7150 * as a parsed object. 7151 * @throws RestCallException If any authentication errors occurred. 7152 */ 7153 public RestRequest formPostPairs(Object uri, String...parameters) throws RestCallException { 7154 return formPost(uri, partList(parameters)); 7155 } 7156 7157 /** 7158 * Perform a <c>GET</c> request against the root URI. 7159 * 7160 * @return 7161 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7162 * as a parsed object. 7163 * @throws RestCallException If any authentication errors occurred. 7164 */ 7165 public RestRequest get() throws RestCallException { 7166 return request(op("GET", null, NO_BODY)); 7167 } 7168 7169 /** 7170 * Perform a <c>GET</c> request against the specified URI. 7171 * 7172 * @param uri 7173 * The URI of the remote REST resource. 7174 * <br>Can be any of the following types: 7175 * <ul> 7176 * <li>{@link URIBuilder} 7177 * <li>{@link URI} 7178 * <li>{@link URL} 7179 * <li>{@link String} 7180 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7181 * </ul> 7182 * @return 7183 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7184 * as a parsed object. 7185 * @throws RestCallException If any authentication errors occurred. 7186 */ 7187 public RestRequest get(Object uri) throws RestCallException { 7188 return request(op("GET", uri, NO_BODY)); 7189 } 7190 7191 /** 7192 * Obtains the connection manager used by this client. 7193 * 7194 * @return The connection manager. 7195 * @deprecated Use {@link HttpClientBuilder}. 7196 */ 7197 @Deprecated(since = "10.0", forRemoval = true) 7198 @Override /* Overridden from HttpClient */ 7199 public ClientConnectionManager getConnectionManager() { return httpClient.getConnectionManager(); } 7200 7201 /** 7202 * Returns the connection manager if one was specified in the client builder. 7203 * 7204 * @return The connection manager. May be <jk>null</jk>. 7205 */ 7206 public HttpClientConnectionManager getHttpClientConnectionManager() { return connectionManager; } 7207 7208 /** 7209 * Obtains the parameters for this client. 7210 * 7211 * These parameters will become defaults for all requests being executed with this client, and for the parameters of dependent objects in this client. 7212 * 7213 * @return The default parameters. 7214 * @deprecated Use {@link RequestConfig}. 7215 */ 7216 @Deprecated(since = "10.0", forRemoval = true) 7217 @Override /* Overridden from HttpClient */ 7218 public HttpParams getParams() { return httpClient.getParams(); } 7219 7220 /** 7221 * Create a new proxy interface against a 3rd-party REST interface. 7222 * 7223 * <p> 7224 * The URI to the REST interface is based on the following values: 7225 * <ul> 7226 * <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). 7227 * <li>The {@link Builder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>). 7228 * <li>The fully-qualified class name of the interface (<c>class-name</c>). 7229 * </ul> 7230 * 7231 * <p> 7232 * The URI calculation is as follows: 7233 * <ul> 7234 * <li><c>remote-path</c> - If remote path is absolute. 7235 * <li><c>root-uri/remote-path</c> - If remote path is relative and root-uri has been specified. 7236 * <li><c>root-uri/class-name</c> - If remote path is not specified. 7237 * </ul> 7238 * 7239 * <p> 7240 * If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. 7241 * 7242 * <h5 class='section'>Examples:</h5> 7243 * <p class='bjava'> 7244 * <jk>package</jk> org.apache.foo; 7245 * 7246 * <ja>@RemoteResource</ja>(path=<js>"http://hostname/resturi/myinterface1"</js>) 7247 * <jk>public interface</jk> MyInterface1 { ... } 7248 * 7249 * <ja>@RemoteResource</ja>(path=<js>"/myinterface2"</js>) 7250 * <jk>public interface</jk> MyInterface2 { ... } 7251 * 7252 * <jk>public interface</jk> MyInterface3 { ... } 7253 * 7254 * <jc>// Resolves to "http://localhost/resturi/myinterface1"</jc> 7255 * MyInterface1 <jv>interface1</jv> = RestClient 7256 * .<jsm>create</jsm>() 7257 * .build() 7258 * .getRemote(MyInterface1.<jk>class</jk>); 7259 * 7260 * <jc>// Resolves to "http://hostname/resturi/myinterface2"</jc> 7261 * MyInterface2 <jv>interface2</jv> = RestClient 7262 * .<jsm>create</jsm>() 7263 * .rootUrl(<js>"http://hostname/resturi"</js>) 7264 * .build() 7265 * .getRemote(MyInterface2.<jk>class</jk>); 7266 * 7267 * <jc>// Resolves to "http://hostname/resturi/org.apache.foo.MyInterface3"</jc> 7268 * MyInterface3 <jv>interface3</jv> = RestClient 7269 * .<jsm>create</jsm>() 7270 * .rootUrl(<js>"http://hostname/resturi"</js>) 7271 * .build() 7272 * .getRemote(MyInterface3.<jk>class</jk>); 7273 * </p> 7274 * 7275 * <h5 class='section'>Notes:</h5><ul> 7276 * <li class='note'> 7277 * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying 7278 * pooling client connection manager. 7279 * </ul> 7280 * 7281 * <h5 class='section'>See Also:</h5><ul> 7282 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxies">REST Proxies</a> 7283 * </ul> 7284 * 7285 * @param <T> The interface to create a proxy for. 7286 * @param interfaceClass The interface to create a proxy for. 7287 * <br>Cannot be <jk>null</jk>. 7288 * @return The new proxy interface. 7289 * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. 7290 */ 7291 public <T> T getRemote(Class<T> interfaceClass) { 7292 return getRemote(interfaceClass, null); 7293 } 7294 7295 /** 7296 * Same as {@link #getRemote(Class)} except explicitly specifies the URI of the REST interface. 7297 * 7298 * <h5 class='section'>See Also:</h5><ul> 7299 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxies">REST Proxies</a> 7300 * </ul> 7301 * 7302 * @param <T> The interface to create a proxy for. 7303 * @param interfaceClass The interface to create a proxy for. 7304 * <br>Cannot be <jk>null</jk>. 7305 * @param rootUrl The URI of the REST interface. 7306 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7307 * @return The new proxy interface. 7308 */ 7309 public <T> T getRemote(Class<T> interfaceClass, Object rootUrl) { 7310 return getRemote(interfaceClass, rootUrl, null, null); 7311 } 7312 7313 /** 7314 * Same as {@link #getRemote(Class, Object)} but allows you to override the serializer and parser used. 7315 * 7316 * <h5 class='section'>See Also:</h5><ul> 7317 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxies">REST Proxies</a> 7318 * </ul> 7319 7320 * @param <T> The interface to create a proxy for. 7321 * @param interfaceClass The interface to create a proxy for. 7322 * <br>Cannot be <jk>null</jk>. 7323 * @param rootUrl The URI of the REST interface. 7324 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7325 * @param serializer The serializer used to serialize POJOs to the body of the HTTP request. 7326 * <br>Can be <jk>null</jk> (will use the default serializer from the client). 7327 * @param parser The parser used to parse POJOs from the body of the HTTP response. 7328 * <br>Can be <jk>null</jk> (will use the default parser from the client). 7329 * @return The new proxy interface. 7330 */ 7331 @SuppressWarnings({ 7332 "java:S6541" // Brain method acceptable - remote proxy creation requires complex initialization logic 7333 }) 7334 public <T> T getRemote(Class<T> interfaceClass, Object rootUrl, Serializer serializer, Parser parser) { 7335 7336 reqnn("interfaceClass", interfaceClass); 7337 7338 if (rootUrl == null) 7339 rootUrl = this.rootUrl != null ? this.rootUrl.get() : null; 7340 7341 final String restUrl2 = trimSlashes(emptyIfNull(rootUrl)); 7342 7343 return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), a(interfaceClass), new InvocationHandler() { 7344 7345 final RemoteMeta rm = new RemoteMeta(interfaceClass); 7346 7347 @Override /* Overridden from InvocationHandler */ 7348 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 7349 var rom = rm.getOperationMeta(method); 7350 7351 // Resolve the effective request URL (interface/method baseUrl + @Url overrides, with the SSRF guard). 7352 var uri = resolveRemoteUri(interfaceClass, restUrl2, method, rom, rm, args); 7353 7354 var ror = rom.getReturns(); 7355 if (ror.isFuture()) { 7356 return getExecutorService().submit(() -> { 7357 try { 7358 return executeRemoteWithRetry(uri, serializer, parser, method, rom, rm, args); 7359 } catch (Exception e) { 7360 throw toRex(e); 7361 } 7362 }); 7363 } else if (ror.isCompletableFuture()) { 7364 var cf = new CompletableFuture<>(); 7365 getExecutorService().submit(() -> { 7366 try { 7367 cf.complete(executeRemoteWithRetry(uri, serializer, parser, method, rom, rm, args)); 7368 } catch (Exception e) { 7369 cf.completeExceptionally(e); 7370 } 7371 return null; 7372 }); 7373 return cf; 7374 } 7375 7376 try { 7377 return executeRemoteWithRetry(uri, serializer, parser, method, rom, rm, args); 7378 } catch (Exception e) { 7379 // Check if this is a RuntimeException wrapping a RestCallException with an Error cause 7380 if (e instanceof RuntimeException e2 && e2.getCause() instanceof RestCallException rce) { 7381 var t = rce.getCause(); 7382 if (nn(t)) { 7383 // Check if the cause matches the method's declared exception types 7384 for (var t2 : method.getExceptionTypes()) 7385 if (t2.isInstance(t)) 7386 throw t; // Rethrow the original Throwable (can be Error) 7387 } 7388 } 7389 throw e; 7390 } 7391 } 7392 }); 7393 } 7394 7395 /** 7396 * Create a new proxy interface against an RRPC-style service. 7397 * 7398 * <p> 7399 * Remote interfaces are interfaces exposed on the server side using either the <c>RrpcServlet</c> 7400 * or <c>RRPC</c> REST methods. 7401 * 7402 * <p> 7403 * The URI to the REST interface is based on the following values: 7404 * <ul> 7405 * <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). 7406 * <li>The {@link Builder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>). 7407 * <li>The fully-qualified class name of the interface (<c>class-name</c>). 7408 * </ul> 7409 * 7410 * <p> 7411 * The URI calculation is as follows: 7412 * <ul> 7413 * <li><c>remote-path</c> - If remote path is absolute. 7414 * <li><c>root-url/remote-path</c> - If remote path is relative and root-url has been specified. 7415 * <li><c>root-url/class-name</c> - If remote path is not specified. 7416 * </ul> 7417 * 7418 * <p> 7419 * If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. 7420 * 7421 * <h5 class='section'>Notes:</h5><ul> 7422 * <li class='note'> 7423 * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying 7424 * pooling client connection manager. 7425 * </ul> 7426 * 7427 * <h5 class='section'>See Also:</h5><ul> 7428 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7429 * </ul> 7430 * 7431 * @param <T> The interface to create a proxy for. 7432 * @param interfaceClass The interface to create a proxy for. 7433 * <br>Cannot be <jk>null</jk>. 7434 * @return The new proxy interface. 7435 * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. 7436 */ 7437 public <T> T getRrpcInterface(Class<T> interfaceClass) { 7438 return getRrpcInterface(interfaceClass, null); 7439 } 7440 7441 /** 7442 * Same as {@link #getRrpcInterface(Class)} except explicitly specifies the URI of the REST interface. 7443 * 7444 * <h5 class='section'>See Also:</h5><ul> 7445 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7446 * </ul> 7447 * 7448 * @param <T> The interface to create a proxy for. 7449 * @param interfaceClass The interface to create a proxy for. 7450 * <br>Cannot be <jk>null</jk>. 7451 * @param uri The URI of the REST interface. 7452 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7453 * @return The new proxy interface. 7454 */ 7455 public <T> T getRrpcInterface(Class<T> interfaceClass, Object uri) { 7456 return getRrpcInterface(interfaceClass, uri, null, null); 7457 } 7458 7459 /** 7460 * Same as {@link #getRrpcInterface(Class, Object)} but allows you to override the serializer and parser used. 7461 * 7462 * <h5 class='section'>See Also:</h5><ul> 7463 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7464 * </ul> 7465 * 7466 * @param <T> The interface to create a proxy for. 7467 * @param interfaceClass The interface to create a proxy for. 7468 * <br>Cannot be <jk>null</jk>. 7469 * @param uri The URI of the REST interface. 7470 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7471 * @param serializer The serializer used to serialize POJOs to the body of the HTTP request. 7472 * <br>Can be <jk>null</jk> (will use the default serializer from the client). 7473 * @param parser The parser used to parse POJOs from the body of the HTTP response. 7474 * <br>Can be <jk>null</jk> (will use the default parser from the client). 7475 * @return The new proxy interface. 7476 */ 7477 public <T> T getRrpcInterface(Class<T> interfaceClass, Object uri, Serializer serializer, Parser parser) { 7478 7479 if (uri == null) { 7480 var rm = new RrpcInterfaceMeta(interfaceClass, ""); 7481 var path = rm.getPath(); 7482 if (path.indexOf("://") == -1) { 7483 var rootUrlValue = rootUrl != null ? rootUrl.get() : null; 7484 if (isEmpty(rootUrlValue)) 7485 throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote interface."); 7486 path = trimSlashes(rootUrlValue) + '/' + path; 7487 } 7488 uri = path; 7489 } 7490 7491 final String restUrl2 = s(uri); 7492 7493 return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), a(interfaceClass), new InvocationHandler() { 7494 7495 final RrpcInterfaceMeta rm = new RrpcInterfaceMeta(interfaceClass, restUrl2); 7496 7497 @Override /* Overridden from InvocationHandler */ 7498 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 7499 var rim = rm.getMethodMeta(method); 7500 7501 var uri = rim.getUri(); 7502 RestResponse res; 7503 7504 try { 7505 // @formatter:off 7506 RestRequest rc = request("POST", uri, true) 7507 .serializer(serializer) 7508 .content(args) 7509 .rethrow(RuntimeException.class) 7510 .rethrow(method.getExceptionTypes()); 7511 // @formatter:on 7512 7513 res = rc.run(); 7514 7515 var v = res.getContent().as(method.getGenericReturnType()); 7516 if (v == null && method.getReturnType().isPrimitive()) 7517 v = ClassInfo.of(method.getReturnType()).getPrimitiveDefault(); 7518 return v; 7519 7520 } catch (Exception e) { 7521 // Check if this is a RuntimeException wrapping a RestCallException with an Error cause 7522 if (e instanceof RuntimeException e2 && e2.getCause() instanceof RestCallException rce) { 7523 var t = rce.getCause(); 7524 if (nn(t)) { 7525 // Check if the cause matches the method's declared exception types 7526 for (var t2 : method.getExceptionTypes()) 7527 if (t2.isInstance(t)) 7528 throw t; // Rethrow the original Throwable (can be Error) 7529 } 7530 } 7531 if (e instanceof RestCallException e2) { 7532 var t = e2.getCause(); 7533 if (nn(t)) { 7534 // Check if the cause matches the method's declared exception types 7535 for (var t2 : method.getExceptionTypes()) 7536 if (t2.isInstance(t)) 7537 throw t; // Rethrow the original Throwable (can be Error) 7538 if (t instanceof Exception t2) 7539 e = t2; 7540 } 7541 } 7542 if (e instanceof RuntimeException e2) 7543 throw e2; 7544 for (var t2 : method.getExceptionTypes()) 7545 if (t2.isInstance(e)) 7546 throw e; 7547 throw toRex(e); 7548 } 7549 } 7550 }); 7551 } 7552 7553 /** 7554 * Perform a <c>HEAD</c> request against the specified URI. 7555 * 7556 * @param uri 7557 * The URI of the remote REST resource. 7558 * <br>Can be any of the following types: 7559 * <ul> 7560 * <li>{@link URIBuilder} 7561 * <li>{@link URI} 7562 * <li>{@link URL} 7563 * <li>{@link String} 7564 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7565 * </ul> 7566 * @return 7567 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7568 * as a parsed object. 7569 * @throws RestCallException If any authentication errors occurred. 7570 */ 7571 public RestRequest head(Object uri) throws RestCallException { 7572 return request(op("HEAD", uri, NO_BODY)); 7573 } 7574 7575 /** 7576 * Perform an <c>OPTIONS</c> request against the specified URI. 7577 * 7578 * @param uri 7579 * The URI of the remote REST resource. 7580 * <br>Can be any of the following types: 7581 * <ul> 7582 * <li>{@link URIBuilder} 7583 * <li>{@link URI} 7584 * <li>{@link URL} 7585 * <li>{@link String} 7586 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7587 * </ul> 7588 * @return 7589 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7590 * as a parsed object. 7591 * @throws RestCallException If any authentication errors occurred. 7592 */ 7593 public RestRequest options(Object uri) throws RestCallException { 7594 return request(op("OPTIONS", uri, NO_BODY)); 7595 } 7596 7597 /** 7598 * Same as {@link #patch(Object, Object)} but don't specify the input yet. 7599 * 7600 * <p> 7601 * You must call {@link RestRequest#content(Object)} to set the contents on the result object. 7602 * 7603 * @param uri 7604 * The URI of the remote REST resource. 7605 * <br>Can be any of the following types: 7606 * <ul> 7607 * <li>{@link URIBuilder} 7608 * <li>{@link URI} 7609 * <li>{@link URL} 7610 * <li>{@link String} 7611 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7612 * </ul> 7613 * @return 7614 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7615 * as a parsed object. 7616 * @throws RestCallException REST call failed. 7617 */ 7618 public RestRequest patch(Object uri) throws RestCallException { 7619 return request(op(PATCH, uri, NO_BODY)); 7620 } 7621 7622 /** 7623 * Perform a <c>PATCH</c> request against the specified URI. 7624 * 7625 * @param uri 7626 * The URI of the remote REST resource. 7627 * <br>Can be any of the following types: 7628 * <ul> 7629 * <li>{@link URIBuilder} 7630 * <li>{@link URI} 7631 * <li>{@link URL} 7632 * <li>{@link String} 7633 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7634 * </ul> 7635 * @param body 7636 * The object to serialize and transmit to the URI as the body of the request. 7637 * Can be of the following types: 7638 * <ul class='spaced-list'> 7639 * <li> 7640 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7641 * <li> 7642 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7643 * <li> 7644 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 7645 * <li> 7646 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7647 * <li> 7648 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7649 * {@link RestClient}. 7650 * <li> 7651 * {@link PartList} - Converted to a URL-encoded FORM post. 7652 * <li> 7653 * {@link Supplier} - A supplier of anything on this list. 7654 * </ul> 7655 * @return 7656 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7657 * as a parsed object. 7658 * @throws RestCallException If any authentication errors occurred. 7659 */ 7660 public RestRequest patch(Object uri, Object body) throws RestCallException { 7661 return request(op(PATCH, uri, body)); 7662 } 7663 7664 /** 7665 * Perform a <c>PATCH</c> request against the specified URI as a plain text body bypassing the serializer. 7666 * 7667 * @param uri 7668 * The URI of the remote REST resource. 7669 * <br>Can be any of the following types: 7670 * <ul> 7671 * <li>{@link URIBuilder} 7672 * <li>{@link URI} 7673 * <li>{@link URL} 7674 * <li>{@link String} 7675 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7676 * </ul> 7677 * @param body 7678 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7679 * @param contentType 7680 * The content type of the request. 7681 * @return 7682 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7683 * as a parsed object. 7684 * @throws RestCallException If any authentication errors occurred. 7685 */ 7686 public RestRequest patch(Object uri, String body, ContentType contentType) throws RestCallException { 7687 return request(op(PATCH, uri, stringBody(body))).header(contentType); 7688 } 7689 7690 /** 7691 * Same as {@link #post(Object, Object)} but don't specify the input yet. 7692 * 7693 * <p> 7694 * You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} to set the 7695 * contents on the result object. 7696 * 7697 * <h5 class='section'>Notes:</h5><ul> 7698 * <li class='note'>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. 7699 * </ul> 7700 * 7701 * @param uri 7702 * The URI of the remote REST resource. 7703 * <br>Can be any of the following types: 7704 * <ul> 7705 * <li>{@link URIBuilder} 7706 * <li>{@link URI} 7707 * <li>{@link URL} 7708 * <li>{@link String} 7709 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7710 * </ul> 7711 * @return 7712 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7713 * as a parsed object. 7714 * @throws RestCallException REST call failed. 7715 */ 7716 public RestRequest post(Object uri) throws RestCallException { 7717 return request(op("POST", uri, NO_BODY)); 7718 } 7719 7720 /** 7721 * Perform a <c>POST</c> request against the specified URI. 7722 * 7723 * <h5 class='section'>Notes:</h5><ul> 7724 * <li class='note'>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. 7725 * </ul> 7726 * 7727 * @param uri 7728 * The URI of the remote REST resource. 7729 * <br>Can be any of the following types: 7730 * <ul> 7731 * <li>{@link URIBuilder} 7732 * <li>{@link URI} 7733 * <li>{@link URL} 7734 * <li>{@link String} 7735 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7736 * </ul> 7737 * @param body 7738 * The object to serialize and transmit to the URI as the body of the request. 7739 * Can be of the following types: 7740 * <ul class='spaced-list'> 7741 * <li> 7742 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7743 * <li> 7744 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7745 * <li> 7746 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7747 * {@link RestClient}. 7748 * <li> 7749 * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7750 * <li> 7751 * {@link PartList} - Converted to a URL-encoded FORM post. 7752 * <li> 7753 * {@link Supplier} - A supplier of anything on this list. 7754 * </ul> 7755 * @return 7756 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7757 * as a parsed object. 7758 * @throws RestCallException If any authentication errors occurred. 7759 */ 7760 public RestRequest post(Object uri, Object body) throws RestCallException { 7761 return request(op("POST", uri, body)); 7762 } 7763 7764 /** 7765 * Perform a <c>POST</c> request against the specified URI as a plain text body bypassing the serializer. 7766 * 7767 * @param uri 7768 * The URI of the remote REST resource. 7769 * <br>Can be any of the following types: 7770 * <ul> 7771 * <li>{@link URIBuilder} 7772 * <li>{@link URI} 7773 * <li>{@link URL} 7774 * <li>{@link String} 7775 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7776 * </ul> 7777 * @param body 7778 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7779 * @param contentType 7780 * The content type of the request. 7781 * @return 7782 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7783 * as a parsed object. 7784 * @throws RestCallException If any authentication errors occurred. 7785 */ 7786 public RestRequest post(Object uri, String body, ContentType contentType) throws RestCallException { 7787 return request(op("POST", uri, stringBody(body))).header(contentType); 7788 } 7789 7790 /** 7791 * Same as {@link #put(Object, Object)} but don't specify the input yet. 7792 * 7793 * <p> 7794 * You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} 7795 * to set the contents on the result object. 7796 * 7797 * @param uri 7798 * The URI of the remote REST resource. 7799 * <br>Can be any of the following types: 7800 * <ul> 7801 * <li>{@link URIBuilder} 7802 * <li>{@link URI} 7803 * <li>{@link URL} 7804 * <li>{@link String} 7805 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7806 * </ul> 7807 * @return 7808 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7809 * as a parsed object. 7810 * @throws RestCallException REST call failed. 7811 */ 7812 public RestRequest put(Object uri) throws RestCallException { 7813 return request(op("PUT", uri, NO_BODY)); 7814 } 7815 7816 /** 7817 * Perform a <c>PUT</c> request against the specified URI. 7818 * 7819 * @param uri 7820 * The URI of the remote REST resource. 7821 * <br>Can be any of the following types: 7822 * <ul> 7823 * <li>{@link URIBuilder} 7824 * <li>{@link URI} 7825 * <li>{@link URL} 7826 * <li>{@link String} 7827 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7828 * </ul> 7829 * @param body 7830 * The object to serialize and transmit to the URI as the body of the request. 7831 * Can be of the following types: 7832 * <ul class='spaced-list'> 7833 * <li> 7834 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7835 * <li> 7836 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7837 * <li> 7838 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7839 * {@link RestClient}. 7840 * <li> 7841 * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7842 * <li> 7843 * {@link PartList} - Converted to a URL-encoded FORM post. 7844 * <li> 7845 * {@link Supplier} - A supplier of anything on this list. 7846 * </ul> 7847 * @return 7848 * A {@link RestRequest} object that can be further tailored before executing the request 7849 * and getting the response as a parsed object. 7850 * @throws RestCallException If any authentication errors occurred. 7851 */ 7852 public RestRequest put(Object uri, Object body) throws RestCallException { 7853 return request(op("PUT", uri, body)); 7854 } 7855 7856 /** 7857 * Perform a <c>PUT</c> request against the specified URI using a plain text body bypassing the serializer. 7858 * 7859 * @param uri 7860 * The URI of the remote REST resource. 7861 * <br>Can be any of the following types: 7862 * <ul> 7863 * <li>{@link URIBuilder} 7864 * <li>{@link URI} 7865 * <li>{@link URL} 7866 * <li>{@link String} 7867 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7868 * </ul> 7869 * @param body 7870 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7871 * @param contentType The content type of the request. 7872 * @return 7873 * A {@link RestRequest} object that can be further tailored before executing the request 7874 * and getting the response as a parsed object. 7875 * @throws RestCallException If any authentication errors occurred. 7876 */ 7877 public RestRequest put(Object uri, String body, ContentType contentType) throws RestCallException { 7878 return request(op("PUT", uri, stringBody(body))).header(contentType); 7879 } 7880 7881 /** 7882 * Perform a generic REST call. 7883 * 7884 * @param method The HTTP method. 7885 * <br>Cannot be <jk>null</jk>. 7886 * @param uri 7887 * The URI of the remote REST resource. 7888 * <br>Can be any of the following types: 7889 * <ul> 7890 * <li>{@link URIBuilder} 7891 * <li>{@link URI} 7892 * <li>{@link URL} 7893 * <li>{@link String} 7894 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7895 * </ul> 7896 * <br>Can be <jk>null</jk> (will result in an invalid request). 7897 * @return 7898 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7899 * as a parsed object. 7900 * @throws RestCallException If any authentication errors occurred. 7901 */ 7902 public RestRequest request(String method, Object uri) throws RestCallException { 7903 return request(op(method, uri, NO_BODY)); 7904 } 7905 7906 /** 7907 * Perform a generic REST call. 7908 * 7909 * <p> 7910 * Typically you're going to use {@link #request(String, Object)} or {@link #request(String, Object, Object)}, 7911 * but this method is provided to allow you to perform non-standard HTTP methods (e.g. HTTP FOO). 7912 * 7913 * @param method The method name (e.g. <js>"GET"</js>, <js>"OPTIONS"</js>). 7914 * <br>Cannot be <jk>null</jk>. 7915 * @param uri 7916 * The URI of the remote REST resource. 7917 * <br>Can be any of the following types: 7918 * <ul> 7919 * <li>{@link URIBuilder} 7920 * <li>{@link URI} 7921 * <li>{@link URL} 7922 * <li>{@link String} 7923 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7924 * </ul> 7925 * <br>Can be <jk>null</jk> (will result in an invalid request). 7926 * @param hasBody Boolean flag indicating if the specified request has content associated with it. 7927 * @return 7928 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7929 * as a parsed object. 7930 * @throws RestCallException If any authentication errors occurred. 7931 */ 7932 public RestRequest request(String method, Object uri, boolean hasBody) throws RestCallException { 7933 return request(op(method, uri, NO_BODY).hasContent(hasBody)); 7934 } 7935 7936 /** 7937 * Perform a generic REST call. 7938 * 7939 * @param method The HTTP method. 7940 * <br>Cannot be <jk>null</jk>. 7941 * @param uri 7942 * The URI of the remote REST resource. 7943 * <br>Can be any of the following types: 7944 * <ul> 7945 * <li>{@link URIBuilder} 7946 * <li>{@link URI} 7947 * <li>{@link URL} 7948 * <li>{@link String} 7949 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7950 * </ul> 7951 * <br>Can be <jk>null</jk> (will result in an invalid request). 7952 * @param body 7953 * The HTTP body content. 7954 * Can be of the following types: 7955 * <ul class='spaced-list'> 7956 * <li> 7957 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7958 * <li> 7959 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7960 * <li> 7961 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 7962 * <li> 7963 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7964 * <li> 7965 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7966 * {@link RestClient}. 7967 * <li> 7968 * {@link PartList} - Converted to a URL-encoded FORM post. 7969 * <li> 7970 * {@link Supplier} - A supplier of anything on this list. 7971 * </ul> 7972 * This parameter is IGNORED if the method type normally does not have content. 7973 * @return 7974 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7975 * as a parsed object. 7976 * @throws RestCallException If any authentication errors occurred. 7977 */ 7978 public RestRequest request(String method, Object uri, Object body) throws RestCallException { 7979 return request(op(method, uri, body)); 7980 } 7981 7982 private static RestOperation op(String method, Object url, Object body) { 7983 return RestOperation.of(method, url, body); 7984 } 7985 7986 private static Reader stringBody(String body) { 7987 return body == null ? null : new StringReader(s(body)); 7988 } 7989 7990 /** 7991 * Creates a {@link RestRequest} object from the specified {@link HttpRequest} object. 7992 * 7993 * <p> 7994 * Subclasses can override this method to provide their own specialized {@link RestRequest} objects. 7995 * 7996 * <h5 class='section'>Notes:</h5><ul> 7997 * <li class='warn'>The SSRF guard of {@code @Remote} calls travels on the request's {@link HttpContext}, which an 7998 * override of {@link RestRequest#run()} in the returned request's class may drop (by copying the request, or 7999 * sending it with another context), and depends on this client's connection manager, which a request bound to a different client does not use. So an SSRF-guard-active 8000 * {@code @Remote} call (one without the {@link Builder#allowPrivateUrls(boolean) allowPrivateUrls} opt-in) is 8001 * refused through such a request, whether this method or {@link #request(String, Object, boolean)} returns it. 8002 * Ordinary requests are unaffected. 8003 * </ul> 8004 * 8005 * @param uri The target. 8006 * @param method The HTTP method (uppercase). 8007 * @param hasBody Whether this method has a request entity. 8008 * @return A new {@link RestRequest} object. 8009 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. 8010 */ 8011 protected RestRequest createRequest(URI uri, String method, boolean hasBody) throws RestCallException { 8012 return new RestRequest(this, uri, method, hasBody); 8013 } 8014 8015 /** 8016 * Creates a {@link RestResponse} object from the specified {@link HttpResponse} object. 8017 * 8018 * <p> 8019 * Subclasses can override this method to provide their own specialized {@link RestResponse} objects. 8020 * 8021 * @param request The request creating this response. 8022 * @param httpResponse The response object to wrap. 8023 * @param parser The parser to use to parse the response. 8024 * 8025 * @return A new {@link RestResponse} object. 8026 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. 8027 */ 8028 protected RestResponse createResponse(RestRequest request, HttpResponse httpResponse, Parser parser) throws RestCallException { 8029 return new RestResponse(this, request, httpResponse, parser); 8030 } 8031 8032 /** 8033 * @deprecated Object.finalize() is deprecated. Use try-with-resources or explicit close() calls instead. 8034 */ 8035 @Override 8036 @Deprecated(since = "9", forRemoval = true) // Object.finalize() is deprecated 8037 @SuppressWarnings({ 8038 "java:S1113" // Kept for detectLeaks; logs warning when RestClient is GC'd without close 8039 }) 8040 protected void finalize() throws Throwable { 8041 if (detectLeaks && ! isClosed.get() && ! keepHttpClientOpen) { 8042 var sb = new StringBuilder("WARNING: RestClient garbage collected before it was finalized."); // NOT DEBUG 8043 if (nn(creationStack)) { 8044 sb.append("\nCreation Stack:"); // NOT DEBUG 8045 for (var e : creationStack) { 8046 sb.append("\n\t").append(e); // NOT DEBUG 8047 } 8048 } 8049 log(WARNING, sb.toString()); 8050 } 8051 } 8052 8053 /** 8054 * Returns the part parser associated with this client. 8055 * 8056 * @return The part parser associated with this client. 8057 */ 8058 protected HttpPartParser getPartParser() { return partParser; } 8059 8060 /** 8061 * Returns the part parser instance of the specified type. 8062 * 8063 * @param c The part parser class. 8064 * @return The part parser. 8065 */ 8066 protected HttpPartParser getPartParser(Class<? extends HttpPartParser> c) { 8067 var x = partParsers.get(c); 8068 if (x == null) { 8069 try { 8070 x = BeanInstantiator.of(c, beanStore).run(); 8071 } catch (ExecutableException e) { 8072 throw toRex(e); 8073 } 8074 partParsers.put(c, x); 8075 } 8076 return x; 8077 } 8078 8079 /** 8080 * Returns the part serializer associated with this client. 8081 * 8082 * @return The part serializer associated with this client. 8083 */ 8084 protected HttpPartSerializer getPartSerializer() { return partSerializer; } 8085 8086 /** 8087 * Returns the part serializer instance of the specified type. 8088 * 8089 * @param c The part serializer class. 8090 * @return The part serializer. 8091 */ 8092 protected HttpPartSerializer getPartSerializer(Class<? extends HttpPartSerializer> c) { 8093 var x = partSerializers.get(c); 8094 if (x == null) { 8095 try { 8096 x = BeanInstantiator.of(c, beanStore).run(); 8097 } catch (ExecutableException e) { 8098 throw toRex(e); 8099 } 8100 partSerializers.put(c, x); 8101 } 8102 return x; 8103 } 8104 8105 /** 8106 * Gets called add the end of the constructor call to perform any post-initialization. 8107 */ 8108 protected void init() { 8109 // No-op: Intentional empty implementation for optional override method 8110 } 8111 8112 /** 8113 * Returns <jk>true</jk> if empty request form-data parameter values should be ignored. 8114 * 8115 * @return <jk>true</jk> if empty request form-data parameter values should be ignored. 8116 */ 8117 protected boolean isSkipEmptyFormData() { return skipEmptyFormData; } 8118 8119 /** 8120 * Returns <jk>true</jk> if empty request header values should be ignored. 8121 * 8122 * @return <jk>true</jk> if empty request header values should be ignored. 8123 */ 8124 protected boolean isSkipEmptyHeaderData() { return skipEmptyHeaderData; } 8125 8126 /** 8127 * Returns <jk>true</jk> if empty request query parameter values should be ignored. 8128 * 8129 * @return <jk>true</jk> if empty request query parameter values should be ignored. 8130 */ 8131 protected boolean isSkipEmptyQueryData() { return skipEmptyQueryData; } 8132 8133 /** 8134 * Logs a message. 8135 * 8136 * @param level The log level. 8137 * @param msg The message with {@link String#format(String, Object...) String.format}-style (<c>%s</c>) arguments. 8138 * @param args The arguments. 8139 */ 8140 protected void log(Level level, String msg, Object...args) { 8141 if (logger.isLoggable(level)) { 8142 String formattedMsg = f(msg, args); 8143 logger.log(level, formattedMsg); 8144 if (logToConsole) 8145 console.println(formattedMsg); 8146 } else if (logToConsole) { 8147 console.println(f(msg, args)); 8148 } 8149 } 8150 8151 /** 8152 * Logs a message. 8153 * 8154 * @param level The log level. 8155 * @param t Thrown exception. 8156 * <br>Can be <jk>null</jk> (no exception stack trace will be logged). 8157 * @param msg The message. 8158 * @param args Optional message arguments. 8159 */ 8160 protected void log(Level level, Throwable t, String msg, Object...args) { 8161 if (logger.isLoggable(level)) { 8162 String formattedMsg = f(msg, args); 8163 logger.log(level, formattedMsg, t); 8164 if (logToConsole) { 8165 console.println(f(msg, args)); 8166 if (nn(t)) 8167 t.printStackTrace(console); 8168 } 8169 } else if (logToConsole) { 8170 console.println(f(msg, args)); 8171 if (nn(t)) 8172 t.printStackTrace(console); 8173 } 8174 } 8175 8176 /** 8177 * Interceptor method called immediately after the RestRequest object is created and all headers/query/form-data has been set on the request from the client. 8178 * 8179 * <p> 8180 * Subclasses can override this method to handle any cleanup operations. 8181 * 8182 * <h5 class='section'>See Also:</h5><ul> 8183 * <li class='jm'>{@link Builder#interceptors(Object...)} 8184 * </ul> 8185 * 8186 * @param req The HTTP request. 8187 * @param res The HTTP response. 8188 * @throws RestCallException If any of the interceptors threw an exception. 8189 */ 8190 protected void onCallClose(RestRequest req, RestResponse res) throws RestCallException { 8191 try { 8192 for (var rci : interceptors) 8193 rci.onClose(req, res); 8194 } catch (RuntimeException | RestCallException e) { 8195 throw e; 8196 } catch (Exception e) { 8197 throw new RestCallException(res, e, "Interceptor threw an exception on close."); 8198 } 8199 } 8200 8201 /** 8202 * Interceptor method called immediately after an HTTP response has been received. 8203 * 8204 * <p> 8205 * Subclasses can override this method to intercept the response and perform special modifications. 8206 * 8207 * <h5 class='section'>See Also:</h5><ul> 8208 * <li class='jm'>{@link Builder#interceptors(Object...)} 8209 * </ul> 8210 * 8211 * @param req The HTTP request. 8212 * @param res The HTTP response. 8213 * @throws RestCallException If any of the interceptors threw an exception. 8214 */ 8215 protected void onCallConnect(RestRequest req, RestResponse res) throws RestCallException { 8216 try { 8217 for (var rci : interceptors) 8218 rci.onConnect(req, res); 8219 } catch (RuntimeException | RestCallException e) { 8220 throw e; 8221 } catch (Exception e) { 8222 throw new RestCallException(res, e, "Interceptor threw an exception on connect."); 8223 } 8224 } 8225 8226 /** 8227 * Interceptor method called immediately after the RestRequest object is created and all headers/query/form-data has been copied from the client. 8228 * 8229 * <p> 8230 * Subclasses can override this method to intercept the request and perform special modifications. 8231 * 8232 * <h5 class='section'>See Also:</h5><ul> 8233 * <li class='jm'>{@link Builder#interceptors(Object...)} 8234 * </ul> 8235 * 8236 * @param req The HTTP request. 8237 * @throws RestCallException If any of the interceptors threw an exception. 8238 */ 8239 protected void onCallInit(RestRequest req) throws RestCallException { 8240 try { 8241 for (var rci : interceptors) 8242 rci.onInit(req); 8243 } catch (RuntimeException | RestCallException e) { 8244 throw e; 8245 } catch (Exception e) { 8246 throw new RestCallException(null, e, "Interceptor threw an exception on init."); 8247 } 8248 } 8249 8250 @Override /* Overridden from MarshallingContextable */ 8251 protected FluentMap<String,Object> properties() { 8252 return super.properties() 8253 .a("errorCodes", errorCodes) 8254 .a("executorService", executorService.get()) 8255 .a("executorServiceShutdownOnClose", executorServiceShutdownOnClose) 8256 .a("headerData", headerData) 8257 .a("interceptors", interceptors) 8258 .a("keepHttpClientOpen", keepHttpClientOpen) 8259 .a("partParser", partParser) 8260 .a("partSerializer", partSerializer) 8261 .a("queryData", queryData) 8262 .a("rootUrl", rootUrl != null ? rootUrl.get() : null); 8263 } 8264 8265 /** 8266 * Perform an arbitrary request against the specified URI. 8267 * 8268 * <p> 8269 * All requests feed through this method so it can be used to intercept request creations and make modifications 8270 * (such as add headers). 8271 * 8272 * @param op The operation that identifies the HTTP method, URL, and optional payload. 8273 * @return 8274 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 8275 * as a parsed object. 8276 * @throws RestCallException If any authentication errors occurred. 8277 */ 8278 protected RestRequest request(RestOperation op) throws RestCallException { 8279 if (isClosed.get()) { 8280 Exception e2 = null; 8281 if (nn(closedStack)) { 8282 e2 = new Exception("Creation stack:"); 8283 e2.setStackTrace(closedStack); 8284 throw new RestCallException(null, e2, "RestClient.close() has already been called. This client cannot be reused."); 8285 } 8286 throw new RestCallException(null, null, 8287 "RestClient.close() has already been called. This client cannot be reused. Closed location stack trace can be displayed by setting the system property 'org.apache.juneau.rest.client2.RestClient.trackCreation' to true."); 8288 } 8289 8290 var req = createRequest(toUri(op.getUri(), rootUrl != null ? rootUrl.get() : null), op.getMethod(), op.hasContent()); 8291 8292 onCallInit(req); 8293 8294 req.content(op.getContent()); 8295 8296 return req; 8297 } 8298 8299 /** 8300 * Entrypoint for executing all requests and returning a response. 8301 * 8302 * <p> 8303 * Subclasses can override this method to provide specialized handling. 8304 * 8305 * <p> 8306 * The behavior of this method can also be modified by specifying a different {@link RestCallHandler}. 8307 * 8308 * <h5 class='section'>Notes:</h5><ul> 8309 * <li class='warn'>The SSRF guard of {@code @Remote} calls travels on the request's {@code HttpContext}, which this 8310 * client cannot guarantee an override passes on unchanged. So a client whose class overrides this method, 8311 * {@link #execute(HttpHost, HttpRequest, HttpContext)} or {@link #execute(HttpUriRequest, HttpContext)} refuses every 8312 * SSRF-guard-active {@code @Remote} call (one without the {@link Builder#allowPrivateUrls(boolean) allowPrivateUrls} 8313 * opt-in). Ordinary requests are unaffected. 8314 * </ul> 8315 * 8316 * <h5 class='section'>See Also:</h5><ul> 8317 * <li class='jm'>{@link Builder#callHandler()} 8318 * </ul> 8319 * 8320 * @param target The target host for the request. 8321 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 8322 * target or by inspecting the request. 8323 * @param request The request to execute. 8324 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 8325 * @return 8326 * The response to the request. 8327 * <br>This is always a final response, never an intermediate response with an 1xx status code. 8328 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 8329 * implementation and configuration of this client. 8330 * @throws IOException In case of a problem or the connection was aborted. 8331 * @throws ClientProtocolException In case of an http protocol error. 8332 */ 8333 protected HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws IOException { 8334 return callHandler.run(target, request, context); 8335 } 8336 8337 // The methods a request is dispatched through on its way from RestRequest to the HttpClient (run(...), then the two 8338 // execute(...) overloads BasicRestCallHandler calls). 8339 private static final List<Method> DISPATCH_METHODS = dispatchMethods(); 8340 8341 private static List<Method> dispatchMethods() { 8342 try { 8343 return List.of( 8344 RestClient.class.getDeclaredMethod("run", HttpHost.class, HttpRequest.class, HttpContext.class), 8345 RestClient.class.getDeclaredMethod("execute", HttpHost.class, HttpRequest.class, HttpContext.class), 8346 RestClient.class.getDeclaredMethod("execute", HttpUriRequest.class, HttpContext.class)); 8347 } catch (NoSuchMethodException e) { 8348 throw new ExceptionInInitializerError(e); 8349 } 8350 } 8351 8352 // Why this client cannot guarantee the SSRF guard of @Remote calls, or null if it can. A connection manager this 8353 // client did not build has no pinned pool, an HttpClient other than the one the default createHttpClient() wired to 8354 // it (builtGuard) does not use it, and builderRefusal says why the HttpClientBuilder may have built the client around 8355 // something else. The guard travels on each request's context, so it holds on another thread as long as the request 8356 // that carries it is the one sent; but anything that may send a different request (a custom call handler, a handler 8357 // bound to another client, a subclass that overrides the request dispatch: each may copy the request, or send it with 8358 // another context) is refused, and these refusals are what keep such paths from going out unpinned. 8359 private String ssrfRefusal(SsrfGuardConnectionManager builtGuard, String builderRefusal) { 8360 var tail = "the SSRF guardrail's connect-time contract (pin-on-connect + redirect revalidation) is not in force. "; 8361 var optOut = "or set allowPrivateUrls(true) if this is an intentional local-dev/intranet target."; 8362 if (ssrfGuard == null && nn(builtGuard) && ! SsrfGuardConnectionManager.canCheckWiring()) 8363 // HTT: only an HttpClient release whose InternalHttpClient has no connManager field, or a JVM that denies 8364 // reflective access to it, gets here. 8365 return "Refusing to send a policy-covered @Remote request: this RestClient cannot read the connection manager of " 8366 + "the HttpClient it built (org.apache.http.impl.client.InternalHttpClient.connManager), so it cannot confirm " 8367 + "that the client uses its SSRF-guard connection manager, and " + tail + "Use an HttpClient 4.5.x release, " 8368 + optOut; 8369 if (ssrfGuard == null && nn(builtGuard)) 8370 return "Refusing to send a policy-covered @Remote request through an HttpClient that is not the one this RestClient " 8371 + "wired to its SSRF-guard connection manager: an overridden createHttpClient() or HttpClientBuilder.build() " 8372 + "returned another client, or a wrapper around one, which may send the request without the pinned pool, so " 8373 + tail + "Return the client super.createHttpClient() or HttpClientBuilder.build() built, unwrapped (customize it " 8374 + "through httpClientBuilder(...) instead), " + optOut; 8375 if (nn(builderRefusal)) 8376 return builderRefusal; 8377 if (ssrfGuard == null) 8378 return "Refusing to send a policy-covered @Remote request through a caller-supplied HttpClient or connection " 8379 + "manager that cannot be guaranteed to honor the SSRF guardrail's connect-time contract (pin-on-connect + " 8380 + "redirect revalidation): the client was supplied via httpClient(...) or an overridden createHttpClient(), " 8381 + "or the connection manager via connectionManager(...) or an overridden createConnectionManager(), instead " 8382 + "of being built by this RestClient. Let this RestClient build its own HttpClient and connection manager, " 8383 + optOut; 8384 if (callHandler.getClass() != BasicRestCallHandler.class) 8385 return "Refusing to send a policy-covered @Remote request through a custom RestCallHandler (" + cn(callHandler) 8386 + "): it may copy the request, or send it with another context, without the SSRF guard the request carries, so " 8387 + tail + "Remove the callHandler(...) setting, " + optOut; 8388 if (((BasicRestCallHandler)callHandler).client() != this) 8389 return "Refusing to send a policy-covered @Remote request through a BasicRestCallHandler bound to a different " 8390 + "RestClient: it sends the request through that client's connection manager, where " + tail 8391 + "Remove the callHandler(...) setting, " + optOut; 8392 var dispatch = overriddenDispatch(getClass()); 8393 if (nn(dispatch)) 8394 return "Refusing to send a policy-covered @Remote request through a RestClient subclass (" + dispatch 8395 + "): the override may copy the request, or send it with another context, without the SSRF guard the request " 8396 + "carries, so " + tail + "Remove the override, " + optOut; 8397 return null; 8398 } 8399 8400 // Why a guarded call cannot be sent through rc (the request createRequest(...) returned), or null if it can: a request 8401 // bound to another client goes out through that client's HttpClient, and an override of RestRequest.run() may copy 8402 // the request, or send it with another context, without the guard it carries. 8403 private String requestRefusal(RestRequest rc) { 8404 if (rc.client != this) 8405 return "Refusing to send a policy-covered @Remote request through a RestRequest bound to a different " 8406 + "RestClient: it sends the request through that client's HttpClient, where the SSRF guardrail's " 8407 + "connect-time contract (pin-on-connect + redirect revalidation) is not in force. Remove the " 8408 + "createRequest(...) or request(...) override that returns it, or set allowPrivateUrls(true) if this is " 8409 + "an intentional local-dev/intranet target."; 8410 for (Class<?> x = rc.getClass(); x != RestRequest.class; x = x.getSuperclass()) { 8411 try { 8412 x.getDeclaredMethod("run"); 8413 return "Refusing to send a policy-covered @Remote request through a RestRequest subclass (" + cn(x) 8414 + " overrides run()): the override may copy the request, or send it with another context, without the " 8415 + "SSRF guard the request carries, so the SSRF guardrail's connect-time contract (pin-on-connect + redirect revalidation) is not in force. Remove the override " 8416 + "(or the createRequest(...) override that returns it), or set allowPrivateUrls(true) if this is an " 8417 + "intentional local-dev/intranet target."; 8418 } catch (@SuppressWarnings("unused") NoSuchMethodException e) { /* Not declared here. */ } 8419 } 8420 return null; 8421 } 8422 8423 // Returns "<class>.<method>(<params>)" for the first dispatch method a subclass between c and RestClient declares, 8424 // or null if there is none. 8425 private static String overriddenDispatch(Class<?> c) { 8426 for (var x = c; x != RestClient.class; x = x.getSuperclass()) { 8427 for (var m : DISPATCH_METHODS) { 8428 try { 8429 x.getDeclaredMethod(m.getName(), m.getParameterTypes()); 8430 return cn(x) + " overrides " + m.getName() + "(" + String.join(",", Arrays.stream(m.getParameterTypes()).map(Class::getSimpleName).toList()) + ")"; 8431 } catch (@SuppressWarnings("unused") NoSuchMethodException e) { /* Not declared here. */ } 8432 } 8433 } 8434 return null; 8435 } 8436 8437 /** 8438 * Resolves the effective request URL for a remote-proxy call, applying the dynamic-URL overrides 8439 * ({@code @Url} parameter, then method-level {@code baseUrl}, then interface-level {@code baseUrl}) over the 8440 * statically-computed interface-base + method path, with the http/https SSRF guardrail. 8441 * 8442 * <p> 8443 * Ported from the next-generation engine's {@code RemoteInvocationHandler.resolveEffectiveUrl(...)}. 8444 */ 8445 String resolveRemoteUri(Class<?> interfaceClass, String restUrl2, Method method, RemoteOperationMeta rom, RemoteMeta rm, Object[] args) { 8446 var fullPath = rom.getFullPath(); 8447 var allowPrivate = isAllowPrivateUrls() || rm.isAllowPrivateUrls(); 8448 String uri; 8449 8450 // Option A: a call-time @Url parameter wins over everything (endpoint replacement). 8451 var urlIdx = rom.getUrlParamIndex(); 8452 if (urlIdx >= 0) { 8453 var arg = (args == null || urlIdx >= args.length) ? null : args[urlIdx]; 8454 var v = arg == null ? null : arg.toString(); 8455 if (ib(v)) 8456 throw new RemoteMetadataException(interfaceClass, "@Url parameter on " + method.getDeclaringClass().getName() + "." + method.getName() + " must not be null or blank."); 8457 uri = RemoteProxyUtils.requireHttpScheme(v.trim(), allowPrivate, allowedAddressRanges); 8458 // A scheme-less @Url value is relative and resolved against the client root URL only. 8459 if (uri.indexOf("://") == -1) 8460 uri = restUrl2 + '/' + trimSlashes(uri); 8461 } else { 8462 // Option B: a declarative base/host override (method-level wins over interface-level). 8463 var baseUrl = firstNonEmpty(rom.getBaseUrl(), rm.getBaseUrl()); 8464 if (ine(baseUrl)) { 8465 uri = RemoteProxyUtils.requireHttpScheme(RemoteProxyUtils.combinePaths(baseUrl, fullPath), allowPrivate, allowedAddressRanges); 8466 } else { 8467 uri = fullPath; 8468 if (uri.indexOf("://") == -1) 8469 uri = restUrl2 + '/' + uri; 8470 } 8471 } 8472 8473 if (uri.indexOf("://") == -1) 8474 throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote resource."); 8475 8476 // SSRF guardrail (parity with juneau-rest-client RemoteClient.resolveEffectiveUrl): the fully-resolved 8477 // absolute URI -- including a scheme-less @Url/default-path combined with the client root above -- must pass 8478 // the full deny-private policy, not just the http/https scheme check (Review R4: the default (no-override) 8479 // path is policy-covered too once it is absolute). 8480 try { 8481 RemoteUrlPolicy.requireAllowedUrl(uri, allowPrivate, allowedAddressRanges); 8482 } catch (IllegalArgumentException e) { 8483 throw new RemoteMetadataException(interfaceClass, e.getMessage()); 8484 } 8485 8486 return uri; 8487 } 8488 8489 /** 8490 * Builds a fresh {@link RestRequest} for a remote-proxy call, binding constant parts (interface + method level), 8491 * content-negotiation overrides ({@code accept}/{@code contentType}), the method-argument parts, and the per-call 8492 * timeout. Re-invoked per retry attempt so a (gated) retry resends a freshly-bound request. 8493 * 8494 * <p> 8495 * Ported from the next-generation engine's {@code RemoteInvocationHandler.buildRequest(...)}. Unlike the NG 8496 * version, argument binding here is driven entirely by the pre-resolved {@link RemoteOperationMeta} (<c>rom</c>) 8497 * rather than live reflection over the interface {@link Method}, and any misconfiguration that needs to identify 8498 * the offending interface/method is already reported earlier by {@link #resolveRemoteUri}; neither the interface 8499 * class nor the {@link Method} is needed here. 8500 */ 8501 RestRequest buildRemoteRequest(String uri, Serializer serializer, Parser parser, RemoteOperationMeta rom, RemoteMeta rm, Object[] args) throws RestCallException { 8502 var httpMethod = rom.getHttpMethod(); 8503 var rc = request(httpMethod, uri, hasContent(httpMethod)); 8504 8505 if (serializer != null) rc.serializer(serializer); 8506 if (parser != null) rc.parser(parser); 8507 8508 // contentType override: select the matching registered serializer (else the client default) and send the 8509 // overridden media type as the Content-Type label (mirrors NG resolveBodyFormat()). 8510 var effContentType = firstNonEmpty(rom.getContentType(), rm.getContentType()); 8511 if (ine(effContentType)) { 8512 var s = getMatchingSerializer(effContentType); 8513 if (s == null) { 8514 var all = serializers.getSerializers(); 8515 if (! all.isEmpty()) 8516 s = all.get(0); 8517 } 8518 if (nn(s)) 8519 rc.serializer(s); 8520 rc.contentType(effContentType); 8521 } 8522 8523 var effAccept = firstNonEmpty(rom.getAccept(), rm.getAccept()); 8524 8525 // Constant headers (interface-level then method-level, method-level wins on name collisions). A constant 8526 // Content-Type/Accept is dropped when the dedicated contentType/accept attribute is in effect. 8527 var methodHeaderNames = new HashSet<String>(); 8528 rom.getConstantHeaders().forEach(e -> methodHeaderNames.add(e.getKey())); 8529 var skipContentType = ine(effContentType); 8530 var skipAccept = ine(effAccept); 8531 rm.getHeaders().forEach(h -> { 8532 var n = h.getName(); 8533 if (methodHeaderNames.contains(n)) 8534 return; 8535 if (skipContentType && eqic(HEADER_CONTENT_TYPE, n)) 8536 return; 8537 if (skipAccept && eqic(HEADER_ACCEPT, n)) 8538 return; 8539 rc.header(h); 8540 }); 8541 rom.getConstantHeaders().forEach(e -> { 8542 if (skipContentType && eqic(HEADER_CONTENT_TYPE, e.getKey())) 8543 return; 8544 if (skipAccept && eqic(HEADER_ACCEPT, e.getKey())) 8545 return; 8546 rc.header(e.getKey(), e.getValue()); 8547 }); 8548 8549 // Constant query/form-data (interface-level then method-level, method-level wins on name collisions). 8550 RemoteProxyUtils.mergeConstants(rm.getQueryData(), rom.getConstantQueryData()).forEach(rc::queryData); 8551 RemoteProxyUtils.mergeConstants(rm.getFormData(), rom.getConstantFormData()).forEach(rc::formData); 8552 8553 // Apply method-level defaults if parameter values are not provided (9.2.0) 8554 rom.forEachPathArg(a -> { 8555 var val = args[a.getIndex()]; 8556 if (val == null) { 8557 var def = a.getSchema().getDefault(); 8558 if (def == null) 8559 def = rom.getPathDefault(a.getName()); 8560 if (nn(def)) 8561 val = def; 8562 } 8563 rc.pathArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer)); 8564 }); 8565 rom.forEachQueryArg(a -> { 8566 var val = args[a.getIndex()]; 8567 if (val == null) { 8568 var def = a.getSchema().getDefault(); 8569 if (def == null) 8570 def = rom.getQueryDefault(a.getName()); 8571 if (nn(def)) 8572 val = def; 8573 } 8574 rc.queryArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 8575 }); 8576 rom.forEachFormDataArg(a -> { 8577 var val = args[a.getIndex()]; 8578 if (val == null) { 8579 var def = a.getSchema().getDefault(); 8580 if (def == null) 8581 def = rom.getFormDataDefault(a.getName()); 8582 if (nn(def)) 8583 val = def; 8584 } 8585 rc.formDataArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 8586 }); 8587 rom.forEachHeaderArg(a -> { 8588 var val = args[a.getIndex()]; 8589 if (val == null) { 8590 var def = a.getSchema().getDefault(); 8591 if (def == null) 8592 def = rom.getHeaderDefault(a.getName()); 8593 if (nn(def)) 8594 val = def; 8595 } 8596 rc.headerArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 8597 }); 8598 8599 var ba = rom.getContentArg(); 8600 if (nn(ba)) { 8601 var val = args[ba.getIndex()]; 8602 if (val == null) { 8603 var def = ba.getSchema().getDefault(); 8604 if (def == null) 8605 def = rom.getContentDefault(); 8606 if (nn(def)) 8607 val = def; 8608 } 8609 rc.content(val, ba.getSchema()); 8610 } else { 8611 var contentDef = rom.getContentDefault(); 8612 if (nn(contentDef)) 8613 rc.content(contentDef); 8614 } 8615 8616 rom.forEachRequestArg(rmba -> { 8617 var rbm = rmba.getMeta(); 8618 var bean = args[rmba.getIndex()]; 8619 if (nn(bean)) { 8620 for (var p : rbm.getProperties()) { 8621 var val = safeSupplier(() -> p.getGetter().invoke(bean)); 8622 if (val instanceof Optional<?> val2) 8623 val = val2.orElse(null); 8624 var pt = p.getPartType(); 8625 var pn = p.getPartName(); 8626 var schema = p.getSchema(); 8627 if (pt == PATH) 8628 rc.pathArg(pn, val, schema, p.getSerializer().orElse(partSerializer)); 8629 else if (nn(val)) { 8630 if (pt == QUERY) 8631 rc.queryArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 8632 else if (pt == FORMDATA) 8633 rc.formDataArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 8634 else if (pt == HEADER) 8635 rc.headerArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 8636 else /* (pt == HttpPartType.BODY) */ 8637 rc.content(val, schema); 8638 } 8639 } 8640 } 8641 }); 8642 8643 // Set the Accept header from the dedicated attribute unless a caller-supplied @Header param already provided one. 8644 if (ine(effAccept) && ! rc.containsHeader(HEADER_ACCEPT)) 8645 rc.accept(effAccept); 8646 8647 // Per-call response/read timeout (method-level overrides interface-level) via a per-call RequestConfig. 8648 var effTimeout = firstNonEmpty(rom.getTimeout(), rm.getTimeout()); 8649 if (ine(effTimeout)) { 8650 var ms = getDuration(effTimeout); 8651 if (ms > 0) { 8652 var base = rc.getConfig(); 8653 var cb = base != null ? RequestConfig.copy(base) : RequestConfig.custom(); 8654 rc.config(cb.setSocketTimeout((int)ms).build()); 8655 } 8656 } 8657 8658 return rc; 8659 } 8660 8661 /** 8662 * Executes a remote-proxy call and processes the return value, applying safe automatic retries when the method 8663 * opts in and all safety gates pass (idempotent verb or {@code retryNonIdempotent}, buffered return mode, and a 8664 * repeatable body). Mirrors the next-generation engine's {@code RemoteInvocationHandler.processReturn(...)}. 8665 */ 8666 @SuppressWarnings({ 8667 "java:S135" // Two continue statements are the two retry triggers (transport failure, retryable status); merging them would obscure the loop 8668 }) 8669 Object executeRemoteWithRetry(String uri, Serializer serializer, Parser parser, Method method, RemoteOperationMeta rom, RemoteMeta rm, Object[] args) throws Exception { 8670 var throwOnError = rm.isThrowOnError() || rom.isThrowOnError(); 8671 var ror = rom.getReturns(); 8672 var maxRetries = (isRetryableMode(ror, method.getReturnType()) && isRetryableVerb(rom, rm)) ? effectiveRetries(rom, rm) : 0; 8673 8674 // Every getRemote() call is policy-covered; the SSRF guard is active unless allowPrivateUrls opts out. 8675 var allowPrivate = isAllowPrivateUrls() || rm.isAllowPrivateUrls(); 8676 var guardActive = ! allowPrivate; 8677 if (guardActive && ! ssrfPolicySupported) 8678 throw new RestCallException(null, null, "%s", ssrfRefusal); 8679 8680 var attempt = 0; 8681 while (true) { 8682 var rc = buildRemoteRequest(uri, serializer, parser, rom, rm, args); 8683 rc.rethrow(RuntimeException.class); 8684 rom.forEachException(rc::rethrow); 8685 // The classic run() throws by default on >=400; the proxy replicates NG's throwIfError() instead, so it 8686 // always suppresses that generic throw and applies the typed/conditional mapping itself. 8687 rc.ignoreErrors(); 8688 if (guardActive) { 8689 var refusal = requestRefusal(rc); 8690 if (nn(refusal)) 8691 throw new RestCallException(null, null, "%s", refusal); 8692 // Carried on the request's context, so the guard holds on whichever thread the request is sent. 8693 rc.ssrfGuard(); 8694 } 8695 8696 // Non-repeatable body: never retry (resending a consumed stream would corrupt the call). 8697 var canRetry = maxRetries > 0 && (attempt > 0 || rc.isBodyRepeatable()); 8698 8699 RestResponse res; 8700 if (guardActive) 8701 RemoteUrlPolicyState.activate(); 8702 try { 8703 res = rc.run(); 8704 } catch (SsrfGuardConnectionManager.GuardRefusedException e) { 8705 // Reported like the other guard refusals (and other transport failures), not as a raw runtime exception. 8706 throw new RestCallException(null, e, "%s", e.getMessage()); 8707 } catch (RestCallException e) { 8708 // A transport failure (IOException cause) is retryable; a reconstructed server exception is not 8709 // caught here (it is not a RestCallException). 8710 if (canRetry && attempt < maxRetries && e.getCause() instanceof IOException) { 8711 attempt++; 8712 backoff(attempt); 8713 continue; 8714 } 8715 Throwable t = e.getCause(); 8716 if (t instanceof RuntimeException t2) 8717 throw t2; 8718 if (t instanceof Exception t2) { 8719 for (var t3 : method.getExceptionTypes()) 8720 if (t3.isInstance(t2)) 8721 throw t2; 8722 } 8723 throw toRex(e); 8724 } finally { 8725 if (guardActive) 8726 RemoteUrlPolicyState.deactivate(); 8727 } 8728 8729 if (canRetry && attempt < maxRetries && isRetryableStatus(res.getStatusCode())) { 8730 res.close(); 8731 attempt++; 8732 backoff(attempt); 8733 continue; 8734 } 8735 8736 return materializeRemote(res, method, rom, throwOnError); 8737 } 8738 } 8739 8740 /** 8741 * Materializes the terminal return value from an already-run response, applying the typed-exception / 8742 * conditional-throw error mapping (mirrors the next-generation engine's {@code throwIfError} + 8743 * {@code materializeResponse}). 8744 */ 8745 Object materializeRemote(RestResponse res, Method method, RemoteOperationMeta rom, boolean throwOnError) throws Exception { 8746 var ror = rom.getReturns(); 8747 var rv = ror.getReturnValue(); 8748 8749 if (rv == RemoteReturn.NONE) { 8750 res.close(); 8751 return null; 8752 } 8753 if (rv == RemoteReturn.STATUS) { 8754 var sc = res.getStatusCode(); 8755 var rt = method.getReturnType(); 8756 res.close(); 8757 if (rt == Integer.class || rt == int.class) 8758 return sc; 8759 if (rt == Boolean.class || rt == boolean.class) 8760 return sc < 400; 8761 throw new RestCallException(res, null, "Invalid return type on method annotated with @RemoteOp(returns=RemoteReturn.STATUS). Only integer and booleans types are valid."); 8762 } 8763 if (rv == RemoteReturn.RESPONSE) 8764 return res; 8765 if (rv == RemoteReturn.BEAN) { 8766 throwRemoteError(res, method, throwOnError); 8767 return res.as(ror.getResponseBeanMeta()); 8768 } 8769 // BODY (default) 8770 throwRemoteError(res, method, throwOnError); 8771 var rt = method.getReturnType(); 8772 Object v = res.getContent().as(ror.getReturnType()); 8773 if (v == null && rt.isPrimitive()) 8774 v = ClassInfo.of(rt).getPrimitiveDefault(); 8775 return v; 8776 } 8777 8778 /** 8779 * Maps an error HTTP status (≥400) to a typed exception declared in the method's {@code throws} clause (a 8780 * {@link org.apache.juneau.http.response.BasicHttpException} subtype whose {@code STATUS_CODE} matches), else 8781 * conditionally throws a generic exception when {@code throwOnError} is set, else lets the error body flow through 8782 * as the return value. Mirrors the next-generation engine's {@code throwIfError(...)}. 8783 */ 8784 void throwRemoteError(RestResponse res, Method method, boolean throwOnError) throws Exception { 8785 var sc = res.getStatusCode(); 8786 if (sc < 400) 8787 return; 8788 for (var et : method.getExceptionTypes()) { 8789 if (org.apache.juneau.http.response.BasicHttpException.class.isAssignableFrom(et) && httpStatusCode(et) == sc) { 8790 var body = res.getContent().asString(); 8791 res.close(); 8792 throw (org.apache.juneau.http.response.BasicHttpException) instantiateHttpType(et, body); 8793 } 8794 } 8795 if (throwOnError) { 8796 var body = res.getContent().asString(); 8797 res.close(); 8798 throw new org.apache.juneau.http.response.BasicHttpException(sc, body); 8799 } 8800 // else: let the error body flow through as the return value (parity with NG throwOnError=false). 8801 } 8802 8803 /** Returns the static {@code STATUS_CODE} field of an HTTP response/exception type, or {@code -1} if absent. */ 8804 private static int httpStatusCode(Class<?> c) { 8805 try { 8806 return c.getField("STATUS_CODE").getInt(null); 8807 } catch (ReflectiveOperationException e) { 8808 return -1; 8809 } 8810 } 8811 8812 /** Instantiates an HTTP response/exception bean, preferring a {@code (String body)} constructor. */ 8813 private static Object instantiateHttpType(Class<?> c, String body) { 8814 try { 8815 return newHttpTypeInstance(c, body); 8816 } catch (ReflectiveOperationException e) { 8817 throw rex(e, "Could not instantiate HTTP response/exception type %s", c.getName()); 8818 } 8819 } 8820 8821 private static Object newHttpTypeInstance(Class<?> c, String body) throws ReflectiveOperationException { 8822 try { 8823 return c.getConstructor(String.class).newInstance(body); 8824 } catch (NoSuchMethodException e) { 8825 return c.getConstructor().newInstance(); 8826 } 8827 } 8828 8829 /** Returns <jk>true</jk> if the return mode/type is a buffered (retryable) shape, not a streaming/wrapper one. */ 8830 private static boolean isRetryableMode(RemoteOperationReturn ror, Class<?> returnType) { 8831 if (ror.getReturnValue() == RemoteReturn.RESPONSE) 8832 return false; 8833 if (ror.isFuture() || ror.isCompletableFuture()) 8834 return false; 8835 return ! (returnType == InputStream.class || returnType == Reader.class || returnType == Optional.class); 8836 } 8837 8838 /** Returns <jk>true</jk> if the verb may auto-retry: idempotent verbs always, POST/PATCH only when opted in. */ 8839 private static boolean isRetryableVerb(RemoteOperationMeta rom, RemoteMeta rm) { 8840 if (isOneOf(rom.getHttpMethod(), "GET", "PUT", "DELETE", "HEAD")) 8841 return true; 8842 return rom.isRetryNonIdempotent() || rm.isRetryNonIdempotent(); 8843 } 8844 8845 /** Returns the effective retry count: a positive method-level value overrides the interface-level default. */ 8846 private static int effectiveRetries(RemoteOperationMeta rom, RemoteMeta rm) { 8847 return rom.getRetries() > 0 ? rom.getRetries() : rm.getRetries(); 8848 } 8849 8850 /** Retryable HTTP statuses: {@code 429} (Too Many Requests) and all {@code 5xx} server errors. */ 8851 private static boolean isRetryableStatus(int statusCode) { 8852 return statusCode == 429 || statusCode >= 500; 8853 } 8854 8855 /** Sleeps a short exponential backoff ({@code 50ms * 2^(attempt-1)}, capped at {@code 1s}) before a retry. */ 8856 private static void backoff(int attempt) { 8857 try { 8858 Thread.sleep(Math.min(50L << (attempt - 1), 1000L)); 8859 } catch (InterruptedException e) { 8860 Thread.currentThread().interrupt(); 8861 } 8862 } 8863 8864 ExecutorService getExecutorService() { 8865 ExecutorService result = executorService.get(); 8866 if (nn(result)) 8867 return result; 8868 synchronized (this) { 8869 result = executorService.get(); 8870 if (nn(result)) 8871 return result; 8872 result = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); 8873 executorService.set(result); 8874 return result; 8875 } 8876 } 8877 8878 <T extends Context> T getInstance(Class<T> c) { 8879 var o = requestContexts.computeIfAbsent(c, k -> { 8880 if (Serializer.class.isAssignableFrom(k)) { 8881 return Serializer.createSerializerBuilder((Class<? extends Serializer>)k).marshallingContext(getMarshallingContext()).build(); 8882 } else if (Parser.class.isAssignableFrom(k)) { 8883 return Parser.createParserBuilder((Class<? extends Parser>)k).marshallingContext(getMarshallingContext()).build(); 8884 } 8885 return null; 8886 }); 8887 return (T)o; 8888 } 8889 8890 /* 8891 * Returns the parser that best matches the specified content type. 8892 * If no match found or the content type is <jk>null</jk>, returns the parser in the list if it's a list of one. 8893 * Returns <jk>null</jk> if no parsers are defined. 8894 */ 8895 Parser getMatchingParser(String mediaType) { 8896 if (parsers.isEmpty()) 8897 return null; 8898 if (nn(mediaType)) { 8899 var p = parsers.getParser(mediaType).orElse(null); 8900 if (nn(p)) 8901 return p; 8902 } 8903 var l = parsers.getParsers(); 8904 return (l.size() == 1 ? l.get(0) : null); 8905 } 8906 8907 /* 8908 * Returns the serializer that best matches the specified content type. 8909 * If no match found or the content type is <jk>null</jk>, returns the serializer in the list if it's a list of one. 8910 * Returns <jk>null</jk> if no serializers are defined. 8911 */ 8912 Serializer getMatchingSerializer(String mediaType) { 8913 if (serializers.isEmpty()) 8914 return null; 8915 if (nn(mediaType)) { 8916 var s = serializers.getSerializer(mediaType).orElse(null); 8917 if (nn(s)) 8918 return s; 8919 } 8920 var l = serializers.getSerializers(); 8921 return (l.size() == 1 ? l.get(0) : null); 8922 } 8923 8924 boolean hasParsers() { 8925 return ! parsers.getParsers().isEmpty(); 8926 } 8927 8928 boolean hasSerializers() { 8929 return ! serializers.getSerializers().isEmpty(); 8930 } 8931 8932 URI toUri(Object x, String rootUrl) throws RestCallException { 8933 try { 8934 if (x instanceof URI x2) 8935 return x2; 8936 if (x instanceof URL x2) 8937 return x2.toURI(); 8938 if (x instanceof URIBuilder x2) 8939 return x2.build(); 8940 var s = x == null ? "" : x.toString(); 8941 if (nn(rootUrl) && ! absUrlPattern.matcher(s).matches()) { 8942 if (s.isEmpty()) 8943 s = rootUrl; 8944 else { 8945 var sb = new StringBuilder(rootUrl); 8946 if (! s.startsWith("/")) 8947 sb.append('/'); 8948 sb.append(s); 8949 s = sb.toString(); 8950 } 8951 } 8952 s = fixUrl(s); 8953 return new URI(s); 8954 } catch (URISyntaxException e) { 8955 throw new RestCallException(null, e, "Invalid URI encountered: %s", x); // Shouldn't happen. 8956 } 8957 } 8958}