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; 018 019import static java.lang.Character.isWhitespace; 020import static java.util.logging.Level.*; 021import static org.apache.juneau.commons.lang.StateEnum.*; 022import static org.apache.juneau.commons.utils.AssertionUtils.*; 023import static org.apache.juneau.commons.utils.CollectionUtils.*; 024import static org.apache.juneau.commons.utils.StringUtils.*; 025import static org.apache.juneau.commons.utils.ThrowableUtils.*; 026import static org.apache.juneau.commons.utils.Utils.*; 027import static org.apache.juneau.http.HttpEntities.*; 028import static org.apache.juneau.http.HttpHeaders.*; 029import static org.apache.juneau.http.HttpMethod.*; 030import static org.apache.juneau.http.HttpParts.*; 031import static org.apache.juneau.httppart.HttpPartType.*; 032import static org.apache.juneau.rest.client.RestOperation.*; 033 034import java.io.*; 035import java.lang.annotation.Annotation; 036import java.lang.reflect.*; 037import java.lang.reflect.Proxy; 038import java.net.*; 039import java.nio.charset.*; 040import java.text.*; 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.auth.*; 052import org.apache.http.client.*; 053import org.apache.http.client.CookieStore; 054import org.apache.http.client.config.*; 055import org.apache.http.client.entity.*; 056import org.apache.http.client.methods.*; 057import org.apache.http.client.utils.*; 058import org.apache.http.config.*; 059import org.apache.http.conn.*; 060import org.apache.http.conn.routing.*; 061import org.apache.http.conn.socket.*; 062import org.apache.http.conn.util.*; 063import org.apache.http.cookie.*; 064import org.apache.http.impl.client.*; 065import org.apache.http.impl.conn.*; 066import org.apache.http.params.*; 067import org.apache.http.protocol.*; 068import org.apache.juneau.*; 069import org.apache.juneau.annotation.*; 070import org.apache.juneau.collections.*; 071import org.apache.juneau.commons.collections.*; 072import org.apache.juneau.commons.collections.FluentMap; 073import org.apache.juneau.commons.function.*; 074import org.apache.juneau.commons.reflect.*; 075import org.apache.juneau.cp.*; 076import org.apache.juneau.html.*; 077import org.apache.juneau.http.entity.*; 078import org.apache.juneau.http.header.*; 079import org.apache.juneau.http.part.*; 080import org.apache.juneau.http.remote.*; 081import org.apache.juneau.http.resource.*; 082import org.apache.juneau.httppart.*; 083import org.apache.juneau.json.*; 084import org.apache.juneau.marshaller.*; 085import org.apache.juneau.msgpack.*; 086import org.apache.juneau.oapi.*; 087import org.apache.juneau.objecttools.*; 088import org.apache.juneau.parser.*; 089import org.apache.juneau.parser.ParseException; 090import org.apache.juneau.plaintext.*; 091import org.apache.juneau.rest.client.assertion.*; 092import org.apache.juneau.rest.client.remote.*; 093import org.apache.juneau.serializer.*; 094import org.apache.juneau.uon.*; 095import org.apache.juneau.urlencoding.*; 096import org.apache.juneau.xml.*; 097 098/** 099 * Utility class for interfacing with remote REST interfaces. 100 * 101 * <p> 102 * Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style 103 * REST calls and the ability to perform marshalling of POJOs to and from HTTP parts. 104 * 105 * <h5 class='figure'>Example:</h5> 106 * <p class='bjava'> 107 * <jc>// Create a basic REST client with JSON support and download a bean.</jc> 108 * MyBean <jv>bean</jv> = RestClient.<jsm>create</jsm>() 109 * .json5() 110 * .build() 111 * .get(<jsf>URI</jsf>) 112 * .run() 113 * .assertStatus().asCode().is(200) 114 * .assertHeader(<js>"Content-Type"</js>).matchesSimple(<js>"application/json*"</js>) 115 * .getContent().as(MyBean.<jk>class</jk>); 116 * </p> 117 * 118 * <p> 119 * Breaking apart the fluent call, we can see the classes being used: 120 * <p class='bjava'> 121 * RestClient.Builder <jv>builder</jv> = RestClient.<jsm>create</jsm>().json5(); 122 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 123 * RestRequest <jv>req</jv> = <jv>client</jv>.get(<jsf>URI</jsf>); 124 * RestResponse <jv>res</jv> = <jv>req</jv>.run(); 125 * RestResponseStatusLineAssertion <jv>statusLineAssertion</jv> = <jv>res</jv>.assertStatus(); 126 * FluentIntegerAssertion<RestResponse> <jv>codeAssertion</jv> = <jv>statusLineAssertion</jv>.asCode(); 127 * <jv>res</jv> = <jv>codeAssertion</jv>.is(200); 128 * FluentStringAssertion<RestResponse> <jv>headerAssertion</jv> = <jv>res</jv>.assertHeader(<js>"Content-Type"</js>); 129 * <jv>res</jv> = <jv>headerAssertion</jv>.matchesSimple(<js>"application/json*"</js>); 130 * RestResponseBody <jv>content</jv> = <jv>res</jv>.getContent(); 131 * MyBean <jv>bean</jv> = <jv>content</jv>.as(MyBean.<jk>class</jk>); 132 * </p> 133 * 134 * <p> 135 * It additionally provides support for creating remote proxy interfaces using REST as the transport medium. 136 * 137 * <h5 class='figure'>Example:</h5> 138 * <p class='bjava'> 139 * <jc>// Define a Remote proxy for interacting with a REST interface.</jc> 140 * <ja>@Remote</ja>(path=<js>"/petstore"</js>) 141 * <jk>public interface</jk> PetStore { 142 * 143 * <ja>@RemotePost</ja>(<js>"/pets"</js>) 144 * Pet addPet( 145 * <ja>@Content</ja> CreatePet <jv>pet</jv>, 146 * <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, 147 * <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> 148 * ); 149 * } 150 * 151 * <jc>// Use a RestClient with default JSON 5 support.</jc> 152 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 153 * 154 * PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); 155 * CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); 156 * Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); 157 * </p> 158 * 159 * <p> 160 * The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality: 161 * <ul class='javatree'> 162 * <li class='jc'>{@link RestClient} <jk>extends</jk> {@link HttpClient}, creates {@link RestRequest} objects. 163 * <li class='jc'>{@link RestRequest} <jk>extends</jk> {@link HttpUriRequest}, creates {@link RestResponse} objects. 164 * <li class='jc'>{@link RestResponse} creates {@link ResponseContent} and {@link ResponseHeader} objects. 165 * <li class='jc'>{@link ResponseContent} <jk>extends</jk> {@link HttpEntity} 166 * <li class='jc'>{@link ResponseHeader} <jk>extends</jk> {@link Header} 167 * </ul> 168 * 169 * 170 * <p> 171 * Instances of this class are built using the {@link Builder} class which can be constructed using 172 * the {@link #create() RestClient.create()} method as shown above. 173 * 174 * <p> 175 * Clients are typically created with a root URI so that relative URIs can be used when making requests. 176 * This is done using the {@link Builder#rootUrl(Object)} method. 177 * 178 * <h5 class='figure'>Example:</h5> 179 * <p class='bjava'> 180 * <jc>// Create a client where all URIs are relative to localhost.</jc> 181 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().rootUrl(<js>"http://localhost:5000"</js>).build(); 182 * 183 * <jc>// Use relative paths.</jc> 184 * String <jv>body</jv> = <jv>client</jv>.get(<js>"/subpath"</js>).run().getContent().asString(); 185 * </p> 186 * 187 * <p> 188 * The {@link RestClient} class creates {@link RestRequest} objects using the following methods: 189 * 190 * <ul class='javatree'> 191 * <li class='jc'>{@link RestClient} 192 * <ul> 193 * <li class='jm'>{@link RestClient#get(Object) get(uri)} / {@link RestClient#get() get()} 194 * <li class='jm'>{@link RestClient#put(Object,Object) put(uri,body)} / {@link RestClient#put(Object) put(uri)} 195 * <li class='jm'>{@link RestClient#post(Object) post(uri,body)} / {@link RestClient#post(Object) post(uri)} 196 * <li class='jm'>{@link RestClient#patch(Object,Object) patch(uri,body)} / {@link RestClient#patch(Object) patch(uri)} 197 * <li class='jm'>{@link RestClient#delete(Object) delete(uri)} 198 * <li class='jm'>{@link RestClient#head(Object) head(uri)} 199 * <li class='jm'>{@link RestClient#options(Object) options(uri)} 200 * <li class='jm'>{@link RestClient#formPost(Object,Object) formPost(uri,body)} / {@link RestClient#formPost(Object) formPost(uri)} 201 * <li class='jm'>{@link RestClient#formPostPairs(Object,String...) formPostPairs(uri,parameters...)} 202 * <li class='jm'>{@link RestClient#request(String,Object,Object) request(method,uri,body)} 203 * </ul> 204 * </ul> 205 * 206 * <p> 207 * The {@link RestRequest} class creates {@link RestResponse} objects using the following methods: 208 * 209 * <ul class='javatree'> 210 * <li class='jc'>{@link RestRequest} 211 * <ul> 212 * <li class='jm'>{@link RestRequest#run() run()} 213 * <li class='jm'>{@link RestRequest#complete() complete()} 214 * </ul> 215 * </ul> 216 * 217 * <p> 218 * The distinction between the two methods is that {@link RestRequest#complete() complete()} automatically consumes the response body and 219 * {@link RestRequest#run() run()} does not. Note that you must consume response bodies in order for HTTP connections to be freed up 220 * for reuse! The {@link InputStream InputStreams} returned by the {@link ResponseContent} object are auto-closing once 221 * they are exhausted, so it is often not necessary to explicitly close them. 222 * 223 * <p> 224 * The following examples show the distinction between the two calls: 225 * 226 * <p class='bjava'> 227 * <jc>// Consuming the response, so use run().</jc> 228 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getContent().asString(); 229 * 230 * <jc>// Only interested in response status code, so use complete().</jc> 231 * <jk>int</jk> <jv>status</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); 232 * </p> 233 * 234 * 235 * <h4 class='topic'>POJO Marshalling</h4> 236 * 237 * <p> 238 * By default, JSON support is provided for HTTP request and response bodies. 239 * Other languages can be specified using any of the following builder methods: 240 * <ul class='javatree'> 241 * <li class='jc'>{@link Builder} 242 * <ul> 243 * <li class='jm'>{@link Builder#json() json()} 244 * <li class='jm'>{@link Builder#json5() json5()} 245 * <li class='jm'>{@link Builder#xml() xml()} 246 * <li class='jm'>{@link Builder#html() html()} 247 * <li class='jm'>{@link Builder#plainText() plainText()} 248 * <li class='jm'>{@link Builder#msgPack() msgPack()} 249 * <li class='jm'>{@link Builder#uon() uon()} 250 * <li class='jm'>{@link Builder#urlEnc() urlEnc()} 251 * <li class='jm'>{@link Builder#openApi() openApi()} 252 * </ul> 253 * </ul> 254 * 255 * <h5 class='figure'>Example:</h5> 256 * <p class='bjava'> 257 * <jc>// Create a basic REST client with JSON 5 support.</jc> 258 * <jc>// Typically easier to use when performing unit tests.</jc> 259 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 260 * </p> 261 * 262 * <p> 263 * Clients can also support multiple languages: 264 * 265 * <h5 class='figure'>Example:</h5> 266 * <p class='bjava'> 267 * <jc>// Create a REST client with support for multiple languages.</jc> 268 * RestClient <jv>client1</jv> = RestClient.<jsm>create</jsm>().json().xml().openApi().build(); 269 * 270 * <jc>// Create a REST client with support for all supported languages.</jc> 271 * RestClient <jv>client2</jv> = RestClient.<jsm>create</jsm>().universal().build(); 272 * </p> 273 * 274 * <p> 275 * When using clients with multiple language support, you must specify the <c>Content-Type</c> header on requests 276 * with bodies to specify which serializer should be selected. 277 * 278 * <p class='bjava'> 279 * <jc>// Create a REST client with support for multiple languages.</jc> 280 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().universal().build(); 281 * 282 * <jv>client</jv> 283 * .post(<jsf>URI</jsf>, <jv>myBean</jv>) 284 * .contentType(<js>"application/json"</js>) 285 * .complete() 286 * .assertStatus().is(200); 287 * </p> 288 * 289 * <p> 290 * Languages can also be specified per-request. 291 * 292 * <p class='bjava'> 293 * <jc>// Create a REST client with no default languages supported.</jc> 294 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().build(); 295 * 296 * <jc>// Use JSON for this request.</jc> 297 * <jv>client</jv> 298 * .post(<jsf>URI</jsf>, <jv>myBean</jv>) 299 * .json() 300 * .complete() 301 * .assertStatus().is(200); 302 * </p> 303 * 304 * 305 * <p> 306 * The {@link Builder} class provides convenience methods for setting common serializer and parser 307 * settings. 308 * 309 * <h5 class='figure'>Example:</h5> 310 * <p class='bjava'> 311 * <jc>// Create a basic REST client with JSON support.</jc> 312 * <jc>// Use single-quotes and whitespace.</jc> 313 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().sq().ws().build(); 314 * </p> 315 * 316 * <p> 317 * Other methods are also provided for specifying the serializers and parsers used for lower-level marshalling support: 318 * <ul class='javatree'> 319 * <li class='jc'>{@link Builder} 320 * <ul> 321 * <li class='jm'>{@link Builder#serializer(Serializer) serializer(Serializer)} 322 * <li class='jm'>{@link Builder#parser(Parser) parser(Parser)} 323 * <li class='jm'>{@link Builder#marshaller(Marshaller) marshaller(Marshaller)} 324 * </ul> 325 * </ul> 326 * 327 * <p> 328 * HTTP parts (headers, query parameters, form data...) are serialized and parsed using the {@link HttpPartSerializer} 329 * and {@link HttpPartParser} APIs. By default, clients are configured to use {@link OpenApiSerializer} and 330 * {@link OpenApiParser}. These can be overridden using the following methods: 331 * <ul class='javatree'> 332 * <li class='jc'>{@link Builder} 333 * <ul> 334 * <li class='jm'>{@link Builder#partSerializer(Class) partSerializer(Class<? extends HttpPartSerializer>)} 335 * <li class='jm'>{@link Builder#partParser(Class) partParser(Class<? extends HttpPartParser>)} 336 * </ul> 337 * </ul> 338 * 339 * 340 * <h4 class='topic'>Request Headers</h4> 341 * <p> 342 * Per-client or per-request headers can be specified using the following methods: 343 * <ul class='javatree'> 344 * <li class='jc'>{@link Builder} 345 * <ul> 346 * <li class='jm'>{@link Builder#headers() headerData()} 347 * <li class='jm'>{@link Builder#header(String,String) header(String,Object)} 348 * <li class='jm'>{@link Builder#header(String,Supplier) header(String,Supplier<?>)} 349 * <li class='jm'>{@link Builder#headers(Header...) headers(Header...)} 350 * <li class='jm'>{@link Builder#headersDefault(Header...) defaultHeaders(Header...)} 351 * </ul> 352 * <li class='jc'>{@link RestRequest} 353 * <ul> 354 * <li class='jm'>{@link RestRequest#header(String,Object) header(String,Object)} 355 * <li class='jm'>{@link RestRequest#headers(Header...) headers(Header...)} 356 * <li class='jm'>{@link RestRequest#headersBean(Object) headersBean(Object)} 357 * <li class='jm'>{@link RestRequest#headerPairs(String...) headerPairs(String...)} 358 * </ul> 359 * </ul> 360 * 361 * <p> 362 * The supplier methods are particularly useful for header values whose values may change over time (such as <c>Authorization</c> headers 363 * which may need to change every few minutes). 364 * </p> 365 * 366 * <h5 class='figure'>Example:</h5> 367 * <p class='bjava'> 368 * <jc>// Create a client that adds a dynamic Authorization header to every request.</jc> 369 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().header(<js>"Authorization"</js>, ()->getMyAuthToken()).build(); 370 * </p> 371 * 372 * <p> 373 * The {@link HttpPartSchema} API allows you to define OpenAPI schemas to POJO data structures on both requests 374 * and responses. 375 * 376 * <h5 class='figure'>Example:</h5> 377 * <p class='bjava'> 378 * <jc>// Create a client that adds a header "Foo: bar|baz" to every request.</jc> 379 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>() 380 * .header(<js>"Foo"</js>, AList.<jsm>of</jsm>(<js>"bar"</js>,<js>"baz"</js>), <jsf>T_ARRAY_PIPES</jsf>) 381 * .build(); 382 * </p> 383 * 384 * <p> 385 * The methods with {@link ListOperation} parameters allow you to control whether new headers get appended, prepended, or 386 * replace existing headers with the same name. 387 * 388 * <h5 class='section'>Notes:</h5><ul> 389 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>Header</c> or 390 * <c>NameValuePair</c> objects use the values returned by that bean directly. 391 * </ul> 392 * 393 * 394 * <h4 class='topic'>Request Query Parameters</h4> 395 * <p> 396 * Per-client or per-request query parameters can be specified using the following methods: 397 * <ul class='javatree'> 398 * <li class='jc'>{@link Builder} 399 * <ul> 400 * <li class='jm'>{@link Builder#queryData() queryData()} 401 * <li class='jm'>{@link Builder#queryData(String,String) queryData(String,String)} 402 * <li class='jm'>{@link Builder#queryData(String,Supplier) queryData(String,Supplier<?>)} 403 * <li class='jm'>{@link Builder#queryData(NameValuePair...) queryData(NameValuePair...)} 404 * <li class='jm'>{@link Builder#queryDataDefault(NameValuePair...) defaultQueryData(NameValuePair...)} 405 * </ul> 406 * <li class='jc'>{@link RestRequest} 407 * <ul> 408 * <li class='jm'>{@link RestRequest#queryData(String,Object) queryData(String,Object)} 409 * <li class='jm'>{@link RestRequest#queryData(NameValuePair...) queryData(NameValuePair...)} 410 * <li class='jm'>{@link RestRequest#queryDataBean(Object) queryDataBean(Object)} 411 * <li class='jm'>{@link RestRequest#queryCustom(Object) queryCustom(Object)} 412 * <li class='jm'>{@link RestRequest#queryDataPairs(String...) queryDataPairs(String...)} 413 * </ul> 414 * </ul> 415 * 416 * <h5 class='figure'>Example:</h5> 417 * <p class='bjava'> 418 * <jc>// Create a client that adds a ?foo=bar query parameter to every request.</jc> 419 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().query(<js>"foo"</js>, <js>"bar"</js>).build(); 420 * 421 * <jc>// Or do it on every request.</jc> 422 * String <jv>response</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).query(<js>"foo"</js>, <js>"bar"</js>).run().getContent().asString(); 423 * </p> 424 * 425 * <h5 class='section'>Notes:</h5><ul> 426 * <li class='note'>Like header values, dynamic values and OpenAPI schemas are supported. 427 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> 428 * objects use the values returned by that bean directly. 429 * </ul> 430 * 431 * 432 * <h4 class='topic'>Request Form Data</h4> 433 * 434 * <p> 435 * Per-client or per-request form-data parameters can be specified using the following methods: 436 * <ul class='javatree'> 437 * <li class='jc'>{@link Builder} 438 * <ul> 439 * <li class='jm'>{@link Builder#formData() formData()} 440 * <li class='jm'>{@link Builder#formData(String,String) formData(String,String)} 441 * <li class='jm'>{@link Builder#formData(String,Supplier) formData(String,Supplier<?>)} 442 * <li class='jm'>{@link Builder#formData(NameValuePair...) formDatas(NameValuePair...)} 443 * <li class='jm'>{@link Builder#formDataDefault(NameValuePair...) defaultFormData(NameValuePair...)} 444 * </ul> 445 * <li class='jc'>{@link RestRequest} 446 * <ul> 447 * <li class='jm'>{@link RestRequest#formData(String,Object) formData(String,Object)} 448 * <li class='jm'>{@link RestRequest#formData(NameValuePair...) formData(NameValuePair...)} 449 * <li class='jm'>{@link RestRequest#formDataBean(Object) formDataBean(Object)} 450 * <li class='jm'>{@link RestRequest#formDataCustom(Object) formDataCustom(Object)} 451 * <li class='jm'>{@link RestRequest#formDataPairs(String...) formDataPairs(String...)} 452 * </ul> 453 * </ul> 454 * 455 * <h5 class='section'>Notes:</h5><ul> 456 * <li class='note'>Like header values, dynamic values and OpenAPI schemas are supported. 457 * <li class='note'>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> 458 * objects use the values returned by that bean directly. 459 * </ul> 460 * 461 * 462 * <h4 class='topic'>Request Body</h4> 463 * 464 * <p> 465 * The request body can either be passed in with the client creator method (e.g. {@link RestClient#post(Object,Object) post(uri,body)}), 466 * or can be specified via the following methods: 467 * 468 * <ul class='javatree'> 469 * <li class='jc'>{@link RestRequest} 470 * <ul> 471 * <li class='jm'>{@link RestRequest#content(Object) body(Object)} 472 * <li class='jm'>{@link RestRequest#content(Object,HttpPartSchema) body(Object,HttpPartSchema)} 473 * </ul> 474 * </ul> 475 * 476 * <p> 477 * The request body can be any of the following types: 478 * <ul class='javatree'> 479 * <li class='jc'> 480 * {@link Object} - POJO to be converted to text using the {@link Serializer} defined on the client or request. 481 * <li class='jc'> 482 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 483 * <li class='jc'> 484 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 485 * <li class='jc'> 486 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 487 * <li class='jc'> 488 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 489 * <li class='jc'> 490 * {@link PartList} - Converted to a URL-encoded FORM post. 491 * <li class='jc'> 492 * {@link Supplier} - A supplier of anything on this list. 493 * </ul> 494 * 495 * <h5 class='section'>Notes:</h5><ul> 496 * <li class='note'>If the serializer on the client or request is explicitly set to <jk>null</jk>, POJOs will be converted to strings 497 * using the registered part serializer as content type <js>"text/plain</js>. If the part serializer is also <jk>null</jk>, 498 * POJOs will be converted to strings using {@link ClassMeta#toString(Object)} which typically just calls {@link Object#toString()}. 499 * </ul> 500 * 501 * 502 * <h4 class='topic'>Response Status</h4> 503 * 504 * <p> 505 * After execution using {@link RestRequest#run()} or {@link RestRequest#complete()}, the following methods can be used 506 * to get the response status: 507 * 508 * <ul class='javatree'> 509 * <li class='jc'>{@link RestResponse} 510 * <ul> 511 * <li class='jm'><c>{@link RestResponse#getStatusLine() getStatusLine()} <jk>returns</jk> {@link StatusLine}</c> 512 * <li class='jm'><c>{@link RestResponse#getStatusCode() getStatusCode()} <jk>returns</jk> <jk>int</jk></c> 513 * <li class='jm'><c>{@link RestResponse#getReasonPhrase() getReasonPhrase()} <jk>returns</jk> String</c> 514 * <li class='jm'><c>{@link RestResponse#assertStatus() assertStatus()} <jk>returns</jk> {@link FluentResponseStatusLineAssertion}</c> 515 * </ul> 516 * </ul> 517 * 518 * <h5 class='figure'>Example:</h5> 519 * <p class='bjava'> 520 * <jc>// Only interested in status code.</jc> 521 * <jk>int</jk> <jv>statusCode</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); 522 * </p> 523 * 524 * <p> 525 * Equivalent methods with mutable parameters are provided to allow access to status values without breaking fluent call chains. 526 * 527 * <h5 class='figure'>Example:</h5> 528 * <p class='bjava'> 529 * <jc>// Interested in multiple values.</jc> 530 * Value<Integer> <jv>statusCode</jv> = Value.<jsm>create</jsm>(); 531 * Value<String> <jv>reasonPhrase</jv> = Value.<jsm>create</jsm>(); 532 * 533 * <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(<jv>statusCode</jv>).getReasonPhrase(<jv>reasonPhrase</jv>); 534 * System.<jsf>err</jsf>.println(<js>"statusCode="</js>+<jv>statusCode</jv>.get()+<js>", reasonPhrase="</js>+<jv>reasonPhrase</jv>.get()); 535 * </p> 536 * 537 * <h5 class='section'>Notes:</h5><ul> 538 * <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 539 * of {@link RestRequest#run()} to make sure the response body gets automatically cleaned up. Otherwise you must 540 * consume the response yourself. 541 * </ul> 542 * 543 * <p> 544 * The assertion method is provided for quickly asserting status codes in fluent calls. 545 * 546 * <h5 class='figure'>Example:</h5> 547 * <p class='bjava'> 548 * <jc>// Status assertion using a static value.</jc> 549 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 550 * .run() 551 * .assertStatus().asCode().isBetween(200,399) 552 * .getContent().asString(); 553 * 554 * <jc>// Status assertion using a predicate.</jc> 555 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 556 * .run() 557 * .assertStatus().asCode().is(<jv>x</jv> -> <jv>x</jv><400) 558 * .getContent().asString(); 559 * </p> 560 * 561 * 562 * <h4 class='topic'>Response Headers</h4> 563 * 564 * <p> 565 * Response headers are accessed through the following methods: 566 * 567 * <ul class='javatree'> 568 * <li class='jc'>{@link RestResponse} 569 * <ul> 570 * <li class='jm'><c>{@link RestResponse#getHeaders(String) getHeaders(String)} <jk>returns</jk> {@link ResponseHeader}[]</c> 571 * <li class='jm'><c>{@link RestResponse#getFirstHeader(String) getFirstHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> 572 * <li class='jm'><c>{@link RestResponse#getLastHeader(String) getLastHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> 573 * <li class='jm'><c>{@link RestResponse#getAllHeaders() getAllHeaders()} <jk>returns</jk> {@link ResponseHeader}[]</c> 574 * <li class='jm'><c>{@link RestResponse#getStringHeader(String) getStringHeader(String)} <jk>returns</jk> String</c> 575 * <li class='jm'><c>{@link RestResponse#containsHeader(String) containsHeader(String)} <jk>returns</jk> <jk>boolean</jk></c> 576 * </ul> 577 * </ul> 578 * 579 * <p> 580 * The {@link RestResponse#getFirstHeader(String)} and {@link RestResponse#getLastHeader(String)} methods return an empty {@link ResponseHeader} object instead of<jk>null</jk>. 581 * This allows it to be used more easily in fluent calls. 582 * 583 * <h5 class='figure'>Example:</h5> 584 * <p class='bjava'> 585 * <jc>// See if response contains Location header.</jc> 586 * <jk>boolean</jk> <jv>hasLocationHeader</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getLastHeader(<js>"Location"</js>).exists(); 587 * </p> 588 * 589 * <p> 590 * The {@link ResponseHeader} class extends from the HttpClient {@link Header} class and provides several convenience 591 * methods: 592 * 593 * <ul class='javatree'> 594 * <li class='jc'>{@link ResponseHeader} 595 * <ul> 596 * <li class='jm'><c>{@link ResponseHeader#isPresent() isPresent()} <jk>returns</jk> <jk>boolean</jk></c> 597 * <li class='jm'><c>{@link ResponseHeader#asString() asString()} <jk>returns</jk> String</c> 598 * <li class='jm'><c>{@link ResponseHeader#as(Type,Type...) as(Type,Type...)} <jk>returns</jk> T</c> 599 * <li class='jm'><c>{@link ResponseHeader#as(Class) as(Class<T>)} <jk>returns</jk> T</c> 600 * <li class='jm'><c>{@link ResponseHeader#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> 601 * <li class='jm'><c>{@link ResponseHeader#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> 602 * <li class='jm'><c>{@link ResponseHeader#asHeader(Class) asHeader(Class<T <jk>extends</jk> BasicHeader> c)} <jk>returns</jk> {@link BasicHeader}</c> 603 * <li class='jm'><c>{@link ResponseHeader#asStringHeader() asStringHeader()} <jk>returns</jk> {@link BasicStringHeader}</c> 604 * <li class='jm'><c>{@link ResponseHeader#asIntegerHeader() asIntegerHeader()} <jk>returns</jk> {@link BasicIntegerHeader}</c> 605 * <li class='jm'><c>{@link ResponseHeader#asLongHeader() asLongHeader()} <jk>returns</jk> {@link BasicLongHeader}</c> 606 * <li class='jm'><c>{@link ResponseHeader#asDateHeader() asDateHeader()} <jk>returns</jk> {@link BasicDateHeader}</c> 607 * <li class='jm'><c>{@link ResponseHeader#asCsvHeader() asCsvHeader()} <jk>returns</jk> {@link BasicCsvHeader}</c> 608 * <li class='jm'><c>{@link ResponseHeader#asEntityTagsHeader() asEntityTagsHeader()} <jk>returns</jk> {@link BasicEntityTagsHeader}</c> 609 * <li class='jm'><c>{@link ResponseHeader#asStringRangesHeader() asStringRangesHeader()} <jk>returns</jk> {@link BasicStringRangesHeader}</c> 610 * <li class='jm'><c>{@link ResponseHeader#asUriHeader() asUriHeader()} <jk>returns</jk> {@link BasicUriHeader}</c> 611 * </ul> 612 * </ul> 613 * 614 * <p> 615 * The {@link ResponseHeader#schema(HttpPartSchema)} method allows you to perform parsing of OpenAPI formats for 616 * header parts. 617 * 618 * <h5 class='figure'>Example:</h5> 619 * <p class='bjava'> 620 * <jc>// Parse the header "Foo: bar|baz".</jc> 621 * List<String> <jv>fooHeader</jv> = <jv>client</jv> 622 * .get(<jsf>URI</jsf>) 623 * .complete() 624 * .getHeader(<js>"Foo"</js>).schema(<jsf>T_ARRAY_PIPES</jsf>).as(List.<jk>class</jk>, String.<jk>class</jk>); 625 * </p> 626 * 627 * <p> 628 * Assertion methods are also provided for fluent-style calls: 629 * 630 * <ul class='javatree'> 631 * <li class='jc'>{@link ResponseHeader} 632 * <ul> 633 * <li class='jm'><c>{@link ResponseHeader#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseHeaderAssertion}</c> 634 * </ul> 635 * </ul> 636 * 637 * <p> 638 * Note how in the following example, the fluent assertion returns control to the {@link RestResponse} object after 639 * the assertion has been completed: 640 * 641 * <h5 class='figure'>Example:</h5> 642 * <p class='bjava'> 643 * <jc>// Assert the response content type is any sort of JSON.</jc> 644 * String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 645 * .run() 646 * .getHeader(<js>"Content-Type"</js>).assertValue().matchesSimple(<js>"application/json*"</js>) 647 * .getContent().asString(); 648 * </p> 649 * 650 * 651 * <h4 class='topic'>Response Body</h4> 652 * 653 * <p> 654 * The response body is accessed through the following method: 655 * 656 * <ul class='javatree'> 657 * <li class='jc'>{@link RestResponse} 658 * <ul> 659 * <li class='jm'><c>{@link RestResponse#getContent() getContent()} <jk>returns</jk> {@link ResponseContent}</c> 660 * </ul> 661 * </ul> 662 * 663 * <p> 664 * The {@link ResponseContent} class extends from the HttpClient {@link HttpEntity} class and provides several convenience 665 * methods: 666 * 667 * <ul class='javatree'> 668 * <li class='jc'>{@link ResponseContent} 669 * <ul> 670 * <li class='jm'><c>{@link ResponseContent#asInputStream() asInputStream()} <jk>returns</jk> InputStream</c> 671 * <li class='jm'><c>{@link ResponseContent#asReader() asReader()} <jk>returns</jk> Reader</c> 672 * <li class='jm'><c>{@link ResponseContent#asReader(Charset) asReader(Charset)} <jk>returns</jk> Reader</c> 673 * <li class='jm'><c>{@link ResponseContent#pipeTo(OutputStream) pipeTo(OutputStream)} <jk>returns</jk> {@link RestResponse}</c> 674 * <li class='jm'><c>{@link ResponseContent#pipeTo(Writer) pipeTo(Writer)} <jk>returns</jk> {@link RestResponse}</c> 675 * <li class='jm'><c>{@link ResponseContent#as(Type,Type...) as(Type,Type...)} <jk>returns</jk> T</c> 676 * <li class='jm'><c>{@link ResponseContent#as(Class) as(Class<T>)} <jk>returns</jk> T</c> 677 * <li class='jm'><c>{@link ResponseContent#asFuture(Class) asFuture(Class<T>)} <jk>returns</jk> Future<T></c> 678 * <li class='jm'><c>{@link ResponseContent#asFuture(Type,Type...) asFuture(Type,Type...)} <jk>returns</jk> Future<T></c> 679 * <li class='jm'><c>{@link ResponseContent#asString() asString()} <jk>returns</jk> String</c> 680 * <li class='jm'><c>{@link ResponseContent#asStringFuture() asStringFuture()} <jk>returns</jk> Future<String></c> 681 * <li class='jm'><c>{@link ResponseContent#asAbbreviatedString(int) asAbbreviatedString(int)} <jk>returns</jk> String</c> 682 * <li class='jm'><c>{@link ResponseContent#asObjectRest(Class) asObjectRest(Class<?>)} <jk>returns</jk> {@link ObjectRest}</c> 683 * <li class='jm'><c>{@link ResponseContent#asObjectRest() asObjectRest()} <jk>returns</jk> {@link ObjectRest}</c> 684 * <li class='jm'><c>{@link ResponseContent#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> 685 * <li class='jm'><c>{@link ResponseContent#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> 686 * </ul> 687 * </ul> 688 * 689 * <br> 690 * 691 * <h5 class='figure'>Examples:</h5> 692 * <p class='bjava'> 693 * <jc>// Parse into a linked-list of strings.</jc> 694 * List<String> <jv>list1</jv> = <jv>client</jv> 695 * .get(<jsf>URI</jsf>) 696 * .run() 697 * .getContent().as(LinkedList.<jk>class</jk>, String.<jk>class</jk>); 698 * 699 * <jc>// Parse into a linked-list of beans.</jc> 700 * List<MyBean> <jv>list2</jv> = <jv>client</jv> 701 * .get(<jsf>URI</jsf>) 702 * .run() 703 * .getContent().as(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 704 * 705 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 706 * List<List<String>> <jv>list3</jv> = <jv>client</jv> 707 * .get(<jsf>URI</jsf>) 708 * .run() 709 * .getContent().as(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 710 * 711 * <jc>// Parse into a map of string keys/values.</jc> 712 * Map<String,String> <jv>map1</jv> = <jv>client</jv> 713 * .get(<jsf>URI</jsf>) 714 * .run() 715 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 716 * 717 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 718 * Map<String,List<MyBean>> <jv>map2</jv> = <jv>client</jv> 719 * .get(<jsf>URI</jsf>) 720 * .run() 721 * .getContent().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 722 * </p> 723 * 724 * <p> 725 * The response body can only be consumed once unless it has been cached into memory. In many cases, the body is 726 * automatically cached when using the assertions methods or methods such as {@link ResponseContent#asString()}. 727 * However, methods that involve reading directly from the input stream cannot be called twice. 728 * In these cases, the {@link RestResponse#cacheContent()} and {@link ResponseContent#cache()} methods are provided 729 * to cache the response body in memory so that you can perform several operations against it. 730 * 731 * <p class='bjava'> 732 * <jc>// Cache the response body so we can access it twice.</jc> 733 * InputStream <jv>inputStream</jv> = <jv>client</jv> 734 * .get(<jsf>URI</jsf>) 735 * .run() 736 * .cacheBody() 737 * .getContent().pipeTo(<jv>someOtherStream</jv>) 738 * .getContent().asInputStream(); 739 * </p> 740 * 741 * <p> 742 * Assertion methods are also provided for fluent-style calls: 743 * 744 * <ul class='javatree'> 745 * <li class='jc'>{@link ResponseContent} 746 * <ul> 747 * <li class='jm'><c>{@link ResponseContent#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseBodyAssertion}</c> 748 * </ul> 749 * </ul> 750 * 751 * <br> 752 * 753 * <h5 class='figure'>Example:</h5> 754 * <p class='bjava'> 755 * <jc>// Assert that the body contains the string "Success".</jc> 756 * String <jv>body</jv> = <jv>client</jv> 757 * .get(<jsf>URI</jsf>) 758 * .run() 759 * .getContent().assertString().contains(<js>"Success"</js>) 760 * .getContent().asString(); 761 * </p> 762 * 763 * <p> 764 * Object assertions allow you to parse the response body into a POJO and then perform various tests on that resulting 765 * POJO. 766 * 767 * <h5 class='figure'>Example:</h5> 768 * <p class='bjava'> 769 * <jc>// Parse bean into POJO and then validate that it was parsed correctly.</jc> 770 * MyBean <jv>bean</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) 771 * .run() 772 * .getContent().assertObject(MyBean.<jk>class</jk>).asJson().is(<js>"{foo:'bar'}"</js>) 773 * .getContent().as(MyBean.<jk>class</jk>); 774 * </p> 775 * 776 * 777 * <h4 class='topic'>Custom Call Handlers</h4> 778 * 779 * <p> 780 * The {@link RestCallHandler} interface provides the ability to provide custom handling of requests. 781 * 782 * <ul class='javatree'> 783 * <li class='jc'>{@link Builder} 784 * <ul> 785 * <li class='jm'>{@link Builder#callHandler() callHandler()} 786 * </ul> 787 * <li class='jic'>{@link RestCallHandler} 788 * <ul> 789 * <li class='jm'><c>{@link RestCallHandler#run(HttpHost,HttpRequest,HttpContext) run(HttpHost,HttpRequest,HttpContext)} <jk>returns</jk> HttpResponse</c> 790 * </ul> 791 * </ul> 792 * 793 * <p> 794 * Note that there are other ways of accomplishing this such as extending the {@link RestClient} class and overriding 795 * the {@link #run(HttpHost,HttpRequest,HttpContext)} method 796 * or by defining your own {@link HttpRequestExecutor}. Using this interface is often simpler though. 797 * 798 * 799 * <h4 class='topic'>Interceptors</h4> 800 * 801 * <p> 802 * The {@link RestCallInterceptor} API provides a quick way of intercepting and manipulating requests and responses beyond 803 * the existing {@link HttpRequestInterceptor} and {@link HttpResponseInterceptor} APIs. 804 * 805 * <ul class='javatree'> 806 * <li class='jc'>{@link Builder} 807 * <ul> 808 * <li class='jm'>{@link Builder#interceptors(Object...) interceptors(Object...)} 809 * </ul> 810 * <li class='jc'>{@link RestRequest} 811 * <ul> 812 * <li class='jm'>{@link RestRequest#interceptors(RestCallInterceptor...) interceptors(RestCallInterceptor...)} 813 * </ul> 814 * <li class='jic'>{@link RestCallInterceptor} 815 * <ul> 816 * <li class='jm'>{@link RestCallInterceptor#onInit(RestRequest) onInit(RestRequest)} 817 * <li class='jm'>{@link RestCallInterceptor#onConnect(RestRequest,RestResponse) onConnect(RestRequest,RestResponse)} 818 * <li class='jm'>{@link RestCallInterceptor#onClose(RestRequest,RestResponse) onClose(RestRequest,RestResponse)} 819 * </ul> 820 * </ul> 821 * 822 * 823 * <h4 class='topic'>Logging / Debugging</h4> 824 * 825 * <p> 826 * The following methods provide logging of requests and responses: 827 * 828 * <ul class='javatree'> 829 * <li class='jc'>{@link Builder} 830 * <ul> 831 * <li class='jm'>{@link Builder#logger(Logger) logger(Logger)} 832 * <li class='jm'>{@link Builder#logToConsole() logToConsole()} 833 * <li class='jm'>{@link Builder#logRequests(DetailLevel,Level,BiPredicate) logRequests(DetailLevel,Level,BiPredicate)} 834 * </ul> 835 * </ul> 836 * 837 * <p> 838 * The following example shows the results of logging all requests that end with <c>/bean</c>. 839 * 840 * <h5 class='figure'>Examples:</h5> 841 * <p class='bjava'> 842 * MyBean <jv>bean</jv> = RestClient 843 * .<jsm>create</jsm>() 844 * .json5() 845 * .logRequests(DetailLevel.<jsf>FULL</jsf>, Level.<jsf>SEVERE</jsf>, (<jv>req</jv>,<jv>res</jv>)-><jv>req</jv>.getUri().endsWith(<js>"/bean"</js>)) 846 * .logToConsole() 847 * .build() 848 * .post(<js>"http://localhost/bean"</js>, <jv>anotherBean</jv>) 849 * .run() 850 * .getContent().as(MyBean.<jk>class</jk>); 851 * </p> 852 * 853 * <p> 854 * This produces the following console output: 855 * 856 * <p class='bconsole'> 857 * === HTTP Call (outgoing) ====================================================== 858 * === REQUEST === 859 * POST http://localhost/bean 860 * ---request headers--- 861 * Accept: application/json5 862 * ---request entity--- 863 * Content-Type: application/json5 864 * ---request content--- 865 * {f:1} 866 * === RESPONSE === 867 * HTTP/1.1 200 868 * ---response headers--- 869 * Content-Type: application/json 870 * ---response content--- 871 * {f:1} 872 * === END =======================================================================", 873 * </p> 874 * 875 * 876 * <p class='notes'> 877 * 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 878 * a performance penalty. 879 * 880 * <p> 881 * Additionally, the following method is also provided for enabling debug mode: 882 * 883 * <ul class='javatree'> 884 * <li class='jc'>{@link Builder} 885 * <ul> 886 * <li class='jm'>{@link Builder#debug() debug()} 887 * </ul> 888 * </ul> 889 * 890 * <p> 891 * Enabling debug mode has the following effects: 892 * <ul> 893 * <li>{@link org.apache.juneau.Context.Builder#debug()} is enabled. 894 * <li>{@link Builder#detectLeaks()} is enabled. 895 * <li>{@link Builder#logToConsole()} is called. 896 * </ul> 897 * 898 * 899 * <h4 class='topic'>REST Proxies</h4> 900 * 901 * <p> 902 * One of the more powerful features of the REST client class is the ability to produce Java interface proxies against 903 * arbitrary remote REST resources. 904 * 905 * <h5 class='figure'>Example:</h5> 906 * <p class='bjava'> 907 * <jc>// Define a Remote proxy for interacting with a REST interface.</jc> 908 * <ja>@Remote</ja>(path=<js>"/petstore"</js>) 909 * <jk>public interface</jk> PetStore { 910 * 911 * <ja>@RemotePost</ja>(<js>"/pets"</js>) 912 * Pet addPet( 913 * <ja>@Content</ja> CreatePet <jv>pet</jv>, 914 * <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, 915 * <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> 916 * ); 917 * } 918 * 919 * <jc>// Use a RestClient with default JSON 5 support.</jc> 920 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build()) 921 * 922 * PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); 923 * CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); 924 * Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); 925 * </p> 926 * 927 * <p> 928 * The methods to retrieve remote interfaces are: 929 * 930 * <ul class='javatree'> 931 * <li class='jc'>{@link RestClient} 932 * <ul> 933 * <li class='jm'><c>{@link RestClient#getRemote(Class) getRemote(Class<T>)} <jk>returns</jk> T</c> 934 * <li class='jm'><c>{@link RestClient#getRemote(Class,Object) getRemote(Class<T>,Object)} <jk>returns</jk> T</c> 935 * <li class='jm'><c>{@link RestClient#getRemote(Class,Object,Serializer,Parser) getRemote(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> 936 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class) getRrpcInterface(Class<T>)} <jk>returns</jk> T</c> 937 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object) getRrpcInterface(Class<T>,Object)} <jk>returns</jk> T</c> 938 * <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object,Serializer,Parser) getRrpcInterface(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> 939 * </ul> 940 * </ul> 941 * 942 * <p> 943 * Two basic types of remote interfaces are provided: 944 * 945 * <ul class='spaced-list'> 946 * <li>{@link Remote @Remote}-annotated interfaces. These can be defined against arbitrary external REST resources. 947 * <li>RPC-over-REST interfaces. These are Java interfaces that allow you to make method calls on server-side POJOs. 948 * </ul> 949 * 950 * <p> 951 * Refer to the following documentation on both flavors: 952 * 953 * <ul class='doctree'> 954 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxyBasics">REST Proxy Basics</a> 955 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 956 * </ul> 957 * 958 * <br> 959 * <hr> 960 * <h4 class='topic'>Customizing Apache HttpClient</h4> 961 * 962 * <p> 963 * Several methods are provided for customizing the underlying HTTP client and client builder classes: 964 * <ul class='javatree'> 965 * <li class='jc'>{@link Builder} 966 * <ul> 967 * <li class='jm'>{@link Builder#httpClientBuilder(HttpClientBuilder) httpClientBuilder(HttpClientBuilder)} - Set the client builder yourself. 968 * <li class='jm'>{@link Builder#createHttpClientBuilder() createHttpClientBuilder()} - Override to create the client builder. 969 * <li class='jm'>{@link Builder#createHttpClient() createHttpClient()} - Override to create the client. 970 * <li class='jm'>{@link Builder#createConnectionManager() createConnectionManager()} - Override to create the connection management. 971 * </ul> 972 * </ul> 973 * 974 * <p> 975 * Additionally, all methods on the <c>HttpClientBuilder</c> class have been extended with fluent setters. 976 * 977 * <h5 class='figure'>Example:</h5> 978 * <p class='bjava'> 979 * <jc>// Create a client with customized HttpClient settings.</jc> 980 * MyBean <jv>bean</jv> = RestClient 981 * .<jsm>create</jsm>() 982 * .disableRedirectHandling() 983 * .connectionManager(<jv>myConnectionManager</jv>) 984 * .addInterceptorFirst(<jv>myHttpRequestInterceptor</jv>) 985 * .build(); 986 * </p> 987 * 988 * <p> 989 * Refer to the {@link HttpClientBuilder HTTP Client Builder API} for more information. 990 * 991 * 992 * <h4 class='topic'>Extending RestClient</h4> 993 * 994 * <p> 995 * The <c>RestClient</c> API has been designed to allow for the ability to be easily extended. 996 * The following example that overrides the primary run method shows how this can be done. 997 * 998 * <h5 class='figure'>Example:</h5> 999 * <p class='bjava'> 1000 * <jk>public class</jk> MyRestClient <jk>extends</jk> RestClient { 1001 * 1002 * <jc>// Must provide this constructor!</jc> 1003 * <jk>public</jk> MyRestClient(RestClient.Builder <jv>builder</jv>) { 1004 * <jk>super</jk>(<jv>builder</jv>); 1005 * } 1006 * 1007 * <jd>/** Optionally override to customize builder settings before initialization. </jd> 1008 * <ja>@Override</ja> 1009 * <jk>protected void</jk> init(RestClient.Builder) {...} 1010 * 1011 * <jd>/** Optionally override to provide post-initialization (e.g. setting up SAML handshakes, etc...). </jd> 1012 * <ja>@Override</ja> 1013 * <jk>protected void</jk> init() {...} 1014 * 1015 * <jd>/** Optionally override to customize requests when they're created (e.g. add headers to each request). </jd> 1016 * <ja>@Override</ja> 1017 * <jk>protected</jk> RestRequest request(RestOperation) {...} 1018 * 1019 * <jd>/** Optionally override to implement your own call handling. </jd> 1020 * <ja>@Override</ja> 1021 * <jk>protected</jk> HttpResponse run(HttpHost, HttpRequest, HttpContext) {...} 1022 * 1023 * <jd>/** Optionally override to customize requests before they're executed. </jd> 1024 * <ja>@Override</ja> 1025 * <jk>protected void</jk> onCallInit(RestRequest) {...} 1026 * 1027 * <jd>/** Optionally override to customize responses as soon as a connection is made. </jd> 1028 * <ja>@Override</ja> 1029 * <jk>protected void</jk> onCallConnect(RestRequest, RestResponse) {...} 1030 * 1031 * <jd>/** Optionally override to perform any call cleanup. </jd> 1032 * <ja>@Override</ja> 1033 * <jk>protected void</jk> onCallClose(RestRequest, RestResponse) {...} 1034 * } 1035 * 1036 * <jc>// Instantiate your client.</jc> 1037 * MyRestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().build(MyRestClient.<jk>class</jk>); 1038 * </p> 1039 * 1040 * <p> 1041 * The {@link RestRequest} and {@link RestResponse} objects can also be extended and integrated by overriding the 1042 * {@link RestClient#createRequest(URI,String,boolean)} and {@link RestClient#createResponse(RestRequest,HttpResponse,Parser)} methods. 1043 * 1044 * <h5 class='section'>Notes:</h5><ul> 1045 * <li class='note'>This class is thread safe and reusable. 1046 * </ul> 1047 * 1048 * <h5 class='section'>See Also:</h5><ul> 1049 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestClientBasics">juneau-rest-client Basics</a> 1050 * </ul> 1051 */ 1052@SuppressWarnings("resource") 1053public class RestClient extends BeanContextable implements HttpClient, Closeable { 1054 /** 1055 * Builder class. 1056 */ 1057 public static class Builder extends BeanContextable.Builder { 1058 1059 private BeanCreator<RestCallHandler> callHandler; 1060 private BeanStore beanStore = BeanStore.create().build(); 1061 private BiPredicate<RestRequest,RestResponse> logRequestsPredicate; 1062 private boolean detectLeaks; 1063 private boolean executorServiceShutdownOnClose; 1064 private boolean ignoreErrors; 1065 private boolean keepHttpClientOpen; 1066 private boolean logToConsole; 1067 private boolean pooled; 1068 private boolean skipEmptyFormData; 1069 private boolean skipEmptyHeaderData; 1070 private boolean skipEmptyQueryData; 1071 private CloseableHttpClient httpClient; 1072 private DetailLevel logRequests; 1073 private ExecutorService executorService; 1074 private HeaderList headerData; 1075 private HttpClientBuilder httpClientBuilder; 1076 private HttpClientConnectionManager connectionManager; 1077 private HttpPartParser.Creator partParser; 1078 private HttpPartSerializer.Creator partSerializer; 1079 private Level logRequestsLevel; 1080 private Logger logger; 1081 private PartList formData; 1082 private PartList pathData; 1083 private PartList queryData; 1084 private ParserSet.Builder parsers; 1085 private Predicate<Integer> errorCodes = x -> x <= 0 || x >= 400; 1086 private PrintStream console; 1087 private SerializerSet.Builder serializers; 1088 private String rootUrl; 1089 private UrlEncodingSerializer.Builder urlEncodingSerializer; 1090 List<RestCallInterceptor> interceptors; 1091 1092 /** 1093 * Constructor. 1094 */ 1095 protected Builder() {} 1096 1097 /** 1098 * Appends an <c>Accept</c> header on this request. 1099 * 1100 * <p> 1101 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>))</c>. 1102 * 1103 * @param value 1104 * The new header value. 1105 * <br>Can be <jk>null</jk> (no Accept header will be added). 1106 * @return This object. 1107 * @see #headers() 1108 */ 1109 public Builder accept(String value) { 1110 return headers(Accept.of(value)); 1111 } 1112 1113 /** 1114 * Sets the value for the <c>Accept-Charset</c> request header on all requests. 1115 * 1116 * <p> 1117 * This is a shortcut for calling <c>headerData().append(AcceptCharset.<jsm>of</jsm>(<jv>value</jv>))</c>. 1118 * 1119 * @param value The new header value. 1120 * <br>Can be <jk>null</jk> (no Accept-Charset header will be added). 1121 * @return This object. 1122 * @see #headers() 1123 */ 1124 public Builder acceptCharset(String value) { 1125 return headers(AcceptCharset.of(value)); 1126 } 1127 1128 /** 1129 * <i><l>Serializer</l> configuration property: </i> Add <js>"_type"</js> properties when needed. 1130 * 1131 * <p> 1132 * When enabled, <js>"_type"</js> properties will be added to beans if their type cannot be inferred 1133 * through reflection. 1134 * 1135 * <p> 1136 * This is used to recreate the correct objects during parsing if the object types cannot be inferred. 1137 * <br>For example, when serializing a <c>Map<String,Object></c> field where the bean class cannot be determined from 1138 * the type of the values. 1139 * 1140 * <p> 1141 * Note the differences between the following settings: 1142 * <ul class='javatree'> 1143 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 1144 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 1145 * </ul> 1146 * 1147 * <h5 class='section'>Example:</h5> 1148 * <p class='bjava'> 1149 * <jc>// Create a JSON client that adds _type to nodes in the request body.</jc> 1150 * RestClient <jv>client</jv> = RestClient 1151 * .<jsm>create</jsm>() 1152 * .json() 1153 * .addBeanTypes() 1154 * .build(); 1155 * 1156 * <jc>// Our map of beans to serialize.</jc> 1157 * <ja>@Bean</ja>(typeName=<js>"mybean"</js>) 1158 * <jk>public class</jk> MyBean { 1159 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1160 * } 1161 * 1162 * AMap <jv>map</jv> = AMap.of(<js>"foo"</js>, <jk>new</jk> MyBean()); 1163 * 1164 * <jc>// Request body will contain: {"foo":{"_type":"mybean","foo":"bar"}}</jc> 1165 * <jv>client</jv> 1166 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 1167 * .run(); 1168 * </p> 1169 * 1170 * <h5 class='section'>See Also:</h5><ul> 1171 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} 1172 * </ul> 1173 * 1174 * @return This object. 1175 */ 1176 public Builder addBeanTypes() { 1177 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::addBeanTypes); 1178 return this; 1179 } 1180 1181 /** 1182 * Adds this protocol interceptor to the head of the protocol processing list. 1183 * 1184 * <h5 class='section'>Notes:</h5><ul> 1185 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1186 * </ul> 1187 * 1188 * @param itcp New property value. 1189 * <br>Cannot be <jk>null</jk>. 1190 * @return This object. 1191 * @see HttpClientBuilder#addInterceptorFirst(HttpRequestInterceptor) 1192 */ 1193 public Builder addInterceptorFirst(HttpRequestInterceptor itcp) { 1194 httpClientBuilder().addInterceptorFirst(assertArgNotNull("itcp", itcp)); 1195 return this; 1196 } 1197 1198 /** 1199 * Adds this protocol interceptor to the head of the protocol processing list. 1200 * 1201 * <h5 class='section'>Notes:</h5><ul> 1202 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1203 * </ul> 1204 * 1205 * @param itcp New property value. 1206 * <br>Cannot be <jk>null</jk>. 1207 * @return This object. 1208 * @see HttpClientBuilder#addInterceptorFirst(HttpResponseInterceptor) 1209 */ 1210 public Builder addInterceptorFirst(HttpResponseInterceptor itcp) { 1211 httpClientBuilder().addInterceptorFirst(assertArgNotNull("itcp", itcp)); 1212 return this; 1213 } 1214 1215 /** 1216 * Adds this protocol interceptor to the tail of the protocol processing list. 1217 * 1218 * <h5 class='section'>Notes:</h5><ul> 1219 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1220 * </ul> 1221 * 1222 * @param itcp New property value. 1223 * <br>Cannot be <jk>null</jk>. 1224 * @return This object. 1225 * @see HttpClientBuilder#addInterceptorLast(HttpRequestInterceptor) 1226 */ 1227 public Builder addInterceptorLast(HttpRequestInterceptor itcp) { 1228 httpClientBuilder().addInterceptorLast(assertArgNotNull("itcp", itcp)); 1229 return this; 1230 } 1231 1232 /** 1233 * Adds this protocol interceptor to the tail of the protocol processing list. 1234 * 1235 * <h5 class='section'>Notes:</h5><ul> 1236 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1237 * </ul> 1238 * 1239 * @param itcp New property value. 1240 * <br>Cannot be <jk>null</jk>. 1241 * @return This object. 1242 * @see HttpClientBuilder#addInterceptorLast(HttpResponseInterceptor) 1243 */ 1244 public Builder addInterceptorLast(HttpResponseInterceptor itcp) { 1245 httpClientBuilder().addInterceptorLast(assertArgNotNull("itcp", itcp)); 1246 return this; 1247 } 1248 1249 /** 1250 * <i><l>Serializer</l> configuration property: </i> Add type attribute to root nodes. 1251 * 1252 * <p> 1253 * When enabled, <js>"_type"</js> properties will be added to top-level beans. 1254 * 1255 * <p> 1256 * When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level 1257 * type information that might normally be included to determine the data type will not be serialized. 1258 * 1259 * <p> 1260 * For example, when serializing a top-level POJO with a {@link Bean#typeName() @Bean(typeName)} value, a 1261 * <js>'_type'</js> attribute will only be added when this setting is enabled. 1262 * 1263 * <p> 1264 * Note the differences between the following settings: 1265 * <ul class='javatree'> 1266 * <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node. 1267 * <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes. 1268 * </ul> 1269 * 1270 * <h5 class='section'>Example:</h5> 1271 * <p class='bjava'> 1272 * <jc>// Create a JSON client that adds _type to root node.</jc> 1273 * RestClient <jv>client</jv> = RestClient 1274 * .<jsm>create</jsm>() 1275 * .json() 1276 * .addRootType() 1277 * .build(); 1278 * 1279 * <jc>// Our bean to serialize.</jc> 1280 * <ja>@Bean</ja>(typeName=<js>"mybean"</js>) 1281 * <jk>public class</jk> MyBean { 1282 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 1283 * } 1284 * 1285 * <jc>// Request body will contain: {"_type":"mybean","foo":"bar"}</jc> 1286 * <jv>client</jv> 1287 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 1288 * .run(); 1289 * </p> 1290 * 1291 * <h5 class='section'>See Also:</h5><ul> 1292 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#addRootType()} 1293 * </ul> 1294 * 1295 * @return This object. 1296 */ 1297 public Builder addRootType() { 1298 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::addRootType); 1299 return this; 1300 } 1301 1302 @Override /* Overridden from Builder */ 1303 public Builder annotations(Annotation...values) { 1304 super.annotations(values); 1305 return this; 1306 } 1307 1308 @Override /* Overridden from Builder */ 1309 public Builder apply(AnnotationWorkList work) { 1310 super.apply(work); 1311 return this; 1312 } 1313 1314 @Override /* Overridden from Builder */ 1315 public Builder applyAnnotations(Class<?>...from) { 1316 super.applyAnnotations(from); 1317 return this; 1318 } 1319 1320 @Override /* Overridden from Builder */ 1321 public Builder applyAnnotations(Object...from) { 1322 super.applyAnnotations(from); 1323 return this; 1324 } 1325 1326 /** 1327 * Assigns {@link BackoffManager} instance. 1328 * 1329 * @param backoffManager New property value. 1330 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1331 * @return This object. 1332 * @see HttpClientBuilder#setBackoffManager(BackoffManager) 1333 */ 1334 public Builder backoffManager(BackoffManager backoffManager) { 1335 httpClientBuilder().setBackoffManager(backoffManager); 1336 return this; 1337 } 1338 1339 /** 1340 * Set up this client to use BASIC auth. 1341 * 1342 * <h5 class='section'>Example:</h5> 1343 * <p class='bjava'> 1344 * <jc>// Construct a client that uses BASIC authentication.</jc> 1345 * RestClient <jv>client</jv> = RestClient 1346 * .<jsm>create</jsm>() 1347 * .basicAuth(<js>"http://localhost"</js>, 80, <js>"me"</js>, <js>"mypassword"</js>) 1348 * .build(); 1349 * </p> 1350 * 1351 * @param host The auth scope hostname. 1352 * <br>Can be <jk>null</jk> (will use <js>"*"</js> as the hostname). 1353 * @param port The auth scope port. 1354 * @param user The username. 1355 * <br>Can be <jk>null</jk> (will use empty string). 1356 * @param pw The password. 1357 * <br>Can be <jk>null</jk> (will use empty string). 1358 * @return This object. 1359 */ 1360 public Builder basicAuth(String host, int port, String user, String pw) { 1361 var scope = new AuthScope(host, port); 1362 var up = new UsernamePasswordCredentials(user, pw); 1363 var p = new BasicCredentialsProvider(); 1364 p.setCredentials(scope, up); 1365 defaultCredentialsProvider(p); 1366 return this; 1367 } 1368 1369 @Override /* Overridden from Builder */ 1370 public Builder beanClassVisibility(Visibility value) { 1371 super.beanClassVisibility(value); 1372 return this; 1373 } 1374 1375 @Override /* Overridden from Builder */ 1376 public Builder beanConstructorVisibility(Visibility value) { 1377 super.beanConstructorVisibility(value); 1378 return this; 1379 } 1380 1381 @Override /* Overridden from Builder */ 1382 public Builder beanContext(BeanContext value) { 1383 super.beanContext(value); 1384 return this; 1385 } 1386 1387 @Override /* Overridden from Builder */ 1388 public Builder beanContext(BeanContext.Builder value) { 1389 super.beanContext(value); 1390 return this; 1391 } 1392 1393 @Override /* Overridden from Builder */ 1394 public Builder beanDictionary(java.lang.Class<?>...values) { 1395 super.beanDictionary(values); 1396 return this; 1397 } 1398 1399 @Override /* Overridden from Builder */ 1400 public Builder beanFieldVisibility(Visibility value) { 1401 super.beanFieldVisibility(value); 1402 return this; 1403 } 1404 1405 @Override /* Overridden from Builder */ 1406 public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) { 1407 super.beanInterceptor(on, value); 1408 return this; 1409 } 1410 1411 @Override /* Overridden from Builder */ 1412 public Builder beanMethodVisibility(Visibility value) { 1413 super.beanMethodVisibility(value); 1414 return this; 1415 } 1416 1417 @Override /* Overridden from Builder */ 1418 public Builder beanProperties(Class<?> beanClass, String properties) { 1419 super.beanProperties(beanClass, properties); 1420 return this; 1421 } 1422 1423 @Override /* Overridden from Builder */ 1424 public Builder beanProperties(Map<String,Object> values) { 1425 super.beanProperties(values); 1426 return this; 1427 } 1428 1429 @Override /* Overridden from Builder */ 1430 public Builder beanProperties(String beanClassName, String properties) { 1431 super.beanProperties(beanClassName, properties); 1432 return this; 1433 } 1434 1435 @Override /* Overridden from Builder */ 1436 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 1437 super.beanPropertiesExcludes(beanClass, properties); 1438 return this; 1439 } 1440 1441 @Override /* Overridden from Builder */ 1442 public Builder beanPropertiesExcludes(Map<String,Object> values) { 1443 super.beanPropertiesExcludes(values); 1444 return this; 1445 } 1446 1447 @Override /* Overridden from Builder */ 1448 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 1449 super.beanPropertiesExcludes(beanClassName, properties); 1450 return this; 1451 } 1452 1453 @Override /* Overridden from Builder */ 1454 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 1455 super.beanPropertiesReadOnly(beanClass, properties); 1456 return this; 1457 } 1458 1459 @Override /* Overridden from Builder */ 1460 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 1461 super.beanPropertiesReadOnly(values); 1462 return this; 1463 } 1464 1465 @Override /* Overridden from Builder */ 1466 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 1467 super.beanPropertiesReadOnly(beanClassName, properties); 1468 return this; 1469 } 1470 1471 @Override /* Overridden from Builder */ 1472 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 1473 super.beanPropertiesWriteOnly(beanClass, properties); 1474 return this; 1475 } 1476 1477 @Override /* Overridden from Builder */ 1478 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 1479 super.beanPropertiesWriteOnly(values); 1480 return this; 1481 } 1482 1483 @Override /* Overridden from Builder */ 1484 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 1485 super.beanPropertiesWriteOnly(beanClassName, properties); 1486 return this; 1487 } 1488 1489 @Override /* Overridden from Builder */ 1490 public Builder beansRequireDefaultConstructor() { 1491 super.beansRequireDefaultConstructor(); 1492 return this; 1493 } 1494 1495 @Override /* Overridden from Builder */ 1496 public Builder beansRequireSerializable() { 1497 super.beansRequireSerializable(); 1498 return this; 1499 } 1500 1501 @Override /* Overridden from Builder */ 1502 public Builder beansRequireSettersForGetters() { 1503 super.beansRequireSettersForGetters(); 1504 return this; 1505 } 1506 1507 @Override /* Overridden from Context.Builder */ 1508 public RestClient build() { 1509 return build(RestClient.class); 1510 } 1511 1512 @Override /* Overridden from Builder */ 1513 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 1514 super.cache(value); 1515 return this; 1516 } 1517 1518 /** 1519 * Returns the creator for the rest call handler. 1520 * 1521 * <p> 1522 * Allows you to provide a custom handler for making HTTP calls. 1523 * 1524 * <h5 class='section'>Example:</h5> 1525 * <p class='bjava'> 1526 * <jc>// Create a client that handles processing of requests using a custom handler.</jc> 1527 * <jk>public class</jk> MyRestCallHandler <jk>implements</jk> RestCallHandler { 1528 * 1529 * <ja>@Override</ja> 1530 * <jk>public</jk> HttpResponse run(HttpHost <jv>target</jv>, HttpRequest <jv>request</jv>, HttpContext <jv>context</jv>) <jk>throws</jk> IOException { 1531 * <jc>// Custom handle requests.</jc> 1532 * } 1533 * } 1534 * 1535 * RestClient <jv>client</jv> = RestClient 1536 * .<jsm>create</jsm>() 1537 * .callHandler(MyRestCallHandler.<jk>class</jk>) 1538 * .build(); 1539 * </p> 1540 * 1541 * <h5 class='section'>Notes:</h5><ul> 1542 * <li class='note'> 1543 * The {@link RestClient#run(HttpHost, HttpRequest, HttpContext)} method can also be overridden to produce the same results. 1544 * <li class='note'> 1545 * Use {@link BeanCreator#impl(Object)} to specify an already instantiated instance. 1546 * <li class='note'> 1547 * Use {@link BeanCreator#type(Class)} to specify a subtype to instantiate. 1548 * <br>Subclass must have a public constructor that takes in any args available 1549 * in the bean store of this builder (including {@link RestClient} itself). 1550 * </ul> 1551 * 1552 * <h5 class='section'>See Also:</h5><ul> 1553 * <li class='jic'>{@link RestCallHandler} 1554 * </ul> 1555 * 1556 * @return The creator for the rest call handler. 1557 */ 1558 public final BeanCreator<RestCallHandler> callHandler() { 1559 if (callHandler == null) 1560 callHandler = createCallHandler(); 1561 return callHandler; 1562 } 1563 1564 /** 1565 * REST call handler class. 1566 * 1567 * <p> 1568 * Specifies a custom handler for making HTTP calls. 1569 * 1570 * <p> 1571 * This is a shortcut for <c>callHandler().type(<jv>value</jv>)</c>. 1572 * 1573 * @param value 1574 * The new value for this setting. 1575 * <br>Cannot be <jk>null</jk>. 1576 * @return This object. 1577 * @see #callHandler() 1578 */ 1579 public Builder callHandler(Class<? extends RestCallHandler> value) { 1580 callHandler().type(assertArgNotNull("value", value)); 1581 return this; 1582 } 1583 1584 /** 1585 * Sets the client version by setting the value for the <js>"Client-Version"</js> header. 1586 * 1587 * <p> 1588 * This is a shortcut for calling <c>headerData().append(ClientVersion.<jsm>of</jsm>(<jv>value</jv>))</c>. 1589 * 1590 * @param value The version string (e.g. <js>"1.2.3"</js>) 1591 * <br>Can be <jk>null</jk> (no Client-Version header will be added). 1592 * @return This object. 1593 * @see #headers() 1594 */ 1595 public Builder clientVersion(String value) { 1596 return headers(ClientVersion.of(value)); 1597 } 1598 1599 /** 1600 * Assigns {@link ConnectionBackoffStrategy} instance. 1601 * 1602 * @param connectionBackoffStrategy New property value. 1603 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1604 * @return This object. 1605 * @see HttpClientBuilder#setConnectionBackoffStrategy(ConnectionBackoffStrategy) 1606 */ 1607 public Builder connectionBackoffStrategy(ConnectionBackoffStrategy connectionBackoffStrategy) { 1608 httpClientBuilder().setConnectionBackoffStrategy(connectionBackoffStrategy); 1609 return this; 1610 } 1611 1612 /** 1613 * Assigns {@link HttpClientConnectionManager} instance. 1614 * 1615 * @param value New property value. 1616 * <br>Can be <jk>null</jk> (value will not be set, a default connection manager will be created). 1617 * @return This object. 1618 * @see HttpClientBuilder#setConnectionManager(HttpClientConnectionManager) 1619 */ 1620 public Builder connectionManager(HttpClientConnectionManager value) { 1621 connectionManager = value; 1622 httpClientBuilder().setConnectionManager(value); 1623 return this; 1624 } 1625 1626 /** 1627 * Defines the connection manager is to be shared by multiple client instances. 1628 * 1629 * <h5 class='section'>Notes:</h5><ul> 1630 * <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. 1631 * </ul> 1632 * 1633 * @param shared New property value. 1634 * @return This object. 1635 * @see HttpClientBuilder#setConnectionManagerShared(boolean) 1636 */ 1637 public Builder connectionManagerShared(boolean shared) { 1638 httpClientBuilder().setConnectionManagerShared(shared); 1639 return this; 1640 } 1641 1642 /** 1643 * Assigns {@link ConnectionReuseStrategy} instance. 1644 * 1645 * @param reuseStrategy New property value. 1646 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1647 * @return This object. 1648 * @see HttpClientBuilder#setConnectionReuseStrategy(ConnectionReuseStrategy) 1649 */ 1650 public Builder connectionReuseStrategy(ConnectionReuseStrategy reuseStrategy) { 1651 httpClientBuilder().setConnectionReuseStrategy(reuseStrategy); 1652 return this; 1653 } 1654 1655 /** 1656 * Sets maximum time to live for persistent connections. 1657 * 1658 * <h5 class='section'>Notes:</h5><ul> 1659 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 1660 * </ul> 1661 * 1662 * @param connTimeToLive New property value. 1663 * @param connTimeToLiveTimeUnit New property value. 1664 * <br>Cannot be <jk>null</jk>. 1665 * @return This object. 1666 * @see HttpClientBuilder#setConnectionTimeToLive(long,TimeUnit) 1667 */ 1668 public Builder connectionTimeToLive(long connTimeToLive, TimeUnit connTimeToLiveTimeUnit) { 1669 httpClientBuilder().setConnectionTimeToLive(connTimeToLive, connTimeToLiveTimeUnit); 1670 return this; 1671 } 1672 1673 /** 1674 * Console print stream 1675 * 1676 * <p> 1677 * Allows you to redirect the console output to a different print stream. 1678 * 1679 * @param value 1680 * The new value for this setting. 1681 * <br>Can be <jk>null</jk> (defaults to <c>System.err</c>). 1682 * @return This object. 1683 */ 1684 public Builder console(PrintStream value) { 1685 console = value; 1686 return this; 1687 } 1688 1689 /** 1690 * Assigns a map of {@link org.apache.http.client.entity.InputStreamFactory InputStreamFactories} to be used for automatic content decompression. 1691 * 1692 * @param contentDecoderMap New property value. 1693 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1694 * @return This object. 1695 * @see HttpClientBuilder#setContentDecoderRegistry(Map) 1696 */ 1697 public Builder contentDecoderRegistry(Map<String,InputStreamFactory> contentDecoderMap) { 1698 httpClientBuilder().setContentDecoderRegistry(contentDecoderMap); 1699 return this; 1700 } 1701 1702 /** 1703 * Sets the value for the <c>Content-Type</c> request header on all requests. 1704 * 1705 * <p> 1706 * This is a shortcut for calling <c>headerData().append(ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 1707 * 1708 * <p> 1709 * This overrides the media type specified on the serializer. 1710 * 1711 * @param value The new header value. 1712 * <br>Can be <jk>null</jk> (no Content-Type header will be added). 1713 * @return This object. 1714 * @see #headers() 1715 */ 1716 public Builder contentType(String value) { 1717 return headers(ContentType.of(value)); 1718 } 1719 1720 @Override /* Overridden from Context.Builder */ 1721 public Builder copy() { 1722 throw new NoSuchMethodError("Not implemented."); 1723 } 1724 1725 /** 1726 * Sets the value for the <c>Debug</c> request header on all requests. 1727 * 1728 * <p> 1729 * This is a shortcut for calling <c>headerData().append(Debug.<jsm>of</jsm>(<jv>value</jv>))</c>. 1730 * 1731 * @return This object. 1732 * @see #headers() 1733 */ 1734 @Override 1735 public Builder debug() { 1736 super.debug(); 1737 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::debug); 1738 return headers(Debug.TRUE); 1739 } 1740 1741 /** 1742 * <i><l>Parser</l> configuration property: </i> Debug output lines. 1743 * 1744 * <p> 1745 * When parse errors occur, this specifies the number of lines of input before and after the 1746 * error location to be printed as part of the exception message. 1747 * 1748 * <h5 class='section'>Example:</h5> 1749 * <p class='bjava'> 1750 * <jc>// Create a parser whose exceptions print out 100 lines before and after the parse error location.</jc> 1751 * RestClient <jv>client</jv> = RestClient 1752 * .<jsm>create</jsm>() 1753 * .json() 1754 * .debug() <jc>// Enable debug mode to capture Reader contents as strings.</jc> 1755 * .debugOuputLines(100) 1756 * .build(); 1757 * 1758 * <jc>// Try to parse some bad JSON.</jc> 1759 * <jk>try</jk> { 1760 * <jv>client</jv> 1761 * .get(<js>"/pathToBadJson"</js>) 1762 * .run() 1763 * .getContent().as(Object.<jk>class</jk>); <jc>// Try to parse it.</jc> 1764 * } <jk>catch</jk> (RestCallException <jv>e</jv>) { 1765 * System.<jsf>err</jsf>.println(<jv>e</jv>.getMessage()); <jc>// Will display 200 lines of the output.</jc> 1766 * } 1767 * </p> 1768 * 1769 * <h5 class='section'>See Also:</h5><ul> 1770 * <li class='jm'>{@link org.apache.juneau.parser.Parser.Builder#debugOutputLines(int)} 1771 * </ul> 1772 * 1773 * @param value 1774 * The new value for this property. 1775 * <br>The default value is <c>5</c>. 1776 * @return This object. 1777 */ 1778 public Builder debugOutputLines(int value) { 1779 parsers().forEach(x -> x.debugOutputLines(value)); 1780 return this; 1781 } 1782 1783 /** 1784 * 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. 1785 * 1786 * @param authSchemeRegistry New property value. 1787 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1788 * @return This object. 1789 * @see HttpClientBuilder#setDefaultAuthSchemeRegistry(Lookup) 1790 */ 1791 public Builder defaultAuthSchemeRegistry(Lookup<AuthSchemeProvider> authSchemeRegistry) { 1792 httpClientBuilder().setDefaultAuthSchemeRegistry(authSchemeRegistry); 1793 return this; 1794 } 1795 1796 /** 1797 * Assigns default {@link ConnectionConfig}. 1798 * 1799 * <h5 class='section'>Notes:</h5><ul> 1800 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 1801 * </ul> 1802 * 1803 * @param config New property value. 1804 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1805 * @return This object. 1806 * @see HttpClientBuilder#setDefaultConnectionConfig(ConnectionConfig) 1807 */ 1808 public Builder defaultConnectionConfig(ConnectionConfig config) { 1809 httpClientBuilder().setDefaultConnectionConfig(config); 1810 return this; 1811 } 1812 1813 /** 1814 * Assigns default {@link CookieSpec} registry which will be used for request execution if not explicitly set in the client execution context. 1815 * 1816 * @param cookieSpecRegistry New property value. 1817 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1818 * @return This object. 1819 * @see HttpClientBuilder#setDefaultCookieSpecRegistry(Lookup) 1820 */ 1821 public Builder defaultCookieSpecRegistry(Lookup<CookieSpecProvider> cookieSpecRegistry) { 1822 httpClientBuilder().setDefaultCookieSpecRegistry(cookieSpecRegistry); 1823 return this; 1824 } 1825 1826 /** 1827 * Assigns default {@link CookieStore} instance which will be used for request execution if not explicitly set in the client execution context. 1828 * 1829 * @param cookieStore New property value. 1830 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1831 * @return This object. 1832 * @see HttpClientBuilder#setDefaultCookieStore(CookieStore) 1833 */ 1834 public Builder defaultCookieStore(CookieStore cookieStore) { 1835 httpClientBuilder().setDefaultCookieStore(cookieStore); 1836 return this; 1837 } 1838 1839 /** 1840 * Assigns default {@link CredentialsProvider} instance which will be used for request execution if not explicitly set in the client execution context. 1841 * 1842 * @param credentialsProvider New property value. 1843 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1844 * @return This object. 1845 * @see HttpClientBuilder#setDefaultCredentialsProvider(CredentialsProvider) 1846 */ 1847 public Builder defaultCredentialsProvider(CredentialsProvider credentialsProvider) { 1848 httpClientBuilder().setDefaultCredentialsProvider(credentialsProvider); 1849 return this; 1850 } 1851 1852 /** 1853 * Assigns default {@link RequestConfig} instance which will be used for request execution if not explicitly set in the client execution context. 1854 * 1855 * @param config New property value. 1856 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1857 * @return This object. 1858 * @see HttpClientBuilder#setDefaultRequestConfig(RequestConfig) 1859 */ 1860 public Builder defaultRequestConfig(RequestConfig config) { 1861 httpClientBuilder().setDefaultRequestConfig(config); 1862 return this; 1863 } 1864 1865 /** 1866 * Assigns default {@link SocketConfig}. 1867 * 1868 * <h5 class='section'>Notes:</h5><ul> 1869 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 1870 * </ul> 1871 * 1872 * @param config New property value. 1873 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 1874 * @return This object. 1875 * @see HttpClientBuilder#setDefaultSocketConfig(SocketConfig) 1876 */ 1877 public Builder defaultSocketConfig(SocketConfig config) { 1878 httpClientBuilder().setDefaultSocketConfig(config); 1879 return this; 1880 } 1881 1882 /** 1883 * <i><l>RestClient</l> configuration property: </i> Enable leak detection. 1884 * 1885 * <p> 1886 * Enable client and request/response leak detection. 1887 * 1888 * <p> 1889 * Causes messages to be logged to the console if clients or request/response objects are not properly closed 1890 * when the <c>finalize</c> methods are invoked. 1891 * 1892 * <p> 1893 * Automatically enabled with {@link org.apache.juneau.Context.Builder#debug()}. 1894 * 1895 * <h5 class='section'>Example:</h5> 1896 * <p class='bjava'> 1897 * <jc>// Create a client that logs a message if </jc> 1898 * RestClient <jv>client</jv> = RestClient 1899 * .<jsm>create</jsm>() 1900 * .detectLeaks() 1901 * .logToConsole() <jc>// Also log the error message to System.err</jc> 1902 * .build(); 1903 * 1904 * <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> 1905 * </p> 1906 * 1907 * @return This object. 1908 */ 1909 public Builder detectLeaks() { 1910 detectLeaks = true; 1911 return this; 1912 } 1913 1914 /** 1915 * <i><l>BeanTraverse</l> configuration property: </i> Automatically detect POJO recursions. 1916 * 1917 * <p> 1918 * When enabled, specifies that recursions should be checked for during traversal. 1919 * 1920 * <p> 1921 * Recursions can occur when traversing models that aren't true trees but rather contain loops. 1922 * <br>In general, unchecked recursions cause stack-overflow-errors. 1923 * <br>These show up as {@link BeanRecursionException BeanRecursionException} with the message <js>"Depth too deep. Stack overflow occurred."</js>. 1924 * 1925 * <h5 class='section'>Notes:</h5><ul> 1926 * <li class='note'> 1927 * Checking for recursion can cause a small performance penalty. 1928 * </ul> 1929 * 1930 * <h5 class='section'>Example:</h5> 1931 * <p class='bjava'> 1932 * <jc>// Create a JSON client that automatically checks for recursions.</jc> 1933 * RestClient <jv>client</jv> = RestClient 1934 * .<jsm>create</jsm>() 1935 * .json() 1936 * .detectRecursions() 1937 * .build(); 1938 * 1939 * <jc>// Create a POJO model with a recursive loop.</jc> 1940 * <jk>public class</jk> A { 1941 * <jk>public</jk> Object <jf>f</jf>; 1942 * } 1943 * A <jv>a</jv> = <jk>new</jk> A(); 1944 * <jv>a</jv>.<jf>f</jf> = <jv>a</jv>; 1945 * 1946 * <jk>try</jk> { 1947 * <jc>// Throws a RestCallException with an inner SerializeException and not a StackOverflowError</jc> 1948 * <jv>client</jv> 1949 * .post(<js>"http://localhost:10000/foo"</js>, <jv>a</jv>) 1950 * .run(); 1951 * } <jk>catch</jk> (RestCallException <jv>e</jv>} { 1952 * <jc>// Handle exception.</jc> 1953 * } 1954 * </p> 1955 * 1956 * <h5 class='section'>See Also:</h5><ul> 1957 * <li class='jm'>{@link org.apache.juneau.BeanTraverseContext.Builder#detectRecursions()} 1958 * </ul> 1959 * 1960 * @return This object. 1961 */ 1962 public Builder detectRecursions() { 1963 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::detectRecursions); 1964 return this; 1965 } 1966 1967 @Override /* Overridden from Builder */ 1968 public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) { 1969 super.dictionaryOn(on, values); 1970 return this; 1971 } 1972 1973 /** 1974 * Disables authentication scheme caching. 1975 * 1976 * <h5 class='section'>Notes:</h5><ul> 1977 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 1978 * </ul> 1979 * 1980 * @return This object. 1981 * @see HttpClientBuilder#disableAuthCaching() 1982 */ 1983 public Builder disableAuthCaching() { 1984 httpClientBuilder().disableAuthCaching(); 1985 return this; 1986 } 1987 1988 /** 1989 * Disables automatic request recovery and re-execution. 1990 * 1991 * @return This object. 1992 * @see HttpClientBuilder#disableAutomaticRetries() 1993 */ 1994 public Builder disableAutomaticRetries() { 1995 httpClientBuilder().disableAutomaticRetries(); 1996 return this; 1997 } 1998 1999 @Override /* Overridden from Builder */ 2000 public Builder disableBeansRequireSomeProperties() { 2001 super.disableBeansRequireSomeProperties(); 2002 return this; 2003 } 2004 2005 /** 2006 * Disables connection state tracking. 2007 * 2008 * @return This object. 2009 * @see HttpClientBuilder#disableConnectionState() 2010 */ 2011 public Builder disableConnectionState() { 2012 httpClientBuilder().disableConnectionState(); 2013 return this; 2014 } 2015 2016 /** 2017 * Disables automatic content decompression. 2018 * 2019 * <h5 class='section'>Notes:</h5><ul> 2020 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 2021 * </ul> 2022 * 2023 * @return This object. 2024 * @see HttpClientBuilder#disableContentCompression() 2025 */ 2026 public Builder disableContentCompression() { 2027 httpClientBuilder().disableContentCompression(); 2028 return this; 2029 } 2030 2031 /** 2032 * Disables state (cookie) management. 2033 * 2034 * <h5 class='section'>Notes:</h5><ul> 2035 * <li class='note'>This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. 2036 * </ul> 2037 * 2038 * @return This object. 2039 * @see HttpClientBuilder#disableCookieManagement() 2040 */ 2041 public Builder disableCookieManagement() { 2042 httpClientBuilder().disableCookieManagement(); 2043 return this; 2044 } 2045 2046 @Override /* Overridden from Builder */ 2047 public Builder disableIgnoreMissingSetters() { 2048 super.disableIgnoreMissingSetters(); 2049 return this; 2050 } 2051 2052 @Override /* Overridden from Builder */ 2053 public Builder disableIgnoreTransientFields() { 2054 super.disableIgnoreTransientFields(); 2055 return this; 2056 } 2057 2058 @Override /* Overridden from Builder */ 2059 public Builder disableIgnoreUnknownNullBeanProperties() { 2060 super.disableIgnoreUnknownNullBeanProperties(); 2061 return this; 2062 } 2063 2064 @Override /* Overridden from Builder */ 2065 public Builder disableInterfaceProxies() { 2066 super.disableInterfaceProxies(); 2067 return this; 2068 } 2069 2070 /** 2071 * Disables automatic redirect handling. 2072 * 2073 * @return This object. 2074 * @see HttpClientBuilder#disableRedirectHandling() 2075 */ 2076 public Builder disableRedirectHandling() { 2077 httpClientBuilder().disableRedirectHandling(); 2078 return this; 2079 } 2080 2081 /** 2082 * Errors codes predicate. 2083 * 2084 * <p> 2085 * Defines a predicate to test for error codes. 2086 * 2087 * <h5 class='section'>Example:</h5> 2088 * <p class='bjava'> 2089 * <jc>// Create a client that considers any 300+ responses to be errors.</jc> 2090 * RestClient <jv>client</jv> = RestClient 2091 * .<jsm>create</jsm>() 2092 * .errorCodes(<jv>x</jv> -> <jv>x</jv>>=300) 2093 * .build(); 2094 * </p> 2095 * 2096 * @param value 2097 * The new value for this setting. 2098 * <br>The default value is <code>x -> x >= 400</code>. 2099 * <br>Cannot be <jk>null</jk>. 2100 * @return This object. 2101 */ 2102 public Builder errorCodes(Predicate<Integer> value) { 2103 errorCodes = assertArgNotNull("value", value); 2104 return this; 2105 } 2106 2107 /** 2108 * Makes this instance of {@link HttpClient} proactively evict expired connections from the connection pool using a background thread. 2109 * 2110 * <h5 class='section'>Notes:</h5><ul> 2111 * <li class='note'>One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. 2112 * <li class='note'>This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. 2113 * <li class='note'>This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. 2114 * </ul> 2115 * 2116 * @return This object. 2117 * @see HttpClientBuilder#evictExpiredConnections() 2118 */ 2119 public Builder evictExpiredConnections() { 2120 httpClientBuilder().evictExpiredConnections(); 2121 return this; 2122 } 2123 2124 /** 2125 * Makes this instance of {@link HttpClient} proactively evict idle connections from the connection pool using a background thread. 2126 * 2127 * <h5 class='section'>Notes:</h5><ul> 2128 * <li class='note'>One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. 2129 * <li class='note'>This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. 2130 * <li class='note'>This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. 2131 * </ul> 2132 * 2133 * @param maxIdleTime New property value. 2134 * @param maxIdleTimeUnit New property value. 2135 * <br>Cannot be <jk>null</jk>. 2136 * @return This object. 2137 * @see HttpClientBuilder#evictIdleConnections(long,TimeUnit) 2138 */ 2139 public Builder evictIdleConnections(long maxIdleTime, TimeUnit maxIdleTimeUnit) { 2140 httpClientBuilder().evictIdleConnections(maxIdleTime, maxIdleTimeUnit); 2141 return this; 2142 } 2143 2144 /** 2145 * <i><l>RestClient</l> configuration property: </i> Executor service. 2146 * 2147 * <p> 2148 * Defines the executor service to use when calling future methods on the {@link RestRequest} class. 2149 * 2150 * <p> 2151 * This executor service is used to create {@link Future} objects on the following methods: 2152 * <ul> 2153 * <li class='jm'>{@link RestRequest#runFuture()} 2154 * <li class='jm'>{@link RestRequest#completeFuture()} 2155 * <li class='jm'>{@link ResponseContent#asFuture(Class)} (and similar methods) 2156 * </ul> 2157 * 2158 * <p> 2159 * The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout 2160 * and a queue size of 10. 2161 * 2162 * <h5 class='section'>Example:</h5> 2163 * <p class='bjava'> 2164 * <jc>// Create a client with a customized executor service.</jc> 2165 * RestClient <jv>client</jv> = RestClient 2166 * .<jsm>create</jsm>() 2167 * .executorService(<jk>new</jk> ThreadPoolExecutor(1, 1, 30, TimeUnit.<jsf>SECONDS</jsf>, <jk>new</jk> ArrayBlockingQueue<Runnable>(10)), <jk>true</jk>) 2168 * .build(); 2169 * 2170 * <jc>// Use it to asynchronously run a request.</jc> 2171 * Future<RestResponse> <jv>responseFuture</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).runFuture(); 2172 * 2173 * <jc>// Do some other stuff.</jc> 2174 * 2175 * <jc>// Now read the response.</jc> 2176 * String <jv>body</jv> = <jv>responseFuture</jv>.get().getContent().asString(); 2177 * 2178 * <jc>// Use it to asynchronously retrieve a response.</jc> 2179 * Future<MyBean> <jv>myBeanFuture</jv> = <jv>client</jv> 2180 * .get(<jsf>URI</jsf>) 2181 * .run() 2182 * .getContent().asFuture(MyBean.<jk>class</jk>); 2183 * 2184 * <jc>// Do some other stuff.</jc> 2185 * 2186 * <jc>// Now read the response.</jc> 2187 * MyBean <jv>bean</jv> = <jv>myBeanFuture</jv>.get(); 2188 * </p> 2189 * 2190 * @param executorService The executor service. 2191 * <br>Can be <jk>null</jk> (a default executor service will be created if needed). 2192 * @param shutdownOnClose Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called. 2193 * @return This object. 2194 */ 2195 public Builder executorService(ExecutorService executorService, boolean shutdownOnClose) { 2196 this.executorService = executorService; 2197 this.executorServiceShutdownOnClose = shutdownOnClose; 2198 return this; 2199 } 2200 2201 @Override /* Overridden from Builder */ 2202 public Builder findFluentSetters() { 2203 super.findFluentSetters(); 2204 return this; 2205 } 2206 2207 @Override /* Overridden from Builder */ 2208 public Builder findFluentSetters(Class<?> on) { 2209 super.findFluentSetters(on); 2210 return this; 2211 } 2212 2213 /** 2214 * Returns the builder for the list of form data parameters that get applied to all requests created by this builder. 2215 * 2216 * <p> 2217 * This is the primary method for accessing the form data parameter list. 2218 * On first call, the builder is created via the method {@link #createFormData()}. 2219 * 2220 * <h5 class='section'>Example:</h5> 2221 * <p class='bjava'> 2222 * <jc>// Create a client that adds a "foo=bar" form-data parameter on every request.</jc> 2223 * RestClient.Builder <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 2224 * <jv>builder</jv>.formData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 2225 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 2226 * </p> 2227 * 2228 * <p> 2229 * The following convenience methods are also provided for updating the parameters: 2230 * <ul> 2231 * <li class='jm'>{@link #formData(NameValuePair...)} 2232 * <li class='jm'>{@link #formDataDefault(NameValuePair...)} 2233 * <li class='jm'>{@link #formData(String,String)} 2234 * <li class='jm'>{@link #formData(String,Supplier)} 2235 * </ul> 2236 * 2237 * @return The form data list builder. 2238 */ 2239 public final PartList formData() { 2240 if (formData == null) 2241 formData = createFormData(); 2242 return formData; 2243 } 2244 2245 /** 2246 * Appends multiple form-data parameters to the request bodies of all URL-encoded form posts. 2247 * 2248 * <h5 class='section'>Example:</h5> 2249 * <p class='bjava'> 2250 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 2251 * 2252 * RestClient <jv>client</jv> = RestClient 2253 * .<jsm>create</jsm>() 2254 * .formData( 2255 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 2256 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 2257 * ) 2258 * .build(); 2259 * </p> 2260 * 2261 * <p> 2262 * This is a shortcut for calling <c>formData().append(<jv>parts</jv>)</c>. 2263 * 2264 * @param parts 2265 * The form-data parameters. 2266 * <br>Can contain <jk>null</jk> values (ignored). 2267 * @return This object. 2268 * @see #formData() 2269 */ 2270 public Builder formData(NameValuePair...parts) { 2271 formData().append(parts); 2272 return this; 2273 } 2274 2275 /** 2276 * Appends a form-data parameter to all request bodies. 2277 * 2278 * <h5 class='section'>Example:</h5> 2279 * <p class='bjava'> 2280 * RestClient <jv>client</jv> = RestClient 2281 * .<jsm>create</jsm>() 2282 * .formData(<js>"foo"</js>, <js>"bar"</js>) 2283 * .build(); 2284 * </p> 2285 * 2286 * <p> 2287 * This is a shortcut for calling <c>formData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2288 * 2289 * @param name The parameter name. 2290 * <br>Cannot be <jk>null</jk>. 2291 * @param value The parameter value. 2292 * <br>Can be <jk>null</jk> (null value will be serialized). 2293 * @return This object. 2294 * @see #formData() 2295 */ 2296 public Builder formData(String name, String value) { 2297 formData().append(name, value); 2298 return this; 2299 } 2300 2301 /** 2302 * Appends a form-data parameter with a dynamic value to all request bodies. 2303 * 2304 * <h5 class='section'>Example:</h5> 2305 * <p class='bjava'> 2306 * RestClient <jv>client</jv> = RestClient 2307 * .<jsm>create</jsm>() 2308 * .formData(<js>"foo"</js>, ()-><js>"bar"</js>) 2309 * .build(); 2310 * </p> 2311 * 2312 * <p> 2313 * This is a shortcut for calling <c>formData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2314 * 2315 * @param name The parameter name. 2316 * <br>Cannot be <jk>null</jk>. 2317 * @param value The parameter value supplier. 2318 * <br>Can be <jk>null</jk> (null value will be serialized). 2319 * @return This object. 2320 * @see #formData() 2321 */ 2322 public Builder formData(String name, Supplier<String> value) { 2323 formData().append(name, value); 2324 return this; 2325 } 2326 2327 /** 2328 * Sets default form-data parameter values. 2329 * 2330 * <p> 2331 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 2332 * 2333 * <h5 class='section'>Example:</h5> 2334 * <p class='bjava'> 2335 * RestClient <jv>client</jv> = RestClient 2336 * .<jsm>create</jsm>() 2337 * .formDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 2338 * .build(); 2339 * </p> 2340 * 2341 * <p> 2342 * This is a shortcut for calling <c>formData().setDefault(<jv>parts</jv>)</c>. 2343 * 2344 * @param parts The parts. 2345 * <br>Can contain <jk>null</jk> values (ignored). 2346 * @return This object. 2347 * @see #formData() 2348 */ 2349 public Builder formDataDefault(NameValuePair...parts) { 2350 formData().setDefault(parts); 2351 return this; 2352 } 2353 2354 /** 2355 * Returns the root URI defined for this client. 2356 * 2357 * <p> 2358 * Returns <jk>null</jk> in leu of an empty string. 2359 * Trailing slashes are trimmed. 2360 * 2361 * @return The root URI defined for this client. 2362 */ 2363 public String getRootUri() { return rootUrl; } 2364 2365 /** 2366 * Appends a header to all requests. 2367 * 2368 * <h5 class='section'>Example:</h5> 2369 * <p class='bjava'> 2370 * RestClient <jv>client</jv> = RestClient 2371 * .<jsm>create</jsm>() 2372 * .header(<js>"Foo"</js>, <js>"bar"</js>); 2373 * .build(); 2374 * </p> 2375 * 2376 * <p> 2377 * This is a shortcut for calling <c>headerData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2378 * 2379 * @param name The header name. 2380 * <br>Cannot be <jk>null</jk>. 2381 * @param value The header value. 2382 * <br>Can be <jk>null</jk> (null value will be serialized). 2383 * @return This object. 2384 * @see #headers() 2385 */ 2386 public Builder header(String name, String value) { 2387 headers().append(name, value); 2388 return this; 2389 } 2390 2391 /** 2392 * Appends a header to all requests using a dynamic value. 2393 * 2394 * <h5 class='section'>Example:</h5> 2395 * <p class='bjava'> 2396 * RestClient <jv>client</jv> = RestClient 2397 * .<jsm>create</jsm>() 2398 * .header(<js>"Foo"</js>, ()-><js>"bar"</js>); 2399 * .build(); 2400 * </p> 2401 * 2402 * <p> 2403 * This is a shortcut for calling <c>headerData().append(<jv>name</jv>,<jv>value</jv>)</c>. 2404 * 2405 * @param name The header name. 2406 * <br>Cannot be <jk>null</jk>. 2407 * @param value The header value supplier. 2408 * <br>Can be <jk>null</jk> (null value will be serialized). 2409 * @return This object. 2410 * @see #headers() 2411 */ 2412 public Builder header(String name, Supplier<String> value) { 2413 headers().append(name, value); 2414 return this; 2415 } 2416 2417 /** 2418 * Returns the builder for the list of headers that get applied to all requests created by this builder. 2419 * 2420 * <p> 2421 * This is the primary method for accessing the request header list. 2422 * On first call, the builder is created via the method {@link #createHeaderData()}. 2423 * 2424 * <h5 class='section'>Example:</h5> 2425 * <p class='bjava'> 2426 * <jc>// Create a client that adds a "Foo: bar" header on every request.</jc> 2427 * RestClient.Builder <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 2428 * <jv>builder</jv>.headerData().setDefault(<js>"Foo"</js>, <js>"bar"</js>)); 2429 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 2430 * </p> 2431 * 2432 * <p> 2433 * The following convenience methods are also provided for updating the headers: 2434 * <ul> 2435 * <li class='jm'>{@link #headers(Header...)} 2436 * <li class='jm'>{@link #headersDefault(Header...)} 2437 * <li class='jm'>{@link #header(String,String)} 2438 * <li class='jm'>{@link #header(String,Supplier)} 2439 * <li class='jm'>{@link #mediaType(String)} 2440 * <li class='jm'>{@link #mediaType(MediaType)} 2441 * <li class='jm'>{@link #accept(String)} 2442 * <li class='jm'>{@link #acceptCharset(String)} 2443 * <li class='jm'>{@link #clientVersion(String)} 2444 * <li class='jm'>{@link #contentType(String)} 2445 * <li class='jm'>{@link #debug()} 2446 * <li class='jm'>{@link #noTrace()} 2447 * </ul> 2448 * 2449 * @return The header list builder. 2450 */ 2451 public final HeaderList headers() { 2452 if (headerData == null) 2453 headerData = createHeaderData(); 2454 return headerData; 2455 } 2456 2457 /** 2458 * Appends multiple headers to all requests. 2459 * 2460 * <h5 class='section'>Example:</h5> 2461 * <p class='bjava'> 2462 * <jk>import static</jk> org.apache.juneau.http.HttpHeaders.*; 2463 * 2464 * RestClient <jv>client</jv> = RestClient 2465 * .<jsm>create</jsm>() 2466 * .headers( 2467 * <jsf>ACCEPT_TEXT_XML</jsf>, 2468 * <jsm>stringHeader</jsm>(<js>"Foo"</js>, <js>"bar"</js>) 2469 * ) 2470 * .build(); 2471 * </p> 2472 * 2473 * <p> 2474 * This is a shortcut for calling <c>headerData().append(<jv>parts</jv>)</c>. 2475 * 2476 * @param parts 2477 * The header to set. 2478 * <br>Can contain <jk>null</jk> values (ignored). 2479 * @return This object. 2480 * @see #headers() 2481 */ 2482 public Builder headers(Header...parts) { 2483 headers().append(parts); 2484 return this; 2485 } 2486 2487 /** 2488 * Sets default header values. 2489 * 2490 * <p> 2491 * Uses default values for specified headers if not otherwise specified on the outgoing requests. 2492 * 2493 * <h5 class='section'>Example:</h5> 2494 * <p class='bjava'> 2495 * RestClient <jv>client</jv> = RestClient 2496 * .<jsm>create</jsm>() 2497 * .headersDefault(<jsm>stringHeader</jsm>(<js>"Foo"</js>, ()-><js>"bar"</js>)); 2498 * .build(); 2499 * </p> 2500 * 2501 * <p> 2502 * This is a shortcut for calling <c>headerData().setDefault(<jv>parts</jv>)</c>. 2503 * 2504 * @param parts The header values. 2505 * <br>Can contain <jk>null</jk> values (ignored). 2506 * @return This object. 2507 * @see #headers() 2508 */ 2509 public Builder headersDefault(Header...parts) { 2510 headers().setDefault(parts); 2511 return this; 2512 } 2513 2514 /** 2515 * Convenience method for specifying HTML as the marshalling transmission media type. 2516 * 2517 * <p> 2518 * POJOs are converted to HTML without any sort of doc wrappers. 2519 * 2520 * <p> 2521 * {@link HtmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2522 * <ul> 2523 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 2524 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2525 * </ul> 2526 * <p> 2527 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2528 * <ul> 2529 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 2530 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2531 * </ul> 2532 * <p> 2533 * <c>Accept</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 * <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden 2537 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2538 * <p> 2539 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2540 * <ul> 2541 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2542 * last-enabled language if the headers are not set. 2543 * </ul> 2544 * <p> 2545 * Identical to calling <c>serializer(HtmlSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2546 * 2547 * <h5 class='section'>Example:</h5> 2548 * <p class='bjava'> 2549 * <jc>// Construct a client that uses HTML marshalling.</jc> 2550 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().html().build(); 2551 * </p> 2552 * 2553 * @return This object. 2554 */ 2555 public Builder html() { 2556 return serializer(HtmlSerializer.class).parser(HtmlParser.class); 2557 } 2558 2559 /** 2560 * Convenience method for specifying HTML DOC as the marshalling transmission media type. 2561 * 2562 * <p> 2563 * POJOs are converted to fully renderable HTML pages. 2564 * 2565 * <p> 2566 * {@link HtmlDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2567 * <ul> 2568 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()} or 2569 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2570 * </ul> 2571 * <p> 2572 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2573 * <ul> 2574 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 2575 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2576 * </ul> 2577 * <p> 2578 * <c>Accept</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 * <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden 2582 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2583 * <p> 2584 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2585 * <ul> 2586 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2587 * last-enabled language if the headers are not set. 2588 * </ul> 2589 * <p> 2590 * Identical to calling <c>serializer(HtmlDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2591 * 2592 * <h5 class='section'>Example:</h5> 2593 * <p class='bjava'> 2594 * <jc>// Construct a client that uses HTML Doc marshalling.</jc> 2595 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().htmlDoc().build(); 2596 * </p> 2597 * 2598 * @return This object. 2599 */ 2600 public Builder htmlDoc() { 2601 return serializer(HtmlDocSerializer.class).parser(HtmlParser.class); 2602 } 2603 2604 /** 2605 * Convenience method for specifying Stripped HTML DOC as the marshalling transmission media type. 2606 * 2607 * <p> 2608 * Same as {@link #htmlDoc()} but without the header and body tags and page title and description. 2609 * 2610 * <p> 2611 * {@link HtmlStrippedDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 2612 * <ul> 2613 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 2614 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2615 * </ul> 2616 * <p> 2617 * {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 2618 * <ul> 2619 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 2620 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 2621 * </ul> 2622 * <p> 2623 * <c>Accept</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 * <c>Content-Type</c> request header will be set to <js>"text/html+stripped"</js> unless overridden 2627 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 2628 * <p> 2629 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 2630 * <ul> 2631 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 2632 * last-enabled language if the headers are not set. 2633 * </ul> 2634 * <p> 2635 * Identical to calling <c>serializer(HtmlStrippedDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. 2636 * 2637 * <h5 class='section'>Example:</h5> 2638 * <p class='bjava'> 2639 * <jc>// Construct a client that uses HTML Stripped Doc marshalling.</jc> 2640 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().htmlStrippedDoc().build(); 2641 * </p> 2642 * 2643 * @return This object. 2644 */ 2645 public Builder htmlStrippedDoc() { 2646 return serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class); 2647 } 2648 2649 /** 2650 * Sets the {@link HttpClient} to be used to handle all HTTP communications with the target server. 2651 * 2652 * <p> 2653 * This can be used to bypass the client created by {@link #createHttpClient()} method. 2654 * 2655 * <h5 class='section'>Example:</h5> 2656 * <p class='bjava'> 2657 * <jc>// Construct a client that uses a customized HttpClient.</jc> 2658 * RestClient <jv>client</jv> = RestClient 2659 * .<jsm>create</jsm>() 2660 * .httpClient(HttpClientBuilder.<jsm>create</jsm>().build()) 2661 * .build(); 2662 * </p> 2663 * 2664 * @param value The {@link HttpClient} to be used to handle all HTTP communications with the target server. 2665 * <br>Can be <jk>null</jk> (a default client will be created). 2666 * @return This object. 2667 */ 2668 public Builder httpClient(CloseableHttpClient value) { 2669 httpClient = value; 2670 return this; 2671 } 2672 2673 /** 2674 * Returns the HTTP client builder. 2675 * 2676 * @return The HTTP client builder. 2677 */ 2678 public final HttpClientBuilder httpClientBuilder() { 2679 if (httpClientBuilder == null) 2680 httpClientBuilder = createHttpClientBuilder(); 2681 return httpClientBuilder; 2682 } 2683 2684 /** 2685 * Sets the {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. 2686 * 2687 * <p> 2688 * This can be used to bypass the builder created by {@link #createHttpClientBuilder()} method. 2689 * 2690 * <h5 class='section'>Example:</h5> 2691 * <p class='bjava'> 2692 * <jc>// Construct a client that uses a customized HttpClientBuilder.</jc> 2693 * RestClient <jv>client</jv> = RestClient 2694 * .<jsm>create</jsm>() 2695 * .httpClientBuilder(HttpClientBuilder.<jsm>create</jsm>()) 2696 * .build(); 2697 * </p> 2698 * 2699 * @param value The {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. 2700 * <br>Can be <jk>null</jk> (a default builder will be created). 2701 * @return This object. 2702 */ 2703 public Builder httpClientBuilder(HttpClientBuilder value) { 2704 httpClientBuilder = value; 2705 return this; 2706 } 2707 2708 /** 2709 * Assigns {@link HttpProcessor} instance. 2710 * 2711 * @param httpprocessor New property value. 2712 * @return This object. 2713 * @see HttpClientBuilder#setHttpProcessor(HttpProcessor) 2714 */ 2715 public Builder httpProcessor(HttpProcessor httpprocessor) { 2716 httpClientBuilder().setHttpProcessor(httpprocessor); 2717 return this; 2718 } 2719 2720 /** 2721 * Ignore errors. 2722 * 2723 * <p> 2724 * When enabled, HTTP error response codes (e.g. <l>>=400</l>) will not cause a {@link RestCallException} to 2725 * be thrown. 2726 * <p> 2727 * Note that this is equivalent to <c>builder.errorCodes(x -> <jk>false</jk>);</c> 2728 * 2729 * <h5 class='section'>Example:</h5> 2730 * <p class='bjava'> 2731 * <jc>// Create a client that doesn't throws a RestCallException when a 500 error occurs.</jc> 2732 * RestClient 2733 * .<jsm>create</jsm>() 2734 * .ignoreErrors() 2735 * .build() 2736 * .get(<js>"/error"</js>) <jc>// Throws a 500 error</jc> 2737 * .run() 2738 * .assertStatus().is(500); 2739 * </p> 2740 * 2741 * @return This object. 2742 */ 2743 public Builder ignoreErrors() { 2744 ignoreErrors = true; 2745 return this; 2746 } 2747 2748 @Override /* Overridden from Builder */ 2749 public Builder ignoreInvocationExceptionsOnGetters() { 2750 super.ignoreInvocationExceptionsOnGetters(); 2751 return this; 2752 } 2753 2754 @Override /* Overridden from Builder */ 2755 public Builder ignoreInvocationExceptionsOnSetters() { 2756 super.ignoreInvocationExceptionsOnSetters(); 2757 return this; 2758 } 2759 2760 /** 2761 * <i><l>BeanTraverse</l> configuration property: </i> Ignore recursion errors. 2762 * 2763 * <p> 2764 * When enabled, when we encounter the same object when traversing a tree, we set the value to <jk>null</jk>. 2765 * 2766 * <p> 2767 * For example, if a model contains the links A->B->C->A, then the JSON generated will look like 2768 * the following when <jsf>BEANTRAVERSE_ignoreRecursions</jsf> is <jk>true</jk>... 2769 * 2770 * <p class='bjson'> 2771 * {A:{B:{C:<jk>null</jk>}}} 2772 * </p> 2773 * 2774 * <h5 class='section'>Notes:</h5><ul> 2775 * <li class='note'> 2776 * Checking for recursion can cause a small performance penalty. 2777 * </ul> 2778 * 2779 * <h5 class='section'>Example:</h5> 2780 * <p class='bjava'> 2781 * <jc>// Create a JSON client that ignores recursions.</jc> 2782 * RestClient <jv>client</jv> = RestClient 2783 * .<jsm>create</jsm>() 2784 * .json() 2785 * .ignoreRecursions() 2786 * .build(); 2787 * 2788 * <jc>// Create a POJO model with a recursive loop.</jc> 2789 * <jk>public class</jk> A { 2790 * <jk>public</jk> Object <jf>f</jf>; 2791 * } 2792 * A <jv>a</jv> = <jk>new</jk> A(); 2793 * <jv>a</jv>.<jf>f</jf> = <jv>a</jv>; 2794 * 2795 * <jc>// Produces request body "{f:null}"</jc> 2796 * <jv>client</jv> 2797 * .post(<js>"http://localhost:10000/foo"</js>, <jv>a</jv>) 2798 * .run(); 2799 * </p> 2800 * 2801 * <h5 class='section'>See Also:</h5><ul> 2802 * <li class='jm'>{@link org.apache.juneau.BeanTraverseContext.Builder#ignoreRecursions()} 2803 * </ul> 2804 * 2805 * @return This object. 2806 */ 2807 public Builder ignoreRecursions() { 2808 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::ignoreRecursions); 2809 return this; 2810 } 2811 2812 @Override /* Overridden from Builder */ 2813 public Builder ignoreUnknownBeanProperties() { 2814 super.ignoreUnknownBeanProperties(); 2815 return this; 2816 } 2817 2818 @Override /* Overridden from Builder */ 2819 public Builder ignoreUnknownEnumValues() { 2820 super.ignoreUnknownEnumValues(); 2821 return this; 2822 } 2823 2824 @Override /* Overridden from Builder */ 2825 public Builder impl(Context value) { 2826 super.impl(value); 2827 return this; 2828 } 2829 2830 @Override /* Overridden from Builder */ 2831 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 2832 super.implClass(interfaceClass, implClass); 2833 return this; 2834 } 2835 2836 @Override /* Overridden from Builder */ 2837 public Builder implClasses(Map<Class<?>,Class<?>> values) { 2838 super.implClasses(values); 2839 return this; 2840 } 2841 2842 /** 2843 * <i><l>BeanTraverse</l> configuration property: </i> Initial depth. 2844 * 2845 * <p> 2846 * The initial indentation level at the root. 2847 * 2848 * <p> 2849 * Useful when constructing document fragments that need to be indented at a certain level when whitespace is enabled. 2850 * 2851 * <h5 class='section'>Example:</h5> 2852 * <p class='bjava'> 2853 * <jc>// Create a REST client with JSON serializer with whitespace enabled and an initial depth of 2.</jc> 2854 * RestClient <jv>client</jv> = RestClient 2855 * .<jsm>create</jsm>() 2856 * .json() 2857 * .ws() 2858 * .initialDepth(2) 2859 * .build(); 2860 * 2861 * <jc>// Our bean to serialize.</jc> 2862 * <jk>public class</jk> MyBean { 2863 * <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>; 2864 * } 2865 * 2866 * <jc>// Produces request body "\t\t{\n\t\t\t'foo':'bar'\n\t\t}\n"</jc> 2867 * <jv>client</jv> 2868 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 2869 * .run(); 2870 * </p> 2871 * 2872 * <h5 class='section'>See Also:</h5><ul> 2873 * <li class='jm'>{@link org.apache.juneau.BeanTraverseContext.Builder#initialDepth(int)} 2874 * </ul> 2875 * 2876 * @param value 2877 * The new value for this property. 2878 * <br>The default is <c>0</c>. 2879 * @return This object. 2880 */ 2881 public Builder initialDepth(int value) { 2882 serializers().forEach(x -> x.initialDepth(value)); 2883 return this; 2884 } 2885 2886 /** 2887 * <i><l>RestClient</l> configuration property: </i> Call interceptors. 2888 * 2889 * <p> 2890 * Adds an interceptor that can be called to hook into specified events in the lifecycle of a single request. 2891 * 2892 * <h5 class='section'>Example:</h5> 2893 * <p class='bjava'> 2894 * <jc>// Customized interceptor (note you can also extend from BasicRestCallInterceptor as well.</jc> 2895 * <jk>public class</jk> MyRestCallInterceptor <jk>implements</jk> RestCallInterceptor { 2896 * 2897 * <ja>@Override</ja> 2898 * <jk>public void</jk> onInit(RestRequest <jv>req</jv>) <jk>throws</jk> Exception { 2899 * <jc>// Intercept immediately after RestRequest object is created and all headers/query/form-data has been 2900 * // set on the request from the client.</jc> 2901 * } 2902 * 2903 * <ja>@Override</ja> 2904 * <jk>public void</jk> onConnect(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2905 * <jc>// Intercept immediately after an HTTP response has been received.</jc> 2906 * } 2907 * 2908 * <ja>@Override</ja> 2909 * <jk>public void</jk> onClose(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2910 * <jc>// Intercept when the response body is consumed.</jc> 2911 * } 2912 * } 2913 * 2914 * <jc>// Create a client with a customized interceptor.</jc> 2915 * RestClient <jv>client</jv> = RestClient 2916 * .<jsm>create</jsm>() 2917 * .interceptors(MyRestCallInterceptor.<jk>class</jk>) 2918 * .build(); 2919 * </p> 2920 * 2921 * <h5 class='section'>Notes:</h5><ul> 2922 * <li class='note'>The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and 2923 * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. 2924 * </ul> 2925 * 2926 * @param values 2927 * The values to add to this setting. 2928 * <br>Can be implementations of any of the following: 2929 * <ul> 2930 * <li class='jic'>{@link RestCallInterceptor} 2931 * <li class='jic'>{@link HttpRequestInterceptor} 2932 * <li class='jic'>{@link HttpResponseInterceptor} 2933 * </ul> 2934 * <br>Can contain <jk>null</jk> values (ignored). 2935 * @return This object. 2936 * @throws Exception If one or more interceptors could not be created. 2937 */ 2938 public Builder interceptors(Class<?>...values) throws Exception { 2939 for (var c : values) { 2940 if (c == null) 2941 continue; 2942 var ci = ClassInfo.of(c); 2943 if (nn(ci)) { 2944 if (ci.isChildOfAny(RestCallInterceptor.class, HttpRequestInterceptor.class, HttpResponseInterceptor.class)) 2945 interceptors(ci.newInstance()); 2946 else 2947 throw new ConfigException("Invalid class of type ''{0}'' passed to interceptors().", ci.getName()); 2948 } 2949 } 2950 return this; 2951 } 2952 2953 /** 2954 * Call interceptors. 2955 * 2956 * <p> 2957 * Adds an interceptor that gets called immediately after a connection is made. 2958 * 2959 * <h5 class='section'>Example:</h5> 2960 * <p class='bjava'> 2961 * <jc>// Create a client with a customized interceptor.</jc> 2962 * RestClient <jv>client</jv> = RestClient 2963 * .<jsm>create</jsm>() 2964 * .interceptors( 2965 * <jk>new</jk> RestCallInterceptor() { 2966 * 2967 * <ja>@Override</ja> 2968 * <jk>public void</jk> onInit(RestRequest <jv>req</jv>) <jk>throws</jk> Exception { 2969 * <jc>// Intercept immediately after RestRequest object is created and all headers/query/form-data has been 2970 * // set on the request from the client.</jc> 2971 * } 2972 * 2973 * <ja>@Override</ja> 2974 * <jk>public void</jk> onConnect(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2975 * <jc>// Intercept immediately after an HTTP response has been received.</jc> 2976 * } 2977 * 2978 * <ja>@Override</ja> 2979 * <jk>public void</jk> onClose(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { 2980 * <jc>// Intercept when the response body is consumed.</jc> 2981 * } 2982 * } 2983 * ) 2984 * .build(); 2985 * </p> 2986 * 2987 * <h5 class='section'>Notes:</h5><ul> 2988 * <li class='note'>The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and 2989 * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. 2990 * </ul> 2991 * 2992 * @param value 2993 * The values to add to this setting. 2994 * <br>Can be implementations of any of the following: 2995 * <ul> 2996 * <li class='jic'>{@link RestCallInterceptor} 2997 * <li class='jic'>{@link HttpRequestInterceptor} 2998 * <li class='jic'>{@link HttpResponseInterceptor} 2999 * </ul> 3000 * <br>Can contain <jk>null</jk> values (ignored). 3001 * @return This object. 3002 */ 3003 public Builder interceptors(Object...value) { 3004 List<RestCallInterceptor> l = list(); 3005 for (var o : value) { 3006 if (o == null) 3007 continue; 3008 ClassInfo ci = ClassInfo.of(o); 3009 if (nn(ci)) { 3010 if (! ci.isChildOfAny(HttpRequestInterceptor.class, HttpResponseInterceptor.class, RestCallInterceptor.class)) 3011 throw new ConfigException("Invalid object of type ''{0}'' passed to interceptors().", ci.getName()); 3012 if (o instanceof HttpRequestInterceptor o2) 3013 addInterceptorLast(o2); 3014 if (o instanceof HttpResponseInterceptor o3) 3015 addInterceptorLast(o3); 3016 if (o instanceof RestCallInterceptor o4) 3017 l.add(o4); 3018 } 3019 } 3020 if (interceptors == null) 3021 interceptors = l; 3022 else 3023 interceptors.addAll(0, l); 3024 return this; 3025 } 3026 3027 @Override /* Overridden from Builder */ 3028 public Builder interfaceClass(Class<?> on, Class<?> value) { 3029 super.interfaceClass(on, value); 3030 return this; 3031 } 3032 3033 @Override /* Overridden from Builder */ 3034 public Builder interfaces(java.lang.Class<?>...value) { 3035 super.interfaces(value); 3036 return this; 3037 } 3038 3039 /** 3040 * Convenience method for specifying JSON as the marshalling transmission media type. 3041 * 3042 * <p> 3043 * {@link JsonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3044 * <ul> 3045 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3046 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3047 * </ul> 3048 * <p> 3049 * {@link JsonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3050 * <ul> 3051 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 3052 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3053 * </ul> 3054 * <p> 3055 * <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden 3056 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}}. 3057 * <p> 3058 * <c>Content-Type</c> request header will be set to <js>"application/json"</js> unless overridden 3059 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3060 * <p> 3061 * Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. 3062 * <ul> 3063 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3064 * last-enabled language if the headers are not set. 3065 * </ul> 3066 * <p> 3067 * Identical to calling <c>serializer(JsonSerializer.<jk>class</jk>).parser(JsonParser.<jk>class</jk>)</c>. 3068 * 3069 * <h5 class='section'>Example:</h5> 3070 * <p class='bjava'> 3071 * <jc>// Construct a client that uses JSON marshalling.</jc> 3072 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().build(); 3073 * </p> 3074 * 3075 * @return This object. 3076 */ 3077 public Builder json() { 3078 return serializer(JsonSerializer.class).parser(JsonParser.class); 3079 } 3080 3081 /** 3082 * Convenience method for specifying Simplified JSON as the marshalling transmission media type. 3083 * 3084 * <p> 3085 * Simplified JSON is typically useful for automated tests because you can do simple string comparison of results 3086 * without having to escape lots of quotes. 3087 * 3088 * <p> 3089 * {@link Json5Serializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3090 * <ul> 3091 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3092 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3093 * </ul> 3094 * <p> 3095 * {@link Json5Parser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3096 * <ul> 3097 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 3098 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3099 * </ul> 3100 * <p> 3101 * <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden 3102 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3103 * <p> 3104 * <c>Content-Type</c> request header will be set to <js>"application/json5"</js> unless overridden 3105 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3106 * <p> 3107 * Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. 3108 * <ul> 3109 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3110 * last-enabled language if the headers are not set. 3111 * </ul> 3112 * <p> 3113 * Identical to calling <c>serializer(Json5Serializer.<jk>class</jk>).parser(Json5Parser.<jk>class</jk>)</c>. 3114 * 3115 * <h5 class='section'>Example:</h5> 3116 * <p class='bjava'> 3117 * <jc>// Construct a client that uses Simplified JSON marshalling.</jc> 3118 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json5().build(); 3119 * </p> 3120 * 3121 * @return This object. 3122 */ 3123 public Builder json5() { 3124 return serializer(Json5Serializer.class).parser(Json5Parser.class); 3125 } 3126 3127 /** 3128 * Assigns {@link ConnectionKeepAliveStrategy} instance. 3129 * 3130 * @param keepAliveStrategy New property value. 3131 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 3132 * @return This object. 3133 * @see HttpClientBuilder#setKeepAliveStrategy(ConnectionKeepAliveStrategy) 3134 */ 3135 public Builder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { 3136 httpClientBuilder().setKeepAliveStrategy(keepAliveStrategy); 3137 return this; 3138 } 3139 3140 /** 3141 * <i><l>RestClient</l> configuration property: </i> Keep HttpClient open. 3142 * 3143 * <p> 3144 * Don't close this client when the {@link RestClient#close()} method is called. 3145 * 3146 * <h5 class='section'>Example:</h5> 3147 * <p class='bjava'> 3148 * <jc>// Create a client with a customized client and don't close the client service.</jc> 3149 * RestClient <jv>client</jv> = RestClient 3150 * .<jsm>create</jsm>() 3151 * .httpClient(<jv>myHttpClient</jv>) 3152 * .keepHttpClientOpen() 3153 * .build(); 3154 * 3155 * <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> 3156 * </p> 3157 * 3158 * @return This object. 3159 */ 3160 public Builder keepHttpClientOpen() { 3161 keepHttpClientOpen = true; 3162 return this; 3163 } 3164 3165 /** 3166 * <i><l>Serializer</l> configuration property: </i> Don't trim null bean property values. 3167 * 3168 * <p> 3169 * When enabled, null bean values will be serialized to the output. 3170 * 3171 * <h5 class='section'>Notes:</h5><ul> 3172 * <li class='note'>Not enabling this setting will cause <c>Map</c>s with <jk>null</jk> values to be lost during parsing. 3173 * </ul> 3174 * 3175 * <h5 class='section'>Example:</h5> 3176 * <p class='bjava'> 3177 * <jc>// Create a REST client with JSON serializer that serializes null properties.</jc> 3178 * RestClient <jv>client</jv> = RestClient 3179 * .<jsm>create</jsm>() 3180 * .json() 3181 * .keepNullProperties() 3182 * .build(); 3183 * 3184 * <jc>// Our bean to serialize.</jc> 3185 * <jk>public class</jk> MyBean { 3186 * <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>; 3187 * } 3188 * 3189 * <jc>// Request body will contain: {foo:null}</jc> 3190 * <jv>client</jv> 3191 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 3192 * .run(); 3193 * </p> 3194 * 3195 * <h5 class='section'>See Also:</h5><ul> 3196 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#keepNullProperties()} 3197 * </ul> 3198 * 3199 * @return This object. 3200 */ 3201 public Builder keepNullProperties() { 3202 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::keepNullProperties); 3203 return this; 3204 } 3205 3206 @Override /* Overridden from Builder */ 3207 public Builder locale(Locale value) { 3208 super.locale(value); 3209 return this; 3210 } 3211 3212 /** 3213 * Logger. 3214 * 3215 * <p> 3216 * Specifies the logger to use for logging. 3217 * 3218 * <p> 3219 * If not specified, uses the following logger: 3220 * <p class='bjava'> 3221 * Logger.<jsm>getLogger</jsm>(RestClient.<jk>class</jk>.getName()); 3222 * </p> 3223 * 3224 * <h5 class='section'>Example:</h5> 3225 * <p class='bjava'> 3226 * <jc>// Construct a client that logs messages to a special logger.</jc> 3227 * RestClient <jv>client</jv> = RestClient 3228 * .<jsm>create</jsm>() 3229 * .logger(Logger.<jsm>getLogger</jsm>(<js>"MyLogger"</js>)) <jc>// Log to MyLogger logger.</jc> 3230 * .logToConsole() <jc>// Also log to console.</jc> 3231 * .logRequests(<jsf>FULL</jsf>, <jsf>WARNING</jsf>) <jc>// Log requests with full detail at WARNING level.</jc> 3232 * .build(); 3233 * </p> 3234 * 3235 * @param value The logger to use for logging. 3236 * <br>Can be <jk>null</jk> (defaults to <c>Logger.getLogger(RestClient.class.getName())</c>). 3237 * @return This object. 3238 */ 3239 public Builder logger(Logger value) { 3240 logger = value; 3241 return this; 3242 } 3243 3244 /** 3245 * Log requests. 3246 * 3247 * <p> 3248 * Causes requests/responses to be logged at the specified log level at the end of the request. 3249 * 3250 * <p> 3251 * <jsf>SIMPLE</jsf> detail produces a log message like the following: 3252 * <p class='bconsole'> 3253 * POST http://localhost:10000/testUrl, HTTP/1.1 200 OK 3254 * </p> 3255 * 3256 * <p> 3257 * <jsf>FULL</jsf> detail produces a log message like the following: 3258 * <p class='bconsole'> 3259 * === HTTP Call (outgoing) ======================================================= 3260 * === REQUEST === 3261 * POST http://localhost:10000/testUrl 3262 * ---request headers--- 3263 * Debug: true 3264 * No-Trace: true 3265 * Accept: application/json 3266 * ---request entity--- 3267 * Content-Type: application/json 3268 * ---request content--- 3269 * {"foo":"bar","baz":123} 3270 * === RESPONSE === 3271 * HTTP/1.1 200 OK 3272 * ---response headers--- 3273 * Content-Type: application/json;charset=utf-8 3274 * Content-Length: 21 3275 * Server: Jetty(8.1.0.v20120127) 3276 * ---response content--- 3277 * {"message":"OK then"} 3278 * === END ======================================================================== 3279 * </p> 3280 * 3281 * <p> 3282 * By default, the message is logged to the default logger. It can be logged to a different logger via the 3283 * {@link #logger(Logger)} method or logged to the console using the 3284 * {@link #logToConsole()} method. 3285 * 3286 * @param detail The detail level of logging. 3287 * @param level The log level. 3288 * @param test A predicate to use per-request to see if the request should be logged. If <jk>null</jk>, always logs. 3289 * @return This object. 3290 */ 3291 public Builder logRequests(DetailLevel detail, Level level, BiPredicate<RestRequest,RestResponse> test) { 3292 logRequests = detail; 3293 logRequestsLevel = level; 3294 logRequestsPredicate = test; 3295 return this; 3296 } 3297 3298 /** 3299 * Log to console. 3300 * 3301 * <p> 3302 * Specifies to log messages to the console. 3303 * 3304 * <h5 class='section'>Example:</h5> 3305 * <p class='bjava'> 3306 * <jc>// Construct a client that logs messages to a special logger.</jc> 3307 * RestClient <jv>client</jv> = RestClient 3308 * .<jsm>create</jsm>() 3309 * .logToConsole() 3310 * .logRequests(<jsf>FULL</jsf>, <jsf>INFO</jsf>) <jc>// Level is ignored when logging to console.</jc> 3311 * .build(); 3312 * </p> 3313 * 3314 * @return This object. 3315 */ 3316 public Builder logToConsole() { 3317 logToConsole = true; 3318 return this; 3319 } 3320 3321 /** 3322 * <i><l>RestClient</l> configuration property: </i> Marshaller 3323 * 3324 * <p> 3325 * Shortcut for specifying the serializers and parsers 3326 * using the serializer and parser defined in a marshaller. 3327 * 3328 * <h5 class='section'>Notes:</h5><ul> 3329 * <li class='note'>When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), 3330 * parser property setters (e.g. {@link #strict()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. 3331 * </ul> 3332 * 3333 * <h5 class='section'>Example:</h5> 3334 * <p class='bjava'> 3335 * <jc>// Create a client that uses Simplified-JSON transport using an existing marshaller.</jc> 3336 * RestClient <jv>client</jv> = RestClient 3337 * .<jsm>create</jsm>() 3338 * .marshaller(Json5.<jsf>DEFAULT_READABLE</jsf>) 3339 * .build(); 3340 * </p> 3341 * 3342 * @param value The values to add to this setting. 3343 * <br>Can be <jk>null</jk> (value will not be set, existing serializers and parsers will be kept). 3344 * @return This object. 3345 */ 3346 public Builder marshaller(Marshaller value) { 3347 if (nn(value)) 3348 serializer(value.getSerializer()).parser(value.getParser()); 3349 return this; 3350 } 3351 3352 /** 3353 * <i><l>RestClient</l> configuration property: </i> Marshalls 3354 * 3355 * <p> 3356 * Shortcut for specifying the serializers and parsers 3357 * using the serializer and parser defined in a marshaller. 3358 * 3359 * <h5 class='section'>Notes:</h5><ul> 3360 * <li class='note'>When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), 3361 * parser property setters (e.g. {@link #strict()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. 3362 * </ul> 3363 * 3364 * <h5 class='section'>Example:</h5> 3365 * <p class='bjava'> 3366 * <jc>// Create a client that uses JSON and XML transport using existing marshalls.</jc> 3367 * RestClient <jv>client</jv> = RestClient 3368 * .<jsm>create</jsm>() 3369 * .marshaller(Json.<jsf>DEFAULT_READABLE</jsf>, Xml.<jsf>DEFAULT_READABLE</jsf>) 3370 * .build(); 3371 * </p> 3372 * 3373 * @param value The values to add to this setting. 3374 * <br>Can contain <jk>null</jk> values (ignored). 3375 * @return This object. 3376 */ 3377 public Builder marshallers(Marshaller...value) { 3378 for (var m : value) 3379 if (nn(m)) 3380 serializer(m.getSerializer()).parser(m.getParser()); 3381 return this; 3382 } 3383 3384 /** 3385 * Assigns maximum connection per route value. 3386 * 3387 * <h5 class='section'>Notes:</h5><ul> 3388 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 3389 * </ul> 3390 * 3391 * @param maxConnPerRoute New property value. 3392 * @return This object. 3393 * @see HttpClientBuilder#setMaxConnPerRoute(int) 3394 */ 3395 public Builder maxConnPerRoute(int maxConnPerRoute) { 3396 httpClientBuilder().setMaxConnPerRoute(maxConnPerRoute); 3397 return this; 3398 } 3399 3400 /** 3401 * Assigns maximum total connection value. 3402 * 3403 * <h5 class='section'>Notes:</h5><ul> 3404 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 3405 * </ul> 3406 * 3407 * @param maxConnTotal New property value. 3408 * @return This object. 3409 * @see HttpClientBuilder#setMaxConnTotal(int) 3410 */ 3411 public Builder maxConnTotal(int maxConnTotal) { 3412 httpClientBuilder().setMaxConnTotal(maxConnTotal); 3413 return this; 3414 } 3415 3416 /** 3417 * <i><l>BeanTraverse</l> configuration property: </i> Max serialization depth. 3418 * 3419 * <p> 3420 * When enabled, abort traversal if specified depth is reached in the POJO tree. 3421 * 3422 * <p> 3423 * If this depth is exceeded, an exception is thrown. 3424 * 3425 * <p> 3426 * This prevents stack overflows from occurring when trying to traverse models with recursive references. 3427 * 3428 * <h5 class='section'>Example:</h5> 3429 * <p class='bjava'> 3430 * <jc>// Create a REST client with JSON serializer that throws an exception if the depth reaches greater than 20.</jc> 3431 * RestClient <jv>client</jv> = RestClient 3432 * .<jsm>create</jsm>() 3433 * .json() 3434 * .maxDepth(20) 3435 * .build(); 3436 * </p> 3437 * 3438 * <h5 class='section'>See Also:</h5><ul> 3439 * <li class='jm'>{@link org.apache.juneau.BeanTraverseContext.Builder#maxDepth(int)} 3440 * </ul> 3441 * 3442 * @param value 3443 * The new value for this property. 3444 * <br>The default is <c>100</c>. 3445 * @return This object. 3446 */ 3447 public Builder maxDepth(int value) { 3448 serializers().forEach(x -> x.maxDepth(value)); 3449 return this; 3450 } 3451 3452 /** 3453 * <i><l>WriterSerializer</l> configuration property: </i> Maximum indentation. 3454 * 3455 * <p> 3456 * Specifies the maximum indentation level in the serialized document. 3457 * 3458 * <h5 class='section'>Notes:</h5><ul> 3459 * <li class='note'>This setting does not apply to the RDF serializers. 3460 * </ul> 3461 * 3462 * <h5 class='section'>Example:</h5> 3463 * <p class='bjava'> 3464 * <jc>// Create a REST client with JSON serializer that indents a maximum of 20 tabs.</jc> 3465 * RestClient <jv>client</jv> = RestClient 3466 * .<jsm>create</jsm>() 3467 * .json() 3468 * .ws() <jc>// Enable whitespace</jc> 3469 * .maxIndent(20) 3470 * .build(); 3471 * </p> 3472 * 3473 * <h5 class='section'>See Also:</h5><ul> 3474 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#maxIndent(int)} 3475 * </ul> 3476 * 3477 * @param value 3478 * The new value for this property. 3479 * <br>The default is <c>100</c>. 3480 * @return This object. 3481 */ 3482 public Builder maxIndent(int value) { 3483 serializers().forEachWS(x -> x.maxIndent(value)); 3484 return this; 3485 } 3486 3487 /** 3488 * Appends the <c>Accept</c> and <c>Content-Type</c> headers on all requests made by this client. 3489 * 3490 * <p> 3491 * Headers are appended to the end of the current header list. 3492 * 3493 * <p> 3494 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>), ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 3495 * 3496 * @param value The new header values. 3497 * <br>Can be <jk>null</jk> (no Accept or Content-Type headers will be added). 3498 * @return This object. 3499 * @see #headers() 3500 */ 3501 @Override 3502 public Builder mediaType(MediaType value) { 3503 super.mediaType(value); 3504 return headers(Accept.of(value), ContentType.of(value)); 3505 } 3506 3507 /** 3508 * Appends the <c>Accept</c> and <c>Content-Type</c> headers on all requests made by this client. 3509 * 3510 * <p> 3511 * Headers are appended to the end of the current header list. 3512 * 3513 * <p> 3514 * This is a shortcut for calling <c>headerData().append(Accept.<jsm>of</jsm>(<jv>value</jv>), ContentType.<jsm>of</jsm>(<jv>value</jv>))</c>. 3515 * 3516 * @param value The new header values. 3517 * <br>Can be <jk>null</jk> (no Accept or Content-Type headers will be added). 3518 * @return This object. 3519 * @see #headers() 3520 */ 3521 public Builder mediaType(String value) { 3522 super.mediaType(MediaType.of(value)); 3523 return headers(Accept.of(value), ContentType.of(value)); 3524 } 3525 3526 /** 3527 * Convenience method for specifying MessagePack as the marshalling transmission media type. 3528 * 3529 * <p> 3530 * MessagePack is a binary equivalent to JSON that takes up considerably less space than JSON. 3531 * 3532 * <p> 3533 * {@link MsgPackSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3534 * <ul> 3535 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3536 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3537 * </ul> 3538 * <p> 3539 * {@link MsgPackParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3540 * <ul> 3541 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 3542 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3543 * </ul> 3544 * <p> 3545 * <c>Accept</c> request header will be set to <js>"octal/msgpack"</js> unless overridden 3546 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3547 * <p> 3548 * <c>Content-Type</c> request header will be set to <js>"octal/msgpack"</js> unless overridden 3549 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3550 * <p> 3551 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 3552 * <ul> 3553 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3554 * last-enabled language if the headers are not set. 3555 * </ul> 3556 * <p> 3557 * Identical to calling <c>serializer(MsgPackSerializer.<jk>class</jk>).parser(MsgPackParser.<jk>class</jk>)</c>. 3558 * 3559 * <h5 class='section'>Example:</h5> 3560 * <p class='bjava'> 3561 * <jc>// Construct a client that uses MessagePack marshalling.</jc> 3562 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().msgPack().build(); 3563 * </p> 3564 * 3565 * @return This object. 3566 */ 3567 public Builder msgPack() { 3568 return serializer(MsgPackSerializer.class).parser(MsgPackParser.class); 3569 } 3570 3571 @Override /* Overridden from Builder */ 3572 public Builder notBeanClasses(java.lang.Class<?>...values) { 3573 super.notBeanClasses(values); 3574 return this; 3575 } 3576 3577 @Override /* Overridden from Builder */ 3578 public Builder notBeanPackages(String...values) { 3579 super.notBeanPackages(values); 3580 return this; 3581 } 3582 3583 /** 3584 * When called, <c>No-Trace: true</c> is added to requests. 3585 * 3586 * <p> 3587 * This gives the opportunity for the servlet to not log errors on invalid requests. 3588 * This is useful for testing purposes when you don't want your log file to show lots of errors that are simply the 3589 * results of testing. 3590 * 3591 * <p> 3592 * It's up to the server to decide whether to allow for this. 3593 * The <c>BasicTestRestLogger</c> class watches for this header and prevents logging of status 400+ responses to 3594 * prevent needless logging of test scenarios. 3595 * 3596 * @return This object. 3597 * @see #headers() 3598 */ 3599 public Builder noTrace() { 3600 return headers(NoTrace.of(true)); 3601 } 3602 3603 /** 3604 * <i><l>OpenApiCommon</l> configuration property: </i> Default collection format for HTTP parts. 3605 * 3606 * <p> 3607 * Specifies the collection format to use for HTTP parts when not otherwise specified via {@link org.apache.juneau.annotation.Schema#collectionFormat()} for the 3608 * OpenAPI serializer and parser on this client. 3609 * 3610 * <h5 class='section'>Example:</h5> 3611 * <p class='bjava'> 3612 * <jc>// Create a REST client with CSV format for http parts.</jc> 3613 * RestClient <jv>client</jv> = RestClient 3614 * .<jsm>create</jsm>() 3615 * .collectionFormat(<jsf>CSV</jsf>) 3616 * .build(); 3617 * 3618 * <jc>// An arbitrary data structure.</jc> 3619 * AList <jv>list</jv> = AList.<jsm>of</jsm>( 3620 * <js>"foo"</js>, 3621 * <js>"bar"</js>, 3622 * AMap.<jsm>of</jsm>( 3623 * <js>"baz"</js>, AList.<jsm>of</jsm>(<js>"qux"</js>,<js>"true"</js>,<js>"123"</js>) 3624 * ) 3625 * ); 3626 * 3627 * <jc>// Set a header with a comma-separated list.</jc> 3628 * <jv>client</jv> 3629 * .get(<js>"/uri"</js>) 3630 * .header(<js>"Foo"</js>, <jv>list</jv>) <jc>// Will be serialized as: foo=bar,baz=qux\,true\,123</jc> 3631 * .run(); 3632 * </p> 3633 * 3634 * <ul class='values javatree'> 3635 * <li class='jc'>{@link HttpPartCollectionFormat} 3636 * <ul> 3637 * <li class='jf'>{@link HttpPartCollectionFormat#CSV CSV} - (default) Comma-separated values (e.g. <js>"foo,bar"</js>). 3638 * <li class='jf'>{@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. <js>"foo bar"</js>). 3639 * <li class='jf'>{@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. <js>"foo\tbar"</js>). 3640 * <li class='jf'>{@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. <js>"foo|bar"</js>). 3641 * <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>). 3642 * <li class='jf'>{@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. <js>"@(foo,bar)"</js>). 3643 * </ul> 3644 * </ul> 3645 * 3646 * <h5 class='section'>See Also:</h5><ul> 3647 * <li class='jm'>{@link org.apache.juneau.oapi.OpenApiSerializer.Builder#collectionFormat(HttpPartCollectionFormat)} 3648 * <li class='jm'>{@link org.apache.juneau.oapi.OpenApiParser.Builder#collectionFormat(HttpPartCollectionFormat)} 3649 * </ul> 3650 * 3651 * @param value 3652 * The new value for this property. 3653 * <br>The default value is {@link HttpPartCollectionFormat#NO_COLLECTION_FORMAT}. 3654 * <br>Cannot be <jk>null</jk>. 3655 * @return This object. 3656 */ 3657 public Builder oapiCollectionFormat(HttpPartCollectionFormat value) { 3658 serializers().forEach(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); 3659 parsers().forEach(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); 3660 partSerializer().builder(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); 3661 partParser().builder(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); 3662 return this; 3663 } 3664 3665 /** 3666 * <i><l>OpenApiCommon</l> configuration property: </i> Default OpenAPI format for HTTP parts. 3667 * 3668 * <p> 3669 * Specifies the format to use for HTTP parts when not otherwise specified via {@link org.apache.juneau.annotation.Schema#format()} for 3670 * the OpenAPI serializer and parser on this client. 3671 * 3672 * <h5 class='section'>Example:</h5> 3673 * <p class='bjava'> 3674 * <jc>// Create a REST client with UON part serialization and parsing.</jc> 3675 * RestClient <jv>client</jv> = RestClient 3676 * .<jsm>create</jsm>() 3677 * .oapiFormat(<jsf>UON</jsf>) 3678 * .build(); 3679 * 3680 * <jc>// Set a header with a value in UON format.</jc> 3681 * <jv>client</jv> 3682 * .get(<js>"/uri"</js>) 3683 * .header(<js>"Foo"</js>, <js>"bar baz"</js>) <jc>// Will be serialized as: 'bar baz'</jc> 3684 * .run(); 3685 * </p> 3686 * 3687 * <ul class='values javatree'> 3688 * <li class='jc'>{@link org.apache.juneau.httppart.HttpPartFormat} 3689 * <ul> 3690 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#UON UON} - UON notation (e.g. <js>"'foo bar'"</js>). 3691 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#INT32 INT32} - Signed 32 bits. 3692 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#INT64 INT64} - Signed 64 bits. 3693 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number. 3694 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number. 3695 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters. 3696 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. <js>"00FF"</js>). 3697 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Spaced-separated hexadecimal encoded octets (e.g. <js>"00 FF"</js>). 3698 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#DATE DATE} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 full-date</a>. 3699 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#DATE_TIME DATE_TIME} - An <a href='http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14'>RFC3339 date-time</a>. 3700 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured. 3701 * <li class='jf'>{@link org.apache.juneau.httppart.HttpPartFormat#NO_FORMAT NO_FORMAT} - (default) Not specified. 3702 * </ul> 3703 * </ul> 3704 * 3705 * <h5 class='section'>See Also:</h5><ul> 3706 * <li class='jm'>{@link org.apache.juneau.oapi.OpenApiSerializer.Builder#format(HttpPartFormat)} 3707 * <li class='jm'>{@link org.apache.juneau.oapi.OpenApiParser.Builder#format(HttpPartFormat)} 3708 * </ul> 3709 * 3710 * @param value 3711 * The new value for this property. 3712 * <br>The default value is {@link HttpPartFormat#NO_FORMAT}. 3713 * <br>Cannot be <jk>null</jk>. 3714 * @return This object. 3715 */ 3716 public Builder oapiFormat(HttpPartFormat value) { 3717 serializers().forEach(OpenApiSerializer.Builder.class, x -> x.format(value)); 3718 parsers().forEach(OpenApiParser.Builder.class, x -> x.format(value)); 3719 partSerializer().builder(OpenApiSerializer.Builder.class).ifPresent(x -> x.format(value)); 3720 partParser().builder(OpenApiParser.Builder.class).ifPresent(x -> x.format(value)); 3721 return this; 3722 } 3723 3724 /** 3725 * Convenience method for specifying OpenAPI as the marshalling transmission media type. 3726 * 3727 * <p> 3728 * OpenAPI is a language that allows serialization to formats that use {@link HttpPartSchema} objects to describe their structure. 3729 * 3730 * <p> 3731 * {@link OpenApiSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 3732 * <ul> 3733 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 3734 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3735 * <li>Typically the {@link RestRequest#content(Object, HttpPartSchema)} method will be used to specify the body of the request with the 3736 * schema describing it's structure. 3737 * </ul> 3738 * <p> 3739 * {@link OpenApiParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 3740 * <ul> 3741 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 3742 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3743 * <li>Typically the {@link ResponseContent#schema(HttpPartSchema)} method will be used to specify the structure of the response body. 3744 * </ul> 3745 * <p> 3746 * <c>Accept</c> request header will be set to <js>"text/openapi"</js> unless overridden 3747 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3748 * <p> 3749 * <c>Content-Type</c> request header will be set to <js>"text/openapi"</js> unless overridden 3750 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 3751 * <p> 3752 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 3753 * <ul> 3754 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 3755 * last-enabled language if the headers are not set. 3756 * </ul> 3757 * <p> 3758 * Identical to calling <c>serializer(OpenApiSerializer.<jk>class</jk>).parser(OpenApiParser.<jk>class</jk>)</c>. 3759 * 3760 * <h5 class='section'>Example:</h5> 3761 * <p class='bjava'> 3762 * <jc>// Construct a client that uses OpenAPI marshalling.</jc> 3763 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().openApi().build(); 3764 * </p> 3765 * 3766 * @return This object. 3767 */ 3768 public Builder openApi() { 3769 return serializer(OpenApiSerializer.class).parser(OpenApiParser.class); 3770 } 3771 3772 /** 3773 * <i><l>UonSerializer</l> configuration property: </i> Parameter format. 3774 * 3775 * <p> 3776 * Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. 3777 * 3778 * <p> 3779 * Specifies the format to use for GET parameter keys and values. 3780 * 3781 * <h5 class='section'>Example:</h5> 3782 * <p class='bjava'> 3783 * <jc>// Create a REST client with URL-Encoded serializer that serializes values in plain-text format.</jc> 3784 * RestClient <jv>client</jv> = RestClient 3785 * .<jsm>create</jsm>() 3786 * .urlEnc() 3787 * .paramFormat(<jsf>PLAINTEXT</jsf>) 3788 * .build(); 3789 * 3790 * <jc>// An arbitrary data structure.</jc> 3791 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>( 3792 * <js>"foo"</js>, <js>"bar"</js>, 3793 * <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>} 3794 * ); 3795 * 3796 * <jc>// Request body will be serialized as: foo=bar,baz=qux,true,123</jc> 3797 * <jv>client</jv> 3798 * .post(<js>"/uri"</js>, <jv>map</jv>) 3799 * .run(); 3800 * </p> 3801 * 3802 * <ul class='values javatree'> 3803 * <li class='jf'>{@link ParamFormat#UON} (default) - Use UON notation for parameters. 3804 * <li class='jf'>{@link ParamFormat#PLAINTEXT} - Use plain text for parameters. 3805 * </ul> 3806 * 3807 * <h5 class='section'>See Also:</h5><ul> 3808 * <li class='jm'>{@link org.apache.juneau.uon.UonSerializer.Builder#paramFormat(ParamFormat)} 3809 * </ul> 3810 * 3811 * @param value The new value for this property. 3812 * <br>Cannot be <jk>null</jk>. 3813 * @return This object. 3814 */ 3815 public Builder paramFormat(ParamFormat value) { 3816 serializers().forEach(UonSerializer.Builder.class, x -> x.paramFormat(value)); 3817 return this; 3818 } 3819 3820 /** 3821 * <i><l>UonSerializer</l> configuration property: </i> Parameter format. 3822 * 3823 * <p> 3824 * Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. 3825 * 3826 * <p> 3827 * Specifies plaintext as the format to use for GET parameter keys and values. 3828 * 3829 * <h5 class='section'>Example:</h5> 3830 * <p class='bjava'> 3831 * <jc>// Create a REST client with URL-Encoded serializer that serializes values in plain-text format.</jc> 3832 * RestClient <jv>client</jv> = RestClient 3833 * .<jsm>create</jsm>() 3834 * .urlEnc() 3835 * .build(); 3836 * 3837 * <jc>// An arbitrary data structure.</jc> 3838 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>( 3839 * <js>"foo"</js>, <js>"bar"</js>, 3840 * <js>"baz"</js>, <jk>new</jk> String[]{<js>"qux"</js>, <js>"true"</js>, <js>"123"</js>} 3841 * ); 3842 * 3843 * <jc>// Request body will be serialized as: foo=bar,baz=qux,true,123</jc> 3844 * <jv>client</jv> 3845 * .post(<js>"/uri"</js>, <jv>map</jv>) 3846 * .run(); 3847 * </p> 3848 * 3849 * <h5 class='section'>See Also:</h5><ul> 3850 * <li class='jm'>{@link org.apache.juneau.uon.UonSerializer.Builder#paramFormatPlain()} 3851 * </ul> 3852 * 3853 * @return This object. 3854 */ 3855 public Builder paramFormatPlain() { 3856 serializers().forEach(UonSerializer.Builder.class, org.apache.juneau.uon.UonSerializer.Builder::paramFormatPlain); 3857 return this; 3858 } 3859 3860 /** 3861 * Parser. 3862 * 3863 * <p> 3864 * Associates the specified {@link Parser Parser} with the HTTP client. 3865 * 3866 * <p> 3867 * The parser is used to parse the HTTP response body into a POJO. 3868 * 3869 * <h5 class='section'>Notes:</h5><ul> 3870 * <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 #strict()}) or 3871 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3872 * </ul> 3873 * 3874 * <h5 class='section'>Example:</h5> 3875 * <p class='bjava'> 3876 * <jc>// Create a client that uses JSON transport for response bodies.</jc> 3877 * RestClient <jv>client</jv> = RestClient 3878 * .<jsm>create</jsm>() 3879 * .parser(JsonParser.<jk>class</jk>) 3880 * .strict() <jc>// Enable strict mode on JsonParser.</jc> 3881 * .build(); 3882 * </p> 3883 * 3884 * @param value 3885 * The new value for this setting. 3886 * <br>The default value is {@link JsonParser#DEFAULT}. 3887 * @return This object. 3888 */ 3889 @SuppressWarnings("unchecked") 3890 public Builder parser(Class<? extends Parser> value) { 3891 return parsers(value); 3892 } 3893 3894 /** 3895 * Parser. 3896 * 3897 * <p> 3898 * Associates the specified {@link Parser Parser} with the HTTP client. 3899 * 3900 * <p> 3901 * The parser is used to parse the HTTP response body into a POJO. 3902 * 3903 * <h5 class='section'>Notes:</h5><ul> 3904 * <li class='note'>When using this method that takes in a pre-instantiated parser, the parser property setters (e.g. {@link #strict()}) or 3905 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 3906 * on this builder class have no effect. 3907 * </ul> 3908 * 3909 * <h5 class='section'>Example:</h5> 3910 * <p class='bjava'> 3911 * <jc>// Create a client that uses a predefined JSON parser for response bodies.</jc> 3912 * RestClient <jv>client</jv> = RestClient 3913 * .<jsm>create</jsm>() 3914 * .parser(JsonParser.<jsf>DEFAULT_STRICT</jsf>) 3915 * .build(); 3916 * </p> 3917 * 3918 * @param value 3919 * The new value for this setting. 3920 * <br>The default value is {@link JsonParser#DEFAULT}. 3921 * <br>Cannot be <jk>null</jk>. 3922 * @return This object. 3923 */ 3924 public Builder parser(Parser value) { 3925 return parsers(value); 3926 } 3927 3928 /** 3929 * Returns the parser group sub-builder. 3930 * 3931 * @return The parser group sub-builder. 3932 */ 3933 public final ParserSet.Builder parsers() { 3934 if (parsers == null) 3935 parsers = createParsers(); 3936 return parsers; 3937 } 3938 3939 /** 3940 * Parsers. 3941 * 3942 * <p> 3943 * Associates the specified {@link Parser Parsers} with the HTTP client. 3944 * 3945 * <p> 3946 * The parsers are used to parse the HTTP response body into a POJO. 3947 * 3948 * <p> 3949 * The parser that best matches the <c>Accept</c> header will be used to parse the response body. 3950 * <br>If no <c>Accept</c> header is specified, the first parser in the list will be used. 3951 * 3952 * <h5 class='section'>Notes:</h5><ul> 3953 * <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 #strict()}) or 3954 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 3955 * </ul> 3956 * 3957 * <h5 class='section'>Example:</h5> 3958 * <p class='bjava'> 3959 * <jc>// Create a client that uses JSON and XML transport for response bodies.</jc> 3960 * RestClient <jv>client</jv> = RestClient 3961 * .<jsm>create</jsm>() 3962 * .parser(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>) 3963 * .strict() <jc>// Enable strict mode on parsers.</jc> 3964 * .build(); 3965 * </p> 3966 * 3967 * @param value 3968 * The new value for this setting. 3969 * <br>The default value is {@link JsonParser#DEFAULT}. 3970 * @return This object. 3971 */ 3972 @SuppressWarnings("unchecked") 3973 public Builder parsers(Class<? extends Parser>...value) { 3974 assertArgNoNulls("value", value); 3975 parsers().add(value); 3976 return this; 3977 } 3978 3979 /** 3980 * Parsers. 3981 * 3982 * <p> 3983 * Associates the specified {@link Parser Parsers} with the HTTP client. 3984 * 3985 * <p> 3986 * The parsers are used to parse the HTTP response body into a POJO. 3987 * 3988 * <p> 3989 * The parser that best matches the <c>Accept</c> header will be used to parse the response body. 3990 * <br>If no <c>Accept</c> header is specified, the first parser in the list will be used. 3991 * 3992 * <h5 class='section'>Notes:</h5><ul> 3993 * <li class='note'>When using this method that takes in pre-instantiated parsers, the parser property setters (e.g. {@link #strict()}) or 3994 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 3995 * on this builder class have no effect. 3996 * </ul> 3997 * 3998 * <h5 class='section'>Example:</h5> 3999 * <p class='bjava'> 4000 * <jc>// Create a client that uses JSON and XML transport for response bodies.</jc> 4001 * RestClient <jv>client</jv> = RestClient 4002 * .<jsm>create</jsm>() 4003 * .parser(JsonParser.<jsf>DEFAULT_STRICT</jsf>, XmlParser.<jsf>DEFAULT</jsf>) 4004 * .build(); 4005 * </p> 4006 * 4007 * @param value 4008 * The new value for this setting. 4009 * <br>The default value is {@link JsonParser#DEFAULT}. 4010 * @return This object. 4011 */ 4012 public Builder parsers(Parser...value) { 4013 assertArgNoNulls("value", value); 4014 parsers().add(value); 4015 return this; 4016 } 4017 4018 /** 4019 * Returns the part parser sub-builder. 4020 * 4021 * @return The part parser sub-builder. 4022 */ 4023 public final HttpPartParser.Creator partParser() { 4024 if (partParser == null) 4025 partParser = createPartParser(); 4026 return partParser; 4027 } 4028 4029 /** 4030 * Part parser. 4031 * 4032 * <p> 4033 * The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. 4034 * 4035 * <p> 4036 * The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. 4037 * 4038 * <h5 class='section'>Example:</h5> 4039 * <p class='bjava'> 4040 * <jc>// Create a client that uses UON format by default for incoming HTTP parts.</jc> 4041 * RestClient <jv>client</jv> = RestClient 4042 * .<jsm>create</jsm>() 4043 * .partParser(UonParser.<jk>class</jk>) 4044 * .build(); 4045 * </p> 4046 * 4047 * @param value 4048 * The new value for this setting. 4049 * <br>The default value is {@link OpenApiParser}. 4050 * <br>Cannot be <jk>null</jk>. 4051 * @return This object. 4052 */ 4053 public Builder partParser(Class<? extends HttpPartParser> value) { 4054 partParser().type(assertArgNotNull("value", value)); 4055 return this; 4056 } 4057 4058 /** 4059 * Part parser. 4060 * 4061 * <p> 4062 * The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. 4063 * 4064 * <p> 4065 * The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. 4066 * 4067 * <h5 class='section'>Example:</h5> 4068 * <p class='bjava'> 4069 * <jc>// Create a client that uses UON format by default for incoming HTTP parts.</jc> 4070 * RestClient <jv>client</jv> = RestClient 4071 * .<jsm>create</jsm>() 4072 * .partParser(UonParser.<jsf>DEFAULT</jsf>) 4073 * .build(); 4074 * </p> 4075 * 4076 * @param value 4077 * The new value for this setting. 4078 * <br>The default value is {@link OpenApiParser}. 4079 * <br>Cannot be <jk>null</jk>. 4080 * @return This object. 4081 */ 4082 public Builder partParser(HttpPartParser value) { 4083 partParser().impl(assertArgNotNull("value", value)); 4084 return this; 4085 } 4086 4087 /** 4088 * Returns the part serializer sub-builder. 4089 * 4090 * @return The part serializer sub-builder. 4091 */ 4092 public final HttpPartSerializer.Creator partSerializer() { 4093 if (partSerializer == null) 4094 partSerializer = createPartSerializer(); 4095 return partSerializer; 4096 } 4097 4098 /** 4099 * Part serializer. 4100 * 4101 * <p> 4102 * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. 4103 * 4104 * <p> 4105 * The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. 4106 * 4107 * <h5 class='section'>Example:</h5> 4108 * <p class='bjava'> 4109 * <jc>// Create a client that uses UON format by default for outgoing HTTP parts.</jc> 4110 * RestClient <jv>client</jv> = RestClient 4111 * .<jsm>create</jsm>() 4112 * .partSerializer(UonSerializer.<jk>class</jk>) 4113 * .build(); 4114 * </p> 4115 * 4116 * @param value 4117 * The new value for this setting. 4118 * <br>The default value is {@link OpenApiSerializer}. 4119 * <br>Cannot be <jk>null</jk>. 4120 * @return This object. 4121 */ 4122 public Builder partSerializer(Class<? extends HttpPartSerializer> value) { 4123 partSerializer().type(assertArgNotNull("value", value)); 4124 return this; 4125 } 4126 4127 /** 4128 * Part serializer. 4129 * 4130 * <p> 4131 * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. 4132 * 4133 * <p> 4134 * The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. 4135 * 4136 * <h5 class='section'>Example:</h5> 4137 * <p class='bjava'> 4138 * <jc>// Create a client that uses UON format by default for outgoing HTTP parts.</jc> 4139 * RestClient <jv>client</jv> = RestClient 4140 * .<jsm>create</jsm>() 4141 * .partSerializer(UonSerializer.<jsf>DEFAULT</jsf>) 4142 * .build(); 4143 * </p> 4144 * 4145 * @param value 4146 * The new value for this setting. 4147 * <br>The default value is {@link OpenApiSerializer}. 4148 * <br>Cannot be <jk>null</jk>. 4149 * @return This object. 4150 */ 4151 public Builder partSerializer(HttpPartSerializer value) { 4152 partSerializer().impl(assertArgNotNull("value", value)); 4153 return this; 4154 } 4155 4156 /** 4157 * Returns the builder for the list of path data parameters that get applied to all requests created by this builder. 4158 * 4159 * <p> 4160 * This is the primary method for accessing the path data parameter list. 4161 * On first call, the builder is created via the method {@link #createFormData()}. 4162 * 4163 * <h5 class='section'>Example:</h5> 4164 * <p class='bjava'> 4165 * <jc>// Create a client that uses "bar" for the "{foo}" path variable on every request.</jc> 4166 * RestClient.Builder <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 4167 * <jv>builder</jv>.pathData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 4168 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 4169 * </p> 4170 * 4171 * <p> 4172 * The following convenience methods are also provided for updating the parameters: 4173 * <ul> 4174 * <li class='jm'>{@link #pathData(NameValuePair...)} 4175 * <li class='jm'>{@link #pathDataDefault(NameValuePair...)} 4176 * <li class='jm'>{@link #pathData(String,String)} 4177 * <li class='jm'>{@link #pathData(String,Supplier)} 4178 * </ul> 4179 * 4180 * @return The form data list builder. 4181 */ 4182 public final PartList pathData() { 4183 if (pathData == null) 4184 pathData = createPathData(); 4185 return pathData; 4186 } 4187 4188 /** 4189 * Sets multiple path parameters on all requests. 4190 * 4191 * <h5 class='section'>Example:</h5> 4192 * <p class='bjava'> 4193 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 4194 * 4195 * RestClient <jv>client</jv> = RestClient 4196 * .<jsm>create</jsm>() 4197 * .pathData( 4198 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 4199 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 4200 * ) 4201 * .build(); 4202 * </p> 4203 * 4204 * <p> 4205 * This is a shortcut for calling <c>pathData().append(<jv>parts</jv>)</c>. 4206 * 4207 * @param parts 4208 * The path parameters. 4209 * <br>Can contain <jk>null</jk> values (ignored). 4210 * @return This object. 4211 * @see #pathData() 4212 */ 4213 public Builder pathData(NameValuePair...parts) { 4214 pathData().append(parts); 4215 return this; 4216 } 4217 4218 /** 4219 * Appends a path parameter to all request bodies. 4220 * 4221 * <h5 class='section'>Example:</h5> 4222 * <p class='bjava'> 4223 * RestClient <jv>client</jv> = RestClient 4224 * .<jsm>create</jsm>() 4225 * .pathData(<js>"foo"</js>, <js>"bar"</js>) 4226 * .build(); 4227 * </p> 4228 * 4229 * <p> 4230 * This is a shortcut for calling <c>pathData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4231 * 4232 * @param name The parameter name. 4233 * <br>Cannot be <jk>null</jk>. 4234 * @param value The parameter value. 4235 * <br>Can be <jk>null</jk> (null value will be serialized). 4236 * @return This object. 4237 * @see #pathData() 4238 */ 4239 public Builder pathData(String name, String value) { 4240 pathData().append(name, value); 4241 return this; 4242 } 4243 4244 /** 4245 * Sets a path parameter with a dynamic value to all request bodies. 4246 * 4247 * <h5 class='section'>Example:</h5> 4248 * <p class='bjava'> 4249 * RestClient <jv>client</jv> = RestClient 4250 * .<jsm>create</jsm>() 4251 * .pathData(<js>"foo"</js>, ()-><js>"bar"</js>) 4252 * .build(); 4253 * </p> 4254 * 4255 * <p> 4256 * This is a shortcut for calling <c>pathData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4257 * 4258 * @param name The parameter name. 4259 * <br>Cannot be <jk>null</jk>. 4260 * @param value The parameter value supplier. 4261 * <br>Can be <jk>null</jk> (null value will be serialized). 4262 * @return This object. 4263 * @see #pathData() 4264 */ 4265 public Builder pathData(String name, Supplier<String> value) { 4266 pathData().set(name, value); 4267 return this; 4268 } 4269 4270 /** 4271 * Sets default path parameter values. 4272 * 4273 * <p> 4274 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 4275 * 4276 * <h5 class='section'>Example:</h5> 4277 * <p class='bjava'> 4278 * RestClient <jv>client</jv> = RestClient 4279 * .<jsm>create</jsm>() 4280 * .pathDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 4281 * .build(); 4282 * </p> 4283 * 4284 * <p> 4285 * This is a shortcut for calling <c>pathData().setDefault(<jv>parts</jv>)</c>. 4286 * 4287 * @param parts The parts. 4288 * <br>Can contain <jk>null</jk> values (ignored). 4289 * @return This object. 4290 * @see #pathData() 4291 */ 4292 public Builder pathDataDefault(NameValuePair...parts) { 4293 pathData().setDefault(parts); 4294 return this; 4295 } 4296 4297 /** 4298 * Convenience method for specifying Plain Text as the marshalling transmission media type. 4299 * 4300 * <p> 4301 * Plain text marshalling typically only works on simple POJOs that can be converted to and from strings using 4302 * swaps, swap methods, etc... 4303 * 4304 * <p> 4305 * {@link PlainTextSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 4306 * <ul> 4307 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 4308 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4309 * </ul> 4310 * <p> 4311 * {@link PlainTextParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 4312 * <ul> 4313 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 4314 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4315 * </ul> 4316 * <p> 4317 * <c>Accept</c> request header will be set to <js>"text/plain"</js> unless overridden 4318 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 4319 * <p> 4320 * <c>Content-Type</c> request header will be set to <js>"text/plain"</js> unless overridden 4321 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 4322 * <p> 4323 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 4324 * <ul> 4325 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 4326 * last-enabled language if the headers are not set. 4327 * </ul> 4328 * <p> 4329 * Identical to calling <c>serializer(PlainTextSerializer.<jk>class</jk>).parser(PlainTextParser.<jk>class</jk>)</c>. 4330 * 4331 * <h5 class='section'>Example:</h5> 4332 * <p class='bjava'> 4333 * <jc>// Construct a client that uses Plain Text marshalling.</jc> 4334 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().plainText().build(); 4335 * </p> 4336 * 4337 * @return This object. 4338 */ 4339 public Builder plainText() { 4340 return serializer(PlainTextSerializer.class).parser(PlainTextParser.class); 4341 } 4342 4343 /** 4344 * When called, the {@link #createConnectionManager()} method will return a {@link PoolingHttpClientConnectionManager} 4345 * instead of a {@link BasicHttpClientConnectionManager}. 4346 * 4347 * <h5 class='section'>Example:</h5> 4348 * <p class='bjava'> 4349 * <jc>// Construct a client that uses pooled connections.</jc> 4350 * RestClient <jv>client</jv> = RestClient 4351 * .<jsm>create</jsm>() 4352 * .pooled() 4353 * .build(); 4354 * </p> 4355 * 4356 * @return This object. 4357 */ 4358 public Builder pooled() { 4359 this.pooled = true; 4360 return this; 4361 } 4362 4363 @Override /* Overridden from Builder */ 4364 public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) { 4365 super.propertyNamer(on, value); 4366 return this; 4367 } 4368 4369 @Override /* Overridden from Builder */ 4370 public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) { 4371 super.propertyNamer(value); 4372 return this; 4373 } 4374 4375 /** 4376 * Assigns default proxy value. 4377 * 4378 * <h5 class='section'>Notes:</h5><ul> 4379 * <li class='note'>This value can be overridden by the {@link #routePlanner(HttpRoutePlanner)} method. 4380 * </ul> 4381 * 4382 * @param proxy New property value. 4383 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4384 * @return This object. 4385 * @see HttpClientBuilder#setProxy(HttpHost) 4386 */ 4387 public Builder proxy(HttpHost proxy) { 4388 httpClientBuilder().setProxy(proxy); 4389 return this; 4390 } 4391 4392 /** 4393 * Assigns {@link AuthenticationStrategy} instance for proxy authentication. 4394 * 4395 * @param proxyAuthStrategy New property value. 4396 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4397 * @return This object. 4398 * @see HttpClientBuilder#setProxyAuthenticationStrategy(AuthenticationStrategy) 4399 */ 4400 public Builder proxyAuthenticationStrategy(AuthenticationStrategy proxyAuthStrategy) { 4401 httpClientBuilder().setProxyAuthenticationStrategy(proxyAuthStrategy); 4402 return this; 4403 } 4404 4405 /** 4406 * Assigns file containing public suffix matcher. 4407 * 4408 * <h5 class='section'>Notes:</h5><ul> 4409 * <li class='note'>Instances of this class can be created with {@link PublicSuffixMatcherLoader}. 4410 * </ul> 4411 * 4412 * @param publicSuffixMatcher New property value. 4413 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4414 * @return This object. 4415 * @see HttpClientBuilder#setPublicSuffixMatcher(PublicSuffixMatcher) 4416 */ 4417 public Builder publicSuffixMatcher(PublicSuffixMatcher publicSuffixMatcher) { 4418 httpClientBuilder().setPublicSuffixMatcher(publicSuffixMatcher); 4419 return this; 4420 } 4421 4422 /** 4423 * Returns the builder for the list of query parameters that get applied to all requests created by this builder. 4424 * 4425 * <p> 4426 * This is the primary method for accessing the query parameter list. 4427 * On first call, the builder is created via the method {@link #createQueryData()}. 4428 * 4429 * <h5 class='section'>Example:</h5> 4430 * <p class='bjava'> 4431 * <jc>// Create a client that adds a "foo=bar" query parameter on every request.</jc> 4432 * RestClient.Builder <jv>builder</jv> = RestClient.<jsm>create</jsm>(); 4433 * <jv>builder</jv>.queryData().setDefault(<js>"foo"</js>, <js>"bar"</js>)); 4434 * RestClient <jv>client</jv> = <jv>builder</jv>.build(); 4435 * </p> 4436 * 4437 * <p> 4438 * The following convenience methods are also provided for updating the parameters: 4439 * <ul> 4440 * <li class='jm'>{@link #queryData(NameValuePair...)} 4441 * <li class='jm'>{@link #queryDataDefault(NameValuePair...)} 4442 * <li class='jm'>{@link #queryData(String,String)} 4443 * <li class='jm'>{@link #queryData(String,Supplier)} 4444 * </ul> 4445 * 4446 * @return The query data list builder. 4447 */ 4448 public final PartList queryData() { 4449 if (queryData == null) 4450 queryData = createQueryData(); 4451 return queryData; 4452 } 4453 4454 /** 4455 * Appends multiple query parameters to the URI of all requests. 4456 * 4457 * <h5 class='section'>Example:</h5> 4458 * <p class='bjava'> 4459 * <jk>import static</jk> org.apache.juneau.http.HttpParts.*; 4460 * 4461 * RestClient <jv>client</jv> = RestClient 4462 * .<jsm>create</jsm>() 4463 * .queryData( 4464 * <jsm>stringPart</jsm>(<js>"foo"</js>, <js>"bar"</js>), 4465 * <jsm>booleanPart</jsm>(<js>"baz"</js>, <jk>true</jk>) 4466 * ) 4467 * .build(); 4468 * </p> 4469 * 4470 * <p> 4471 * This is a shortcut for calling <c>queryData().append(<jv>parts</jv>)</c>. 4472 * 4473 * @param parts 4474 * The query parameters. 4475 * <br>Can contain <jk>null</jk> values (ignored). 4476 * @return This object. 4477 * @see #queryData() 4478 */ 4479 public Builder queryData(NameValuePair...parts) { 4480 queryData().append(parts); 4481 return this; 4482 } 4483 4484 /** 4485 * Appends a query parameter to the URI. 4486 * 4487 * <h5 class='section'>Example:</h5> 4488 * <p class='bjava'> 4489 * RestClient <jv>client</jv> = RestClient 4490 * .<jsm>create</jsm>() 4491 * .queryData(<js>"foo"</js>, <js>"bar"</js>) 4492 * .build(); 4493 * </p> 4494 * 4495 * <p> 4496 * This is a shortcut for calling <c>queryData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4497 * 4498 * @param name The parameter name. 4499 * <br>Cannot be <jk>null</jk>. 4500 * @param value The parameter value. 4501 * <br>Can be <jk>null</jk> (null value will be serialized). 4502 * @return This object. 4503 * @see #queryData() 4504 */ 4505 public Builder queryData(String name, String value) { 4506 queryData().append(name, value); 4507 return this; 4508 } 4509 4510 /** 4511 * Appends a query parameter with a dynamic value to the URI. 4512 * 4513 * <h5 class='section'>Example:</h5> 4514 * <p class='bjava'> 4515 * RestClient <jv>client</jv> = RestClient 4516 * .<jsm>create</jsm>() 4517 * .queryData(<js>"foo"</js>, ()-><js>"bar"</js>) 4518 * .build(); 4519 * </p> 4520 * 4521 * <p> 4522 * This is a shortcut for calling <c>queryData().append(<jv>name</jv>,<jv>value</jv>)</c>. 4523 * 4524 * @param name The parameter name. 4525 * <br>Cannot be <jk>null</jk>. 4526 * @param value The parameter value supplier. 4527 * <br>Can be <jk>null</jk> (null value will be serialized). 4528 * @return This object. 4529 * @see #queryData() 4530 */ 4531 public Builder queryData(String name, Supplier<String> value) { 4532 queryData().append(name, value); 4533 return this; 4534 } 4535 4536 /** 4537 * Sets default query parameter values. 4538 * 4539 * <p> 4540 * Uses default values for specified parameters if not otherwise specified on the outgoing requests. 4541 * 4542 * <h5 class='section'>Example:</h5> 4543 * <p class='bjava'> 4544 * RestClient <jv>client</jv> = RestClient 4545 * .<jsm>create</jsm>() 4546 * .queryDataDefault(<jsm>stringPart</jsm>(<js>"foo"</js>, ()-><js>"bar"</js>)); 4547 * .build(); 4548 * </p> 4549 * 4550 * <p> 4551 * This is a shortcut for calling <c>queryData().setDefault(<jv>parts</jv>)</c>. 4552 * 4553 * @param parts The parts. 4554 * <br>Can contain <jk>null</jk> values (ignored). 4555 * @return This object. 4556 * @see #queryData() 4557 */ 4558 public Builder queryDataDefault(NameValuePair...parts) { 4559 queryData().setDefault(parts); 4560 return this; 4561 } 4562 4563 /** 4564 * <i><l>WriterSerializer</l> configuration property: </i> Quote character. 4565 * 4566 * <p> 4567 * Specifies the character to use for quoting attributes and values. 4568 * 4569 * <h5 class='section'>Notes:</h5><ul> 4570 * <li class='note'>This setting does not apply to the RDF serializers. 4571 * </ul> 4572 * 4573 * <h5 class='section'>Example:</h5> 4574 * <p class='bjava'> 4575 * <jc>// Create a REST client with JSON serializer that uses single quotes.</jc> 4576 * RestClient <jv>client</jv> = RestClient 4577 * .<jsm>create</jsm>() 4578 * .json() 4579 * .quoteChar(<js>'\''</js>) 4580 * .build(); 4581 * 4582 * <jc>// A bean with a single property</jc> 4583 * <jk>public class</jk> MyBean { 4584 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 4585 * } 4586 * 4587 * <jc>// Request body will contain: {'foo':'bar'}</jc> 4588 * <jv>client</jv> 4589 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 4590 * .run(); 4591 * </p> 4592 * 4593 * <h5 class='section'>See Also:</h5><ul> 4594 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#quoteChar(char)} 4595 * </ul> 4596 * 4597 * @param value 4598 * The new value for this property. 4599 * <br>The default is <js>'"'</js>. 4600 * @return This object. 4601 */ 4602 public Builder quoteChar(char value) { 4603 serializers().forEachWS(x -> x.quoteChar(value)); 4604 return this; 4605 } 4606 4607 /** 4608 * Assigns {@link RedirectStrategy} instance. 4609 * 4610 * <h5 class='section'>Notes:</h5><ul> 4611 * <li class='note'>This value can be overridden by the {@link #disableRedirectHandling()} method. 4612 * </ul> 4613 * 4614 * @param redirectStrategy New property value. 4615 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4616 * @return This object. 4617 * @see HttpClientBuilder#setRedirectStrategy(RedirectStrategy) 4618 */ 4619 public Builder redirectStrategy(RedirectStrategy redirectStrategy) { 4620 httpClientBuilder().setRedirectStrategy(redirectStrategy); 4621 return this; 4622 } 4623 4624 /** 4625 * Assigns {@link HttpRequestExecutor} instance. 4626 * 4627 * @param requestExec New property value. 4628 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4629 * @return This object. 4630 * @see HttpClientBuilder#setRequestExecutor(HttpRequestExecutor) 4631 */ 4632 public Builder requestExecutor(HttpRequestExecutor requestExec) { 4633 httpClientBuilder().setRequestExecutor(requestExec); 4634 return this; 4635 } 4636 4637 /** 4638 * Assigns {@link HttpRequestRetryHandler} instance. 4639 * 4640 * <h5 class='section'>Notes:</h5><ul> 4641 * <li class='note'>This value can be overridden by the {@link #disableAutomaticRetries()} method. 4642 * </ul> 4643 * 4644 * @param retryHandler New property value. 4645 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4646 * @return This object. 4647 * @see HttpClientBuilder#setRetryHandler(HttpRequestRetryHandler) 4648 */ 4649 public Builder retryHandler(HttpRequestRetryHandler retryHandler) { 4650 httpClientBuilder().setRetryHandler(retryHandler); 4651 return this; 4652 } 4653 4654 /** 4655 * <i><l>RestClient</l> configuration property: </i> Root URI. 4656 * 4657 * <p> 4658 * When set, relative URI strings passed in through the various rest call methods (e.g. {@link RestClient#get(Object)} 4659 * will be prefixed with the specified root. 4660 * <br>This root URI is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URI string. 4661 * 4662 * <h5 class='section'>Example:</h5> 4663 * <p class='bjava'> 4664 * <jc>// Create a client that uses UON format by default for HTTP parts.</jc> 4665 * RestClient <jv>client</jv> = RestClient 4666 * .<jsm>create</jsm>() 4667 * .rootUrl(<js>"http://localhost:10000/foo"</js>) 4668 * .build(); 4669 * 4670 * Bar <jv>bar</jv> = <jv>client</jv> 4671 * .get(<js>"/bar"</js>) <jc>// Relative to http://localhost:10000/foo</jc> 4672 * .run() 4673 * .getContent().as(Bar.<jk>class</jk>); 4674 * </p> 4675 * 4676 * @param value 4677 * The root URI to prefix to relative URI strings. 4678 * <br>Trailing slashes are trimmed. 4679 * <br>Usually a <c>String</c> but you can also pass in <c>URI</c> and <c>URL</c> objects as well. 4680 * <br>Can be <jk>null</jk> (no root URL will be set). 4681 * @return This object. 4682 */ 4683 public Builder rootUrl(Object value) { 4684 var s = s(value); 4685 if (ne(s)) 4686 s = s.replaceAll("\\/$", ""); 4687 if (isEmpty(s)) 4688 rootUrl = null; 4689 else if (s.indexOf("://") == -1) 4690 throw rex("Invalid rootUrl value: ''{0}''. Must be a valid absolute URL.", value); 4691 else 4692 rootUrl = s; 4693 return this; 4694 } 4695 4696 /** 4697 * Assigns {@link HttpRoutePlanner} instance. 4698 * 4699 * @param routePlanner New property value. 4700 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4701 * @return This object. 4702 * @see HttpClientBuilder#setRoutePlanner(HttpRoutePlanner) 4703 */ 4704 public Builder routePlanner(HttpRoutePlanner routePlanner) { 4705 httpClientBuilder().setRoutePlanner(routePlanner); 4706 return this; 4707 } 4708 4709 /** 4710 * Assigns {@link SchemePortResolver} instance. 4711 * 4712 * @param schemePortResolver New property value. 4713 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4714 * @return This object. 4715 * @see HttpClientBuilder#setSchemePortResolver(SchemePortResolver) 4716 */ 4717 public Builder schemePortResolver(SchemePortResolver schemePortResolver) { 4718 httpClientBuilder().setSchemePortResolver(schemePortResolver); 4719 return this; 4720 } 4721 4722 /** 4723 * Serializer. 4724 * 4725 * <p> 4726 * Associates the specified {@link Serializer Serializer} with the HTTP client. 4727 * 4728 * <p> 4729 * The serializer is used to serialize POJOs into the HTTP request body. 4730 * 4731 * <h5 class='section'>Notes:</h5><ul> 4732 * <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 4733 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4734 * </ul> 4735 * 4736 * <h5 class='section'>Example:</h5> 4737 * <p class='bjava'> 4738 * <jc>// Create a client that uses JSON transport for request bodies.</jc> 4739 * RestClient <jv>client</jv> = RestClient 4740 * .<jsm>create</jsm>() 4741 * .serializer(JsonSerializer.<jk>class</jk>) 4742 * .sortCollections() <jc>// Sort any collections being serialized.</jc> 4743 * .build(); 4744 * </p> 4745 * 4746 * @param value 4747 * The new value for this setting. 4748 * <br>The default is {@link JsonSerializer}. 4749 * @return This object. 4750 */ 4751 @SuppressWarnings("unchecked") 4752 public Builder serializer(Class<? extends Serializer> value) { 4753 return serializers(value); 4754 } 4755 4756 /** 4757 * Serializer. 4758 * 4759 * <p> 4760 * Associates the specified {@link Serializer Serializer} with the HTTP client. 4761 * 4762 * <p> 4763 * The serializer is used to serialize POJOs into the HTTP request body. 4764 * 4765 * <h5 class='section'>Notes:</h5><ul> 4766 * <li class='note'>When using this method that takes in a pre-instantiated serializer, the serializer property setters (e.g. {@link #sortCollections()}) or 4767 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 4768 * on this builder class have no effect. 4769 * </ul> 4770 * 4771 * <h5 class='section'>Example:</h5> 4772 * <p class='bjava'> 4773 * <jc>// Create a client that uses a predefined JSON serializer request bodies.</jc> 4774 * RestClient <jv>client</jv> = RestClient 4775 * .<jsm>create</jsm>() 4776 * .serializer(JsonSerializer.<jsf>DEFAULT_READABLE</jsf>) 4777 * .build(); 4778 * </p> 4779 * 4780 * @param value 4781 * The new value for this setting. 4782 * <br>The default is {@link JsonSerializer}. 4783 * <br>Cannot be <jk>null</jk>. 4784 * @return This object. 4785 */ 4786 public Builder serializer(Serializer value) { 4787 return serializers(value); 4788 } 4789 4790 /** 4791 * Returns the serializer group sub-builder. 4792 * 4793 * @return The serializer group sub-builder. 4794 */ 4795 public final SerializerSet.Builder serializers() { 4796 if (serializers == null) 4797 serializers = createSerializers(); 4798 return serializers; 4799 } 4800 4801 /** 4802 * Serializers. 4803 * 4804 * <p> 4805 * Associates the specified {@link Serializer Serializers} with the HTTP client. 4806 * 4807 * <p> 4808 * The serializer is used to serialize POJOs into the HTTP request body. 4809 * 4810 * <p> 4811 * The serializer that best matches the <c>Content-Type</c> header will be used to serialize the request body. 4812 * <br>If no <c>Content-Type</c> header is specified, the first serializer in the list will be used. 4813 * 4814 * <h5 class='section'>Notes:</h5><ul> 4815 * <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 4816 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 4817 * </ul> 4818 * 4819 * <h5 class='section'>Example:</h5> 4820 * <p class='bjava'> 4821 * <jc>// Create a client that uses JSON and XML transport for request bodies.</jc> 4822 * RestClient <jv>client</jv> = RestClient 4823 * .<jsm>create</jsm>() 4824 * .serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>) 4825 * .sortCollections() <jc>// Sort any collections being serialized.</jc> 4826 * .build(); 4827 * </p> 4828 * 4829 * @param value 4830 * The new value for this setting. 4831 * <br>The default is {@link JsonSerializer}. 4832 * @return This object. 4833 */ 4834 @SuppressWarnings("unchecked") 4835 public Builder serializers(Class<? extends Serializer>...value) { 4836 assertArgNoNulls("value", value); 4837 serializers().add(value); 4838 return this; 4839 } 4840 4841 /** 4842 * Serializers. 4843 * 4844 * <p> 4845 * Associates the specified {@link Serializer Serializers} with the HTTP client. 4846 * 4847 * <p> 4848 * The serializer is used to serialize POJOs into the HTTP request body. 4849 * 4850 * <p> 4851 * The serializer that best matches the <c>Content-Type</c> header will be used to serialize the request body. 4852 * <br>If no <c>Content-Type</c> header is specified, the first serializer in the list will be used. 4853 * 4854 * <h5 class='section'>Notes:</h5><ul> 4855 * <li class='note'>When using this method that takes in a pre-instantiated serializers, the serializer property setters (e.g. {@link #sortCollections()}) or 4856 * bean context property setters (e.g. {@link #swaps(Class...)}) defined 4857 * on this builder class have no effect. 4858 * </ul> 4859 * 4860 * <h5 class='section'>Example:</h5> 4861 * <p class='bjava'> 4862 * <jc>// Create a client that uses predefined JSON and XML serializers for request bodies.</jc> 4863 * RestClient <jv>client</jv> = RestClient 4864 * .<jsm>create</jsm>() 4865 * .serializers(JsonSerializer.<jsf>DEFAULT_READABLE</jsf>, XmlSerializer.<jsf>DEFAULT_READABLE</jsf>) 4866 * .build(); 4867 * </p> 4868 * 4869 * @param value 4870 * The new value for this setting. 4871 * <br>The default is {@link JsonSerializer}. 4872 * @return This object. 4873 */ 4874 public Builder serializers(Serializer...value) { 4875 assertArgNoNulls("value", value); 4876 serializers().add(value); 4877 return this; 4878 } 4879 4880 /** 4881 * Assigns {@link ServiceUnavailableRetryStrategy} instance. 4882 * 4883 * @param serviceUnavailStrategy New property value. 4884 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 4885 * @return This object. 4886 * @see HttpClientBuilder#setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy) 4887 */ 4888 public Builder serviceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy serviceUnavailStrategy) { 4889 httpClientBuilder().setServiceUnavailableRetryStrategy(serviceUnavailStrategy); 4890 return this; 4891 } 4892 4893 /** 4894 * Skip empty form data. 4895 * 4896 * <p> 4897 * When enabled, form data consisting of empty strings will be skipped on requests. 4898 * Note that <jk>null</jk> values are already skipped. 4899 * 4900 * <p> 4901 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4902 * 4903 * @return This object. 4904 */ 4905 public Builder skipEmptyFormData() { 4906 return skipEmptyFormData(true); 4907 } 4908 4909 /** 4910 * Skip empty form data. 4911 * 4912 * <p> 4913 * When enabled, form data consisting of empty strings will be skipped on requests. 4914 * Note that <jk>null</jk> values are already skipped. 4915 * 4916 * <p> 4917 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4918 * 4919 * @param value 4920 * The new value for this setting. 4921 * <br>The default is <jk>false</jk>. 4922 * @return This object. 4923 */ 4924 public Builder skipEmptyFormData(boolean value) { 4925 skipEmptyFormData = true; 4926 return this; 4927 } 4928 4929 /** 4930 * Skip empty header data. 4931 * 4932 * <p> 4933 * When enabled, headers consisting of empty strings will be skipped on requests. 4934 * Note that <jk>null</jk> values are already skipped. 4935 * 4936 * <p> 4937 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4938 * 4939 * @return This object. 4940 */ 4941 public Builder skipEmptyHeaderData() { 4942 return skipEmptyHeaderData(true); 4943 } 4944 4945 /** 4946 * Skip empty header data. 4947 * 4948 * <p> 4949 * When enabled, headers consisting of empty strings will be skipped on requests. 4950 * Note that <jk>null</jk> values are already skipped. 4951 * 4952 * <p> 4953 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4954 * 4955 * @param value 4956 * The new value for this setting. 4957 * <br>The default is <jk>false</jk>. 4958 * @return This object. 4959 */ 4960 public Builder skipEmptyHeaderData(boolean value) { 4961 skipEmptyHeaderData = true; 4962 return this; 4963 } 4964 4965 /** 4966 * Skip empty query data. 4967 * 4968 * <p> 4969 * When enabled, query parameters consisting of empty strings will be skipped on requests. 4970 * Note that <jk>null</jk> values are already skipped. 4971 * 4972 * <p> 4973 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4974 * 4975 * @return This object. 4976 */ 4977 public Builder skipEmptyQueryData() { 4978 return skipEmptyQueryData(true); 4979 } 4980 4981 /** 4982 * Skip empty query data. 4983 * 4984 * <p> 4985 * When enabled, query parameters consisting of empty strings will be skipped on requests. 4986 * Note that <jk>null</jk> values are already skipped. 4987 * 4988 * <p> 4989 * The {@link Schema#skipIfEmpty()} annotation overrides this setting. 4990 * 4991 * @param value 4992 * The new value for this setting. 4993 * <br>The default is <jk>false</jk>. 4994 * @return This object. 4995 */ 4996 public Builder skipEmptyQueryData(boolean value) { 4997 skipEmptyQueryData = true; 4998 return this; 4999 } 5000 5001 /** 5002 * <i><l>Serializer</l> configuration property: </i> Sort arrays and collections alphabetically. 5003 * 5004 * <p> 5005 * When enabled, copies and sorts the contents of arrays and collections before serializing them. 5006 * 5007 * <p> 5008 * Note that this introduces a performance penalty since it requires copying the existing collection. 5009 * 5010 * <h5 class='section'>Example:</h5> 5011 * <p class='bjava'> 5012 * <jc>// Create a REST client with JSON serializer that sorts arrays and collections before serialization.</jc> 5013 * RestClient <jv>client</jv> = RestClient 5014 * .<jsm>create</jsm>() 5015 * .json() 5016 * .sortCollections() 5017 * .build(); 5018 * 5019 * <jc>// An unsorted array</jc> 5020 * String[] <jv>array</jv> = {<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>} 5021 * 5022 * <jc>// Request body will contain: ["bar","baz","foo"]</jc> 5023 * <jv>client</jv> 5024 * .post(<js>"http://localhost:10000/foo"</js>, <jv>array</jv>) 5025 * .run(); 5026 * </p> 5027 * 5028 * <h5 class='section'>See Also:</h5><ul> 5029 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#sortCollections()} 5030 * </ul> 5031 * 5032 * @return This object. 5033 */ 5034 public Builder sortCollections() { 5035 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::sortCollections); 5036 return this; 5037 } 5038 5039 /** 5040 * <i><l>Serializer</l> configuration property: </i> Sort maps alphabetically. 5041 * 5042 * <p> 5043 * When enabled, copies and sorts the contents of maps by their keys before serializing them. 5044 * 5045 * <p> 5046 * Note that this introduces a performance penalty. 5047 * 5048 * <h5 class='section'>Example:</h5> 5049 * <p class='bjava'> 5050 * <jc>// Create a REST client with JSON serializer that sorts maps before serialization.</jc> 5051 * RestClient <jv>client</jv> = RestClient 5052 * .<jsm>create</jsm>() 5053 * .json() 5054 * .sortMaps() 5055 * .build(); 5056 * 5057 * <jc>// An unsorted map.</jc> 5058 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>(<js>"foo"</js>,1,<js>"bar"</js>,2,<js>"baz"</js>,3); 5059 * 5060 * <jc>// Request body will contain: {"bar":2,"baz":3,"foo":1}</jc> 5061 * <jv>client</jv> 5062 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 5063 * .run(); 5064 * </p> 5065 * 5066 * <h5 class='section'>See Also:</h5><ul> 5067 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#sortMaps()} 5068 * </ul> 5069 * 5070 * @return This object. 5071 */ 5072 public Builder sortMaps() { 5073 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::sortMaps); 5074 return this; 5075 } 5076 5077 @Override /* Overridden from Builder */ 5078 public Builder sortProperties() { 5079 super.sortProperties(); 5080 return this; 5081 } 5082 5083 @Override /* Overridden from Builder */ 5084 public Builder sortProperties(java.lang.Class<?>...on) { 5085 super.sortProperties(on); 5086 return this; 5087 } 5088 5089 /** 5090 * <i><l>WriterSerializer</l> configuration property: </i> Quote character. 5091 * 5092 * <p> 5093 * Specifies to use single quotes for quoting attributes and values. 5094 * 5095 * <h5 class='section'>Notes:</h5><ul> 5096 * <li class='note'>This setting does not apply to the RDF serializers. 5097 * </ul> 5098 * 5099 * <h5 class='section'>Example:</h5> 5100 * <p class='bjava'> 5101 * <jc>// Create a REST client with JSON serializer that uses single quotes.</jc> 5102 * RestClient <jv>client</jv> = RestClient 5103 * .<jsm>create</jsm>() 5104 * .json() 5105 * .sq() 5106 * .build(); 5107 * 5108 * <jc>// A bean with a single property</jc> 5109 * <jk>public class</jk> MyBean { 5110 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5111 * } 5112 * 5113 * <jc>// Request body will contain: {'foo':'bar'}</jc> 5114 * <jv>client</jv> 5115 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5116 * .run(); 5117 * </p> 5118 * 5119 * <h5 class='section'>See Also:</h5><ul> 5120 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#quoteChar(char)} 5121 * </ul> 5122 * 5123 * @return This object. 5124 */ 5125 public Builder sq() { 5126 serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::sq); 5127 return this; 5128 } 5129 5130 /** 5131 * Assigns {@link SSLContext} instance. 5132 * 5133 * <h5 class='section'>Notes:</h5><ul> 5134 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} 5135 * and the {@link #sslSocketFactory(LayeredConnectionSocketFactory)} methods. 5136 * </ul> 5137 * 5138 * @param sslContext New property value. 5139 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5140 * @return This object. 5141 * @see HttpClientBuilder#setSSLContext(SSLContext) 5142 */ 5143 public Builder sslContext(SSLContext sslContext) { 5144 httpClientBuilder().setSSLContext(sslContext); 5145 return this; 5146 } 5147 5148 /** 5149 * Assigns {@link javax.net.ssl.HostnameVerifier} instance. 5150 * 5151 * <h5 class='section'>Notes:</h5><ul> 5152 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} 5153 * and the {@link #sslSocketFactory(LayeredConnectionSocketFactory)} methods. 5154 * </ul> 5155 * 5156 * @param hostnameVerifier New property value. 5157 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5158 * @return This object. 5159 * @see HttpClientBuilder#setSSLHostnameVerifier(HostnameVerifier) 5160 */ 5161 public Builder sslHostnameVerifier(HostnameVerifier hostnameVerifier) { 5162 httpClientBuilder().setSSLHostnameVerifier(hostnameVerifier); 5163 return this; 5164 } 5165 5166 /** 5167 * Assigns {@link LayeredConnectionSocketFactory} instance. 5168 * 5169 * <h5 class='section'>Notes:</h5><ul> 5170 * <li class='note'>This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. 5171 * </ul> 5172 * 5173 * @param sslSocketFactory New property value. 5174 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5175 * @return This object. 5176 * @see HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) 5177 */ 5178 public Builder sslSocketFactory(LayeredConnectionSocketFactory sslSocketFactory) { 5179 httpClientBuilder().setSSLSocketFactory(sslSocketFactory); 5180 return this; 5181 } 5182 5183 @Override /* Overridden from Builder */ 5184 public Builder stopClass(Class<?> on, Class<?> value) { 5185 super.stopClass(on, value); 5186 return this; 5187 } 5188 5189 /** 5190 * <i><l>Parser</l> configuration property: </i> Strict mode. 5191 * 5192 * <p> 5193 * When enabled, strict mode for the parser is enabled. 5194 * 5195 * <p> 5196 * Strict mode can mean different things for different parsers. 5197 * 5198 * <table class='styled'> 5199 * <tr><th>Parser class</th><th>Strict behavior</th></tr> 5200 * <tr> 5201 * <td>All reader-based parsers</td> 5202 * <td> 5203 * When enabled, throws {@link ParseException ParseExceptions} on malformed charset input. 5204 * Otherwise, malformed input is ignored. 5205 * </td> 5206 * </tr> 5207 * <tr> 5208 * <td>{@link JsonParser}</td> 5209 * <td> 5210 * When enabled, throws exceptions on the following invalid JSON syntax: 5211 * <ul> 5212 * <li>Unquoted attributes. 5213 * <li>Missing attribute values. 5214 * <li>Concatenated strings. 5215 * <li>Javascript comments. 5216 * <li>Numbers and booleans when Strings are expected. 5217 * <li>Numbers valid in Java but not JSON (e.g. octal notation, etc...) 5218 * </ul> 5219 * </td> 5220 * </tr> 5221 * </table> 5222 * 5223 * <h5 class='section'>Example:</h5> 5224 * <p class='bjava'> 5225 * <jc>// Create a REST client with JSON parser using strict mode.</jc> 5226 * RestClient <jv>client</jv> = RestClient 5227 * .<jsm>create</jsm>() 5228 * .json() 5229 * .strict() 5230 * .build(); 5231 * 5232 * <jc>// Try to parse some bad JSON.</jc> 5233 * <jk>try</jk> { 5234 * <jv>client</jv> 5235 * .get(<js>"/pathToBadJson"</js>) 5236 * .run() 5237 * .getContent().as(Object.<jk>class</jk>); <jc>// Try to parse it.</jc> 5238 * } <jk>catch</jk> (RestCallException <jv>e</jv>) { 5239 * <jc>// Handle exception.</jc> 5240 * } 5241 * </p> 5242 * 5243 * <h5 class='section'>See Also:</h5><ul> 5244 * <li class='jm'>{@link org.apache.juneau.parser.Parser.Builder#strict()} 5245 * </ul> 5246 * 5247 * @return This object. 5248 */ 5249 public Builder strict() { 5250 parsers().forEach(org.apache.juneau.parser.Parser.Builder::strict); 5251 return this; 5252 } 5253 5254 @Override /* Overridden from Builder */ 5255 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 5256 super.swap(normalClass, swappedClass, swapFunction); 5257 return this; 5258 } 5259 5260 @Override /* Overridden from Builder */ 5261 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 5262 super.swap(normalClass, swappedClass, swapFunction, unswapFunction); 5263 return this; 5264 } 5265 5266 @Override /* Overridden from Builder */ 5267 public Builder swaps(Class<?>...values) { 5268 super.swaps(values); 5269 return this; 5270 } 5271 5272 @Override /* Overridden from Builder */ 5273 public Builder swaps(Object...values) { 5274 super.swaps(values); 5275 return this; 5276 } 5277 5278 /** 5279 * Assigns {@link AuthenticationStrategy} instance for target host authentication. 5280 * 5281 * @param targetAuthStrategy New property value. 5282 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5283 * @return This object. 5284 * @see HttpClientBuilder#setTargetAuthenticationStrategy(AuthenticationStrategy) 5285 */ 5286 public Builder targetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy) { 5287 httpClientBuilder().setTargetAuthenticationStrategy(targetAuthStrategy); 5288 return this; 5289 } 5290 5291 @Override /* Overridden from Builder */ 5292 public Builder timeZone(TimeZone value) { 5293 super.timeZone(value); 5294 return this; 5295 } 5296 5297 /** 5298 * <i><l>Serializer</l> configuration property: </i> Trim empty lists and arrays. 5299 * 5300 * <p> 5301 * When enabled, empty lists and arrays will not be serialized. 5302 * 5303 * <p> 5304 * Note that enabling this setting has the following effects on parsing: 5305 * <ul class='spaced-list'> 5306 * <li> 5307 * Map entries with empty list values will be lost. 5308 * <li> 5309 * Bean properties with empty list values will not be set. 5310 * </ul> 5311 * 5312 * <h5 class='section'>Example:</h5> 5313 * <p class='bjava'> 5314 * <jc>// Create a serializer that skips empty arrays and collections.</jc> 5315 * WriterSerializer <jv>serializer</jv> = JsonSerializer 5316 * .<jsm>create</jsm>() 5317 * .trimEmptyCollections() 5318 * .build(); 5319 * 5320 * <jc>// A bean with a field with an empty array.</jc> 5321 * <jk>public class</jk> MyBean { 5322 * <jk>public</jk> String[] <jf>foo</jf> = {}; 5323 * } 5324 * 5325 * <jc>// Request body will contain: {}</jc> 5326 * <jv>client</jv> 5327 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5328 * .run(); 5329 * </p> 5330 * 5331 * <h5 class='section'>See Also:</h5><ul> 5332 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#trimEmptyCollections()} 5333 * </ul> 5334 * 5335 * @return This object. 5336 */ 5337 public Builder trimEmptyCollections() { 5338 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimEmptyCollections); 5339 return this; 5340 } 5341 5342 /** 5343 * <i><l>Serializer</l> configuration property: </i> Trim empty maps. 5344 * 5345 * <p> 5346 * When enabled, empty map values will not be serialized to the output. 5347 * 5348 * <p> 5349 * Note that enabling this setting has the following effects on parsing: 5350 * <ul class='spaced-list'> 5351 * <li> 5352 * Bean properties with empty map values will not be set. 5353 * </ul> 5354 * 5355 * <h5 class='section'>Example:</h5> 5356 * <p class='bjava'> 5357 * <jc>// Create a REST client with JSON serializer that skips empty maps.</jc> 5358 * RestClient <jv>client</jv> = RestClient 5359 * .<jsm>create</jsm>() 5360 * .json() 5361 * .trimEmptyMaps() 5362 * .build(); 5363 * 5364 * <jc>// A bean with a field with an empty map.</jc> 5365 * <jk>public class</jk> MyBean { 5366 * <jk>public</jk> AMap <jf>foo</jf> = AMap.<jsm>of</jsm>(); 5367 * } 5368 * 5369 * <jc>// Request body will contain: {}</jc> 5370 * <jv>client</jv> 5371 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5372 * .run(); 5373 * </p> 5374 * 5375 * <h5 class='section'>See Also:</h5><ul> 5376 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#trimEmptyMaps()} 5377 * </ul> 5378 * 5379 * @return This object. 5380 */ 5381 public Builder trimEmptyMaps() { 5382 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimEmptyMaps); 5383 return this; 5384 } 5385 5386 /** 5387 * <i><l>Parser</l> configuration property: </i> Trim parsed strings. 5388 * 5389 * <p> 5390 * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being added to 5391 * the POJO. 5392 * 5393 * <h5 class='section'>Example:</h5> 5394 * <p class='bjava'> 5395 * <jc>// Create a REST client with JSON parser with trim-strings enabled.</jc> 5396 * RestClient <jv>client</jv> = RestClient 5397 * .<jsm>create</jsm>() 5398 * .json() 5399 * .trimStringsOnRead() 5400 * .build(); 5401 * 5402 * <jc>// Try to parse JSON containing {" foo ":" bar "}.</jc> 5403 * Map<String,String> <jv>map</jv> = <jv>client</jv> 5404 * .get(<js>"/pathToJson"</js>) 5405 * .run() 5406 * .getContent().as(HashMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 5407 * 5408 * <jc>// Make sure strings are trimmed.</jc> 5409 * <jsm>assertEquals</jsm>(<js>"bar"</js>, <jv>map</jv>.get(<js>"foo"</js>)); 5410 * </p> 5411 * 5412 * <h5 class='section'>See Also:</h5><ul> 5413 * <li class='jm'>{@link org.apache.juneau.parser.Parser.Builder#trimStrings()} 5414 * </ul> 5415 * 5416 * @return This object. 5417 */ 5418 public Builder trimStringsOnRead() { 5419 parsers().forEach(org.apache.juneau.parser.Parser.Builder::trimStrings); 5420 return this; 5421 } 5422 5423 /** 5424 * <i><l>Serializer</l> configuration property: </i> Trim strings. 5425 * 5426 * <p> 5427 * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized. 5428 * 5429 * <h5 class='section'>Example:</h5> 5430 * <p class='bjava'> 5431 * <jc>// Create a REST client with JSON serializer that trims strings before serialization.</jc> 5432 * RestClient <jv>client</jv> = RestClient 5433 * .<jsm>create</jsm>() 5434 * .json() 5435 * .trimStrings() 5436 * .build(); 5437 * 5438 * <jc>// A map with space-padded keys/values</jc> 5439 * AMap <jv>map</jv> = AMap.<jsm>of</jsm>(<js>" foo "</js>, <js>" bar "</js>); 5440 * 5441 * <jc>// Request body will contain: {"foo":"bar"}</jc> 5442 * <jv>client</jv> 5443 * .post(<js>"http://localhost:10000/foo"</js>, <jv>map</jv>) 5444 * .run(); 5445 * </p> 5446 * 5447 * <h5 class='section'>See Also:</h5><ul> 5448 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#trimStrings()} 5449 * </ul> 5450 * 5451 * @return This object. 5452 */ 5453 public Builder trimStringsOnWrite() { 5454 serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimStrings); 5455 return this; 5456 } 5457 5458 @Override /* Overridden from Builder */ 5459 public Builder type(Class<? extends org.apache.juneau.Context> value) { 5460 super.type(value); 5461 return this; 5462 } 5463 5464 @Override /* Overridden from Builder */ 5465 public Builder typeName(Class<?> on, String value) { 5466 super.typeName(on, value); 5467 return this; 5468 } 5469 5470 @Override /* Overridden from Builder */ 5471 public Builder typePropertyName(Class<?> on, String value) { 5472 super.typePropertyName(on, value); 5473 return this; 5474 } 5475 5476 @Override /* Overridden from Builder */ 5477 public Builder typePropertyName(String value) { 5478 super.typePropertyName(value); 5479 return this; 5480 } 5481 5482 /** 5483 * Convenience method for specifying all available transmission types. 5484 * 5485 * <p> 5486 * All basic Juneau serializers will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5487 * <ul> 5488 * <li>The serializers can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5489 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5490 * </ul> 5491 * <p> 5492 * All basic Juneau parsers will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5493 * <ul> 5494 * <li>The parsers can be configured using any of the parser property setters (e.g. {@link #strict()}) or 5495 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5496 * </ul> 5497 * <p> 5498 * <c>Accept</c> request header must be set via {@link #headers()}, or per-request 5499 * via {@link RestRequest#header(Header)} in order for the correct parser to be selected. 5500 * <p> 5501 * <c>Content-Type</c> request header must be set via {@link #headers()}, 5502 * or per-request via {@link RestRequest#header(Header)} in order for the correct serializer to be selected. 5503 * <p> 5504 * Similar to calling <c>json().json5().html().xml().uon().urlEnc().openApi().msgPack().plainText()</c>. 5505 * 5506 * <h5 class='section'>Example:</h5> 5507 * <p class='bjava'> 5508 * <jc>// Construct a client that uses universal marshalling.</jc> 5509 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().universal().build(); 5510 * </p> 5511 * 5512 * @return This object. 5513 */ 5514 @SuppressWarnings("unchecked") 5515 public Builder universal() { 5516 // @formatter:off 5517 return 5518 serializers( 5519 JsonSerializer.class, 5520 Json5Serializer.class, 5521 HtmlSerializer.class, 5522 XmlSerializer.class, 5523 UonSerializer.class, 5524 UrlEncodingSerializer.class, 5525 OpenApiSerializer.class, 5526 MsgPackSerializer.class, 5527 PlainTextSerializer.class 5528 ) 5529 .parsers( 5530 JsonParser.class, 5531 Json5Parser.class, 5532 XmlParser.class, 5533 HtmlParser.class, 5534 UonParser.class, 5535 UrlEncodingParser.class, 5536 OpenApiParser.class, 5537 MsgPackParser.class, 5538 PlainTextParser.class 5539 ); 5540 // @formatter:on 5541 } 5542 5543 /** 5544 * Convenience method for specifying UON as the marshalling transmission media type. 5545 * 5546 * <p> 5547 * UON is Url-Encoding Object notation that is equivalent to JSON but suitable for transmission as URL-encoded 5548 * query and form post values. 5549 * 5550 * <p> 5551 * {@link UonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5552 * <ul> 5553 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5554 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5555 * </ul> 5556 * <p> 5557 * {@link UonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5558 * <ul> 5559 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 5560 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5561 * </ul> 5562 * <p> 5563 * <c>Accept</c> request header will be set to <js>"text/uon"</js> unless overridden 5564 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5565 * <p> 5566 * <c>Content-Type</c> request header will be set to <js>"text/uon"</js> unless overridden 5567 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5568 * <p> 5569 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5570 * <ul> 5571 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5572 * last-enabled language if the headers are not set. 5573 * </ul> 5574 * <p> 5575 * Identical to calling <c>serializer(UonSerializer.<jk>class</jk>).parser(UonParser.<jk>class</jk>)</c>. 5576 * 5577 * <h5 class='section'>Example:</h5> 5578 * <p class='bjava'> 5579 * <jc>// Construct a client that uses UON marshalling.</jc> 5580 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().uon().build(); 5581 * </p> 5582 * 5583 * @return This object. 5584 */ 5585 public Builder uon() { 5586 return serializer(UonSerializer.class).parser(UonParser.class); 5587 } 5588 5589 /** 5590 * <i><l>Serializer</l> configuration property: </i> URI context bean. 5591 * 5592 * <p> 5593 * Bean used for resolution of URIs to absolute or root-relative form. 5594 * 5595 * <h5 class='section'>Example:</h5> 5596 * <p class='bjava'> 5597 * <jc>// Our URI contextual information.</jc> 5598 * String <jv>authority</jv> = <js>"http://localhost:10000"</js>; 5599 * String <jv>contextRoot</jv> = <js>"/myContext"</js>; 5600 * String <jv>servletPath</jv> = <js>"/myServlet"</js>; 5601 * String <jv>pathInfo</jv> = <js>"/foo"</js>; 5602 * 5603 * <jc>// Create a UriContext object.</jc> 5604 * UriContext <jv>uriContext</jv> = <jk>new</jk> UriContext(<jv>authority</jv>, <jv>contextRoot</jv>, <jv>servletPath</jv>, <jv>pathInfo</jv>); 5605 * 5606 * <jc>// Create a REST client with JSON serializer and associate our context.</jc> 5607 * RestClient <jv>client</jv> = RestClient 5608 * .<jsm>create</jsm>() 5609 * .json() 5610 * .uriContext(<jv>uriContext</jv>) 5611 * .uriRelativity(<jsf>RESOURCE</jsf>) <jc>// Assume relative paths are relative to servlet.</jc> 5612 * .uriResolution(<jsf>ABSOLUTE</jsf>) <jc>// Serialize URIs as absolute paths.</jc> 5613 * .build(); 5614 * 5615 * <jc>// A relative URI</jc> 5616 * URI <jv>uri</jv> = <jk>new</jk> URI(<js>"bar"</js>); 5617 * 5618 * <jc>// Request body will contain: "http://localhost:10000/myContext/myServlet/foo/bar"</jc> 5619 * <jv>client</jv> 5620 * .post(<js>"http://localhost:10000/foo"</js>, <jv>uri</jv>) 5621 * .run(); 5622 * </p> 5623 * 5624 * <h5 class='section'>See Also:</h5><ul> 5625 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#uriContext(UriContext)} 5626 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5627 * </ul> 5628 * 5629 * @param value The new value for this property. 5630 * <br>Cannot be <jk>null</jk>. 5631 * @return This object. 5632 */ 5633 public Builder uriContext(UriContext value) { 5634 serializers().forEach(x -> x.uriContext(assertArgNotNull("value", value))); 5635 return this; 5636 } 5637 5638 /** 5639 * <i><l>Serializer</l> configuration property: </i> URI relativity. 5640 * 5641 * <p> 5642 * Defines what relative URIs are relative to when serializing any of the following: 5643 * <ul> 5644 * <li>{@link java.net.URI} 5645 * <li>{@link java.net.URL} 5646 * <li>Properties and classes annotated with {@link Uri @Uri} 5647 * </ul> 5648 * 5649 * <p> 5650 * See {@link #uriContext(UriContext)} for examples. 5651 * 5652 * <ul class='values javatree'> 5653 * <li class='jf'>{@link org.apache.juneau.UriRelativity#RESOURCE} 5654 * - Relative URIs should be considered relative to the servlet URI. 5655 * <li class='jf'>{@link org.apache.juneau.UriRelativity#PATH_INFO} 5656 * - Relative URIs should be considered relative to the request URI. 5657 * </ul> 5658 * 5659 * <h5 class='section'>See Also:</h5><ul> 5660 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#uriRelativity(UriRelativity)} 5661 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5662 * </ul> 5663 * 5664 * @param value 5665 * The new value for this property. 5666 * <br>The default is {@link UriRelativity#RESOURCE} 5667 * <br>Cannot be <jk>null</jk>. 5668 * @return This object. 5669 */ 5670 public Builder uriRelativity(UriRelativity value) { 5671 serializers().forEach(x -> x.uriRelativity(assertArgNotNull("value", value))); 5672 return this; 5673 } 5674 5675 /** 5676 * <i><l>Serializer</l> configuration property: </i> URI resolution. 5677 * 5678 * <p> 5679 * Defines the resolution level for URIs when serializing any of the following: 5680 * <ul> 5681 * <li>{@link java.net.URI} 5682 * <li>{@link java.net.URL} 5683 * <li>Properties and classes annotated with {@link Uri @Uri} 5684 * </ul> 5685 * 5686 * <p> 5687 * See {@link #uriContext(UriContext)} for examples. 5688 * 5689 * <ul class='values'> 5690 * <li class='jf'>{@link UriResolution#ABSOLUTE} 5691 * - Resolve to an absolute URI (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>). 5692 * <li class='jf'>{@link UriResolution#ROOT_RELATIVE} 5693 * - Resolve to a root-relative URI (e.g. <js>"/context-root/servlet-path/path-info"</js>). 5694 * <li class='jf'>{@link UriResolution#NONE} 5695 * - Don't do any URI resolution. 5696 * </ul> 5697 * 5698 * <h5 class='section'>See Also:</h5><ul> 5699 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#uriResolution(UriResolution)} 5700 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a> 5701 * </ul> 5702 * 5703 * @param value 5704 * The new value for this property. 5705 * <br>The default is {@link UriResolution#NONE} 5706 * <br>Cannot be <jk>null</jk>. 5707 * @return This object. 5708 */ 5709 public Builder uriResolution(UriResolution value) { 5710 serializers().forEach(x -> x.uriResolution(assertArgNotNull("value", value))); 5711 return this; 5712 } 5713 5714 /** 5715 * Convenience method for specifying URL-Encoding as the marshalling transmission media type. 5716 * 5717 * <p> 5718 * {@link UrlEncodingSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5719 * <ul> 5720 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5721 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5722 * <li>This serializer is NOT used when using the {@link RestRequest#formData(String, Object)} (and related) methods for constructing 5723 * the request body. Instead, the part serializer specified via {@link #partSerializer(Class)} is used. 5724 * </ul> 5725 * <p> 5726 * {@link UrlEncodingParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5727 * <ul> 5728 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 5729 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5730 * </ul> 5731 * <p> 5732 * <c>Accept</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden 5733 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5734 * <p> 5735 * <c>Content-Type</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden 5736 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5737 * <p> 5738 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5739 * <ul> 5740 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5741 * last-enabled language if the headers are not set. 5742 * </ul> 5743 * <p> 5744 * Identical to calling <c>serializer(UrlEncodingSerializer.<jk>class</jk>).parser(UrlEncodingParser.<jk>class</jk>)</c>. 5745 * 5746 * <h5 class='section'>Example:</h5> 5747 * <p class='bjava'> 5748 * <jc>// Construct a client that uses URL-Encoded marshalling.</jc> 5749 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().urlEnc().build(); 5750 * </p> 5751 * 5752 * @return This object. 5753 */ 5754 public Builder urlEnc() { 5755 return serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class); 5756 } 5757 5758 /** 5759 * Returns the URL-encoding serializer sub-builder. 5760 * 5761 * @return The URL-encoding serializer sub-builder. 5762 */ 5763 public final UrlEncodingSerializer.Builder urlEncodingSerializer() { 5764 if (urlEncodingSerializer == null) 5765 urlEncodingSerializer = createUrlEncodingSerializer(); 5766 return urlEncodingSerializer; 5767 } 5768 5769 @Override /* Overridden from Builder */ 5770 public Builder useEnumNames() { 5771 super.useEnumNames(); 5772 return this; 5773 } 5774 5775 @Override /* Overridden from Builder */ 5776 public Builder useJavaBeanIntrospector() { 5777 super.useJavaBeanIntrospector(); 5778 return this; 5779 } 5780 5781 /** 5782 * Assigns {@link UserTokenHandler} instance. 5783 * 5784 * <h5 class='section'>Notes:</h5><ul> 5785 * <li class='note'>This value can be overridden by the {@link #disableConnectionState()} method. 5786 * </ul> 5787 * 5788 * @param userTokenHandler New property value. 5789 * <br>Can be <jk>null</jk> (value will not be set, default behavior will be used). 5790 * @return This object. 5791 * @see HttpClientBuilder#setUserTokenHandler(UserTokenHandler) 5792 */ 5793 public Builder userTokenHandler(UserTokenHandler userTokenHandler) { 5794 httpClientBuilder().setUserTokenHandler(userTokenHandler); 5795 return this; 5796 } 5797 5798 /** 5799 * Use system properties when creating and configuring default implementations. 5800 * 5801 * @return This object. 5802 * @see HttpClientBuilder#useSystemProperties() 5803 */ 5804 public Builder useSystemProperties() { 5805 httpClientBuilder().useSystemProperties(); 5806 return this; 5807 } 5808 5809 /** 5810 * <i><l>WriterSerializer</l> configuration property: </i> Use whitespace. 5811 * 5812 * <p> 5813 * When enabled, whitespace is added to the output to improve readability. 5814 * 5815 * <h5 class='section'>Example:</h5> 5816 * <p class='bjava'> 5817 * <jc>// Create a REST client with JSON serializer with whitespace enabled.</jc> 5818 * RestClient <jv>client</jv> = RestClient 5819 * .<jsm>create</jsm>() 5820 * .json() 5821 * .useWhitespace() 5822 * .build(); 5823 * 5824 * <jc>// A bean with a single property</jc> 5825 * <jk>public class</jk> MyBean { 5826 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5827 * } 5828 * 5829 * <jc>// Request body will contain: {\n\t"foo": "bar"\n\}\n</jc> 5830 * <jv>client</jv> 5831 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5832 * .run(); 5833 * </p> 5834 * 5835 * <h5 class='section'>See Also:</h5><ul> 5836 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#useWhitespace()} 5837 * </ul> 5838 * @return This object. 5839 */ 5840 public Builder useWhitespace() { 5841 serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::useWhitespace); 5842 return this; 5843 } 5844 5845 /** 5846 * <i><l>WriterSerializer</l> configuration property: </i> Use whitespace. 5847 * 5848 * <p> 5849 * When enabled, whitespace is added to the output to improve readability. 5850 * 5851 * <h5 class='section'>Example:</h5> 5852 * <p class='bjava'> 5853 * <jc>// Create a REST client with JSON serializer with whitespace enabled.</jc> 5854 * RestClient <jv>client</jv> = RestClient 5855 * .<jsm>create</jsm>() 5856 * .json() 5857 * .ws() 5858 * .build(); 5859 * 5860 * <jc>// A bean with a single property</jc> 5861 * <jk>public class</jk> MyBean { 5862 * <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>; 5863 * } 5864 * 5865 * <jc>// Request body will contain: {\n\t"foo": "bar"\n\}\n</jc> 5866 * <jv>client</jv> 5867 * .post(<js>"http://localhost:10000/foo"</js>, <jk>new</jk> MyBean()) 5868 * .run(); 5869 * </p> 5870 * 5871 * <h5 class='section'>See Also:</h5><ul> 5872 * <li class='jm'>{@link org.apache.juneau.serializer.WriterSerializer.Builder#useWhitespace()} 5873 * </ul> 5874 * 5875 * @return This object. 5876 */ 5877 public Builder ws() { 5878 serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::ws); 5879 return this; 5880 } 5881 5882 /** 5883 * Convenience method for specifying XML as the marshalling transmission media type. 5884 * 5885 * <p> 5886 * {@link XmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. 5887 * <ul> 5888 * <li>The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or 5889 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5890 * </ul> 5891 * <p> 5892 * {@link XmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. 5893 * <ul> 5894 * <li>The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or 5895 * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. 5896 * </ul> 5897 * <p> 5898 * <c>Accept</c> request header will be set to <js>"text/xml"</js> unless overridden 5899 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5900 * <p> 5901 * <c>Content-Type</c> request header will be set to <js>"text/xml"</js> unless overridden 5902 * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. 5903 * <p> 5904 * Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. 5905 * <ul> 5906 * <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the 5907 * last-enabled language if the headers are not set. 5908 * </ul> 5909 * <p> 5910 * Identical to calling <c>serializer(XmlSerializer.<jk>class</jk>).parser(XmlParser.<jk>class</jk>)</c>. 5911 * 5912 * <h5 class='section'>Example:</h5> 5913 * <p class='bjava'> 5914 * <jc>// Construct a client that uses XML marshalling.</jc> 5915 * RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().xml().build(); 5916 * </p> 5917 * 5918 * @return This object. 5919 */ 5920 public Builder xml() { 5921 return serializer(XmlSerializer.class).parser(XmlParser.class); 5922 } 5923 5924 /** 5925 * Creates the creator for the rest call handler. 5926 * 5927 * <p> 5928 * Subclasses can override this method to provide their own implementation. 5929 * 5930 * <p> 5931 * The default behavior creates a bean creator initialized to return a {@link BasicRestCallHandler}. 5932 * 5933 * @return The creator for the rest call handler. 5934 * @see #callHandler() 5935 */ 5936 protected BeanCreator<RestCallHandler> createCallHandler() { 5937 return beanStore.createBean(RestCallHandler.class).type(BasicRestCallHandler.class); 5938 } 5939 5940 /** 5941 * Creates the {@link HttpClientConnectionManager} returned by {@link #createConnectionManager()}. 5942 * 5943 * <p> 5944 * Subclasses can override this method to provide their own connection manager. 5945 * 5946 * <p> 5947 * The default implementation returns an instance of a {@link PoolingHttpClientConnectionManager} if {@link #pooled()} 5948 * was called or {@link BasicHttpClientConnectionManager} if not.. 5949 * 5950 * <h5 class='section'>Example:</h5> 5951 * <p class='bjava'> 5952 * <jc>// A Builder that provides it's own customized HttpClientConnectionManager.</jc> 5953 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 5954 * <ja>@Override</ja> 5955 * <jk>protected</jk> HttpClientConnectionManager createConnectionManager() { 5956 * <jk>return new</jk> PoolingHttpClientConnectionManager(); 5957 * } 5958 * } 5959 * 5960 * <jc>// Instantiate.</jc> 5961 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 5962 * </p> 5963 * 5964 * @return The HTTP client builder to use to create the HTTP client. 5965 */ 5966 protected HttpClientConnectionManager createConnectionManager() { 5967 return (pooled ? new PoolingHttpClientConnectionManager() : new BasicHttpClientConnectionManager()); 5968 } 5969 5970 /** 5971 * Creates the builder for the form data list. 5972 * 5973 * <p> 5974 * Subclasses can override this method to provide their own implementation. 5975 * 5976 * <p> 5977 * The default behavior creates an empty builder. 5978 * 5979 * @return The query data list builder. 5980 * @see #formData() 5981 */ 5982 protected PartList createFormData() { 5983 return PartList.create(); 5984 } 5985 5986 /** 5987 * Creates the builder for the header list. 5988 * 5989 * <p> 5990 * Subclasses can override this method to provide their own implementation. 5991 * 5992 * <p> 5993 * The default behavior creates an empty builder. 5994 * 5995 * @return The header list builder. 5996 * @see #headers() 5997 */ 5998 protected HeaderList createHeaderData() { 5999 return HeaderList.create(); 6000 } 6001 6002 /** 6003 * Creates an instance of an {@link HttpClient} to be used to handle all HTTP communications with the target server. 6004 * 6005 * <p> 6006 * This HTTP client is used when the HTTP client is not specified through one of the constructors or the 6007 * {@link #httpClient(CloseableHttpClient)} method. 6008 * 6009 * <p> 6010 * Subclasses can override this method to provide specially-configured HTTP clients to handle stuff such as 6011 * SSL/TLS certificate handling, authentication, etc. 6012 * 6013 * <p> 6014 * The default implementation returns an instance of {@link HttpClient} using the client builder returned by 6015 * {@link #createHttpClientBuilder()}. 6016 * 6017 * <h5 class='section'>Example:</h5> 6018 * <p class='bjava'> 6019 * <jc>// A Builder that provides it's own customized HttpClient.</jc> 6020 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 6021 * <ja>@Override</ja> 6022 * <jk>protected</jk> HttpClientBuilder createHttpClient() { 6023 * <jk>return</jk> HttpClientBuilder.<jsm>create</jsm>().build(); 6024 * } 6025 * } 6026 * 6027 * <jc>// Instantiate.</jc> 6028 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 6029 * </p> 6030 * 6031 * @return The HTTP client to use. 6032 */ 6033 protected CloseableHttpClient createHttpClient() { 6034 if (connectionManager == null) 6035 connectionManager = createConnectionManager(); 6036 httpClientBuilder().setConnectionManager(connectionManager); 6037 return httpClientBuilder().build(); 6038 } 6039 6040 /** 6041 * Creates an instance of an {@link HttpClientBuilder} to be used to create the {@link HttpClient}. 6042 * 6043 * <p> 6044 * Subclasses can override this method to provide their own client builder. 6045 * The builder can also be specified using the {@link #httpClientBuilder(HttpClientBuilder)} method. 6046 * 6047 * <h5 class='section'>Example:</h5> 6048 * <p class='bjava'> 6049 * <jc>// A Builder that provides it's own customized HttpClientBuilder.</jc> 6050 * <jk>public class</jk> MyBuilder <jk>extends</jk> Builder { 6051 * <ja>@Override</ja> 6052 * <jk>protected</jk> HttpClientBuilder createHttpClientBuilder() { 6053 * <jk>return</jk> HttpClientBuilder.<jsm>create</jsm>(); 6054 * } 6055 * } 6056 * 6057 * <jc>// Instantiate.</jc> 6058 * RestClient <jv>client</jv> = <jk>new</jk> MyBuilder().build(); 6059 * </p> 6060 * 6061 * @return The HTTP client builder to use to create the HTTP client. 6062 */ 6063 protected HttpClientBuilder createHttpClientBuilder() { 6064 return HttpClientBuilder.create(); 6065 } 6066 6067 /** 6068 * Instantiates the parser group sub-builder. 6069 * 6070 * @return A new parser group sub-builder. 6071 */ 6072 protected ParserSet.Builder createParsers() { 6073 return ParserSet.create().beanContext(beanContext()); 6074 } 6075 6076 /** 6077 * Instantiates the part parser sub-builder. 6078 * 6079 * @return A new part parser sub-builder. 6080 */ 6081 protected HttpPartParser.Creator createPartParser() { 6082 return HttpPartParser.creator().type(OpenApiParser.class).beanContext(beanContext()); 6083 } 6084 6085 /** 6086 * Instantiates the part serializer sub-builder. 6087 * 6088 * @return A new part serializer sub-builder. 6089 */ 6090 protected HttpPartSerializer.Creator createPartSerializer() { 6091 return HttpPartSerializer.creator().type(OpenApiSerializer.class).beanContext(beanContext()); 6092 } 6093 6094 /** 6095 * Creates the builder for the path data list. 6096 * 6097 * <p> 6098 * Subclasses can override this method to provide their own implementation. 6099 * 6100 * <p> 6101 * The default behavior creates an empty builder. 6102 * 6103 * @return The query data list builder. 6104 * @see #pathData() 6105 */ 6106 protected PartList createPathData() { 6107 return PartList.create(); 6108 } 6109 6110 /** 6111 * Creates the builder for the query data list. 6112 * 6113 * <p> 6114 * Subclasses can override this method to provide their own implementation. 6115 * 6116 * <p> 6117 * The default behavior creates an empty builder. 6118 * 6119 * @return The query data list builder. 6120 * @see #queryData() 6121 */ 6122 protected PartList createQueryData() { 6123 return PartList.create(); 6124 } 6125 6126 /** 6127 * Instantiates the serializer group sub-builder. 6128 * 6129 * @return A new serializer group sub-builder. 6130 */ 6131 protected SerializerSet.Builder createSerializers() { 6132 return SerializerSet.create().beanContext(beanContext()); 6133 } 6134 6135 /** 6136 * Instantiates the URL-encoding serializer sub-builder. 6137 * 6138 * @return A new URL-encoding serializer sub-builder. 6139 */ 6140 protected UrlEncodingSerializer.Builder createUrlEncodingSerializer() { 6141 return UrlEncodingSerializer.create().beanContext(beanContext()); 6142 } 6143 6144 final CloseableHttpClient getHttpClient() { return nn(httpClient) ? httpClient : createHttpClient(); } 6145 } 6146 6147 private static final RestCallInterceptor[] EMPTY_REST_CALL_INTERCEPTORS = {}; 6148 private static final ConcurrentHashMap<Class<?>,Context> requestContexts = new ConcurrentHashMap<>(); 6149 private static final BiPredicate<RestRequest,RestResponse> LOG_REQUESTS_PREDICATE_DEFAULT = (req, res) -> true; 6150 6151 /** 6152 * Instantiates a new clean-slate {@link Builder} object. 6153 * 6154 * @return A new {@link Builder} object. 6155 */ 6156 public static Builder create() { 6157 return new Builder(); 6158 } 6159 6160 protected final boolean detectLeaks; 6161 protected final boolean ignoreErrors; 6162 protected final boolean keepHttpClientOpen; 6163 protected final boolean skipEmptyFormData; 6164 protected final boolean skipEmptyHeaderData; 6165 protected final boolean skipEmptyQueryData; 6166 protected final BiPredicate<RestRequest,RestResponse> logRequestsPredicate; 6167 protected final CloseableHttpClient httpClient; 6168 protected final DetailLevel logRequests; 6169 protected final HeaderList headerData; 6170 protected final HttpPartParser partParser; 6171 protected final HttpPartSerializer partSerializer; 6172 protected final Level logRequestsLevel; 6173 protected final PartList formData; 6174 protected final PartList pathData; 6175 protected final PartList queryData; 6176 protected final ParserSet parsers; 6177 protected final RestCallInterceptor[] interceptors; 6178 protected final SerializerSet serializers; 6179 protected final UrlEncodingSerializer urlEncodingSerializer; // Used for form posts only. 6180 Predicate<Integer> errorCodes; 6181 private final BeanStore beanStore; 6182 private final HttpClientConnectionManager connectionManager; 6183 private final Logger logger; 6184 private final Map<Class<?>,HttpPartParser> partParsers = new ConcurrentHashMap<>(); 6185 private final Map<Class<?>,HttpPartSerializer> partSerializers = new ConcurrentHashMap<>(); 6186 private final Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*"); 6187 private final PrintStream console; 6188 private final RestCallHandler callHandler; 6189 private StackTraceElement[] closedStack; 6190 private final StackTraceElement[] creationStack; 6191 private final String rootUrl; 6192 private final boolean executorServiceShutdownOnClose; 6193 private final boolean logToConsole; 6194 private final AtomicBoolean isClosed = new AtomicBoolean(false); 6195 private final AtomicReference<ExecutorService> executorService = new AtomicReference<>(); 6196 6197 /** 6198 * Constructor. 6199 * 6200 * @param builder The builder for this client. 6201 */ 6202 public RestClient(Builder builder) { 6203 super(builder); 6204 6205 beanStore = builder.beanStore.addBean(RestClient.class, this); 6206 6207 callHandler = builder.callHandler().run(); 6208 connectionManager = builder.connectionManager; 6209 console = nn(builder.console) ? builder.console : System.err; 6210 creationStack = isDebug() ? Thread.currentThread().getStackTrace() : null; 6211 detectLeaks = builder.detectLeaks; 6212 errorCodes = builder.errorCodes; 6213 formData = builder.formData().copy(); 6214 headerData = builder.headers().copy(); 6215 httpClient = builder.getHttpClient(); 6216 ignoreErrors = builder.ignoreErrors; 6217 interceptors = nn(builder.interceptors) ? builder.interceptors.toArray(EMPTY_REST_CALL_INTERCEPTORS) : EMPTY_REST_CALL_INTERCEPTORS; 6218 keepHttpClientOpen = builder.keepHttpClientOpen; 6219 logger = nn(builder.logger) ? builder.logger : Logger.getLogger(cn(RestClient.class)); 6220 logRequests = nn(builder.logRequests) ? builder.logRequests : isDebug() ? DetailLevel.FULL : DetailLevel.NONE; 6221 logRequestsLevel = nn(builder.logRequestsLevel) ? builder.logRequestsLevel : isDebug() ? Level.WARNING : Level.OFF; 6222 logRequestsPredicate = nn(builder.logRequestsPredicate) ? builder.logRequestsPredicate : LOG_REQUESTS_PREDICATE_DEFAULT; 6223 logToConsole = builder.logToConsole || isDebug(); 6224 parsers = builder.parsers().build(); 6225 partParser = builder.partParser().create(); 6226 partSerializer = builder.partSerializer().create(); 6227 pathData = builder.pathData().copy(); 6228 queryData = builder.queryData().copy(); 6229 rootUrl = builder.rootUrl; 6230 serializers = builder.serializers().build(); 6231 skipEmptyFormData = builder.skipEmptyFormData; 6232 skipEmptyHeaderData = builder.skipEmptyHeaderData; 6233 skipEmptyQueryData = builder.skipEmptyQueryData; 6234 urlEncodingSerializer = builder.urlEncodingSerializer().build(); 6235 if (builder.executorService != null) 6236 executorService.set(builder.executorService); 6237 executorServiceShutdownOnClose = builder.executorServiceShutdownOnClose; 6238 6239 init(); 6240 } 6241 6242 /** 6243 * Performs a REST call where the entire call is specified in a simple string. 6244 * 6245 * <p> 6246 * This method is useful for performing callbacks when the target of a callback is passed in 6247 * on an initial request, for example to signal when a long-running process has completed. 6248 * 6249 * <p> 6250 * The call string can be any of the following formats: 6251 * <ul class='spaced-list'> 6252 * <li> 6253 * <js>"[method] [uri]"</js> - e.g. <js>"GET http://localhost/callback"</js> 6254 * <li> 6255 * <js>"[method] [uri] [payload]"</js> - e.g. <js>"POST http://localhost/callback some text payload"</js> 6256 * <li> 6257 * <js>"[method] [headers] [uri] [payload]"</js> - e.g. <js>"POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}"</js> 6258 * </ul> 6259 * <p> 6260 * The payload will always be sent using a simple {@link StringEntity}. 6261 * 6262 * @param callString The call string. 6263 * <br>Can be <jk>null</jk> or empty (treated as empty string, will result in an invalid request). 6264 * @return 6265 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6266 * as a parsed object. 6267 * @throws RestCallException REST call failed. 6268 */ 6269 public RestRequest callback(String callString) throws RestCallException { 6270 callString = emptyIfNull(callString); 6271 6272 // S1 - Looking for end of method. 6273 // S2 - Found end of method, looking for beginning of URI or headers. 6274 // S3 - Found beginning of headers, looking for end of headers. 6275 // S4 - Found end of headers, looking for beginning of URI. 6276 // S5 - Found beginning of URI, looking for end of URI. 6277 6278 var state = S1; 6279 6280 var mark = 0; 6281 var method = (String)null; 6282 var headers = (String)null; 6283 var uri = (String)null; 6284 var content = (String)null; 6285 for (var i = 0; i < callString.length(); i++) { 6286 var c = callString.charAt(i); 6287 if (state == S1) { 6288 if (isWhitespace(c)) { 6289 method = callString.substring(mark, i); 6290 state = S2; 6291 } 6292 } else if (state == S2) { 6293 if (! isWhitespace(c)) { 6294 mark = i; 6295 if (c == '{') 6296 state = S3; 6297 else 6298 state = S5; 6299 } 6300 } else if (state == S3) { 6301 if (c == '}') { 6302 headers = callString.substring(mark, i + 1); 6303 state = S4; 6304 } 6305 } else if (state == S4) { 6306 if (! isWhitespace(c)) { 6307 mark = i; 6308 state = S5; 6309 } 6310 } else /* (state == S5) */ { 6311 if (isWhitespace(c)) { 6312 uri = callString.substring(mark, i); 6313 content = callString.substring(i).trim(); 6314 break; 6315 } 6316 } 6317 } 6318 6319 if (state != S5) 6320 throw new RestCallException(null, null, "Invalid format for call string. State={0}", state); 6321 6322 try { 6323 var req = request(method, uri, ne(content)); 6324 if (nn(headers)) 6325 JsonMap.ofJson(headers).forEach((k, v) -> req.header(stringHeader(k, s(v)))); 6326 if (ne(content)) 6327 req.contentString(content); 6328 return req; 6329 } catch (ParseException e) { 6330 throw new RestCallException(null, e, "Invalid format for call string."); 6331 } 6332 } 6333 6334 /** 6335 * Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}. 6336 * 6337 * <p> 6338 * It's good practice to call this method after the client is no longer used. 6339 * 6340 * @throws IOException Thrown by underlying stream. 6341 */ 6342 @Override 6343 public void close() throws IOException { 6344 isClosed.set(true); 6345 ExecutorService es = executorService.get(); 6346 if (! keepHttpClientOpen) 6347 httpClient.close(); 6348 if (nn(es) && executorServiceShutdownOnClose) 6349 es.shutdown(); 6350 if (nn(creationStack)) 6351 closedStack = Thread.currentThread().getStackTrace(); 6352 } 6353 6354 /** 6355 * Same as {@link #close()}, but ignores any exceptions. 6356 */ 6357 public void closeQuietly() { 6358 isClosed.set(true); 6359 try { 6360 if (! keepHttpClientOpen) 6361 httpClient.close(); 6362 ExecutorService es = executorService.get(); 6363 if (nn(es) && executorServiceShutdownOnClose) 6364 es.shutdown(); 6365 } catch (@SuppressWarnings("unused") Throwable t) {} 6366 if (nn(creationStack)) 6367 closedStack = Thread.currentThread().getStackTrace(); 6368 } 6369 6370 @Override /* Overridden from Context */ 6371 public Builder copy() { 6372 throw new NoSuchMethodError("Not implemented."); 6373 } 6374 6375 /** 6376 * Creates a mutable copy of the form data defined on this client. 6377 * 6378 * <p> 6379 * Used during the construction of {@link RestRequest} objects. 6380 * 6381 * <p> 6382 * Subclasses can override this method to provide their own builder. 6383 * 6384 * @return A new builder. 6385 */ 6386 public PartList createFormData() { 6387 return formData.copy(); 6388 } 6389 6390 /** 6391 * Creates a mutable copy of the header data defined on this client. 6392 * 6393 * <p> 6394 * Used during the construction of {@link RestRequest} objects. 6395 * 6396 * <p> 6397 * Subclasses can override this method to provide their own builder. 6398 * 6399 * @return A new builder. 6400 */ 6401 public HeaderList createHeaderData() { 6402 return headerData.copy(); 6403 } 6404 6405 /** 6406 * Creates a mutable copy of the path data defined on this client. 6407 * 6408 * <p> 6409 * Used during the construction of {@link RestRequest} objects. 6410 * 6411 * <p> 6412 * Subclasses can override this method to provide their own builder. 6413 * 6414 * @return A new builder. 6415 */ 6416 public PartList createPathData() { 6417 return pathData.copy(); 6418 } 6419 6420 /** 6421 * Creates a mutable copy of the query data defined on this client. 6422 * 6423 * <p> 6424 * Used during the construction of {@link RestRequest} objects. 6425 * 6426 * <p> 6427 * Subclasses can override this method to provide their own builder. 6428 * 6429 * @return A new builder. 6430 */ 6431 public PartList createQueryData() { 6432 return queryData.copy(); 6433 } 6434 6435 /** 6436 * Perform a <c>DELETE</c> request against the specified URI. 6437 * 6438 * @param uri 6439 * The URI of the remote REST resource. 6440 * <br>Can be any of the following types: 6441 * <ul> 6442 * <li>{@link URIBuilder} 6443 * <li>{@link URI} 6444 * <li>{@link URL} 6445 * <li>{@link String} 6446 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6447 * </ul> 6448 * @return 6449 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6450 * as a parsed object. 6451 * @throws RestCallException If any authentication errors occurred. 6452 */ 6453 public RestRequest delete(Object uri) throws RestCallException { 6454 return request(op("DELETE", uri, NO_BODY)); 6455 } 6456 6457 /** 6458 * Executes HTTP request using the default context. 6459 * 6460 * <h5 class='section'>Notes:</h5><ul> 6461 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6462 * </ul> 6463 * 6464 * @param target The target host for the request. 6465 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 6466 * target or by inspecting the request. 6467 * @param request The request to execute. 6468 * @return The response to the request. 6469 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6470 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6471 * implementation and configuration of this client. 6472 * @throws IOException In case of a problem or the connection was aborted. 6473 * @throws ClientProtocolException In case of an http protocol error. 6474 */ 6475 @Override /* Overridden from HttpClient */ 6476 public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException, ClientProtocolException { 6477 return httpClient.execute(target, request); 6478 } 6479 6480 /** 6481 * Executes HTTP request using the given context. 6482 * 6483 * <h5 class='section'>Notes:</h5><ul> 6484 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6485 * <li class='note'>The {@link #run(HttpHost,HttpRequest,HttpContext)} method has been provided as a wrapper around this method. 6486 * Subclasses can override these methods for handling requests with and without bodies separately. 6487 * <li class='note'>The {@link RestCallHandler} interface can also be implemented to intercept this method. 6488 * </ul> 6489 * 6490 * @param target The target host for the request. 6491 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 6492 * target or by inspecting the request. 6493 * @param request The request to execute. 6494 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6495 * @return 6496 * The response to the request. 6497 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6498 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6499 * implementation and configuration of this client. 6500 * @throws IOException In case of a problem or the connection was aborted. 6501 * @throws ClientProtocolException In case of an http protocol error. 6502 */ 6503 @Override /* Overridden from HttpClient */ 6504 public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException, ClientProtocolException { 6505 return httpClient.execute(target, request, context); 6506 } 6507 6508 /** 6509 * Executes HTTP request to the target using the default context and processes the response using the given response handler. 6510 * 6511 * <p> 6512 * The content entity associated with the response is fully consumed and the underlying connection is released back 6513 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6514 * from having to manage resource deallocation internally. 6515 * 6516 * <h5 class='section'>Notes:</h5><ul> 6517 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6518 * </ul> 6519 * 6520 * @param target 6521 * The target host for the request. 6522 * <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. 6523 * @param request The request to execute. 6524 * @param responseHandler The response handler. 6525 * @return The response object as generated by the response handler. 6526 * @throws IOException In case of a problem or the connection was aborted. 6527 * @throws ClientProtocolException In case of an http protocol error. 6528 */ 6529 @Override /* Overridden from HttpClient */ 6530 public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException { 6531 return httpClient.execute(target, request, responseHandler); 6532 } 6533 6534 /** 6535 * Executes a request using the default context and processes the response using the given response handler. 6536 * 6537 * <p> 6538 * The content entity associated with the response is fully consumed and the underlying connection is released back 6539 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6540 * from having to manage resource deallocation internally. 6541 * 6542 * <h5 class='section'>Notes:</h5><ul> 6543 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6544 * </ul> 6545 * 6546 * @param target 6547 * The target host for the request. 6548 * <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. 6549 * @param request The request to execute. 6550 * @param responseHandler The response handler. 6551 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6552 * @return The response object as generated by the response handler. 6553 * @throws IOException In case of a problem or the connection was aborted. 6554 * @throws ClientProtocolException In case of an http protocol error. 6555 */ 6556 @Override /* Overridden from HttpClient */ 6557 public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException, ClientProtocolException { 6558 return httpClient.execute(target, request, responseHandler, context); 6559 } 6560 6561 /** 6562 * Executes HTTP request using the default context. 6563 * 6564 * <h5 class='section'>Notes:</h5><ul> 6565 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6566 * </ul> 6567 * 6568 * @param request The request to execute. 6569 * @return 6570 * The response to the request. 6571 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6572 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6573 * implementation and configuration of this client. 6574 * @throws IOException In case of a problem or the connection was aborted. 6575 * @throws ClientProtocolException In case of an http protocol error. 6576 */ 6577 @Override /* Overridden from HttpClient */ 6578 public HttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException { 6579 return httpClient.execute(request); 6580 } 6581 6582 /** 6583 * Executes HTTP request using the given context. 6584 * 6585 * <h5 class='section'>Notes:</h5><ul> 6586 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6587 * </ul> 6588 * 6589 * @param request The request to execute. 6590 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6591 * @return 6592 * The response to the request. 6593 * <br>This is always a final response, never an intermediate response with an 1xx status code. 6594 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 6595 * implementation and configuration of this client. 6596 * @throws IOException In case of a problem or the connection was aborted. 6597 * @throws ClientProtocolException In case of an http protocol error. 6598 */ 6599 @Override /* Overridden from HttpClient */ 6600 public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException, ClientProtocolException { 6601 return httpClient.execute(request, context); 6602 } 6603 6604 /** 6605 * Executes HTTP request using the default context and processes the response using the given response handler. 6606 * 6607 * <p> 6608 * The content entity associated with the response is fully consumed and the underlying connection is released back 6609 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6610 * from having to manage resource deallocation internally. 6611 * 6612 * <h5 class='section'>Notes:</h5><ul> 6613 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6614 * </ul> 6615 * 6616 * @param request The request to execute. 6617 * @param responseHandler The response handler. 6618 * @return Object returned by response handler. 6619 * @throws IOException In case of a problem or the connection was aborted. 6620 * @throws ClientProtocolException In case of an http protocol error. 6621 */ 6622 @Override /* Overridden from HttpClient */ 6623 public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException { 6624 return httpClient.execute(request, responseHandler); 6625 } 6626 6627 /** 6628 * Executes HTTP request using the given context and processes the response using the given response handler. 6629 * 6630 * <p> 6631 * The content entity associated with the response is fully consumed and the underlying connection is released back 6632 * to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} 6633 * from having to manage resource deallocation internally. 6634 * 6635 * <h5 class='section'>Notes:</h5><ul> 6636 * <li class='note'>This method gets passed on directly to the underlying {@link HttpClient} class. 6637 * </ul> 6638 * 6639 * @param request The request to execute. 6640 * @param responseHandler The response handler. 6641 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 6642 * @return The response object as generated by the response handler. 6643 * @throws IOException In case of a problem or the connection was aborted. 6644 * @throws ClientProtocolException In case of an http protocol error. 6645 */ 6646 @Override /* Overridden from HttpClient */ 6647 public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException, ClientProtocolException { 6648 return httpClient.execute(request, responseHandler, context); 6649 } 6650 6651 /** 6652 * Same as {@link #formPost(Object, Object)} but doesn't specify the input yet. 6653 * 6654 * @param uri 6655 * The URI of the remote REST resource. 6656 * <br>Can be any of the following types: 6657 * <ul> 6658 * <li>{@link URIBuilder} 6659 * <li>{@link URI} 6660 * <li>{@link URL} 6661 * <li>{@link String} 6662 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6663 * </ul> 6664 * @return 6665 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6666 * as a parsed object. 6667 * @throws RestCallException If any authentication errors occurred. 6668 */ 6669 public RestRequest formPost(Object uri) throws RestCallException { 6670 return request(op("POST", uri, NO_BODY)); 6671 } 6672 6673 /** 6674 * Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> 6675 * against the specified URI. 6676 * 6677 * @param uri 6678 * The URI of the remote REST resource. 6679 * <br>Can be any of the following types: 6680 * <ul> 6681 * <li>{@link URIBuilder} 6682 * <li>{@link URI} 6683 * <li>{@link URL} 6684 * <li>{@link String} 6685 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6686 * </ul> 6687 * @param body 6688 * The object to serialize and transmit to the URI as the body of the request. 6689 * <ul class='spaced-list'> 6690 * <li>{@link NameValuePair} - URL-encoded as a single name-value pair. 6691 * <li>{@link NameValuePair} array - URL-encoded as name value pairs. 6692 * <li>{@link PartList} - URL-encoded as name value pairs. 6693 * <li>{@link Reader}/{@link InputStream}- Streamed directly and <l>Content-Type</l> set to <js>"application/x-www-form-urlencoded"</js> 6694 * <li>{@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 6695 * <li>{@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 6696 * <li>{@link Object} - Converted to a {@link SerializedEntity} using {@link UrlEncodingSerializer} to serialize. 6697 * <li>{@link Supplier} - A supplier of anything on this list. 6698 * </ul> 6699 * @return 6700 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6701 * as a parsed object. 6702 * @throws RestCallException If any authentication errors occurred. 6703 */ 6704 public RestRequest formPost(Object uri, Object body) throws RestCallException { 6705 var req = request(op("POST", uri, NO_BODY)); 6706 try { 6707 if (body instanceof Supplier body2) 6708 body = body2.get(); 6709 if (body instanceof NameValuePair body2) 6710 return req.content(new UrlEncodedFormEntity(l(body2))); 6711 if (body instanceof NameValuePair[]) 6712 return req.content(new UrlEncodedFormEntity(l((NameValuePair[])body))); 6713 if (body instanceof PartList body2) 6714 return req.content(new UrlEncodedFormEntity(body2)); 6715 if (body instanceof HttpResource body2) 6716 body2.getHeaders().forEach(x -> req.header(x)); 6717 if (body instanceof HttpEntity body3) { 6718 if (body3.getContentType() == null) 6719 req.header(ContentType.APPLICATION_FORM_URLENCODED); 6720 return req.content(body3); 6721 } 6722 if (body instanceof Reader || body instanceof InputStream) 6723 return req.header(ContentType.APPLICATION_FORM_URLENCODED).content(body); 6724 return req.content(serializedEntity(body, urlEncodingSerializer, null)); 6725 } catch (IOException e) { 6726 throw new RestCallException(null, e, "Could not read form post body."); 6727 } 6728 } 6729 6730 /** 6731 * Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> 6732 * against the specified URI. 6733 * 6734 * @param uri 6735 * The URI of the remote REST resource. 6736 * <br>Can be any of the following types: 6737 * <ul> 6738 * <li>{@link URIBuilder} 6739 * <li>{@link URI} 6740 * <li>{@link URL} 6741 * <li>{@link String} 6742 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6743 * </ul> 6744 * @param parameters 6745 * The parameters of the form post. 6746 * <br>The parameters represent name/value pairs and must be an even number of arguments. 6747 * <br>Parameters are converted to {@link BasicPart} objects. 6748 * @return 6749 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6750 * as a parsed object. 6751 * @throws RestCallException If any authentication errors occurred. 6752 */ 6753 public RestRequest formPostPairs(Object uri, String...parameters) throws RestCallException { 6754 return formPost(uri, partList(parameters)); 6755 } 6756 6757 /** 6758 * Perform a <c>GET</c> request against the root URI. 6759 * 6760 * @return 6761 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6762 * as a parsed object. 6763 * @throws RestCallException If any authentication errors occurred. 6764 */ 6765 public RestRequest get() throws RestCallException { 6766 return request(op("GET", null, NO_BODY)); 6767 } 6768 6769 /** 6770 * Perform a <c>GET</c> request against the specified URI. 6771 * 6772 * @param uri 6773 * The URI of the remote REST resource. 6774 * <br>Can be any of the following types: 6775 * <ul> 6776 * <li>{@link URIBuilder} 6777 * <li>{@link URI} 6778 * <li>{@link URL} 6779 * <li>{@link String} 6780 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 6781 * </ul> 6782 * @return 6783 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 6784 * as a parsed object. 6785 * @throws RestCallException If any authentication errors occurred. 6786 */ 6787 public RestRequest get(Object uri) throws RestCallException { 6788 return request(op("GET", uri, NO_BODY)); 6789 } 6790 6791 /** 6792 * Obtains the connection manager used by this client. 6793 * 6794 * @return The connection manager. 6795 * @deprecated Use {@link HttpClientBuilder}. 6796 */ 6797 @Deprecated 6798 @Override /* Overridden from HttpClient */ 6799 public ClientConnectionManager getConnectionManager() { return httpClient.getConnectionManager(); } 6800 6801 /** 6802 * Returns the connection manager if one was specified in the client builder. 6803 * 6804 * @return The connection manager. May be <jk>null</jk>. 6805 */ 6806 public HttpClientConnectionManager getHttpClientConnectionManager() { return connectionManager; } 6807 6808 /** 6809 * Obtains the parameters for this client. 6810 * 6811 * These parameters will become defaults for all requests being executed with this client, and for the parameters of dependent objects in this client. 6812 * 6813 * @return The default parameters. 6814 * @deprecated Use {@link RequestConfig}. 6815 */ 6816 @Deprecated 6817 @Override /* Overridden from HttpClient */ 6818 public HttpParams getParams() { return httpClient.getParams(); } 6819 6820 /** 6821 * Create a new proxy interface against a 3rd-party REST interface. 6822 * 6823 * <p> 6824 * The URI to the REST interface is based on the following values: 6825 * <ul> 6826 * <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). 6827 * <li>The {@link Builder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>). 6828 * <li>The fully-qualified class name of the interface (<c>class-name</c>). 6829 * </ul> 6830 * 6831 * <p> 6832 * The URI calculation is as follows: 6833 * <ul> 6834 * <li><c>remote-path</c> - If remote path is absolute. 6835 * <li><c>root-uri/remote-path</c> - If remote path is relative and root-uri has been specified. 6836 * <li><c>root-uri/class-name</c> - If remote path is not specified. 6837 * </ul> 6838 * 6839 * <p> 6840 * If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. 6841 * 6842 * <h5 class='section'>Examples:</h5> 6843 * <p class='bjava'> 6844 * <jk>package</jk> org.apache.foo; 6845 * 6846 * <ja>@RemoteResource</ja>(path=<js>"http://hostname/resturi/myinterface1"</js>) 6847 * <jk>public interface</jk> MyInterface1 { ... } 6848 * 6849 * <ja>@RemoteResource</ja>(path=<js>"/myinterface2"</js>) 6850 * <jk>public interface</jk> MyInterface2 { ... } 6851 * 6852 * <jk>public interface</jk> MyInterface3 { ... } 6853 * 6854 * <jc>// Resolves to "http://localhost/resturi/myinterface1"</jc> 6855 * MyInterface1 <jv>interface1</jv> = RestClient 6856 * .<jsm>create</jsm>() 6857 * .build() 6858 * .getRemote(MyInterface1.<jk>class</jk>); 6859 * 6860 * <jc>// Resolves to "http://hostname/resturi/myinterface2"</jc> 6861 * MyInterface2 <jv>interface2</jv> = RestClient 6862 * .<jsm>create</jsm>() 6863 * .rootUrl(<js>"http://hostname/resturi"</js>) 6864 * .build() 6865 * .getRemote(MyInterface2.<jk>class</jk>); 6866 * 6867 * <jc>// Resolves to "http://hostname/resturi/org.apache.foo.MyInterface3"</jc> 6868 * MyInterface3 <jv>interface3</jv> = RestClient 6869 * .<jsm>create</jsm>() 6870 * .rootUrl(<js>"http://hostname/resturi"</js>) 6871 * .build() 6872 * .getRemote(MyInterface3.<jk>class</jk>); 6873 * </p> 6874 * 6875 * <h5 class='section'>Notes:</h5><ul> 6876 * <li class='note'> 6877 * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying 6878 * pooling client connection manager. 6879 * </ul> 6880 * 6881 * <h5 class='section'>See Also:</h5><ul> 6882 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxyBasics">REST Proxy Basics</a> 6883 * </ul> 6884 * 6885 * @param <T> The interface to create a proxy for. 6886 * @param interfaceClass The interface to create a proxy for. 6887 * <br>Cannot be <jk>null</jk>. 6888 * @return The new proxy interface. 6889 * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. 6890 */ 6891 public <T> T getRemote(Class<T> interfaceClass) { 6892 return getRemote(interfaceClass, null); 6893 } 6894 6895 /** 6896 * Same as {@link #getRemote(Class)} except explicitly specifies the URI of the REST interface. 6897 * 6898 * <h5 class='section'>See Also:</h5><ul> 6899 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxyBasics">REST Proxy Basics</a> 6900 * </ul> 6901 * 6902 * @param <T> The interface to create a proxy for. 6903 * @param interfaceClass The interface to create a proxy for. 6904 * <br>Cannot be <jk>null</jk>. 6905 * @param rootUrl The URI of the REST interface. 6906 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 6907 * @return The new proxy interface. 6908 */ 6909 public <T> T getRemote(Class<T> interfaceClass, Object rootUrl) { 6910 return getRemote(interfaceClass, rootUrl, null, null); 6911 } 6912 6913 /** 6914 * Same as {@link #getRemote(Class, Object)} but allows you to override the serializer and parser used. 6915 * 6916 * <h5 class='section'>See Also:</h5><ul> 6917 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestProxyBasics">REST Proxy Basics</a> 6918 * </ul> 6919 6920 * @param <T> The interface to create a proxy for. 6921 * @param interfaceClass The interface to create a proxy for. 6922 * <br>Cannot be <jk>null</jk>. 6923 * @param rootUrl The URI of the REST interface. 6924 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 6925 * @param serializer The serializer used to serialize POJOs to the body of the HTTP request. 6926 * <br>Can be <jk>null</jk> (will use the default serializer from the client). 6927 * @param parser The parser used to parse POJOs from the body of the HTTP response. 6928 * <br>Can be <jk>null</jk> (will use the default parser from the client). 6929 * @return The new proxy interface. 6930 */ 6931 @SuppressWarnings({ "unchecked" }) 6932 public <T> T getRemote(Class<T> interfaceClass, Object rootUrl, Serializer serializer, Parser parser) { 6933 6934 if (rootUrl == null) 6935 rootUrl = this.rootUrl; 6936 6937 final String restUrl2 = trimSlashes(emptyIfNull(rootUrl)); 6938 6939 return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), a(interfaceClass), new InvocationHandler() { 6940 6941 final RemoteMeta rm = new RemoteMeta(interfaceClass); 6942 6943 @Override /* Overridden from InvocationHandler */ 6944 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 6945 var rom = rm.getOperationMeta(method); 6946 6947 var uri = rom.getFullPath(); 6948 if (uri.indexOf("://") == -1) 6949 uri = restUrl2 + '/' + uri; 6950 if (uri.indexOf("://") == -1) 6951 throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote resource."); 6952 6953 var httpMethod = rom.getHttpMethod(); 6954 var rc = request(httpMethod, uri, hasContent(httpMethod)); 6955 6956 if (serializer != null) rc.serializer(serializer); 6957 if (parser != null) rc.parser(parser); 6958 6959 rm.getHeaders().forEach(x -> rc.header(x)); 6960 6961 // Apply method-level defaults if parameter values are not provided (9.2.0) 6962 rom.forEachPathArg(a -> { 6963 var val = args[a.getIndex()]; 6964 if (val == null) { 6965 // Check parameter-level default first (9.2.0) 6966 var def = a.getSchema().getDefault(); 6967 // Fall back to method-level default if parameter-level not set 6968 if (def == null) 6969 def = rom.getPathDefault(a.getName()); 6970 if (nn(def)) 6971 val = def; 6972 } 6973 rc.pathArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer)); 6974 }); 6975 rom.forEachQueryArg(a -> { 6976 var val = args[a.getIndex()]; 6977 if (val == null) { 6978 // Check parameter-level default first (9.2.0) 6979 var def = a.getSchema().getDefault(); 6980 // Fall back to method-level default if parameter-level not set 6981 if (def == null) 6982 def = rom.getQueryDefault(a.getName()); 6983 if (nn(def)) 6984 val = def; 6985 } 6986 rc.queryArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 6987 }); 6988 rom.forEachFormDataArg(a -> { 6989 var val = args[a.getIndex()]; 6990 if (val == null) { 6991 // Check parameter-level default first (9.2.0) 6992 var def = a.getSchema().getDefault(); 6993 // Fall back to method-level default if parameter-level not set 6994 if (def == null) 6995 def = rom.getFormDataDefault(a.getName()); 6996 if (nn(def)) 6997 val = def; 6998 } 6999 rc.formDataArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 7000 }); 7001 rom.forEachHeaderArg(a -> { 7002 var val = args[a.getIndex()]; 7003 if (val == null) { 7004 // Check parameter-level default first (9.2.0) 7005 var def = a.getSchema().getDefault(); 7006 // Fall back to method-level default if parameter-level not set 7007 if (def == null) 7008 def = rom.getHeaderDefault(a.getName()); 7009 if (nn(def)) 7010 val = def; 7011 } 7012 rc.headerArg(a.getName(), val, a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty()); 7013 }); 7014 7015 var ba = rom.getContentArg(); 7016 if (nn(ba)) { 7017 var val = args[ba.getIndex()]; 7018 if (val == null) { 7019 // Check parameter-level default first (9.2.0) 7020 var def = ba.getSchema().getDefault(); 7021 // Fall back to method-level default if parameter-level not set 7022 if (def == null) 7023 def = rom.getContentDefault(); 7024 if (nn(def)) 7025 val = def; 7026 } 7027 rc.content(val, ba.getSchema()); 7028 } else { 7029 // Apply Content default if no parameter is present 7030 var contentDef = rom.getContentDefault(); 7031 if (nn(contentDef)) 7032 rc.content(contentDef); 7033 } 7034 7035 rom.forEachRequestArg(rmba -> { 7036 var rbm = rmba.getMeta(); 7037 var bean = args[rmba.getIndex()]; 7038 if (nn(bean)) { 7039 for (var p : rbm.getProperties()) { 7040 var val = safeSupplier(() -> p.getGetter().invoke(bean)); 7041 var pt = p.getPartType(); 7042 var pn = p.getPartName(); 7043 var schema = p.getSchema(); 7044 if (pt == PATH) 7045 rc.pathArg(pn, val, schema, p.getSerializer().orElse(partSerializer)); 7046 else if (nn(val)) { 7047 if (pt == QUERY) 7048 rc.queryArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 7049 else if (pt == FORMDATA) 7050 rc.formDataArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 7051 else if (pt == HEADER) 7052 rc.headerArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); 7053 else /* (pt == HttpPartType.BODY) */ 7054 rc.content(val, schema); 7055 } 7056 } 7057 } 7058 }); 7059 7060 var ror = rom.getReturns(); 7061 if (ror.isFuture()) { 7062 return getExecutorService().submit(() -> { 7063 try { 7064 return executeRemote(interfaceClass, rc, method, rom); 7065 } catch (Exception e) { 7066 throw e; 7067 } catch (Throwable e) { 7068 throw toRex(e); 7069 } 7070 }); 7071 } else if (ror.isCompletableFuture()) { 7072 var cf = new CompletableFuture<>(); 7073 getExecutorService().submit(() -> { 7074 try { 7075 cf.complete(executeRemote(interfaceClass, rc, method, rom)); 7076 } catch (Throwable e) { 7077 cf.completeExceptionally(e); 7078 } 7079 return null; 7080 }); 7081 return cf; 7082 } 7083 7084 return executeRemote(interfaceClass, rc, method, rom); 7085 } 7086 }); 7087 } 7088 7089 /** 7090 * Create a new proxy interface against an RRPC-style service. 7091 * 7092 * <p> 7093 * Remote interfaces are interfaces exposed on the server side using either the <c>RrpcServlet</c> 7094 * or <c>RRPC</c> REST methods. 7095 * 7096 * <p> 7097 * The URI to the REST interface is based on the following values: 7098 * <ul> 7099 * <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). 7100 * <li>The {@link Builder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>). 7101 * <li>The fully-qualified class name of the interface (<c>class-name</c>). 7102 * </ul> 7103 * 7104 * <p> 7105 * The URI calculation is as follows: 7106 * <ul> 7107 * <li><c>remote-path</c> - If remote path is absolute. 7108 * <li><c>root-url/remote-path</c> - If remote path is relative and root-url has been specified. 7109 * <li><c>root-url/class-name</c> - If remote path is not specified. 7110 * </ul> 7111 * 7112 * <p> 7113 * If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. 7114 * 7115 * <h5 class='section'>Notes:</h5><ul> 7116 * <li class='note'> 7117 * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying 7118 * pooling client connection manager. 7119 * </ul> 7120 * 7121 * <h5 class='section'>See Also:</h5><ul> 7122 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7123 * </ul> 7124 * 7125 * @param <T> The interface to create a proxy for. 7126 * @param interfaceClass The interface to create a proxy for. 7127 * <br>Cannot be <jk>null</jk>. 7128 * @return The new proxy interface. 7129 * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. 7130 */ 7131 public <T> T getRrpcInterface(Class<T> interfaceClass) { 7132 return getRrpcInterface(interfaceClass, null); 7133 } 7134 7135 /** 7136 * Same as {@link #getRrpcInterface(Class)} except explicitly specifies the URI of the REST interface. 7137 * 7138 * <h5 class='section'>See Also:</h5><ul> 7139 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7140 * </ul> 7141 * 7142 * @param <T> The interface to create a proxy for. 7143 * @param interfaceClass The interface to create a proxy for. 7144 * <br>Cannot be <jk>null</jk>. 7145 * @param uri The URI of the REST interface. 7146 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7147 * @return The new proxy interface. 7148 */ 7149 public <T> T getRrpcInterface(Class<T> interfaceClass, Object uri) { 7150 return getRrpcInterface(interfaceClass, uri, null, null); 7151 } 7152 7153 /** 7154 * Same as {@link #getRrpcInterface(Class, Object)} but allows you to override the serializer and parser used. 7155 * 7156 * <h5 class='section'>See Also:</h5><ul> 7157 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestRpc">REST/RPC</a> 7158 * </ul> 7159 * 7160 * @param <T> The interface to create a proxy for. 7161 * @param interfaceClass The interface to create a proxy for. 7162 * <br>Cannot be <jk>null</jk>. 7163 * @param uri The URI of the REST interface. 7164 * <br>Can be <jk>null</jk> (will use the root URL from the client builder if set). 7165 * @param serializer The serializer used to serialize POJOs to the body of the HTTP request. 7166 * <br>Can be <jk>null</jk> (will use the default serializer from the client). 7167 * @param parser The parser used to parse POJOs from the body of the HTTP response. 7168 * <br>Can be <jk>null</jk> (will use the default parser from the client). 7169 * @return The new proxy interface. 7170 */ 7171 @SuppressWarnings({ "unchecked" }) 7172 public <T> T getRrpcInterface(Class<T> interfaceClass, Object uri, Serializer serializer, Parser parser) { 7173 7174 if (uri == null) { 7175 var rm = new RrpcInterfaceMeta(interfaceClass, ""); 7176 var path = rm.getPath(); 7177 if (path.indexOf("://") == -1) { 7178 if (isEmpty(rootUrl)) 7179 throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote interface."); 7180 path = trimSlashes(rootUrl) + '/' + path; 7181 } 7182 uri = path; 7183 } 7184 7185 final String restUrl2 = s(uri); 7186 7187 return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), a(interfaceClass), new InvocationHandler() { 7188 7189 final RrpcInterfaceMeta rm = new RrpcInterfaceMeta(interfaceClass, restUrl2); 7190 7191 @Override /* Overridden from InvocationHandler */ 7192 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 7193 var rim = rm.getMethodMeta(method); 7194 7195 var uri = rim.getUri(); 7196 var res = (RestResponse)null; 7197 7198 try { 7199 // @formatter:off 7200 RestRequest rc = request("POST", uri, true) 7201 .serializer(serializer) 7202 .content(args) 7203 .rethrow(RuntimeException.class) 7204 .rethrow(method.getExceptionTypes()); 7205 // @formatter:on 7206 7207 res = rc.run(); 7208 7209 var v = res.getContent().as(method.getGenericReturnType()); 7210 if (v == null && method.getReturnType().isPrimitive()) 7211 v = ClassInfo.of(method.getReturnType()).getPrimitiveDefault(); 7212 return v; 7213 7214 } catch (Throwable e) { 7215 if (e instanceof RestCallException e2) { 7216 var t = e2.getCause(); 7217 if (nn(t)) 7218 e = t; 7219 } 7220 if (e instanceof RuntimeException e2) 7221 throw e2; 7222 for (var t2 : method.getExceptionTypes()) 7223 if (t2.isInstance(e)) 7224 throw e; 7225 throw toRex(e); 7226 } 7227 } 7228 }); 7229 } 7230 7231 /** 7232 * Perform a <c>HEAD</c> request against the specified URI. 7233 * 7234 * @param uri 7235 * The URI of the remote REST resource. 7236 * <br>Can be any of the following types: 7237 * <ul> 7238 * <li>{@link URIBuilder} 7239 * <li>{@link URI} 7240 * <li>{@link URL} 7241 * <li>{@link String} 7242 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7243 * </ul> 7244 * @return 7245 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7246 * as a parsed object. 7247 * @throws RestCallException If any authentication errors occurred. 7248 */ 7249 public RestRequest head(Object uri) throws RestCallException { 7250 return request(op("HEAD", uri, NO_BODY)); 7251 } 7252 7253 /** 7254 * Perform an <c>OPTIONS</c> request against the specified URI. 7255 * 7256 * @param uri 7257 * The URI of the remote REST resource. 7258 * <br>Can be any of the following types: 7259 * <ul> 7260 * <li>{@link URIBuilder} 7261 * <li>{@link URI} 7262 * <li>{@link URL} 7263 * <li>{@link String} 7264 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7265 * </ul> 7266 * @return 7267 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7268 * as a parsed object. 7269 * @throws RestCallException If any authentication errors occurred. 7270 */ 7271 public RestRequest options(Object uri) throws RestCallException { 7272 return request(op("OPTIONS", uri, NO_BODY)); 7273 } 7274 7275 /** 7276 * Same as {@link #patch(Object, Object)} but don't specify the input yet. 7277 * 7278 * <p> 7279 * You must call {@link RestRequest#content(Object)} to set the contents on the result object. 7280 * 7281 * @param uri 7282 * The URI of the remote REST resource. 7283 * <br>Can be any of the following types: 7284 * <ul> 7285 * <li>{@link URIBuilder} 7286 * <li>{@link URI} 7287 * <li>{@link URL} 7288 * <li>{@link String} 7289 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7290 * </ul> 7291 * @return 7292 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7293 * as a parsed object. 7294 * @throws RestCallException REST call failed. 7295 */ 7296 public RestRequest patch(Object uri) throws RestCallException { 7297 return request(op("PATCH", uri, NO_BODY)); 7298 } 7299 7300 /** 7301 * Perform a <c>PATCH</c> request against the specified URI. 7302 * 7303 * @param uri 7304 * The URI of the remote REST resource. 7305 * <br>Can be any of the following types: 7306 * <ul> 7307 * <li>{@link URIBuilder} 7308 * <li>{@link URI} 7309 * <li>{@link URL} 7310 * <li>{@link String} 7311 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7312 * </ul> 7313 * @param body 7314 * The object to serialize and transmit to the URI as the body of the request. 7315 * Can be of the following types: 7316 * <ul class='spaced-list'> 7317 * <li> 7318 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7319 * <li> 7320 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7321 * <li> 7322 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 7323 * <li> 7324 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7325 * <li> 7326 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7327 * {@link RestClient}. 7328 * <li> 7329 * {@link PartList} - Converted to a URL-encoded FORM post. 7330 * <li> 7331 * {@link Supplier} - A supplier of anything on this list. 7332 * </ul> 7333 * @return 7334 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7335 * as a parsed object. 7336 * @throws RestCallException If any authentication errors occurred. 7337 */ 7338 public RestRequest patch(Object uri, Object body) throws RestCallException { 7339 return request(op("PATCH", uri, body)); 7340 } 7341 7342 /** 7343 * Perform a <c>PATCH</c> request against the specified URI as a plain text body bypassing the serializer. 7344 * 7345 * @param uri 7346 * The URI of the remote REST resource. 7347 * <br>Can be any of the following types: 7348 * <ul> 7349 * <li>{@link URIBuilder} 7350 * <li>{@link URI} 7351 * <li>{@link URL} 7352 * <li>{@link String} 7353 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7354 * </ul> 7355 * @param body 7356 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7357 * @param contentType 7358 * The content type of the request. 7359 * @return 7360 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7361 * as a parsed object. 7362 * @throws RestCallException If any authentication errors occurred. 7363 */ 7364 public RestRequest patch(Object uri, String body, ContentType contentType) throws RestCallException { 7365 return request(op("PATCH", uri, stringBody(body))).header(contentType); 7366 } 7367 7368 /** 7369 * Same as {@link #post(Object, Object)} but don't specify the input yet. 7370 * 7371 * <p> 7372 * You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} to set the 7373 * contents on the result object. 7374 * 7375 * <h5 class='section'>Notes:</h5><ul> 7376 * <li class='note'>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. 7377 * </ul> 7378 * 7379 * @param uri 7380 * The URI of the remote REST resource. 7381 * <br>Can be any of the following types: 7382 * <ul> 7383 * <li>{@link URIBuilder} 7384 * <li>{@link URI} 7385 * <li>{@link URL} 7386 * <li>{@link String} 7387 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7388 * </ul> 7389 * @return 7390 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7391 * as a parsed object. 7392 * @throws RestCallException REST call failed. 7393 */ 7394 public RestRequest post(Object uri) throws RestCallException { 7395 return request(op("POST", uri, NO_BODY)); 7396 } 7397 7398 /** 7399 * Perform a <c>POST</c> request against the specified URI. 7400 * 7401 * <h5 class='section'>Notes:</h5><ul> 7402 * <li class='note'>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. 7403 * </ul> 7404 * 7405 * @param uri 7406 * The URI of the remote REST resource. 7407 * <br>Can be any of the following types: 7408 * <ul> 7409 * <li>{@link URIBuilder} 7410 * <li>{@link URI} 7411 * <li>{@link URL} 7412 * <li>{@link String} 7413 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7414 * </ul> 7415 * @param body 7416 * The object to serialize and transmit to the URI as the body of the request. 7417 * Can be of the following types: 7418 * <ul class='spaced-list'> 7419 * <li> 7420 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7421 * <li> 7422 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7423 * <li> 7424 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7425 * {@link RestClient}. 7426 * <li> 7427 * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7428 * <li> 7429 * {@link PartList} - Converted to a URL-encoded FORM post. 7430 * <li> 7431 * {@link Supplier} - A supplier of anything on this list. 7432 * </ul> 7433 * @return 7434 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7435 * as a parsed object. 7436 * @throws RestCallException If any authentication errors occurred. 7437 */ 7438 public RestRequest post(Object uri, Object body) throws RestCallException { 7439 return request(op("POST", uri, body)); 7440 } 7441 7442 /** 7443 * Perform a <c>POST</c> request against the specified URI as a plain text body bypassing the serializer. 7444 * 7445 * @param uri 7446 * The URI of the remote REST resource. 7447 * <br>Can be any of the following types: 7448 * <ul> 7449 * <li>{@link URIBuilder} 7450 * <li>{@link URI} 7451 * <li>{@link URL} 7452 * <li>{@link String} 7453 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7454 * </ul> 7455 * @param body 7456 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7457 * @param contentType 7458 * The content type of the request. 7459 * @return 7460 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7461 * as a parsed object. 7462 * @throws RestCallException If any authentication errors occurred. 7463 */ 7464 public RestRequest post(Object uri, String body, ContentType contentType) throws RestCallException { 7465 return request(op("POST", uri, stringBody(body))).header(contentType); 7466 } 7467 7468 /** 7469 * Same as {@link #put(Object, Object)} but don't specify the input yet. 7470 * 7471 * <p> 7472 * You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} 7473 * to set the contents on the result object. 7474 * 7475 * @param uri 7476 * The URI of the remote REST resource. 7477 * <br>Can be any of the following types: 7478 * <ul> 7479 * <li>{@link URIBuilder} 7480 * <li>{@link URI} 7481 * <li>{@link URL} 7482 * <li>{@link String} 7483 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7484 * </ul> 7485 * @return 7486 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7487 * as a parsed object. 7488 * @throws RestCallException REST call failed. 7489 */ 7490 public RestRequest put(Object uri) throws RestCallException { 7491 return request(op("PUT", uri, NO_BODY)); 7492 } 7493 7494 /** 7495 * Perform a <c>PUT</c> request against the specified URI. 7496 * 7497 * @param uri 7498 * The URI of the remote REST resource. 7499 * <br>Can be any of the following types: 7500 * <ul> 7501 * <li>{@link URIBuilder} 7502 * <li>{@link URI} 7503 * <li>{@link URL} 7504 * <li>{@link String} 7505 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7506 * </ul> 7507 * @param body 7508 * The object to serialize and transmit to the URI as the body of the request. 7509 * Can be of the following types: 7510 * <ul class='spaced-list'> 7511 * <li> 7512 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7513 * <li> 7514 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7515 * <li> 7516 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7517 * {@link RestClient}. 7518 * <li> 7519 * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7520 * <li> 7521 * {@link PartList} - Converted to a URL-encoded FORM post. 7522 * <li> 7523 * {@link Supplier} - A supplier of anything on this list. 7524 * </ul> 7525 * @return 7526 * A {@link RestRequest} object that can be further tailored before executing the request 7527 * and getting the response as a parsed object. 7528 * @throws RestCallException If any authentication errors occurred. 7529 */ 7530 public RestRequest put(Object uri, Object body) throws RestCallException { 7531 return request(op("PUT", uri, body)); 7532 } 7533 7534 /** 7535 * Perform a <c>PUT</c> request against the specified URI using a plain text body bypassing the serializer. 7536 * 7537 * @param uri 7538 * The URI of the remote REST resource. 7539 * <br>Can be any of the following types: 7540 * <ul> 7541 * <li>{@link URIBuilder} 7542 * <li>{@link URI} 7543 * <li>{@link URL} 7544 * <li>{@link String} 7545 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7546 * </ul> 7547 * @param body 7548 * The object to serialize and transmit to the URI as the body of the request bypassing the serializer. 7549 * @param contentType The content type of the request. 7550 * @return 7551 * A {@link RestRequest} object that can be further tailored before executing the request 7552 * and getting the response as a parsed object. 7553 * @throws RestCallException If any authentication errors occurred. 7554 */ 7555 public RestRequest put(Object uri, String body, ContentType contentType) throws RestCallException { 7556 return request(op("PUT", uri, stringBody(body))).header(contentType); 7557 } 7558 7559 /** 7560 * Perform a generic REST call. 7561 * 7562 * @param method The HTTP method. 7563 * <br>Cannot be <jk>null</jk>. 7564 * @param uri 7565 * The URI of the remote REST resource. 7566 * <br>Can be any of the following types: 7567 * <ul> 7568 * <li>{@link URIBuilder} 7569 * <li>{@link URI} 7570 * <li>{@link URL} 7571 * <li>{@link String} 7572 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7573 * </ul> 7574 * <br>Can be <jk>null</jk> (will result in an invalid request). 7575 * @return 7576 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7577 * as a parsed object. 7578 * @throws RestCallException If any authentication errors occurred. 7579 */ 7580 public RestRequest request(String method, Object uri) throws RestCallException { 7581 return request(op(method, uri, NO_BODY)); 7582 } 7583 7584 /** 7585 * Perform a generic REST call. 7586 * 7587 * <p> 7588 * Typically you're going to use {@link #request(String, Object)} or {@link #request(String, Object, Object)}, 7589 * but this method is provided to allow you to perform non-standard HTTP methods (e.g. HTTP FOO). 7590 * 7591 * @param method The method name (e.g. <js>"GET"</js>, <js>"OPTIONS"</js>). 7592 * <br>Cannot be <jk>null</jk>. 7593 * @param uri 7594 * The URI of the remote REST resource. 7595 * <br>Can be any of the following types: 7596 * <ul> 7597 * <li>{@link URIBuilder} 7598 * <li>{@link URI} 7599 * <li>{@link URL} 7600 * <li>{@link String} 7601 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7602 * </ul> 7603 * <br>Can be <jk>null</jk> (will result in an invalid request). 7604 * @param hasBody Boolean flag indicating if the specified request has content associated with it. 7605 * @return 7606 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7607 * as a parsed object. 7608 * @throws RestCallException If any authentication errors occurred. 7609 */ 7610 public RestRequest request(String method, Object uri, boolean hasBody) throws RestCallException { 7611 return request(op(method, uri, NO_BODY).hasContent(hasBody)); 7612 } 7613 7614 /** 7615 * Perform a generic REST call. 7616 * 7617 * @param method The HTTP method. 7618 * <br>Cannot be <jk>null</jk>. 7619 * @param uri 7620 * The URI of the remote REST resource. 7621 * <br>Can be any of the following types: 7622 * <ul> 7623 * <li>{@link URIBuilder} 7624 * <li>{@link URI} 7625 * <li>{@link URL} 7626 * <li>{@link String} 7627 * <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> 7628 * </ul> 7629 * <br>Can be <jk>null</jk> (will result in an invalid request). 7630 * @param body 7631 * The HTTP body content. 7632 * Can be of the following types: 7633 * <ul class='spaced-list'> 7634 * <li> 7635 * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. 7636 * <li> 7637 * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. 7638 * <li> 7639 * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. 7640 * <li> 7641 * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. 7642 * <li> 7643 * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the 7644 * {@link RestClient}. 7645 * <li> 7646 * {@link PartList} - Converted to a URL-encoded FORM post. 7647 * <li> 7648 * {@link Supplier} - A supplier of anything on this list. 7649 * </ul> 7650 * This parameter is IGNORED if the method type normally does not have content. 7651 * @return 7652 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7653 * as a parsed object. 7654 * @throws RestCallException If any authentication errors occurred. 7655 */ 7656 public RestRequest request(String method, Object uri, Object body) throws RestCallException { 7657 return request(op(method, uri, body)); 7658 } 7659 7660 private static RestOperation op(String method, Object url, Object body) { 7661 return RestOperation.of(method, url, body); 7662 } 7663 7664 private static Reader stringBody(String body) { 7665 return body == null ? null : new StringReader(s(body)); 7666 } 7667 7668 /** 7669 * Creates a {@link RestRequest} object from the specified {@link HttpRequest} object. 7670 * 7671 * <p> 7672 * Subclasses can override this method to provide their own specialized {@link RestRequest} objects. 7673 * 7674 * @param uri The target. 7675 * @param method The HTTP method (uppercase). 7676 * @param hasBody Whether this method has a request entity. 7677 * @return A new {@link RestRequest} object. 7678 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. 7679 */ 7680 protected RestRequest createRequest(URI uri, String method, boolean hasBody) throws RestCallException { 7681 return new RestRequest(this, uri, method, hasBody); 7682 } 7683 7684 /** 7685 * Creates a {@link RestResponse} object from the specified {@link HttpResponse} object. 7686 * 7687 * <p> 7688 * Subclasses can override this method to provide their own specialized {@link RestResponse} objects. 7689 * 7690 * @param request The request creating this response. 7691 * @param httpResponse The response object to wrap. 7692 * @param parser The parser to use to parse the response. 7693 * 7694 * @return A new {@link RestResponse} object. 7695 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. 7696 */ 7697 protected RestResponse createResponse(RestRequest request, HttpResponse httpResponse, Parser parser) throws RestCallException { 7698 return new RestResponse(this, request, httpResponse, parser); 7699 } 7700 7701 @Override 7702 protected void finalize() throws Throwable { 7703 if (detectLeaks && ! isClosed.get() && ! keepHttpClientOpen) { 7704 var sb = new StringBuilder("WARNING: RestClient garbage collected before it was finalized."); // NOT DEBUG 7705 if (nn(creationStack)) { 7706 sb.append("\nCreation Stack:"); // NOT DEBUG 7707 for (var e : creationStack) 7708 sb.append("\n\t" + e); // NOT DEBUG 7709 } 7710 log(WARNING, sb.toString()); 7711 } 7712 } 7713 7714 /** 7715 * Returns the part parser associated with this client. 7716 * 7717 * @return The part parser associated with this client. 7718 */ 7719 protected HttpPartParser getPartParser() { return partParser; } 7720 7721 /** 7722 * Returns the part parser instance of the specified type. 7723 * 7724 * @param c The part parser class. 7725 * @return The part parser. 7726 */ 7727 protected HttpPartParser getPartParser(Class<? extends HttpPartParser> c) { 7728 var x = partParsers.get(c); 7729 if (x == null) { 7730 try { 7731 x = beanStore.createBean(c).run(); 7732 } catch (ExecutableException e) { 7733 throw toRex(e); 7734 } 7735 partParsers.put(c, x); 7736 } 7737 return x; 7738 } 7739 7740 /** 7741 * Returns the part serializer associated with this client. 7742 * 7743 * @return The part serializer associated with this client. 7744 */ 7745 protected HttpPartSerializer getPartSerializer() { return partSerializer; } 7746 7747 /** 7748 * Returns the part serializer instance of the specified type. 7749 * 7750 * @param c The part serializer class. 7751 * @return The part serializer. 7752 */ 7753 protected HttpPartSerializer getPartSerializer(Class<? extends HttpPartSerializer> c) { 7754 var x = partSerializers.get(c); 7755 if (x == null) { 7756 try { 7757 x = beanStore.createBean(c).run(); 7758 } catch (ExecutableException e) { 7759 throw toRex(e); 7760 } 7761 partSerializers.put(c, x); 7762 } 7763 return x; 7764 } 7765 7766 /** 7767 * Gets called add the end of the constructor call to perform any post-initialization. 7768 */ 7769 protected void init() {} 7770 7771 /** 7772 * Returns <jk>true</jk> if empty request form-data parameter values should be ignored. 7773 * 7774 * @return <jk>true</jk> if empty request form-data parameter values should be ignored. 7775 */ 7776 protected boolean isSkipEmptyFormData() { return skipEmptyFormData; } 7777 7778 /** 7779 * Returns <jk>true</jk> if empty request header values should be ignored. 7780 * 7781 * @return <jk>true</jk> if empty request header values should be ignored. 7782 */ 7783 protected boolean isSkipEmptyHeaderData() { return skipEmptyHeaderData; } 7784 7785 /** 7786 * Returns <jk>true</jk> if empty request query parameter values should be ignored. 7787 * 7788 * @return <jk>true</jk> if empty request query parameter values should be ignored. 7789 */ 7790 protected boolean isSkipEmptyQueryData() { return skipEmptyQueryData; } 7791 7792 /** 7793 * Logs a message. 7794 * 7795 * @param level The log level. 7796 * @param msg The message with {@link MessageFormat}-style arguments. 7797 * @param args The arguments. 7798 */ 7799 protected void log(Level level, String msg, Object...args) { 7800 logger.log(level, f(msg, args)); 7801 if (logToConsole) 7802 console.println(f(msg, args)); 7803 } 7804 7805 /** 7806 * Logs a message. 7807 * 7808 * @param level The log level. 7809 * @param t Thrown exception. 7810 * <br>Can be <jk>null</jk> (no exception stack trace will be logged). 7811 * @param msg The message. 7812 * @param args Optional message arguments. 7813 */ 7814 protected void log(Level level, Throwable t, String msg, Object...args) { 7815 logger.log(level, t, fs(msg, args)); 7816 if (logToConsole) { 7817 console.println(f(msg, args)); 7818 if (nn(t)) 7819 t.printStackTrace(console); 7820 } 7821 } 7822 7823 /** 7824 * 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. 7825 * 7826 * <p> 7827 * Subclasses can override this method to handle any cleanup operations. 7828 * 7829 * <h5 class='section'>See Also:</h5><ul> 7830 * <li class='jm'>{@link Builder#interceptors(Object...)} 7831 * </ul> 7832 * 7833 * @param req The HTTP request. 7834 * @param res The HTTP response. 7835 * @throws RestCallException If any of the interceptors threw an exception. 7836 */ 7837 protected void onCallClose(RestRequest req, RestResponse res) throws RestCallException { 7838 try { 7839 for (var rci : interceptors) 7840 rci.onClose(req, res); 7841 } catch (RuntimeException | RestCallException e) { 7842 throw e; 7843 } catch (Exception e) { 7844 throw new RestCallException(res, e, "Interceptor threw an exception on close."); 7845 } 7846 } 7847 7848 /** 7849 * Interceptor method called immediately after an HTTP response has been received. 7850 * 7851 * <p> 7852 * Subclasses can override this method to intercept the response and perform special modifications. 7853 * 7854 * <h5 class='section'>See Also:</h5><ul> 7855 * <li class='jm'>{@link Builder#interceptors(Object...)} 7856 * </ul> 7857 * 7858 * @param req The HTTP request. 7859 * @param res The HTTP response. 7860 * @throws RestCallException If any of the interceptors threw an exception. 7861 */ 7862 protected void onCallConnect(RestRequest req, RestResponse res) throws RestCallException { 7863 try { 7864 for (var rci : interceptors) 7865 rci.onConnect(req, res); 7866 } catch (RuntimeException | RestCallException e) { 7867 throw e; 7868 } catch (Exception e) { 7869 throw new RestCallException(res, e, "Interceptor threw an exception on connect."); 7870 } 7871 } 7872 7873 /** 7874 * Interceptor method called immediately after the RestRequest object is created and all headers/query/form-data has been copied from the client. 7875 * 7876 * <p> 7877 * Subclasses can override this method to intercept the request and perform special modifications. 7878 * 7879 * <h5 class='section'>See Also:</h5><ul> 7880 * <li class='jm'>{@link Builder#interceptors(Object...)} 7881 * </ul> 7882 * 7883 * @param req The HTTP request. 7884 * @throws RestCallException If any of the interceptors threw an exception. 7885 */ 7886 protected void onCallInit(RestRequest req) throws RestCallException { 7887 try { 7888 for (var rci : interceptors) 7889 rci.onInit(req); 7890 } catch (RuntimeException | RestCallException e) { 7891 throw e; 7892 } catch (Exception e) { 7893 throw new RestCallException(null, e, "Interceptor threw an exception on init."); 7894 } 7895 } 7896 7897 @Override /* Overridden from BeanContextable */ 7898 protected FluentMap<String,Object> properties() { 7899 return super.properties() 7900 .a("errorCodes", errorCodes) 7901 .a("executorService", executorService.get()) 7902 .a("executorServiceShutdownOnClose", executorServiceShutdownOnClose) 7903 .a("headerData", headerData) 7904 .a("interceptors", interceptors) 7905 .a("keepHttpClientOpen", keepHttpClientOpen) 7906 .a("partParser", partParser) 7907 .a("partSerializer", partSerializer) 7908 .a("queryData", queryData) 7909 .a("rootUrl", rootUrl); 7910 } 7911 7912 /** 7913 * Perform an arbitrary request against the specified URI. 7914 * 7915 * <p> 7916 * All requests feed through this method so it can be used to intercept request creations and make modifications 7917 * (such as add headers). 7918 * 7919 * @param op The operation that identifies the HTTP method, URL, and optional payload. 7920 * @return 7921 * A {@link RestRequest} object that can be further tailored before executing the request and getting the response 7922 * as a parsed object. 7923 * @throws RestCallException If any authentication errors occurred. 7924 */ 7925 protected RestRequest request(RestOperation op) throws RestCallException { 7926 if (isClosed.get()) { 7927 var e2 = (Exception)null; 7928 if (nn(closedStack)) { 7929 e2 = new Exception("Creation stack:"); 7930 e2.setStackTrace(closedStack); 7931 throw new RestCallException(null, e2, "RestClient.close() has already been called. This client cannot be reused."); 7932 } 7933 throw new RestCallException(null, null, 7934 "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."); 7935 } 7936 7937 var req = createRequest(toUri(op.getUri(), rootUrl), op.getMethod(), op.hasContent()); 7938 7939 onCallInit(req); 7940 7941 req.content(op.getContent()); 7942 7943 return req; 7944 } 7945 7946 /** 7947 * Entrypoint for executing all requests and returning a response. 7948 * 7949 * <p> 7950 * Subclasses can override this method to provide specialized handling. 7951 * 7952 * <p> 7953 * The behavior of this method can also be modified by specifying a different {@link RestCallHandler}. 7954 * 7955 * <h5 class='section'>See Also:</h5><ul> 7956 * <li class='jm'>{@link Builder#callHandler()} 7957 * </ul> 7958 * 7959 * @param target The target host for the request. 7960 * <br>Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default 7961 * target or by inspecting the request. 7962 * @param request The request to execute. 7963 * @param context The context to use for the execution, or <jk>null</jk> to use the default context. 7964 * @return 7965 * The response to the request. 7966 * <br>This is always a final response, never an intermediate response with an 1xx status code. 7967 * <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the 7968 * implementation and configuration of this client. 7969 * @throws IOException In case of a problem or the connection was aborted. 7970 * @throws ClientProtocolException In case of an http protocol error. 7971 */ 7972 protected HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws ClientProtocolException, IOException { 7973 return callHandler.run(target, request, context); 7974 } 7975 7976 Object executeRemote(Class<?> interfaceClass, RestRequest rc, Method method, RemoteOperationMeta rom) throws Throwable { 7977 RemoteOperationReturn ror = rom.getReturns(); 7978 7979 try { 7980 var ret = (Object)null; 7981 var res = (RestResponse)null; 7982 rc.rethrow(RuntimeException.class); 7983 rom.forEachException(x -> rc.rethrow(x)); 7984 if (ror.getReturnValue() == RemoteReturn.NONE) { 7985 res = rc.complete(); 7986 } else if (ror.getReturnValue() == RemoteReturn.STATUS) { 7987 res = rc.complete(); 7988 int returnCode = res.getStatusCode(); 7989 var rt = method.getReturnType(); 7990 if (rt == Integer.class || rt == int.class) 7991 ret = returnCode; 7992 else if (rt == Boolean.class || rt == boolean.class) 7993 ret = returnCode < 400; 7994 else 7995 throw new RestCallException(res, null, "Invalid return type on method annotated with @RemoteOp(returns=RemoteReturn.STATUS). Only integer and booleans types are valid."); 7996 } else if (ror.getReturnValue() == RemoteReturn.BEAN) { 7997 rc.ignoreErrors(); 7998 res = rc.run(); 7999 ret = res.as(ror.getResponseBeanMeta()); 8000 } else { 8001 var rt = method.getReturnType(); 8002 if (Throwable.class.isAssignableFrom(rt)) 8003 rc.ignoreErrors(); 8004 res = rc.run(); 8005 Object v = res.getContent().as(ror.getReturnType()); 8006 if (v == null && rt.isPrimitive()) 8007 v = ClassInfo.of(rt).getPrimitiveDefault(); 8008 ret = v; 8009 } 8010 return ret; 8011 } catch (RestCallException e) { 8012 Throwable t = e.getCause(); 8013 if (t instanceof RuntimeException t2) 8014 throw t2; 8015 for (var t3 : method.getExceptionTypes()) 8016 if (t3.isInstance(t)) 8017 throw t; 8018 throw toRex(e); 8019 } 8020 } 8021 8022 ExecutorService getExecutorService() { 8023 ExecutorService result = executorService.get(); 8024 if (nn(result)) 8025 return result; 8026 synchronized (this) { 8027 result = executorService.get(); 8028 if (nn(result)) 8029 return result; 8030 result = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); 8031 executorService.set(result); 8032 return result; 8033 } 8034 } 8035 8036 @SuppressWarnings("unchecked") 8037 <T extends Context> T getInstance(Class<T> c) { 8038 var o = requestContexts.get(c); 8039 if (o == null) { 8040 if (Serializer.class.isAssignableFrom(c)) { 8041 o = Serializer.createSerializerBuilder((Class<? extends Serializer>)c).beanContext(getBeanContext()).build(); 8042 } else if (Parser.class.isAssignableFrom(c)) { 8043 o = Parser.createParserBuilder((Class<? extends Parser>)c).beanContext(getBeanContext()).build(); 8044 } 8045 requestContexts.put(c, o); 8046 } 8047 return (T)o; 8048 } 8049 8050 /* 8051 * Returns the parser that best matches the specified content type. 8052 * If no match found or the content type is null, returns the parser in the list if it's a list of one. 8053 * Returns null if no parsers are defined. 8054 */ 8055 Parser getMatchingParser(String mediaType) { 8056 if (parsers.isEmpty()) 8057 return null; 8058 if (nn(mediaType)) { 8059 var p = parsers.getParser(mediaType); 8060 if (nn(p)) 8061 return p; 8062 } 8063 var l = parsers.getParsers(); 8064 return (l.size() == 1 ? l.get(0) : null); 8065 } 8066 8067 /* 8068 * Returns the serializer that best matches the specified content type. 8069 * If no match found or the content type is null, returns the serializer in the list if it's a list of one. 8070 * Returns null if no serializers are defined. 8071 */ 8072 Serializer getMatchingSerializer(String mediaType) { 8073 if (serializers.isEmpty()) 8074 return null; 8075 if (nn(mediaType)) { 8076 var s = serializers.getSerializer(mediaType); 8077 if (nn(s)) 8078 return s; 8079 } 8080 var l = serializers.getSerializers(); 8081 return (l.size() == 1 ? l.get(0) : null); 8082 } 8083 8084 boolean hasParsers() { 8085 return ! parsers.getParsers().isEmpty(); 8086 } 8087 8088 boolean hasSerializers() { 8089 return ! serializers.getSerializers().isEmpty(); 8090 } 8091 8092 URI toUri(Object x, String rootUrl) throws RestCallException { 8093 try { 8094 if (x instanceof URI x2) 8095 return x2; 8096 if (x instanceof URL x3) 8097 return x3.toURI(); 8098 if (x instanceof URIBuilder x4) 8099 return x4.build(); 8100 var s = x == null ? "" : x.toString(); 8101 if (nn(rootUrl) && ! absUrlPattern.matcher(s).matches()) { 8102 if (s.isEmpty()) 8103 s = rootUrl; 8104 else { 8105 var sb = new StringBuilder(rootUrl); 8106 if (! s.startsWith("/")) 8107 sb.append('/'); 8108 sb.append(s); 8109 s = sb.toString(); 8110 } 8111 } 8112 s = fixUrl(s); 8113 return new URI(s); 8114 } catch (URISyntaxException e) { 8115 throw new RestCallException(null, e, "Invalid URI encountered: {0}", x); // Shouldn't happen. 8116 } 8117 } 8118}