// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * | |
// * with the License. You may obtain a copy of the License at * | |
// * * | |
// * http://www.apache.org/licenses/LICENSE-2.0 * | |
// * * | |
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * | |
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * | |
// * specific language governing permissions and limitations under the License. * | |
// *************************************************************************************************************************** | |
package org.apache.juneau.rest.client; | |
import static org.apache.juneau.internal.StringUtils.*; | |
import static org.apache.juneau.AddFlag.*; | |
import static org.apache.juneau.httppart.HttpPartType.*; | |
import static org.apache.juneau.http.HttpMethod.*; | |
import static org.apache.juneau.http.HttpHeaders.*; | |
import static org.apache.juneau.http.HttpParts.*; | |
import static org.apache.juneau.http.HttpEntities.*; | |
import static java.util.logging.Level.*; | |
import static org.apache.juneau.internal.ClassUtils.*; | |
import static org.apache.juneau.internal.ExceptionUtils.*; | |
import static org.apache.juneau.internal.StateMachineState.*; | |
import static java.lang.Character.*; | |
import static java.util.Collections.*; | |
import java.io.*; | |
import java.lang.reflect.*; | |
import java.lang.reflect.Proxy; | |
import java.net.*; | |
import java.net.URI; | |
import java.nio.charset.*; | |
import java.text.*; | |
import java.util.*; | |
import java.util.concurrent.*; | |
import java.util.function.*; | |
import java.util.logging.*; | |
import java.util.regex.*; | |
import org.apache.http.*; | |
import org.apache.http.client.*; | |
import org.apache.http.client.config.*; | |
import org.apache.http.client.entity.*; | |
import org.apache.http.client.methods.*; | |
import org.apache.http.client.utils.*; | |
import org.apache.http.conn.*; | |
import org.apache.http.impl.client.*; | |
import org.apache.http.params.*; | |
import org.apache.http.protocol.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.annotation.*; | |
import org.apache.juneau.collections.*; | |
import org.apache.juneau.cp.*; | |
import org.apache.juneau.http.remote.RemoteReturn; | |
import org.apache.juneau.http.resource.*; | |
import org.apache.juneau.http.entity.*; | |
import org.apache.juneau.http.header.*; | |
import org.apache.juneau.http.part.*; | |
import org.apache.juneau.http.remote.*; | |
import org.apache.juneau.httppart.*; | |
import org.apache.juneau.httppart.bean.*; | |
import org.apache.juneau.internal.*; | |
import org.apache.juneau.marshall.*; | |
import org.apache.juneau.oapi.*; | |
import org.apache.juneau.parser.*; | |
import org.apache.juneau.parser.ParseException; | |
import org.apache.juneau.reflect.*; | |
import org.apache.juneau.rest.client.assertion.*; | |
import org.apache.juneau.rest.client.remote.*; | |
import org.apache.juneau.serializer.*; | |
import org.apache.juneau.urlencoding.*; | |
import org.apache.juneau.utils.*; | |
import org.apache.juneau.http.HttpHeaders; | |
/** | |
* Utility class for interfacing with remote REST interfaces. | |
* | |
* <p class='w900'> | |
* Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style | |
* REST calls and the ability to perform marshalling of POJOs to and from HTTP parts. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a basic REST client with JSON support and download a bean.</jc> | |
* MyBean <jv>bean</jv> = RestClient.<jsm>create</jsm>() | |
* .simpleJson() | |
* .build() | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .assertStatus().code().is(200) | |
* .assertHeader(<js>"Content-Type"</js>).matchesSimple(<js>"application/json*"</js>) | |
* .getBody().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* Breaking apart the fluent call, we can see the classes being used: | |
* <p class='bcode w800'> | |
* RestClientBuilder <jv>builder</jv> = RestClient.<jsm>create</jsm>().simpleJson(); | |
* RestClient <jv>client</jv> = <jv>builder</jv>.build(); | |
* RestRequest <jv>req</jv> = <jv>client</jv>.get(<jsf>URI</jsf>); | |
* RestResponse <jv>res</jv> = <jv>req</jv>.run(); | |
* RestResponseStatusLineAssertion <jv>statusLineAssertion</jv> = <jv>res</jv>.assertStatus(); | |
* FluentIntegerAssertion<RestResponse> <jv>codeAssertion</jv> = <jv>statusLineAssertion</jv>.code(); | |
* <jv>res</jv> = <jv>codeAssertion</jv>.is(200); | |
* FluentStringAssertion<RestResponse> <jv>headerAssertion</jv> = <jv>res</jv>.assertHeader(<js>"Content-Type"</js>); | |
* <jv>res</jv> = <jv>headerAssertion</jv>.matchesSimple(<js>"application/json*"</js>); | |
* RestResponseBody <jv>body</jv> = <jv>res</jv>.getBody(); | |
* MyBean <jv>bean</jv> = <jv>body</jv>.as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* It additionally provides support for creating remote proxy interfaces using REST as the transport medium. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Define a Remote proxy for interacting with a REST interface.</jc> | |
* <ja>@Remote</ja>(path=<js>"/petstore"</js>) | |
* <jk>public interface</jk> PetStore { | |
* | |
* <ja>@RemotePost</ja>(<js>"/pets"</js>) | |
* Pet addPet( | |
* <ja>@Body</ja> CreatePet <jv>pet</jv>, | |
* <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, | |
* <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> | |
* ); | |
* } | |
* | |
* <jc>// Use a RestClient with default Simple JSON support.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().simpleJson().build(); | |
* | |
* PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); | |
* CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); | |
* Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClient} <jk>extends</jk> {@link HttpClient}, creates {@link RestRequest} objects. | |
* <li class='jc'>{@link RestRequest} <jk>extends</jk> {@link HttpUriRequest}, creates {@link RestResponse} objects. | |
* <li class='jc'>{@link RestResponse} creates {@link ResponseBody} and {@link ResponseHeader} objects. | |
* <li class='jc'>{@link ResponseBody} <jk>extends</jk> {@link HttpEntity} | |
* <li class='jc'>{@link ResponseHeader} <jk>extends</jk> {@link Header} | |
* </ul> | |
* | |
* | |
* <p class='w900'> | |
* Instances of this class are built using the {@link RestClientBuilder} class which can be constructed using | |
* the {@link #create() RestClient.create()} method as shown above. | |
* | |
* <p class='w900'> | |
* Clients are typically created with a root URI so that relative URIs can be used when making requests. | |
* This is done using the {@link RestClientBuilder#rootUri(Object)} method. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client where all URIs are relative to localhost.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().rootUri(<js>"http://localhost:5000"</js>).build(); | |
* | |
* <jc>// Use relative paths.</jc> | |
* String <jv>body</jv> = <jv>client</jv>.get(<js>"/subpath"</js>).run().getBody().asString(); | |
* </p> | |
* | |
* <p class='w900'> | |
* The {@link RestClient} class creates {@link RestRequest} objects using the following methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClient} | |
* <ul> | |
* <li class='jm'>{@link RestClient#get(Object) get(uri)} / {@link RestClient#get() get()} | |
* <li class='jm'>{@link RestClient#put(Object,Object) put(uri,body)} / {@link RestClient#put(Object) put(uri)} | |
* <li class='jm'>{@link RestClient#post(Object) post(uri,body)} / {@link RestClient#post(Object) post(uri)} | |
* <li class='jm'>{@link RestClient#patch(Object,Object) patch(uri,body)} / {@link RestClient#patch(Object) patch(uri)} | |
* <li class='jm'>{@link RestClient#delete(Object) delete(uri)} | |
* <li class='jm'>{@link RestClient#head(Object) head(uri)} | |
* <li class='jm'>{@link RestClient#options(Object) options(uri)} | |
* <li class='jm'>{@link RestClient#formPost(Object,Object) formPost(uri,body)} / {@link RestClient#formPost(Object) formPost(uri)} | |
* <li class='jm'>{@link RestClient#formPostPairs(Object,Object...) formPostPairs(uri,parameters...)} | |
* <li class='jm'>{@link RestClient#request(String,Object,Object) request(method,uri,body)} | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* The {@link RestRequest} class creates {@link RestResponse} objects using the following methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#run() run()} | |
* <li class='jm'>{@link RestRequest#complete() complete()} | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* The distinction between the two methods is that {@link RestRequest#complete() complete()} automatically consumes the response body and | |
* {@link RestRequest#run() run()} does not. Note that you must consume response bodies in order for HTTP connections to be freed up | |
* for reuse! The {@link InputStream InputStreams} returned by the {@link ResponseBody} object are auto-closing once | |
* they are exhausted, so it is often not necessary to explicitly close them. | |
* | |
* <p class='w900'> | |
* The following examples show the distinction between the two calls: | |
* | |
* <p class='bcode w800'> | |
* <jc>// Consuming the response, so use run().</jc> | |
* String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).run().getBody().asString(); | |
* | |
* <jc>// Only interested in response status code, so use complete().</jc> | |
* <jk>int</jk> <jv>status</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); | |
* </p> | |
* | |
* | |
* <h4 class='topic'>POJO Marshalling</h4> | |
* | |
* <p class='w900'> | |
* By default, JSON support is provided for HTTP request and response bodies. | |
* Other languages can be specified using any of the following builder methods: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#json() json()} | |
* <li class='jm'>{@link RestClientBuilder#simpleJson() simpleJson()} | |
* <li class='jm'>{@link RestClientBuilder#xml() xml()} | |
* <li class='jm'>{@link RestClientBuilder#html() html()} | |
* <li class='jm'>{@link RestClientBuilder#plainText() plainText()} | |
* <li class='jm'>{@link RestClientBuilder#msgPack() msgPack()} | |
* <li class='jm'>{@link RestClientBuilder#uon() uon()} | |
* <li class='jm'>{@link RestClientBuilder#urlEnc() urlEnc()} | |
* <li class='jm'>{@link RestClientBuilder#openApi() openApi()} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a basic REST client with Simplified-JSON support.</jc> | |
* <jc>// Typically easier to use when performing unit tests.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().simpleJson().build(); | |
* </p> | |
* | |
* <p> | |
* Clients can also support multiple languages: | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a REST client with support for multiple languages.</jc> | |
* RestClient <jv>client1</jv> = RestClient.<jsm>create</jsm>().json().xml().openApi().build(); | |
* | |
* <jc>// Create a REST client with support for all supported languages.</jc> | |
* RestClient <jv>client2</jv> = RestClient.<jsm>create</jsm>().universal().build(); | |
* </p> | |
* | |
* <p class='w900'> | |
* When using clients with multiple language support, you must specify the <c>Content-Type</c> header on requests | |
* with bodies to specify which serializer should be selected. | |
* | |
* <p class='bcode w800'> | |
* <jc>// Create a REST client with support for multiple languages.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().universal().build(); | |
* | |
* <jv>client</jv> | |
* .post(<jsf>URI</jsf>, <jv>myBean</jv>) | |
* .contentType(<js>"application/json"</js>) | |
* .complete() | |
* .assertStatus().is(200); | |
* </p> | |
* | |
* <p> | |
* Languages can also be specified per-request. | |
* | |
* <p class='bcode w800'> | |
* <jc>// Create a REST client with no default languages supported.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().build(); | |
* | |
* <jc>// Use JSON for this request.</jc> | |
* <jv>client</jv> | |
* .post(<jsf>URI</jsf>, <jv>myBean</jv>) | |
* .json() | |
* .complete() | |
* .assertStatus().is(200); | |
* </p> | |
* | |
* | |
* <p class='w900'> | |
* The {@link RestClientBuilder} class provides convenience methods for setting common serializer and parser | |
* settings. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a basic REST client with JSON support.</jc> | |
* <jc>// Use single-quotes and whitespace.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().sq().ws().build(); | |
* | |
* <jc>// Same, but using properties.</jc> | |
* RestClient <jv>client2</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .json() | |
* .set(<jsf>WSERIALIZER_quoteChar</jsf>, <js>'\''</js>) | |
* .set(<jsf>WSERIALIZER_useWhitespace</jsf>) | |
* .build(); | |
* </p> | |
* | |
* <p class='w900'> | |
* Other methods are also provided for specifying the serializers and parsers used for lower-level marshalling support: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#serializer(Serializer) serializer(Serializer)} | |
* <li class='jm'>{@link RestClientBuilder#parser(Parser) parser(Parser)} | |
* <li class='jm'>{@link RestClientBuilder#marshall(Marshall) marshall(Marshall)} | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* HTTP parts (headers, query parameters, form data...) are serialized and parsed using the {@link HttpPartSerializer} | |
* and {@link HttpPartParser} APIs. By default, clients are configured to use {@link OpenApiSerializer} and | |
* {@link OpenApiParser}. These can be overridden using the following methods: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#partSerializer(Class) partSerializer(Class<? extends HttpPartSerializer>)} | |
* <li class='jm'>{@link RestClientBuilder#partParser(Class) partParser(Class<? extends HttpPartParser>)} | |
* </ul> | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Request Headers</h4> | |
* <p class='w900'> | |
* Per-client or per-request headers can be specified using the following methods: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#header(String,Object) header(String,Object)} | |
* <li class='jm'>{@link RestClientBuilder#header(String,Object,HttpPartSchema) header(String,Object,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#header(String,Supplier) header(String,Supplier<?>)} | |
* <li class='jm'>{@link RestClientBuilder#header(String,Supplier,HttpPartSchema) header(String,Supplier<?>,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#header(Header) header(Header)} | |
* <li class='jm'>{@link RestClientBuilder#headers(Object...) headers(Object...)} | |
* <li class='jm'>{@link RestClientBuilder#headerPairs(Object...) headerPairs(Object...)} | |
* </ul> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#header(String,Object) header(String,Object)} | |
* <li class='jm'>{@link RestRequest#header(String,Object,HttpPartSchema) header(String,Object,HttpPartSchema)} | |
* <li class='jm'>{@link RestRequest#header(AddFlag,String,Object) header(AddFlag,String,Object)} | |
* <li class='jm'>{@link RestRequest#header(AddFlag,String,Object,HttpPartSchema) header(AddFlag,String,Object,HttpPartSchema)} | |
* <li class='jm'>{@link RestRequest#header(Header) header(Header)} | |
* <li class='jm'>{@link RestRequest#headers(Object...) headers(Object...)} | |
* <li class='jm'>{@link RestRequest#headers(AddFlag,Object...) headers(AddFlag;Object...)} | |
* <li class='jm'>{@link RestRequest#headerPairs(Object...) headers(Object...)} | |
* </ul> | |
* </ul> | |
* | |
* <p> | |
* Additionally, methods are provided on the client builder and per request for all standard HTTP headers | |
* such as {@link RestClientBuilder#authorization(Object) authorization(Object)}. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that adds an Authorization header to every request.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().authorization(<js>"Foo"</js>).build(); | |
* | |
* <jc>// Or do it per-request.</jc> | |
* String <jv>response</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).authorization(<js>"Foo"</js>).run().getBody().asString(); | |
* | |
* <jc>// Or use an HttpHeader.</jc> | |
* <jv>response</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).headers(Authorization.<jsm>of</jsm>(<js>"Foo"</js>)).run().getBody().asString(); | |
* </p> | |
* | |
* <p class='w900'> | |
* The supplier methods are particularly useful for header values whose values may change over time (such as <c>Authorization</c> headers | |
* which may need to change every few minutes). | |
* </p> | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that adds a dynamic Authorization header to every request.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().header(<js>"Authorization"</js>, ()->getMyAuthToken()).build(); | |
* </p> | |
* | |
* <p> | |
* The {@link HttpPartSchema} API allows you to define OpenAPI schemas to POJO data structures on both requests | |
* and responses. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that adds a header "Foo: bar|baz" to every request.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>() | |
* .header(<js>"Foo"</js>, AList.<jsm>of</jsm>(<js>"bar"</js>,<js>"baz"</js>), <jsf>T_ARRAY_PIPES</jsf>) | |
* .build(); | |
* </p> | |
* | |
* <p> | |
* The methods with {@link AddFlag} parameters allow you to control whether new headers get appended, prepended, or | |
* replace existing headers with the same name. | |
* | |
* <ul class='notes'> | |
* <li>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>Header</c> or | |
* <c>NameValuePair</c> objects use the values returned by that bean directly. | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Request Query Parameters</h4> | |
* <p> | |
* Per-client or per-request query parameters can be specified using the following methods: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#query(String,Object) query(String,Object)} | |
* <li class='jm'>{@link RestClientBuilder#query(String,Object,HttpPartSchema) query(String,Object,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#query(String,Supplier) query(String,Supplier<?>)} | |
* <li class='jm'>{@link RestClientBuilder#query(String,Supplier,HttpPartSchema) query(String,Supplier<?>,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#queries(Object...) queries(Object...)} | |
* <li class='jm'>{@link RestClientBuilder#queryPairs(Object...) queryPairs(Object...)} | |
* </ul> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#query(String,Object) query(String,Object)} | |
* <li class='jm'>{@link RestRequest#query(AddFlag,String,Object) query(AddFlag,String,Object)} | |
* <li class='jm'>{@link RestRequest#queries(Object...) queries(Object...)} | |
* <li class='jm'>{@link RestRequest#queries(AddFlag,Object...) queries(AddFlag,Object...)} | |
* <li class='jm'>{@link RestRequest#queryPairs(Object...) queryPairs(Object...)} | |
* <li class='jm'>{@link RestRequest#queryCustom(Object) queryCustom(Object)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that adds a ?foo=bar query parameter to every request.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().query(<js>"foo"</js>, <js>"bar"</js>).build(); | |
* | |
* <jc>// Or do it on every request.</jc> | |
* String <jv>response</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).query(<js>"foo"</js>, <js>"bar"</js>).run().getBody().asString(); | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li>Like header values, dynamic values and OpenAPI schemas are supported. | |
* <li>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> | |
* objects use the values returned by that bean directly. | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Request Form Data</h4> | |
* | |
* <p class='w900'> | |
* Per-client or per-request form-data parameters can be specified using the following methods: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#formData(String,Object) formData(String,Object)} | |
* <li class='jm'>{@link RestClientBuilder#formData(String,Object,HttpPartSchema) formData(String,Object,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#formData(String,Supplier) formData(String,Supplier<?>)} | |
* <li class='jm'>{@link RestClientBuilder#formData(String,Supplier,HttpPartSchema) formData(String,Supplier<?>,HttpPartSchema)} | |
* <li class='jm'>{@link RestClientBuilder#formDatas(Object...) formDatas(Object...)} | |
* <li class='jm'>{@link RestClientBuilder#formDataPairs(Object...) formDataPairs(Object...)} | |
* </ul> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#formData(String,Object) formData(String,Object)} | |
* <li class='jm'>{@link RestRequest#formData(AddFlag,String,Object) formData(AddFlag,String,Object)} | |
* <li class='jm'>{@link RestRequest#formDatas(Object...) formDatas(Object...)} | |
* <li class='jm'>{@link RestRequest#formDatas(AddFlag,Object...) formDatas(AddFlag,Object...)} | |
* <li class='jm'>{@link RestRequest#formDataPairs(Object...) formDataPairs(Object...)} | |
* <li class='jm'>{@link RestRequest#formDataCustom(Object) formDataCustom(Object)} | |
* </ul> | |
* </ul> | |
* | |
* <ul class='notes'> | |
* <li>Like header values, dynamic values and OpenAPI schemas are supported. | |
* <li>Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in <c>NameValuePair</c> | |
* objects use the values returned by that bean directly. | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Request Body</h4> | |
* | |
* <p class='w900'> | |
* The request body can either be passed in with the client creator method (e.g. {@link RestClient#post(Object,Object) post(uri,body)}), | |
* or can be specified via the following methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#body(Object) body(Object)} | |
* <li class='jm'>{@link RestRequest#body(Object,HttpPartSchema) body(Object,HttpPartSchema)} | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* The request body can be any of the following types: | |
* <ul class='javatree'> | |
* <li class='jc'> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} defined on the client or request. | |
* <li class='jc'> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li class='jc'> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li class='jc'> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li class='jc'> | |
* {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li class='jc'> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li class='jc'> | |
* {@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* | |
* <ul class='notes'> | |
* <li>If the serializer on the client or request is explicitly set to <jk>null</jk>, POJOs will be converted to strings | |
* using the registered part serializer as content type <js>"text/plain</js>. If the part serializer is also <jk>null</jk>, | |
* POJOs will be converted to strings using {@link ClassMeta#toString(Object)} which typically just calls {@link Object#toString()}. | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Response Status</h4> | |
* | |
* <p class='w900'> | |
* After execution using {@link RestRequest#run()} or {@link RestRequest#complete()}, the following methods can be used | |
* to get the response status: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestResponse} | |
* <ul> | |
* <li class='jm'><c>{@link RestResponse#getStatusLine() getStatusLine()} <jk>returns</jk> {@link StatusLine}</c> | |
* <li class='jm'><c>{@link RestResponse#getStatusCode() getStatusCode()} <jk>returns</jk> <jk>int</jk></c> | |
* <li class='jm'><c>{@link RestResponse#getReasonPhrase() getReasonPhrase()} <jk>returns</jk> String</c> | |
* <li class='jm'><c>{@link RestResponse#assertStatus() assertStatus()} <jk>returns</jk> {@link FluentResponseStatusLineAssertion}</c> | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Only interested in status code.</jc> | |
* <jk>int</jk> <jv>statusCode</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(); | |
* </p> | |
* | |
* <p class='w900'> | |
* Equivalent methods with mutable parameters are provided to allow access to status values without breaking fluent call chains. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Interested in multiple values.</jc> | |
* Mutable<Integer> <jv>statusCode</jv> = Mutable.<jsm>create</jsm>(); | |
* Mutable<String> <jv>reasonPhrase</jv> = Mutable.<jsm>create</jsm>(); | |
* | |
* <jv>client</jv>.get(<jsf>URI</jsf>).complete().getStatusCode(<jv>statusCode</jv>).getReasonPhrase(<jv>reasonPhrase</jv>); | |
* System.<jsf>err</jsf>.println(<js>"statusCode="</js>+<jv>statusCode</jv>.get()+<js>", reasonPhrase="</js>+<jv>reasonPhrase</jv>.get()); | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li>If you are only interested in the response status and not the response body, be sure to use {@link RestRequest#complete()} instead | |
* of {@link RestRequest#run()} to make sure the response body gets automatically cleaned up. Otherwise you must | |
* consume the response yourself. | |
* </ul> | |
* | |
* <p class='w900'> | |
* The assertion method is provided for quickly asserting status codes in fluent calls. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Status assertion using a static value.</jc> | |
* String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) | |
* .run() | |
* .assertStatus().code().isBetween(200,399) | |
* .getBody().asString(); | |
* | |
* <jc>// Status assertion using a predicate.</jc> | |
* String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) | |
* .run() | |
* .assertStatus().code().passes(<jv>x</jv> -> <jv>x</jv><400) | |
* .getBody().asString(); | |
* </p> | |
* | |
* | |
* <h4 class='topic'>Response Headers</h4> | |
* | |
* <p class='w900'> | |
* Response headers are accessed through the following methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestResponse} | |
* <ul> | |
* <li class='jm'><c>{@link RestResponse#getHeaders(String) getHeaders(String)} <jk>returns</jk> {@link ResponseHeader}[]</c> | |
* <li class='jm'><c>{@link RestResponse#getFirstHeader(String) getFirstHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> | |
* <li class='jm'><c>{@link RestResponse#getLastHeader(String) getLastHeader(String)} <jk>returns</jk> {@link ResponseHeader}</c> | |
* <li class='jm'><c>{@link RestResponse#getAllHeaders() getAllHeaders()} <jk>returns</jk> {@link ResponseHeader}[]</c> | |
* <li class='jm'><c>{@link RestResponse#getStringHeader(String) getStringHeader(String)} <jk>returns</jk> String</c> | |
* <li class='jm'><c>{@link RestResponse#containsHeader(String) containsHeader(String)} <jk>returns</jk> <jk>boolean</jk></c> | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* The {@link RestResponse#getFirstHeader(String)} and {@link RestResponse#getLastHeader(String)} methods return an empty {@link ResponseHeader} object instead of<jk>null</jk>. | |
* This allows it to be used more easily in fluent calls. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// See if response contains Location header.</jc> | |
* <jk>boolean</jk> <jv>hasLocationHeader</jv> = client.get(<jsf>URI</jsf>).complete().getLastHeader(<js>"Location"</js>).exists(); | |
* </p> | |
* | |
* <p class='w900'> | |
* The {@link ResponseHeader} class extends from the HttpClient {@link Header} class and provides several convenience | |
* methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link ResponseHeader} | |
* <ul> | |
* <li class='jm'><c>{@link ResponseHeader#isPresent() isPresent()} <jk>returns</jk> <jk>boolean</jk></c> | |
* <li class='jm'><c>{@link ResponseHeader#asString() asString()} <jk>returns</jk> String</c> | |
* <li class='jm'><c>{@link ResponseHeader#asType(Type,Type...) asType(Type,Type...)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link ResponseHeader#asType(Class) asType(Class<T>)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link ResponseHeader#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asHeader(Class) asHeader(Class<T <jk>extends</jk> BasicHeader> c)} <jk>returns</jk> {@link BasicHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asStringHeader() asStringHeader()} <jk>returns</jk> {@link BasicStringHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asIntegerHeader() asIntegerHeader()} <jk>returns</jk> {@link BasicIntegerHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asLongHeader() asLongHeader()} <jk>returns</jk> {@link BasicLongHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asDateHeader() asDateHeader()} <jk>returns</jk> {@link BasicDateHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asCsvArrayHeader() asCsvArrayHeader()} <jk>returns</jk> {@link BasicCsvArrayHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asEntityTagArrayHeader() asEntityValidatorArrayHeader()} <jk>returns</jk> {@link BasicEntityTagArrayHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asStringRangeArrayHeader() asRangeArrayHeader()} <jk>returns</jk> {@link BasicStringRangeArrayHeader}</c> | |
* <li class='jm'><c>{@link ResponseHeader#asUriHeader() asUriHeader()} <jk>returns</jk> {@link BasicUriHeader}</c> | |
* </ul> | |
* </ul> | |
* | |
* <p> | |
* The {@link ResponseHeader#schema(HttpPartSchema)} method allows you to perform parsing of OpenAPI formats for | |
* header parts. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Parse the header "Foo: bar|baz".</jc> | |
* List<String> <jv>fooHeader</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .complete() | |
* .getHeader(<js>"Foo"</js>).schema(<jsf>T_ARRAY_PIPES</jsf>).as(List.<jk>class</jk>, String.<jk>class</jk>); | |
* </p> | |
* | |
* <p> | |
* Assertion methods are also provided for fluent-style calls: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link ResponseHeader} | |
* <ul> | |
* <li class='jm'><c>{@link ResponseHeader#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseHeaderAssertion}</c> | |
* </ul> | |
* </ul> | |
* | |
* <p> | |
* Note how in the following example, the fluent assertion returns control to the {@link RestResponse} object after | |
* the assertion has been completed: | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Assert the response content type is any sort of JSON.</jc> | |
* String <jv>body</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) | |
* .run() | |
* .getHeader(<js>"Content-Type"</js>).assertValue().matchesSimple(<js>"application/json*"</js>) | |
* .getBody().asString(); | |
* </p> | |
* | |
* | |
* <h4 class='topic'>Response Body</h4> | |
* | |
* <p class='w900'> | |
* The response body is accessed through the following method: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestResponse} | |
* <ul> | |
* <li class='jm'><c>{@link RestResponse#getBody() getBody()} <jk>returns</jk> {@link ResponseBody}</c> | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* The {@link ResponseBody} class extends from the HttpClient {@link HttpEntity} class and provides several convenience | |
* methods: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link ResponseBody} | |
* <ul> | |
* <li class='jm'><c>{@link ResponseBody#asInputStream() asInputStream()} <jk>returns</jk> InputStream</c> | |
* <li class='jm'><c>{@link ResponseBody#asReader() asReader()} <jk>returns</jk> Reader</c> | |
* <li class='jm'><c>{@link ResponseBody#asReader(Charset) asReader(Charset)} <jk>returns</jk> Reader</c> | |
* <li class='jm'><c>{@link ResponseBody#pipeTo(OutputStream) pipeTo(OutputStream)} <jk>returns</jk> {@link RestResponse}</c> | |
* <li class='jm'><c>{@link ResponseBody#pipeTo(Writer) pipeTo(Writer)} <jk>returns</jk> {@link RestResponse}</c> | |
* <li class='jm'><c>{@link ResponseBody#asType(Type,Type...) asType(Type,Type...)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link ResponseBody#asType(Class) asType(Class<T>)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link ResponseBody#asFuture(Class) asFuture(Class<T>)} <jk>returns</jk> Future<T></c> | |
* <li class='jm'><c>{@link ResponseBody#asFuture(Type,Type...) asFuture(Type,Type...)} <jk>returns</jk> Future<T></c> | |
* <li class='jm'><c>{@link ResponseBody#asString() asString()} <jk>returns</jk> String</c> | |
* <li class='jm'><c>{@link ResponseBody#asStringFuture() asStringFuture()} <jk>returns</jk> Future<String></c> | |
* <li class='jm'><c>{@link ResponseBody#asAbbreviatedString(int) asAbbreviatedString(int)} <jk>returns</jk> String</c> | |
* <li class='jm'><c>{@link ResponseBody#asPojoRest(Class) asPojoRest(Class<?>)} <jk>returns</jk> {@link PojoRest}</c> | |
* <li class='jm'><c>{@link ResponseBody#asPojoRest() asPojoRest()} <jk>returns</jk> {@link PojoRest}</c> | |
* <li class='jm'><c>{@link ResponseBody#asMatcher(Pattern) asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c> | |
* <li class='jm'><c>{@link ResponseBody#asMatcher(String) asMatcher(String)} <jk>returns</jk> {@link Matcher}</c> | |
* </ul> | |
* </ul> | |
* | |
* <br> | |
* | |
* <h5 class='figure'>Examples:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Parse into a linked-list of strings.</jc> | |
* List<String> <jv>l1</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().as(LinkedList.<jk>class</jk>, String.<jk>class</jk>); | |
* | |
* <jc>// Parse into a linked-list of beans.</jc> | |
* List<MyBean> <jv>l2</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().as(LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); | |
* | |
* <jc>// Parse into a linked-list of linked-lists of strings.</jc> | |
* List<List<String>> <jv>l3</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().as(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); | |
* | |
* <jc>// Parse into a map of string keys/values.</jc> | |
* Map<String,String> <jv>m1</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); | |
* | |
* <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> | |
* Map<String,List<MyBean>> <jv>m2<jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().as(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* The response body can only be consumed once unless it has been cached into memory. In many cases, the body is | |
* automatically cached when using the assertions methods or methods such as {@link ResponseBody#asString()}. | |
* However, methods that involve reading directly from the input stream cannot be called twice. | |
* In these cases, the {@link RestResponse#cacheBody()} and {@link ResponseBody#cache()} methods are provided | |
* to cache the response body in memory so that you can perform several operations against it. | |
* | |
* <p class='bcode w800'> | |
* <jc>// Cache the response body so we can access it twice.</jc> | |
* InputStream <jv>inputStream</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .cacheBody() | |
* .getBody().pipeTo(<jv>someOtherStream</jv>) | |
* .getBody().asInputStream(); | |
* </p> | |
* | |
* <p> | |
* Assertion methods are also provided for fluent-style calls: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link ResponseBody} | |
* <ul> | |
* <li class='jm'><c>{@link ResponseBody#assertValue() assertValue()} <jk>returns</jk> {@link FluentResponseBodyAssertion}</c> | |
* </ul> | |
* </ul> | |
* | |
* <br> | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Assert that the body contains the string "Success".</jc> | |
* String <jv>body</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().assertString().contains(<js>"Success"</js>) | |
* .getBody().asString(); | |
* </p> | |
* | |
* <p> | |
* Object assertions allow you to parse the response body into a POJO and then perform various tests on that resulting | |
* POJO. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Parse bean into POJO and then validate that it was parsed correctly.</jc> | |
* MyBean <jv>bean</jv> = <jv>client</jv>.get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().assertObject(MyBean.<jk>class</jk>).json().is(<js>"{foo:'bar'}"</js>) | |
* .getBody().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* | |
* <h4 class='topic'>Custom Call Handlers</h4> | |
* | |
* <p class='w900'> | |
* The {@link RestCallHandler} interface provides the ability to provide custom handling of requests. | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#callHandler(Class) callHandler(Class<? extends RestCallHandler>)} | |
* <li class='jm'>{@link RestClientBuilder#callHandler(RestCallHandler) callHandler(RestCallHandler)} | |
* </ul> | |
* <li class='jic'>{@link RestCallHandler} | |
* <ul> | |
* <li class='jm'><c>{@link RestCallHandler#run(HttpHost,HttpRequest,HttpContext) run(HttpHost,HttpRequest,HttpContext)} <jk>returns</jk> HttpResponse</c> | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* Note that there are other ways of accomplishing this such as extending the {@link RestClient} class and overriding | |
* the {@link #run(HttpHost,HttpRequest,HttpContext)} method | |
* or by defining your own {@link HttpRequestExecutor}. Using this interface is often simpler though. | |
* | |
* | |
* <h4 class='topic'>Interceptors</h4> | |
* | |
* <p class='w900'> | |
* The {@link RestCallInterceptor} API provides a quick way of intercepting and manipulating requests and responses beyond | |
* the existing {@link HttpRequestInterceptor} and {@link HttpResponseInterceptor} APIs. | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#interceptors(Object...) interceptors(Object...)} | |
* </ul> | |
* <li class='jc'>{@link RestRequest} | |
* <ul> | |
* <li class='jm'>{@link RestRequest#interceptors(RestCallInterceptor...) interceptors(RestCallInterceptor...)} | |
* </ul> | |
* <li class='jic'>{@link RestCallInterceptor} | |
* <ul> | |
* <li class='jm'>{@link RestCallInterceptor#onInit(RestRequest) onInit(RestRequest)} | |
* <li class='jm'>{@link RestCallInterceptor#onConnect(RestRequest,RestResponse) onConnect(RestRequest,RestResponse)} | |
* <li class='jm'>{@link RestCallInterceptor#onClose(RestRequest,RestResponse) onClose(RestRequest,RestResponse)} | |
* </ul> | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>Logging / Debugging</h4> | |
* | |
* <p class='w900'> | |
* The following methods provide logging of requests and responses: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#logger(Logger) logger(Logger)} | |
* <li class='jm'>{@link RestClientBuilder#logToConsole() logToConsole()} | |
* <li class='jm'>{@link RestClientBuilder#logRequests(DetailLevel,Level,BiPredicate) logRequests(DetailLevel,Level,BiPredicate)} | |
* </ul> | |
* </ul> | |
* | |
* <p> | |
* The following example shows the results of logging all requests that end with <c>/bean</c>. | |
* | |
* <h5 class='figure'>Examples:</h5> | |
* <p class='bcode w800'> | |
* MyBean <jv>bean</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .simpleJson() | |
* .logRequests(DetailLevel.<jsf>FULL</jsf>, Level.<jsf>SEVERE</jsf>, (<jv>req</jv>,<jv>res</jv>)-><jv>req</jv>.getUri().endsWith(<js>"/bean"</js>)) | |
* .logToConsole() | |
* .build() | |
* .post(<js>"http://localhost/bean"</js>, <jv>anotherBean</jv>) | |
* .run() | |
* .getBody().as(MyBean.<jk>class</jk>); | |
* </p> | |
* | |
* <p> | |
* This produces the following console output: | |
* | |
* <p class='bcode w800 console'> | |
* === HTTP Call (outgoing) ====================================================== | |
* === REQUEST === | |
* POST http://localhost/bean | |
* ---request headers--- | |
* Accept: application/json+simple | |
* ---request entity--- | |
* Content-Type: application/json+simple | |
* ---request content--- | |
* {f:1} | |
* === RESPONSE === | |
* HTTP/1.1 200 | |
* ---response headers--- | |
* Content-Type: application/json | |
* ---response content--- | |
* {f:1} | |
* === END =======================================================================", | |
* </p> | |
* | |
* | |
* <p class='notes w900'> | |
* 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 | |
* a performance penalty. | |
* | |
* <p class='w900'> | |
* Additionally, the following method is also provided for enabling debug mode: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#debug() debug()} | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* Enabling debug mode has the following effects: | |
* <ul> | |
* <li>{@link Context#CONTEXT_debug} is enabled. | |
* <li>{@link #RESTCLIENT_leakDetection} is enabled. | |
* <li>{@link RestClientBuilder#logToConsole()} is called. | |
* </ul> | |
* | |
* | |
* <h4 class='topic'>REST Proxies</h4> | |
* | |
* <p class='w900'> | |
* One of the more powerful features of the REST client class is the ability to produce Java interface proxies against | |
* arbitrary remote REST resources. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Define a Remote proxy for interacting with a REST interface.</jc> | |
* <ja>@Remote</ja>(path=<js>"/petstore"</js>) | |
* <jk>public interface</jk> PetStore { | |
* | |
* <ja>@RemotePost</ja>(<js>"/pets"</js>) | |
* Pet addPet( | |
* <ja>@Body</ja> CreatePet <jv>pet</jv>, | |
* <ja>@Header</ja>(<js>"E-Tag"</js>) UUID <jv>etag</jv>, | |
* <ja>@Query</ja>(<js>"debug"</js>) <jk>boolean</jk> <jv>debug</jv> | |
* ); | |
* } | |
* | |
* <jc>// Use a RestClient with default Simple JSON support.</jc> | |
* RestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().simpleJson().build()) | |
* | |
* PetStore <jv>store</jv> = <jv>client</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:10000"</js>); | |
* CreatePet <jv>createPet</jv> = <jk>new</jk> CreatePet(<js>"Fluffy"</js>, 9.99); | |
* Pet <jv>pet</jv> = <jv>store</jv>.addPet(<jv>createPet</jv>, UUID.<jsm>randomUUID</jsm>(), <jk>true</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* The methods to retrieve remote interfaces are: | |
* | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClient} | |
* <ul> | |
* <li class='jm'><c>{@link RestClient#getRemote(Class) getRemote(Class<T>)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link RestClient#getRemote(Class,Object) getRemote(Class<T>,Object)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link RestClient#getRemote(Class,Object,Serializer,Parser) getRemote(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link RestClient#getRrpcInterface(Class) getRrpcInterface(Class<T>)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object) getRrpcInterface(Class<T>,Object)} <jk>returns</jk> T</c> | |
* <li class='jm'><c>{@link RestClient#getRrpcInterface(Class,Object,Serializer,Parser) getRrpcInterface(Class<T>,Object,Serializer,Parser)} <jk>returns</jk> T</c> | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* Two basic types of remote interfaces are provided: | |
* | |
* <ul class='spaced-list'> | |
* <li>{@link Remote @Remote}-annotated interfaces. These can be defined against arbitrary external REST resources. | |
* <li>RPC-over-REST interfaces. These are Java interfaces that allow you to make method calls on server-side POJOs. | |
* </ul> | |
* | |
* <p class='w900'> | |
* Refer to the following documentation on both flavors: | |
* | |
* <ul class='doctree'> | |
* <li class='link'>{@doc RestcProxies} | |
* <li class='link'>{@doc RestRpc} | |
* </ul> | |
* | |
* <br> | |
* <hr class='w900'> | |
* <h4 class='topic'>Customizing Apache HttpClient</h4> | |
* | |
* <p class='w900'> | |
* Several methods are provided for customizing the underlying HTTP client and client builder classes: | |
* <ul class='javatree'> | |
* <li class='jc'>{@link RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link RestClientBuilder#httpClientBuilder(HttpClientBuilder) httpClientBuilder(HttpClientBuilder)} - Set the client builder yourself. | |
* <li class='jm'>{@link RestClientBuilder#createHttpClientBuilder() createHttpClientBuilder()} - Override to create the client builder. | |
* <li class='jm'>{@link RestClientBuilder#createHttpClient() createHttpClient()} - Override to create the client. | |
* <li class='jm'>{@link RestClientBuilder#createConnectionManager() createConnectionManager()} - Override to create the connection management. | |
* </ul> | |
* </ul> | |
* | |
* <p class='w900'> | |
* Additionally, all methods on the <c>HttpClientBuilder</c> class have been extended with fluent setters. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client with customized HttpClient settings.</jc> | |
* MyBean <jv>bean</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .disableRedirectHandling() | |
* .connectionManager(<jv>myConnectionManager</jv>) | |
* .addInterceptorFirst(<jv>myHttpRequestInterceptor</jv>) | |
* .build(); | |
* </p> | |
* | |
* <p> | |
* Refer to the {@link HttpClientBuilder HTTP Client Builder API} for more information. | |
* | |
* | |
* <h4 class='topic'>Extending RestClient</h4> | |
* | |
* <p class='w900'> | |
* The <c>RestClient</c> API has been designed to allow for the ability to be easily extended. | |
* The following example that overrides the primary run method shows how this can be done. | |
* | |
* <h5 class='figure'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jk>public class</jk> MyRestClient <jk>extends</jk> RestClient { | |
* | |
* <jc>// Must provide this constructor!</jc> | |
* <jk>public</jk> MyRestClient(ContextProperties <jv>properties</jv>) { | |
* <jk>super</jk>(<jv>properties</jv>); | |
* } | |
* | |
* <ja>@Override</ja> | |
* <jk>public</jk> HttpResponse run(HttpHost <jv>target</jv>, HttpRequest <jv>request</jv>, HttpContext <jv>context</jv>) <jk>throws</jk> IOException { | |
* <jc>// Perform special handling of requests.</jc> | |
* } | |
* } | |
* | |
* <jc>// Instantiate your client.</jc> | |
* MyRestClient <jv>client</jv> = RestClient.<jsm>create</jsm>().json().build(MyRestClient.<jk>class</jk>); | |
* </p> | |
* | |
* <p class='w900'> | |
* The {@link RestRequest} and {@link RestResponse} objects can also be extended and integrated by overriding the | |
* {@link RestClient#createRequest(URI, String, boolean)} and {@link RestClient#createResponse(RestRequest, HttpResponse, Parser)} methods. | |
* | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc juneau-rest-client} | |
* </ul> | |
*/ | |
@ConfigurableContext(nocache=true) | |
public class RestClient extends BeanContext implements HttpClient, Closeable, RestCallHandler, RestCallInterceptor { | |
//------------------------------------------------------------------------------------------------------------------- | |
// Configurable properties | |
//------------------------------------------------------------------------------------------------------------------- | |
private static final String PREFIX = "RestClient."; | |
/** | |
* Configuration property: REST call handler. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_callHandler RESTCLIENT_callHandler} | |
* <li><b>Name:</b> <js>"RestClient.callHandler.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link org.apache.juneau.rest.client.RestCallHandler}></c> | |
* <li>{@link org.apache.juneau.rest.client.RestCallHandler} | |
* </ul> | |
* <li><b>Default:</b> <c><jk>null</jk></c> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#callHandler(Class)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#callHandler(RestCallHandler)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* | |
* <p> | |
* Allows you to provide a custom handler for making HTTP calls. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that handles processing of requests using a custom handler.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .callHandler( | |
* <jk>new</jk> RestCallHandler() { | |
* <ja>@Override</ja> | |
* <jk>public</jk> HttpResponse run(HttpHost <jv>target</jv>, HttpRequest <jv>request</jv>, HttpContext <jv>context</jv>) <jk>throws</jk> IOException { | |
* <jc>// Custom handle requests.</jc> | |
* } | |
* } | |
* ) | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_callHandler = PREFIX + "callHandler.o"; | |
/** | |
* Configuration property: Connection manager | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_connectionManager RESTCLIENT_connectionManager} | |
* <li><b>Name:</b> <js>"RestClient.connectionManager.o"</js> | |
* <li><b>System property:</b> <c>RestClient.connectionManager</c> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><b>Data type:</b> {@link org.apache.http.conn.HttpClientConnectionManager}</c> | |
* </ul> | |
* <li><b>Default:</b> Value returned by {@link org.apache.juneau.rest.client.RestClientBuilder#createConnectionManager()} | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#connectionManager(HttpClientConnectionManager)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Allows you to override the connection manager used by the HTTP client. | |
*/ | |
public static final String RESTCLIENT_connectionManager = PREFIX + "connectionManager.o"; | |
/** | |
* Configuration property: Console print stream. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_console RESTCLIENT_console} | |
* <li><b>Name:</b> <js>"RestClient.console.o"</js> | |
* <li><b>System property:</b> <c>RestClient.console</c> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><b>Data type:</b> <c>Class<? <jk>extends</jk> {@link java.io.PrintStream}> | {@link java.io.PrintStream}</c> | |
* </ul> | |
* <li><b>Default:</b> <c>System.<jsf>out</jsf></c> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#console(PrintStream)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#console(Class)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Allows you to redirect the console output to a different print stream. | |
*/ | |
public static final String RESTCLIENT_console = PREFIX + "console.o"; | |
/** | |
* Configuration property: Error codes predicate. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_errorCodes RESTCLIENT_errorCodes} | |
* <li><b>Name:</b> <js>"RestClient.errorCodes.o"</js> | |
* <li><b>Data type:</b> {@link java.util.function.Predicate}<{@link java.lang.Integer}> | |
* <li><b>Default:</b> <code>x -> x>=400</code> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#errorCodes(Predicate)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#errorCodes(Predicate)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* | |
* <p> | |
* Defines a predicate to test for error codes. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that considers any 300+ responses to be errors.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .errorCodes(<jv>x</jv> -> <jv>x</jv> >= 300) | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_errorCodes = PREFIX + "errorCodes.o"; | |
/** | |
* Configuration property: Executor service. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_executorService RESTCLIENT_executorService} | |
* <li><b>Name:</b> <js>"RestClient.executorService.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link java.util.concurrent.ExecutorService}></c> | |
* <li>{@link java.util.concurrent.ExecutorService} | |
* </ul> | |
* <li><b>Default:</b> <jk>null</jk>. | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#executorService(ExecutorService, boolean)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Defines the executor service to use when calling future methods on the {@link RestRequest} class. | |
* | |
* <p> | |
* This executor service is used to create {@link Future} objects on the following methods: | |
* <ul> | |
* <li class='jm'>{@link RestRequest#runFuture()} | |
* <li class='jm'>{@link RestRequest#completeFuture()} | |
* <li class='jm'>{@link ResponseBody#asFuture(Class)} (and similar methods) | |
* </ul> | |
* | |
* <p> | |
* The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout | |
* and a queue size of 10. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client with a customized executor service.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .executorService(<jk>new</jk> ThreadPoolExecutor(1, 1, 30, TimeUnit.<jsf>SECONDS</jsf>, <jk>new</jk> ArrayBlockingQueue<Runnable>(10)), <jk>true</jk>) | |
* .build(); | |
* | |
* <jc>// Use it to asynchronously run a request.</jc> | |
* Future<RestResponse> <jv>responseFuture</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).runFuture(); | |
* | |
* <jc>// Do some other stuff.</jc> | |
* | |
* <jc>// Now read the response.</jc> | |
* String <jv>body</jv> = <jv>responseFuture</jv>.get().getBody().asString(); | |
* | |
* <jc>// Use it to asynchronously retrieve a response.</jc> | |
* Future<MyBean> <jv>myBeanFuture</jv> = <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .run() | |
* .getBody().asFuture(MyBean.<jk>class</jk>); | |
* | |
* <jc>// Do some other stuff.</jc> | |
* | |
* <jc>// Now read the response.</jc> | |
* MyBean <jv>bean</jv> = <jv>myBeanFuture</jv>.get(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_executorService = PREFIX + "executorService.o"; | |
/** | |
* Configuration property: Shut down executor service on close. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_executorServiceShutdownOnClose RESTCLIENT_executorServiceShutdownOnClose} | |
* <li><b>Name:</b> <js>"RestClient.executorServiceShutdownOnClose.b"</js> | |
* <li><b>Data type:</b> <jk>boolean</jk> | |
* <li><b>System property:</b> <c>RestClient.executorServiceShutdownOnClose</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_EXECUTORSERVICESHUTDOWNONCLOSE</c> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#executorService(ExecutorService, boolean)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called. | |
*/ | |
public static final String RESTCLIENT_executorServiceShutdownOnClose = PREFIX + "executorServiceShutdownOnClose.b"; | |
/** | |
* Configuration property: Request form-data parameters. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_formData RESTCLIENT_formData} | |
* <li><b>Name:</b> <js>"RestClient.formData.lo"</js> | |
* <li><b>Data type:</b> <c>List<{@link org.apache.http.NameValuePair}></c> | |
* <li><b>Default:</b> empty map | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#formData(String,Object) formData(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#formData(String,Object,HttpPartSchema,HttpPartSerializer) formData(String,Object,HttpPartSerializer,HttpPartSchema)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#formDatas(Object...) formDatas(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#formDataPairs(Object...) formDataPairs(Map<String, Object>)} | |
* </ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestRequest} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formData(String,Object) formData(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formData(AddFlag,String,Object) formData(EnumSet<AddFlag>,String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formDatas(Object...) formDatas(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formDatas(AddFlag,Object...) formDatas(EnumSet<AddFlag>Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formDataPairs(Object...) formDataPairs(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#formDataCustom(Object) formDataCustom(Object)} | |
* </ul> | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Query parameters to add to every request. | |
*/ | |
public static final String RESTCLIENT_formData = PREFIX + "formData.lo"; | |
/** | |
* Configuration property: Request headers. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_headers RESTCLIENT_headers} | |
* <li><b>Name:</b> <js>"RestClient.headers.lo"</js> | |
* <li><b>Data type:</b> <c>List<{@link org.apache.http.Header} | {@link org.apache.http.NameValuePair}></c> | |
* <li><b>Default:</b> empty map | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#header(String,Object) header(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#header(String,Object,HttpPartSchema,HttpPartSerializer) header(String,Object,HttpPartSerializer,HttpPartSchema)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#header(Header) header(Header)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#headers(Object...) headers(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#headerPairs(Object...) headerPairs(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#accept(Object) accept(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#acceptCharset(Object) acceptCharset(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#acceptEncoding(Object) acceptEncoding(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#acceptLanguage(Object) acceptLanguage(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#authorization(Object) authorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#cacheControl(Object) cacheControl(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#clientVersion(Object) clientVersion(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#connection(Object) connection(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#contentLength(Object) contentLength(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#contentType(Object) contentType(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#date(Object) date(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#expect(Object) expect(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#forwarded(Object) forwarded(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#from(Object) from(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#host(Object) host(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ifMatch(Object) ifMatch(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ifModifiedSince(Object) ifModifiedSince(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ifNoneMatch(Object) ifNoneMatch(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ifRange(Object) ifRange(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ifUnmodifiedSince(Object) ifUnmodifiedSince(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#maxForwards(Object) maxForwards(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#origin(Object) origin(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#pragma(Object) pragma(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#proxyAuthorization(Object) proxyAuthorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#range(Object) proxyAuthorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#referer(Object) referer(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#te(Object) te(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#userAgent(Object) userAgent(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#upgrade(Object) upgrade(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#via(Object) via(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#warning(Object) warning(Object)} | |
* </ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestRequest} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#header(String,Object) header(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#header(AddFlag,String,Object) header(EnumSet>AddFlag>,String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#header(Header) header(Header)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#headers(Object...) headers(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#headers(AddFlag,Object...) headers(EnumSet>AddFlag>,Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#headerPairs(Object...) headerPairs(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#accept(Object) accept(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#acceptCharset(Object) acceptCharset(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#acceptEncoding(Object) acceptEncoding(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#acceptLanguage(Object) acceptLanguage(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#authorization(Object) authorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#cacheControl(Object) cacheControl(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#clientVersion(Object) clientVersion(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#connection(Object) connection(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#contentLength(Object) contentLength(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#contentType(Object) contentType(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#date(Object) date(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#expect(Object) expect(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#forwarded(Object) forwarded(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#from(Object) from(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#hostHeader(Object) hostHeader(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ifMatch(Object) ifMatch(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ifModifiedSince(Object) ifModifiedSince(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ifNoneMatch(Object) ifNoneMatch(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ifRange(Object) ifRange(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ifUnmodifiedSince(Object) ifUnmodifiedSince(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#maxForwards(Object) maxForwards(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#origin(Object) origin(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#pragma(Object) pragma(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#proxyAuthorization(Object) proxyAuthorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#range(Object) proxyAuthorization(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#referer(Object) referer(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#te(Object) te(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#userAgent(Object) userAgent(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#upgrade(Object) upgrade(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#via(Object) via(Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#warning(Object) warning(Object)} | |
* </ul> | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Headers to add to every request. | |
*/ | |
public static final String RESTCLIENT_headers = PREFIX + "headers.lo"; | |
/** | |
* Configuration property: Ignore errors. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_ignoreErrors RESTCLIENT_ignoreErrors} | |
* <li><b>Name:</b> <js>"RestClient.ignoreErrors.b"</js> | |
* <li><b>Data type:</b> <jk>boolean</jk> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ignoreErrors()} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#ignoreErrors(boolean)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#ignoreErrors()} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* | |
* <p> | |
* When enabled, HTTP error response codes (e.g. <l>>=400</l>) will not cause a {@link RestCallException} to | |
* be thrown. | |
* <p> | |
* Note that this is equivalent to <c>builder.errorCodes(x -> <jk>false</jk>);</c> | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that doesn't throws a RestCallException when a 500 error occurs.</jc> | |
* RestClient | |
* .<jsm>create</jsm>() | |
* .ignoreErrors() | |
* .build() | |
* .get(<js>"/error"</js>) <jc>// Throws a 500 error</jc> | |
* .run() | |
* .assertStatus().is(500); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_ignoreErrors = PREFIX + "ignoreErrors.b"; | |
/** | |
* Configuration property: Call interceptors. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_interceptors RESTCLIENT_interceptors} | |
* <li><b>Name:</b> <js>"RestClient.interceptors.lo"</js> | |
* <li><b>Data type:</b> <c>List<Class<{@link org.apache.juneau.rest.client.RestCallInterceptor}> | {@link org.apache.juneau.rest.client.RestCallInterceptor}>></c> | |
* <li><b>Default:</b> empty list. | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#interceptors(Object...)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Adds an interceptor that can be called to hook into specified events in the lifecycle of a single request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Customized interceptor (note you can also extend from BasicRestCallInterceptor as well.</jc> | |
* <jk>public class</jk> MyRestCallInterceptor <jk>implements</jk> RestCallInterceptor { | |
* | |
* <ja>@Override</ja> | |
* <jk>public void</jk> onInit(RestRequest <jv>req</jv>) <jk>throws</jk> Exception { | |
* <jc>// Intercept immediately after RestRequest object is created and all headers/query/form-data has been | |
* // set on the request from the client.</jc> | |
* } | |
* <ja>@Override</ja> | |
* <jk>public void</jk> onConnect(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { | |
* <jc>// Intercept immediately after an HTTP response has been received.</jc> | |
* } | |
* | |
* <ja>@Override</ja> | |
* <jk>public void</jk> onClose(RestRequest <jv>req</jv>, RestResponse <jv>res</jv>) <jk>throws</jk> Exception { | |
* <jc>// Intercept when the response body is consumed.</jc> | |
* } | |
* } | |
* | |
* <jc>// Create a client with a customized interceptor.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .interceptors(MyRestCallInterceptor.<jk>class</jk>) | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_interceptors = PREFIX + "interceptors.lo"; | |
/** | |
* Add to the Call interceptors property. | |
*/ | |
public static final String RESTCLIENT_interceptors_add = PREFIX + "interceptors.so/add"; | |
/** | |
* Configuration property: Keep HttpClient open. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_keepHttpClientOpen RESTCLIENT_keepHttpClientOpen} | |
* <li><b>Name:</b> <js>"RestClient.keepHttpClientOpen.b"</js> | |
* <li><b>Data type:</b> <jk>boolean</jk> | |
* <li><b>System property:</b> <c>RestClient.keepHttpClientOpen</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_KEEPHTTPCLIENTOPEN</c> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#keepHttpClientOpen()} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Don't close this client when the {@link RestClient#close()} method is called. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client with a customized client and don't close the client service.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .httpClient(<jv>myHttpClient</jv>) | |
* .keepHttpClientOpen() | |
* .build(); | |
* | |
* <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> | |
* </p> | |
*/ | |
public static final String RESTCLIENT_keepHttpClientOpen = PREFIX + "keepHttpClientOpen.b"; | |
/** | |
* Configuration property: Enable leak detection. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_leakDetection RESTCLIENT_leakDetection} | |
* <li><b>Name:</b> <js>"RestClient.leakDetection.b"</js> | |
* <li><b>Data type:</b> <jk>boolean</jk> | |
* <li><b>System property:</b> <c>RestClient.leakDetection</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_LEAKDETECTION</c> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#leakDetection()} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Enable client and request/response leak detection. | |
* | |
* <p> | |
* Causes messages to be logged to the console if clients or request/response objects are not properly closed | |
* when the <c>finalize</c> methods are invoked. | |
* | |
* <p> | |
* Automatically enabled with {@link Context#CONTEXT_debug}. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that logs a message if </jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .leakDetection() | |
* .logToConsole() <jc>// Also log the error message to System.err</jc> | |
* .build(); | |
* | |
* <jv>client</jv>.closeQuietly(); <jc>// Customized HttpClient won't be closed.</jc> | |
* </p> | |
*/ | |
public static final String RESTCLIENT_leakDetection = PREFIX + "leakDetection.b"; | |
/** | |
* Configuration property: Logger. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logger RESTCLIENT_logger} | |
* <li><b>Name:</b> <js>"RestClient.logger.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li>{@link java.util.logging.Logger} | |
* </ul> | |
* <li><b>Default:</b> <c>Logger.<jsm>getLogger</jsm>(RestClient.<jk>class</jk>.getName())</c> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#logger(Logger)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Specifies the logger to use for logging. | |
*/ | |
public static final String RESTCLIENT_logger = PREFIX + "logger.o"; | |
/** | |
* Configuration property: Log to console. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logToConsole RESTCLIENT_logToConsole} | |
* <li><b>Name:</b> <js>"RestClient.logToConsole.b"</js> | |
* <li><b>System property:</b> <c>RestClient.logToConsole</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_LOGTOCONSOLE</c> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><jk>boolean</jk> | |
* </ul> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#logToConsole()} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* When enabled, log messages are sent to the console in addition to the existing logger. | |
*/ | |
public static final String RESTCLIENT_logToConsole = PREFIX + "logToConsole.b"; | |
/** | |
* Configuration property: Log requests. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logRequests RESTCLIENT_logRequests} | |
* <li><b>Name:</b> <js>"RestClient.logRequests.s"</js> | |
* <li><b>System property:</b> <c>RestClient.logRequests</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_LOGREQUESTS</c> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li>{@link org.apache.juneau.DetailLevel} | |
* </ul> | |
* <li><b>Default:</b> {@link org.apache.juneau.DetailLevel#NONE} | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#logRequests(DetailLevel,Level,BiPredicate)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Causes requests/responses to be logged at the specified log level at the end of the request. | |
* | |
* <p> | |
* <jsf>SIMPLE</jsf> detail produces a log message like the following: | |
* <p class='bcode w800 console'> | |
* POST http://localhost:10000/testUrl, HTTP/1.1 200 OK | |
* </p> | |
* | |
* <p> | |
* <jsf>FULL</jsf> detail produces a log message like the following: | |
* <p class='bcode w800 console'> | |
* === HTTP Call (outgoing) ======================================================= | |
* === REQUEST === | |
* POST http://localhost:10000/testUrl | |
* ---request headers--- | |
* Debug: true | |
* No-Trace: true | |
* Accept: application/json | |
* ---request entity--- | |
* Content-Type: application/json | |
* ---request content--- | |
* {"foo":"bar","baz":123} | |
* === RESPONSE === | |
* HTTP/1.1 200 OK | |
* ---response headers--- | |
* Content-Type: application/json;charset=utf-8 | |
* Content-Length: 21 | |
* Server: Jetty(8.1.0.v20120127) | |
* ---response content--- | |
* {"message":"OK then"} | |
* === END ======================================================================== | |
* </p> | |
* | |
* <p> | |
* By default, the message is logged to the default logger. It can be logged to a different logger via the | |
* {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logger} setting or logged to the console using the | |
* {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logToConsole} setting. | |
*/ | |
public static final String RESTCLIENT_logRequests = PREFIX + "logRequests.s"; | |
/** | |
* Configuration property: Log requests log level. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logRequestsLevel RESTCLIENT_logRequestsLevel} | |
* <li><b>Name:</b> <js>"RestClient.logRequestsLevel.s"</js> | |
* <li><b>System property:</b> <c>RestClient.logRequestsLevel</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_LOGREQUESTLEVEL</c> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li>{@link java.util.logging.Level} | |
* </ul> | |
* <li><b>Default:</b> {@link java.util.logging.Level#INFO} | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#logRequests(DetailLevel,Level,BiPredicate)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Used in combination with {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logRequests} to specify the | |
* level to log request messages to. | |
*/ | |
public static final String RESTCLIENT_logRequestsLevel = PREFIX + "logRequestsLevel.s"; | |
/** | |
* Configuration property: Log requests predicate. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logRequestsPredicate RESTCLIENT_logRequestsPredicate} | |
* <li><b>Name:</b> <js>"RestClient.logRequestsPredicate.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li>{@link java.util.function.BiPredicate}<{@link org.apache.juneau.rest.client.RestRequest},{@link org.apache.juneau.rest.client.RestResponse}> | |
* </ul> | |
* <li><b>Default:</b> <c>(req,res) -> <jk>true</jk></c> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#logRequests(DetailLevel,Level,BiPredicate)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Used in combination with {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_logRequests} to specify the | |
* level to log request messages to. | |
*/ | |
public static final String RESTCLIENT_logRequestsPredicate = PREFIX + "logRequestsPredicate.o"; | |
/** | |
* Configuration property: Parsers. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_parsers RESTCLIENT_parsers} | |
* <li><b>Name:</b> <js>"RestClient.parsers.lo"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link org.apache.juneau.parser.Parser}></c> | |
* <li>{@link org.apache.juneau.parser.Parser} | |
* </ul> | |
* <li><b>Default:</b> No parsers. | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#parser(Class)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#parser(Parser)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#parsers(Class...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#parsers(Parser...)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Associates the specified {@link Parser Parsers} with the HTTP client. | |
* | |
* <p> | |
* The parsers are used to parse the HTTP response body into a POJO. | |
* | |
* <p> | |
* The parser that best matches the <c>Accept</c> header will be used to parse the response body. | |
* <br>If no <c>Accept</c> header is specified, the first parser in the list will be used. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that uses JSON and XML transport for response bodies.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .parser(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>) | |
* .strict() <jc>// Enable strict mode on parsers.</jc> | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_parsers = PREFIX + "parsers.lo"; | |
/** | |
* Configuration property: Part parser. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_partParser RESTCLIENT_partParser} | |
* <li><b>Name:</b> <js>"RestClient.partParser.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link org.apache.juneau.httppart.HttpPartParser}></c> | |
* <li>{@link org.apache.juneau.httppart.HttpPartParser} | |
* </ul> | |
* <li><b>Default:</b> {@link org.apache.juneau.oapi.OpenApiParser}; | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#partParser(Class)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#partParser(HttpPartParser)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. | |
* | |
* <p> | |
* The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that uses UON format by default for incoming HTTP parts.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .partParser(UonParser.<jk>class</jk>) | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_partParser = PREFIX + "partParser.o"; | |
/** | |
* Configuration property: Part serializer. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_partSerializer RESTCLIENT_partSerializer} | |
* <li><b>Name:</b> <js>"RestClient.partSerializer.o"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link org.apache.juneau.httppart.HttpPartSerializer}></c> | |
* <li>{@link org.apache.juneau.httppart.HttpPartSerializer} | |
* </ul> | |
* <li><b>Default:</b> {@link org.apache.juneau.oapi.OpenApiSerializer}; | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#partSerializer(Class)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#partSerializer(HttpPartSerializer)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. | |
* | |
* <p> | |
* The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that uses UON format by default for outgoing HTTP parts.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .partSerializer(UonSerializer.<jk>class</jk>) | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_partSerializer = PREFIX + "partSerializer.o"; | |
/** | |
* Configuration property: Request query parameters. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_query RESTCLIENT_query} | |
* <li><b>Name:</b> <js>"RestClient.query.lo"</js> | |
* <li><b>Data type:</b> <c>List<{@link org.apache.http.NameValuePair}></c> | |
* <li><b>Default:</b> empty map | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestClientBuilder} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#queries(Object...) queries(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#query(String,Object) query(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#query(String,Object,HttpPartSchema,HttpPartSerializer) query(String,Object,HttpPartSerializer,HttpPartSchema)} | |
* </ul> | |
* <li class='jc'>{@link org.apache.juneau.rest.client.RestRequest} | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#query(String,Object) query(String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#query(AddFlag,String,Object) query(EnumSet<AddFlag>,String,Object)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#queries(Object...) queries(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#queries(AddFlag,Object...) queries(EnumSet<AddFlag>,Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#queryPairs(Object...) queryPairs(Object...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestRequest#queryCustom(Object) queryCustom(Object)} | |
* </ul> | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Query parameters to add to every request. | |
*/ | |
public static final String RESTCLIENT_query = PREFIX + "query.lo"; | |
/** | |
* Configuration property: Root URI. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_rootUri RESTCLIENT_rootUri} | |
* <li><b>Name:</b> <js>"RestClient.rootUri.s"</js> | |
* <li><b>Data type:</b> <c>String</c> | |
* <li><b>System property:</b> <c>RestClient.rootUri</c> | |
* <li><b>Environment variable:</b> <c>RESTCLIENT_ROOTURI</c> | |
* <li><b>Default:</b> <jk>false</jk> | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#rootUri(Object)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* When set, relative URI strings passed in through the various rest call methods (e.g. {@link RestClient#get(Object)} | |
* will be prefixed with the specified root. | |
* <br>This root URI is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URI string. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that uses UON format by default for HTTP parts.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .rootUri(<js>"http://localhost:10000/foo"</js>) | |
* .build(); | |
* | |
* Bar <jv>bar</jv> = <jv>client</jv> | |
* .get(<js>"/bar"</js>) <jc>// Relative to http://localhost:10000/foo</jc> | |
* .run() | |
* .getBody().as(Bar.<jk>class</jk>); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_rootUri = PREFIX + "rootUri.s"; | |
/** | |
* Configuration property: Serializers. | |
* | |
* <h5 class='section'>Property:</h5> | |
* <ul class='spaced-list'> | |
* <li><b>ID:</b> {@link org.apache.juneau.rest.client.RestClient#RESTCLIENT_serializers RESTCLIENT_serializers} | |
* <li><b>Name:</b> <js>"RestClient.serializers.lo"</js> | |
* <li><b>Data type:</b> | |
* <ul> | |
* <li><c>Class<? <jk>extends</jk> {@link org.apache.juneau.serializer.Serializer}></c> | |
* <li>{@link org.apache.juneau.serializer.Serializer} | |
* </ul> | |
* <li><b>Default:</b> No serializers. | |
* <li><b>Methods:</b> | |
* <ul> | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#serializer(Class)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#serializer(Serializer)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#serializers(Class...)} | |
* <li class='jm'>{@link org.apache.juneau.rest.client.RestClientBuilder#serializers(Serializer...)} | |
* </ul> | |
* </ul> | |
* | |
* <h5 class='section'>Description:</h5> | |
* <p> | |
* Associates the specified {@link Serializer Serializers} with the HTTP client. | |
* | |
* <p> | |
* The serializer is used to serialize POJOs into the HTTP request body. | |
* | |
* <p> | |
* The serializer that best matches the <c>Content-Type</c> header will be used to serialize the request body. | |
* <br>If no <c>Content-Type</c> header is specified, the first serializer in the list will be used. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bcode w800'> | |
* <jc>// Create a client that uses JSON and XML transport for request bodies.</jc> | |
* RestClient <jv>client</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .serializers(JsonSerializer.<jk>class</jk>,XmlSerializer.<jk>class</jk>) | |
* .sortCollections() <jc>// Sort any collections being serialized.</jc> | |
* .build(); | |
* </p> | |
*/ | |
public static final String RESTCLIENT_serializers = PREFIX + "serializers.lo"; | |
static final String RESTCLIENT_httpClient = PREFIX + "httpClient.o"; | |
static final String RESTCLIENT_httpClientBuilder= PREFIX + "httpClientBuilder.o"; | |
private final HeaderList headers; | |
private final PartList query, formData; | |
final CloseableHttpClient httpClient; | |
private final HttpClientConnectionManager connectionManager; | |
private final boolean keepHttpClientOpen, leakDetection; | |
private final UrlEncodingSerializer urlEncodingSerializer; // Used for form posts only. | |
private final HttpPartSerializer partSerializer; | |
private final HttpPartParser partParser; | |
private final RestCallHandler callHandler; | |
private final String rootUri; | |
private volatile boolean isClosed = false; | |
private final StackTraceElement[] creationStack; | |
private final Logger logger; | |
final DetailLevel logRequests; | |
final BiPredicate<RestRequest,RestResponse> logRequestsPredicate; | |
final Level logRequestsLevel; | |
final boolean ignoreErrors; | |
private final boolean logToConsole; | |
private final PrintStream console; | |
private StackTraceElement[] closedStack; | |
private static final ConcurrentHashMap<Class<?>,Context> requestContexts = new ConcurrentHashMap<>(); | |
// These are read directly by RestCall. | |
final SerializerGroup serializers; | |
final ParserGroup parsers; | |
Predicate<Integer> errorCodes; | |
final RestCallInterceptor[] interceptors; | |
// This is lazy-created. | |
private volatile ExecutorService executorService; | |
private final boolean executorServiceShutdownOnClose; | |
/** | |
* Instantiates a new clean-slate {@link RestClientBuilder} object. | |
* | |
* @return A new {@link RestClientBuilder} object. | |
*/ | |
public static RestClientBuilder create() { | |
return new RestClientBuilder(); | |
} | |
@Override /* Context */ | |
public RestClientBuilder copy() { | |
return new RestClientBuilder(this); | |
} | |
private static final | |
Predicate<Integer> ERROR_CODES_DEFAULT = x -> x<=0 || x>=400; | |
private static final | |
BiPredicate<RestRequest,RestResponse> LOG_REQUESTS_PREDICATE_DEFAULT = (req,res) -> true; | |
/** | |
* Constructor. | |
* | |
* @param cp The property store containing the unmodifiable configuration for this client. | |
*/ | |
@SuppressWarnings("unchecked") | |
protected RestClient(ContextProperties cp) { | |
super(cp); | |
this.httpClient = cp.getInstance(RESTCLIENT_httpClient, CloseableHttpClient.class).orElse(null); | |
this.connectionManager = cp.getInstance(RESTCLIENT_connectionManager, HttpClientConnectionManager.class).orElse(null); | |
this.keepHttpClientOpen = cp.getBoolean(RESTCLIENT_keepHttpClientOpen).orElse(false); | |
this.errorCodes = cp.getInstance(RESTCLIENT_errorCodes, Predicate.class).orElse(ERROR_CODES_DEFAULT); | |
this.executorServiceShutdownOnClose = cp.getBoolean(RESTCLIENT_executorServiceShutdownOnClose).orElse(false); | |
this.rootUri = StringUtils.nullIfEmpty(cp.getString(RESTCLIENT_rootUri).orElse("").replaceAll("\\/$", "")); | |
this.leakDetection = cp.getBoolean(RESTCLIENT_leakDetection).orElse(isDebug()); | |
this.ignoreErrors = cp.getBoolean(RESTCLIENT_ignoreErrors).orElse(false); | |
this.logger = cp.getInstance(RESTCLIENT_logger, Logger.class).orElseGet(()->Logger.getLogger(RestClient.class.getName())); | |
this.logRequests = cp.getInstance(RESTCLIENT_logRequests, DetailLevel.class).orElse(isDebug() ? DetailLevel.FULL : DetailLevel.NONE); | |
this.logRequestsLevel = cp.getInstance(RESTCLIENT_logRequestsLevel, Level.class).orElse(isDebug() ? Level.WARNING : Level.OFF); | |
this.logToConsole = cp.getBoolean(RESTCLIENT_logToConsole).orElse(isDebug()); | |
this.console = cp.getInstance(RESTCLIENT_console, PrintStream.class).orElse(System.err); | |
this.logRequestsPredicate = cp.getInstance(RESTCLIENT_logRequestsPredicate, BiPredicate.class).orElse(LOG_REQUESTS_PREDICATE_DEFAULT); | |
SerializerGroupBuilder sgb = SerializerGroup.create(); | |
for (Object o : cp.getArray(RESTCLIENT_serializers, Object.class).orElse(new Object[0])) { | |
if (o instanceof Serializer) { | |
sgb.append((Serializer)o); // Don't apply ContextProperties. | |
} else if (o instanceof Class) { | |
Class<?> c = (Class<?>)o; | |
if (! Serializer.class.isAssignableFrom(c)) | |
throw new ConfigException("RESTCLIENT_serializers property had invalid class of type ''{0}''", className(c)); | |
sgb.append(ContextCache.INSTANCE.create((Class<? extends Serializer>)o, cp)); | |
} else { | |
throw new ConfigException("RESTCLIENT_serializers property had invalid object of type ''{0}''", className(o)); | |
} | |
} | |
this.serializers = sgb.build(); | |
ParserGroupBuilder pgb = ParserGroup.create(); | |
for (Object o : cp.getArray(RESTCLIENT_parsers, Object.class).orElse(new Object[0])) { | |
if (o instanceof Parser) { | |
pgb.append((Parser)o); // Don't apply ContextProperties. | |
} else if (o instanceof Class) { | |
Class<?> c = (Class<?>)o; | |
if (! Parser.class.isAssignableFrom(c)) | |
throw new ConfigException("RESTCLIENT_parsers property had invalid class of type ''{0}''", className(c)); | |
pgb.append(ContextCache.INSTANCE.create((Class<? extends Parser>)o, cp)); | |
} else { | |
throw new ConfigException("RESTCLIENT_parsers property had invalid object of type ''{0}''", className(o)); | |
} | |
} | |
this.parsers = pgb.build(); | |
BeanStore bs = new BeanStore() | |
.addBean(ContextProperties.class, cp) | |
.addBean(RestClient.class, this); | |
this.urlEncodingSerializer = UrlEncodingSerializer.create().apply(cp).build(); | |
this.partSerializer = cp.getInstance(RESTCLIENT_partSerializer, HttpPartSerializer.class, bs).orElseGet(bs.createBeanSupplier(OpenApiSerializer.class)); | |
this.partParser = cp.getInstance(RESTCLIENT_partParser, HttpPartParser.class, bs).orElseGet(bs.createBeanSupplier(OpenApiParser.class)); | |
this.executorService = cp.getInstance(RESTCLIENT_executorService, ExecutorService.class).orElse(null); | |
HttpPartSerializerSession partSerializerSession = partSerializer.createPartSession(null); | |
HeaderListBuilder headers = HeaderList.create(); | |
for (Object o : cp.getList(RESTCLIENT_headers, Object.class).orElse(emptyList())) { | |
o = buildBuilders(o, partSerializerSession); | |
if (o instanceof HeaderList) | |
headers.append(((HeaderList)o)); | |
else | |
headers.append(HttpHeaders.cast(o)); | |
} | |
this.headers = headers.build(); | |
PartListBuilder query = PartList.create(); | |
for (Object o : cp.getList(RESTCLIENT_query, Object.class).orElse(emptyList())) { | |
o = buildBuilders(o, partSerializerSession); | |
if (o instanceof PartList) | |
query.append((PartList)o); | |
else | |
query.append(BasicPart.cast(o)); | |
} | |
this.query = query.build(); | |
PartListBuilder formData = PartList.create(); | |
for (Object o : cp.getList(RESTCLIENT_formData, Object.class).orElse(emptyList())) { | |
o = buildBuilders(o, partSerializerSession); | |
if (o instanceof PartList) | |
formData.append((PartList)o); | |
else | |
formData.append(BasicPart.cast(o)); | |
} | |
this.formData = formData.build(); | |
this.callHandler = cp.getInstance(RESTCLIENT_callHandler, RestCallHandler.class, bs).orElseGet(bs.createBeanSupplier(BasicRestCallHandler.class)); | |
this.interceptors = cp.getInstanceArray(RESTCLIENT_interceptors, RestCallInterceptor.class).orElse(new RestCallInterceptor[0]); | |
creationStack = isDebug() ? Thread.currentThread().getStackTrace() : null; | |
} | |
private static Object buildBuilders(Object o, HttpPartSerializerSession ss) { | |
if (o instanceof SerializedHeader) | |
return ((SerializedHeader)o).copyWith(ss, null); | |
if (o instanceof SerializedPart) | |
return ((SerializedPart)o).copy().copyWith(ss, null); | |
return o; | |
} | |
/** | |
* Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}. | |
* | |
* <p> | |
* It's good practice to call this method after the client is no longer used. | |
* | |
* @throws IOException Thrown by underlying stream. | |
*/ | |
@Override | |
public void close() throws IOException { | |
isClosed = true; | |
if (! keepHttpClientOpen) | |
httpClient.close(); | |
if (executorService != null && executorServiceShutdownOnClose) | |
executorService.shutdown(); | |
if (creationStack != null) | |
closedStack = Thread.currentThread().getStackTrace(); | |
} | |
/** | |
* Same as {@link #close()}, but ignores any exceptions. | |
*/ | |
public void closeQuietly() { | |
isClosed = true; | |
try { | |
if (! keepHttpClientOpen) | |
httpClient.close(); | |
if (executorService != null && executorServiceShutdownOnClose) | |
executorService.shutdown(); | |
} catch (Throwable t) {} | |
if (creationStack != null) | |
closedStack = Thread.currentThread().getStackTrace(); | |
} | |
/** | |
* Entrypoint for executing all requests and returning a response. | |
* | |
* <p> | |
* Subclasses can override this method to provide specialized handling. | |
* | |
* <p> | |
* The behavior of this method can also be modified by specifying a different {@link RestCallHandler}. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestClient#RESTCLIENT_callHandler} | |
* <li class='jm'>{@link RestClientBuilder#callHandler(Class)} | |
* <li class='jm'>{@link RestClientBuilder#callHandler(RestCallHandler)} | |
* </ul> | |
* | |
* @param target The target host for the request. | |
* <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. | |
* @param request The request to execute. | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return | |
* The response to the request. | |
* <br>This is always a final response, never an intermediate response with an 1xx status code. | |
* <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the | |
* implementation and configuration of this client. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* RestCallHandler */ | |
public HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws ClientProtocolException, IOException { | |
return callHandler.run(target, request, context); | |
} | |
/** | |
* Perform a <c>GET</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest get(Object uri) throws RestCallException { | |
return request(op("GET", uri)); | |
} | |
/** | |
* Perform a <c>GET</c> request against the root URI. | |
* | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest get() throws RestCallException { | |
return request(op("GET", null)); | |
} | |
/** | |
* Perform a <c>PUT</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request. | |
* Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* {@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request | |
* and getting the response as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest put(Object uri, Object body) throws RestCallException { | |
return request(op("PUT", uri, body)); | |
} | |
/** | |
* Perform a <c>PUT</c> request against the specified URI using a plain text body bypassing the serializer. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request bypassing the serializer. | |
* @param contentType The content type of the request. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request | |
* and getting the response as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest put(Object uri, String body, String contentType) throws RestCallException { | |
return request(op("PUT", uri, stringBody(body))).contentType(contentType); | |
} | |
/** | |
* Same as {@link #put(Object, Object)} but don't specify the input yet. | |
* | |
* <p> | |
* You must call either {@link RestRequest#body(Object)} or {@link RestRequest#formData(String, Object)} | |
* to set the contents on the result object. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException REST call failed. | |
*/ | |
public RestRequest put(Object uri) throws RestCallException { | |
return request(op("PUT", uri)); | |
} | |
/** | |
* Perform a <c>POST</c> request against the specified URI. | |
* | |
* <ul class='notes'> | |
* <li>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. | |
* </ul> | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request. | |
* Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* {@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest post(Object uri, Object body) throws RestCallException { | |
return request(op("POST", uri, body)); | |
} | |
/** | |
* Perform a <c>POST</c> request against the specified URI as a plain text body bypassing the serializer. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request bypassing the serializer. | |
* @param contentType | |
* The content type of the request. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest post(Object uri, String body, String contentType) throws RestCallException { | |
return request(op("POST", uri, stringBody(body))).contentType(contentType); | |
} | |
/** | |
* Same as {@link #post(Object, Object)} but don't specify the input yet. | |
* | |
* <p> | |
* You must call either {@link RestRequest#body(Object)} or {@link RestRequest#formData(String, Object)} to set the | |
* contents on the result object. | |
* | |
* <ul class='notes'> | |
* <li>Use {@link #formPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts. | |
* </ul> | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException REST call failed. | |
*/ | |
public RestRequest post(Object uri) throws RestCallException { | |
return request(op("POST", uri)); | |
} | |
/** | |
* Perform a <c>DELETE</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest delete(Object uri) throws RestCallException { | |
return request(op("DELETE", uri)); | |
} | |
/** | |
* Perform an <c>OPTIONS</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest options(Object uri) throws RestCallException { | |
return request(op("OPTIONS", uri)); | |
} | |
/** | |
* Perform a <c>HEAD</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest head(Object uri) throws RestCallException { | |
return request(op("HEAD", uri)); | |
} | |
/** | |
* Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> | |
* against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request. | |
* <ul class='spaced-list'> | |
* <li>{@link NameValuePair} - URL-encoded as a single name-value pair. | |
* <li>{@link NameValuePair} array - URL-encoded as name value pairs. | |
* <li>{@link PartList} - URL-encoded as name value pairs. | |
* <li>{@link Reader}/{@link InputStream}- Streamed directly and <l>Content-Type</l> set to <js>"application/x-www-form-urlencoded"</js> | |
* <li>{@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li>{@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li>{@link Object} - Converted to a {@link SerializedEntity} using {@link UrlEncodingSerializer} to serialize. | |
* <li>{@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest formPost(Object uri, Object body) throws RestCallException { | |
RestRequest req = request(op("POST", uri)); | |
try { | |
if (body instanceof Supplier) | |
body = ((Supplier<?>)body).get(); | |
if (body instanceof NameValuePair) | |
return req.body(new UrlEncodedFormEntity(AList.of((NameValuePair)body))); | |
if (body instanceof NameValuePair[]) | |
return req.body(new UrlEncodedFormEntity(Arrays.asList((NameValuePair[])body))); | |
if (body instanceof PartList) | |
return req.body(new UrlEncodedFormEntity(((PartList)body).asNameValuePairs())); | |
if (body instanceof HttpResource) | |
((HttpResource)body).getHeaders().forEach(x-> req.header(x)); | |
if (body instanceof HttpEntity) { | |
HttpEntity e = (HttpEntity)body; | |
if (e.getContentType() == null) | |
req.contentType("application/x-www-form-urlencoded"); | |
return req.body(e); | |
} | |
if (body instanceof Reader || body instanceof InputStream) | |
return req.contentType("application/x-www-form-urlencoded").body(body); | |
return req.body(serializedEntity(body, urlEncodingSerializer, null).build()); | |
} catch (IOException e) { | |
throw new RestCallException(null, e, "Could not read form post body."); | |
} | |
} | |
/** | |
* Same as {@link #formPost(Object, Object)} but doesn't specify the input yet. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest formPost(Object uri) throws RestCallException { | |
return request(op("POST", uri)); | |
} | |
/** | |
* Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c> | |
* against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param parameters | |
* The parameters of the form post. | |
* <br>The parameters represent name/value pairs and must be an even number of arguments. | |
* <br>Parameters are converted to {@link BasicPart} objects. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest formPostPairs(Object uri, Object...parameters) throws RestCallException { | |
return formPost(uri, partList(parameters)); | |
} | |
/** | |
* Perform a <c>PATCH</c> request against the specified URI. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request. | |
* Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li> | |
* {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* {@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest patch(Object uri, Object body) throws RestCallException { | |
return request(op("PATCH", uri, body)); | |
} | |
/** | |
* Perform a <c>PATCH</c> request against the specified URI as a plain text body bypassing the serializer. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The object to serialize and transmit to the URI as the body of the request bypassing the serializer. | |
* @param contentType | |
* The content type of the request. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest patch(Object uri, String body, String contentType) throws RestCallException { | |
return request(op("PATCH", uri, stringBody(body))).contentType(contentType); | |
} | |
/** | |
* Same as {@link #patch(Object, Object)} but don't specify the input yet. | |
* | |
* <p> | |
* You must call {@link RestRequest#body(Object)} to set the contents on the result object. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException REST call failed. | |
*/ | |
public RestRequest patch(Object uri) throws RestCallException { | |
return request(op("PATCH", uri)); | |
} | |
/** | |
* Performs a REST call where the entire call is specified in a simple string. | |
* | |
* <p> | |
* This method is useful for performing callbacks when the target of a callback is passed in | |
* on an initial request, for example to signal when a long-running process has completed. | |
* | |
* <p> | |
* The call string can be any of the following formats: | |
* <ul class='spaced-list'> | |
* <li> | |
* <js>"[method] [uri]"</js> - e.g. <js>"GET http://localhost/callback"</js> | |
* <li> | |
* <js>"[method] [uri] [payload]"</js> - e.g. <js>"POST http://localhost/callback some text payload"</js> | |
* <li> | |
* <js>"[method] [headers] [uri] [payload]"</js> - e.g. <js>"POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}"</js> | |
* </ul> | |
* <p> | |
* The payload will always be sent using a simple {@link StringEntity}. | |
* | |
* @param callString The call string. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException REST call failed. | |
*/ | |
public RestRequest callback(String callString) throws RestCallException { | |
callString = emptyIfNull(callString); | |
// S01 - Looking for end of method. | |
// S02 - Found end of method, looking for beginning of URI or headers. | |
// S03 - Found beginning of headers, looking for end of headers. | |
// S04 - Found end of headers, looking for beginning of URI. | |
// S05 - Found beginning of URI, looking for end of URI. | |
StateMachineState state = S01; | |
int mark = 0; | |
String method = null, headers = null, uri = null, content = null; | |
for (int i = 0; i < callString.length(); i++) { | |
char c = callString.charAt(i); | |
if (state == S01) { | |
if (isWhitespace(c)) { | |
method = callString.substring(mark, i); | |
state = S02; | |
} | |
} else if (state == S02) { | |
if (! isWhitespace(c)) { | |
mark = i; | |
if (c == '{') | |
state = S03; | |
else | |
state = S05; | |
} | |
} else if (state == S03) { | |
if (c == '}') { | |
headers = callString.substring(mark, i+1); | |
state = S04; | |
} | |
} else if (state == S04) { | |
if (! isWhitespace(c)) { | |
mark = i; | |
state = S05; | |
} | |
} else /* (state == S05) */ { | |
if (isWhitespace(c)) { | |
uri = callString.substring(mark, i); | |
content = callString.substring(i).trim(); | |
break; | |
} | |
} | |
} | |
if (state != S05) | |
throw new RestCallException(null, null, "Invalid format for call string. State={0}", state); | |
try { | |
RestRequest req = request(method, uri, isNotEmpty(content)); | |
if (headers != null) | |
for (Map.Entry<String,Object> e : OMap.ofJson(headers).entrySet()) | |
req.header(stringHeader(e.getKey(), stringify(e.getValue()))); | |
if (isNotEmpty(content)) | |
req.bodyString(content); | |
return req; | |
} catch (ParseException e) { | |
throw new RestCallException(null, e, "Invalid format for call string."); | |
} | |
} | |
/** | |
* Perform a generic REST call. | |
* | |
* @param method The HTTP method. | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param body | |
* The HTTP body content. | |
* Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li> | |
* {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* {@link Supplier} - A supplier of anything on this list. | |
* </ul> | |
* This parameter is IGNORED if the method type normally does not have content. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest request(String method, Object uri, Object body) throws RestCallException { | |
return request(op(method, uri, body)); | |
} | |
/** | |
* Perform a generic REST call. | |
* | |
* @param method The HTTP method. | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest request(String method, Object uri) throws RestCallException { | |
return request(op(method, uri)); | |
} | |
/** | |
* Perform a generic REST call. | |
* | |
* <p> | |
* Typically you're going to use {@link #request(String, Object)} or {@link #request(String, Object, Object)}, | |
* but this method is provided to allow you to perform non-standard HTTP methods (e.g. HTTP FOO). | |
* | |
* @param method The method name (e.g. <js>"GET"</js>, <js>"OPTIONS"</js>). | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @param hasBody Boolean flag indicating if the specified request has content associated with it. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest request(String method, Object uri, boolean hasBody) throws RestCallException { | |
return request(op(method, uri).hasBody(hasBody)); | |
} | |
/** | |
* Perform an arbitrary request against the specified URI. | |
* | |
* @param op The operation that identifies the HTTP method, URL, and optional payload. | |
* @return | |
* A {@link RestRequest} object that can be further tailored before executing the request and getting the response | |
* as a parsed object. | |
* @throws RestCallException If any authentication errors occurred. | |
*/ | |
public RestRequest request(RestOperation op) throws RestCallException { | |
if (isClosed) { | |
Exception e2 = null; | |
if (closedStack != null) { | |
e2 = new Exception("Creation stack:"); | |
e2.setStackTrace(closedStack); | |
throw new RestCallException(null, e2, "RestClient.close() has already been called. This client cannot be reused."); | |
} | |
throw new RestCallException(null, null, "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."); | |
} | |
RestRequest req = createRequest(toURI(op.getUri(), rootUri), op.getMethod(), op.hasBody()); | |
headers.forEach(x -> req.header(APPEND, x)); | |
query.forEach(x -> req.query(APPEND, x.getName(), x.getValue())); | |
formData.forEach(x -> req.formData(APPEND, x.getName(), x.getValue())); | |
onInit(req); | |
if (op.hasBody()) | |
req.body(op.getBody()); | |
return req; | |
} | |
/** | |
* Creates a {@link RestRequest} object from the specified {@link HttpRequest} object. | |
* | |
* <p> | |
* Subclasses can override this method to provide their own specialized {@link RestRequest} objects. | |
* | |
* @param uri The target. | |
* @param method The HTTP method (uppercase). | |
* @param hasBody Whether this method has a request entity. | |
* @return A new {@link RestRequest} object. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
protected RestRequest createRequest(URI uri, String method, boolean hasBody) throws RestCallException { | |
return new RestRequest(this, uri, method, hasBody); | |
} | |
/** | |
* Creates a {@link RestResponse} object from the specified {@link HttpResponse} object. | |
* | |
* <p> | |
* Subclasses can override this method to provide their own specialized {@link RestResponse} objects. | |
* | |
* @param request The request creating this response. | |
* @param httpResponse The response object to wrap. | |
* @param parser The parser to use to parse the response. | |
* | |
* @return A new {@link RestResponse} object. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
protected RestResponse createResponse(RestRequest request, HttpResponse httpResponse, Parser parser) throws RestCallException { | |
return new RestResponse(this, request, httpResponse, parser); | |
} | |
/** | |
* Create a new proxy interface against a 3rd-party REST interface. | |
* | |
* <p> | |
* The URI to the REST interface is based on the following values: | |
* <ul> | |
* <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). | |
* <li>The {@link RestClientBuilder#rootUri(Object) rootUri} on the client (<c>root-url</c>). | |
* <li>The fully-qualified class name of the interface (<c>class-name</c>). | |
* </ul> | |
* | |
* <p> | |
* The URI calculation is as follows: | |
* <ul> | |
* <li><c>remote-path</c> - If remote path is absolute. | |
* <li><c>root-uri/remote-path</c> - If remote path is relative and root-uri has been specified. | |
* <li><c>root-uri/class-name</c> - If remote path is not specified. | |
* </ul> | |
* | |
* <p> | |
* If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. | |
* | |
* <h5 class='section'>Examples:</h5> | |
* <p class='bcode w800'> | |
* <jk>package</jk> <jk>org.apache.foo</jk>; | |
* | |
* <ja>@RemoteResource</ja>(path=<js>"http://hostname/resturi/myinterface1"</js>) | |
* <jk>public interface</jk> MyInterface1 { ... } | |
* | |
* <ja>@RemoteResource</ja>(path=<js>"/myinterface2"</js>) | |
* <jk>public interface</jk> MyInterface2 { ... } | |
* | |
* <jk>public interface</jk> MyInterface3 { ... } | |
* | |
* <jc>// Resolves to "http://localhost/resturi/myinterface1"</jc> | |
* MyInterface1 <jv>i1</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .build() | |
* .getRemote(MyInterface1.<jk>class</jk>); | |
* | |
* <jc>// Resolves to "http://hostname/resturi/myinterface2"</jc> | |
* MyInterface2 <jv>i2</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .rootUri(<js>"http://hostname/resturi"</js>) | |
* .build() | |
* .getRemote(MyInterface2.<jk>class</jk>); | |
* | |
* <jc>// Resolves to "http://hostname/resturi/org.apache.foo.MyInterface3"</jc> | |
* MyInterface3 <jv>i3</jv> = RestClient | |
* .<jsm>create</jsm>() | |
* .rootUri(<js>"http://hostname/resturi"</js>) | |
* .build() | |
* .getRemote(MyInterface3.<jk>class</jk>); | |
* </p> | |
* | |
* <ul class='notes'> | |
* <li> | |
* If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying | |
* pooling client connection manager. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestcProxies} | |
* </ul> | |
* | |
* @param interfaceClass The interface to create a proxy for. | |
* @return The new proxy interface. | |
* @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. | |
*/ | |
public <T> T getRemote(Class<T> interfaceClass) { | |
return getRemote(interfaceClass, null); | |
} | |
/** | |
* Same as {@link #getRemote(Class)} except explicitly specifies the URI of the REST interface. | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestcProxies} | |
* </ul> | |
* | |
* @param interfaceClass The interface to create a proxy for. | |
* @param rootUri The URI of the REST interface. | |
* @return The new proxy interface. | |
*/ | |
public <T> T getRemote(Class<T> interfaceClass, Object rootUri) { | |
return getRemote(interfaceClass, rootUri, null, null); | |
} | |
/** | |
* Same as {@link #getRemote(Class, Object)} but allows you to override the serializer and parser used. | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestcProxies} | |
* </ul> | |
* @param interfaceClass The interface to create a proxy for. | |
* @param rootUri The URI of the REST interface. | |
* @param serializer The serializer used to serialize POJOs to the body of the HTTP request. | |
* @param parser The parser used to parse POJOs from the body of the HTTP response. | |
* @return The new proxy interface. | |
*/ | |
@SuppressWarnings({ "unchecked" }) | |
public <T> T getRemote(final Class<T> interfaceClass, Object rootUri, final Serializer serializer, final Parser parser) { | |
if (rootUri == null) | |
rootUri = this.rootUri; | |
final String restUrl2 = trimSlashes(emptyIfNull(rootUri)); | |
return (T)Proxy.newProxyInstance( | |
interfaceClass.getClassLoader(), | |
new Class[] { interfaceClass }, | |
new InvocationHandler() { | |
final RemoteMeta rm = new RemoteMeta(interfaceClass); | |
@Override /* InvocationHandler */ | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
RemoteOperationMeta rom = rm.getOperationMeta(method); | |
String uri = rom.getFullPath(); | |
if (uri.indexOf("://") == -1) | |
uri = restUrl2 + '/' + uri; | |
if (uri.indexOf("://") == -1) | |
throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote resource."); | |
String httpMethod = rom.getHttpMethod(); | |
HttpPartSerializerSession s = getPartSerializerSession(); | |
RestRequest rc = request(httpMethod, uri, hasContent(httpMethod)); | |
rc.serializer(serializer); | |
rc.parser(parser); | |
rm.getHeaders().forEach(x -> rc.header(APPEND, x)); | |
for (RemoteOperationArg a : rom.getPathArgs()) | |
rc.pathArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer(s)); | |
for (RemoteOperationArg a : rom.getQueryArgs()) | |
rc.queryArg(a.isSkipIfEmpty() ? SKIP_IF_EMPTY_FLAGS : DEFAULT_FLAGS, a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer(s)); | |
for (RemoteOperationArg a : rom.getFormDataArgs()) | |
rc.formDataArg(a.isSkipIfEmpty() ? SKIP_IF_EMPTY_FLAGS : DEFAULT_FLAGS, a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer(s)); | |
for (RemoteOperationArg a : rom.getHeaderArgs()) | |
rc.headerArg(a.isSkipIfEmpty() ? SKIP_IF_EMPTY_FLAGS : DEFAULT_FLAGS, a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer(s)); | |
RemoteOperationArg ba = rom.getBodyArg(); | |
if (ba != null) | |
rc.body(args[ba.getIndex()], ba.getSchema()); | |
if (rom.getRequestArgs().length > 0) { | |
for (RemoteOperationBeanArg rmba : rom.getRequestArgs()) { | |
RequestBeanMeta rbm = rmba.getMeta(); | |
Object bean = args[rmba.getIndex()]; | |
if (bean != null) { | |
for (RequestBeanPropertyMeta p : rbm.getProperties()) { | |
Object val = p.getGetter().invoke(bean); | |
HttpPartType pt = p.getPartType(); | |
HttpPartSerializerSession ps = p.getSerializer(s); | |
String pn = p.getPartName(); | |
HttpPartSchema schema = p.getSchema(); | |
EnumSet<AddFlag> flags = schema.isSkipIfEmpty() ? SKIP_IF_EMPTY_FLAGS : DEFAULT_FLAGS; | |
if (pt == PATH) | |
rc.pathArg(pn, val, schema, p.getSerializer(s)); | |
else if (val != null) { | |
if (pt == QUERY) | |
rc.queryArg(flags, pn, val, schema, ps); | |
else if (pt == FORMDATA) | |
rc.formDataArg(flags, pn, val, schema, ps); | |
else if (pt == HEADER) | |
rc.headerArg(flags, pn, val, schema, ps); | |
else /* (pt == HttpPartType.BODY) */ | |
rc.body(val, schema); | |
} | |
} | |
} | |
} | |
} | |
RemoteOperationReturn ror = rom.getReturns(); | |
if (ror.isFuture()) { | |
return getExecutorService().submit(new Callable<Object>() { | |
@Override | |
public Object call() throws Exception { | |
try { | |
return executeRemote(interfaceClass, rc, method, rom); | |
} catch (Exception e) { | |
throw e; | |
} catch (Throwable e) { | |
throw runtimeException(e); | |
} | |
} | |
}); | |
} else if (ror.isCompletableFuture()) { | |
CompletableFuture<Object> cf = new CompletableFuture<>(); | |
getExecutorService().submit(new Callable<Object>() { | |
@Override | |
public Object call() throws Exception { | |
try { | |
cf.complete(executeRemote(interfaceClass, rc, method, rom)); | |
} catch (Throwable e) { | |
cf.completeExceptionally(e); | |
} | |
return null; | |
} | |
}); | |
return cf; | |
} | |
return executeRemote(interfaceClass, rc, method, rom); | |
} | |
}); | |
} | |
static Consumer<Header> wrapper(Consumer<Header> consumer) { | |
return i -> { | |
try { | |
consumer.accept(i); | |
} catch (Exception e) { | |
throw runtimeException(e); | |
} | |
}; | |
} | |
Object executeRemote(Class<?> interfaceClass, RestRequest rc, Method method, RemoteOperationMeta rom) throws Throwable { | |
RemoteOperationReturn ror = rom.getReturns(); | |
try { | |
Object ret = null; | |
RestResponse res = null; | |
rc.rethrow(RuntimeException.class).rethrow(rom.getExceptions()); | |
if (ror.getReturnValue() == RemoteReturn.NONE) { | |
res = rc.complete(); | |
} else if (ror.getReturnValue() == RemoteReturn.STATUS) { | |
res = rc.complete(); | |
int returnCode = res.getStatusCode(); | |
Class<?> rt = method.getReturnType(); | |
if (rt == Integer.class || rt == int.class) | |
ret = returnCode; | |
else if (rt == Boolean.class || rt == boolean.class) | |
ret = returnCode < 400; | |
else | |
throw new RestCallException(res, null, "Invalid return type on method annotated with @RemoteOp(returns=RemoteReturn.STATUS). Only integer and booleans types are valid."); | |
} else if (ror.getReturnValue() == RemoteReturn.BEAN) { | |
rc.ignoreErrors(); | |
res = rc.run(); | |
ret = res.as(ror.getResponseBeanMeta()); | |
} else { | |
Class<?> rt = method.getReturnType(); | |
if (Throwable.class.isAssignableFrom(rt)) | |
rc.ignoreErrors(); | |
res = rc.run(); | |
Object v = res.getBody().asType(ror.getReturnType()); | |
if (v == null && rt.isPrimitive()) | |
v = ClassInfo.of(rt).getPrimitiveDefault(); | |
ret = v; | |
} | |
return ret; | |
} catch (RestCallException e) { | |
Throwable t = e.getCause(); | |
if (t instanceof RuntimeException) | |
throw t; | |
for (Class<?> t2 : method.getExceptionTypes()) | |
if (t2.isInstance(t)) | |
throw t; | |
throw runtimeException(e); | |
} | |
} | |
/** | |
* Create a new proxy interface against an RRPC-style service. | |
* | |
* <p> | |
* Remote interfaces are interfaces exposed on the server side using either the <c>RrpcServlet</c> | |
* or <c>RRPC</c> REST methods. | |
* | |
* <p> | |
* The URI to the REST interface is based on the following values: | |
* <ul> | |
* <li>The {@link Remote#path() @Remote(path)} annotation on the interface (<c>remote-path</c>). | |
* <li>The {@link RestClientBuilder#rootUri(Object) rootUri} on the client (<c>root-url</c>). | |
* <li>The fully-qualified class name of the interface (<c>class-name</c>). | |
* </ul> | |
* | |
* <p> | |
* The URI calculation is as follows: | |
* <ul> | |
* <li><c>remote-path</c> - If remote path is absolute. | |
* <li><c>root-url/remote-path</c> - If remote path is relative and root-url has been specified. | |
* <li><c>root-url/class-name</c> - If remote path is not specified. | |
* </ul> | |
* | |
* <p> | |
* If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. | |
* | |
* <ul class='notes'> | |
* <li> | |
* If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying | |
* pooling client connection manager. | |
* </ul> | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestRpc} | |
* </ul> | |
* | |
* @param interfaceClass The interface to create a proxy for. | |
* @return The new proxy interface. | |
* @throws RemoteMetadataException If the REST URI cannot be determined based on the information given. | |
*/ | |
public <T> T getRrpcInterface(final Class<T> interfaceClass) { | |
return getRrpcInterface(interfaceClass, null); | |
} | |
/** | |
* Same as {@link #getRrpcInterface(Class)} except explicitly specifies the URI of the REST interface. | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestRpc} | |
* </ul> | |
* | |
* @param interfaceClass The interface to create a proxy for. | |
* @param uri The URI of the REST interface. | |
* @return The new proxy interface. | |
*/ | |
public <T> T getRrpcInterface(final Class<T> interfaceClass, final Object uri) { | |
return getRrpcInterface(interfaceClass, uri, null, null); | |
} | |
/** | |
* Same as {@link #getRrpcInterface(Class, Object)} but allows you to override the serializer and parser used. | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc RestRpc} | |
* </ul> | |
* | |
* @param interfaceClass The interface to create a proxy for. | |
* @param uri The URI of the REST interface. | |
* @param serializer The serializer used to serialize POJOs to the body of the HTTP request. | |
* @param parser The parser used to parse POJOs from the body of the HTTP response. | |
* @return The new proxy interface. | |
*/ | |
@SuppressWarnings({ "unchecked" }) | |
public <T> T getRrpcInterface(final Class<T> interfaceClass, Object uri, final Serializer serializer, final Parser parser) { | |
if (uri == null) { | |
RrpcInterfaceMeta rm = new RrpcInterfaceMeta(interfaceClass, ""); | |
String path = rm.getPath(); | |
if (path.indexOf("://") == -1) { | |
if (isEmpty(rootUri)) | |
throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote interface."); | |
path = trimSlashes(rootUri) + '/' + path; | |
} | |
uri = path; | |
} | |
final String restUrl2 = stringify(uri); | |
return (T)Proxy.newProxyInstance( | |
interfaceClass.getClassLoader(), | |
new Class[] { interfaceClass }, | |
new InvocationHandler() { | |
final RrpcInterfaceMeta rm = new RrpcInterfaceMeta(interfaceClass, restUrl2); | |
@Override /* InvocationHandler */ | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
RrpcInterfaceMethodMeta rim = rm.getMethodMeta(method); | |
String uri = rim.getUri(); | |
RestResponse res = null; | |
try { | |
RestRequest rc = request("POST", uri, true) | |
.serializer(serializer) | |
.body(args) | |
.rethrow(RuntimeException.class) | |
.rethrow(method.getExceptionTypes()); | |
res = rc.run(); | |
Object v = res.getBody().asType(method.getGenericReturnType()); | |
if (v == null && method.getReturnType().isPrimitive()) | |
v = ClassInfo.of(method.getReturnType()).getPrimitiveDefault(); | |
return v; | |
} catch (Throwable e) { | |
if (e instanceof RestCallException) { | |
Throwable t = e.getCause(); | |
if (t != null) | |
e = t; | |
} | |
if (e instanceof RuntimeException) | |
throw e; | |
for (Class<?> t2 : method.getExceptionTypes()) | |
if (t2.isInstance(e)) | |
throw e; | |
throw runtimeException(e); | |
} | |
} | |
}); | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
if (leakDetection && ! isClosed && ! keepHttpClientOpen) { | |
StringBuilder sb = new StringBuilder("WARNING: RestClient garbage collected before it was finalized."); // NOT DEBUG | |
if (creationStack != null) { | |
sb.append("\nCreation Stack:"); // NOT DEBUG | |
for (StackTraceElement e : creationStack) | |
sb.append("\n\t" + e); // NOT DEBUG | |
} | |
log(WARNING, sb.toString()); | |
} | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param t Thrown exception. Can be <jk>null</jk>. | |
* @param msg The message. | |
* @param args Optional message arguments. | |
*/ | |
protected void log(Level level, Throwable t, String msg, Object...args) { | |
logger.log(level, t, msg(msg, args)); | |
if (logToConsole) { | |
console.println(msg(msg, args).get()); | |
if (t != null) | |
t.printStackTrace(console); | |
} | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param msg The message with {@link MessageFormat}-style arguments. | |
* @param args The arguments. | |
*/ | |
protected void log(Level level, String msg, Object...args) { | |
logger.log(level, msg(msg, args)); | |
if (logToConsole) | |
console.println(msg(msg, args).get()); | |
} | |
private Supplier<String> msg(String msg, Object...args) { | |
return ()->args.length == 0 ? msg : MessageFormat.format(msg, args); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// RestCallInterceptor methods | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Interceptor method called immediately after the RestRequest object is created and all headers/query/form-data has been copied from the client. | |
* | |
* <p> | |
* Subclasses can override this method to intercept the request and perform special modifications. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestClient#RESTCLIENT_interceptors} | |
* <li class='jm'>{@link RestClientBuilder#interceptors(Object...)} | |
* </ul> | |
* | |
* @param req The HTTP request. | |
* @throws RestCallException If any of the interceptors threw an exception. | |
*/ | |
@Override | |
public void onInit(RestRequest req) throws RestCallException { | |
try { | |
for (RestCallInterceptor rci : interceptors) | |
rci.onInit(req); | |
} catch (RuntimeException | RestCallException e) { | |
throw e; | |
} catch (Exception e) { | |
throw new RestCallException(null, e, "Interceptor threw an exception on init."); | |
} | |
} | |
/** | |
* Interceptor method called immediately after an HTTP response has been received. | |
* | |
* <p> | |
* Subclasses can override this method to intercept the response and perform special modifications. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestClient#RESTCLIENT_interceptors} | |
* <li class='jm'>{@link RestClientBuilder#interceptors(Object...)} | |
* </ul> | |
* | |
* @param req The HTTP request. | |
* @param res The HTTP response. | |
* @throws RestCallException If any of the interceptors threw an exception. | |
*/ | |
@Override | |
public void onConnect(RestRequest req, RestResponse res) throws RestCallException { | |
try { | |
for (RestCallInterceptor rci : interceptors) | |
rci.onConnect(req, res); | |
} catch (RuntimeException | RestCallException e) { | |
throw e; | |
} catch (Exception e) { | |
throw new RestCallException(res, e, "Interceptor threw an exception on connect."); | |
} | |
} | |
/** | |
* 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. | |
* | |
* <p> | |
* Subclasses can override this method to handle any cleanup operations. | |
* | |
* <ul class='seealso'> | |
* <li class='jf'>{@link RestClient#RESTCLIENT_interceptors} | |
* <li class='jm'>{@link RestClientBuilder#interceptors(Object...)} | |
* </ul> | |
* | |
* @param req The HTTP request. | |
* @param res The HTTP response. | |
* @throws RestCallException If any of the interceptors threw an exception. | |
*/ | |
@Override | |
public void onClose(RestRequest req, RestResponse res) throws RestCallException { | |
try { | |
for (RestCallInterceptor rci : interceptors) | |
rci.onClose(req, res); | |
} catch (RuntimeException | RestCallException e) { | |
throw e; | |
} catch (Exception e) { | |
throw new RestCallException(res, e, "Interceptor threw an exception on close."); | |
} | |
} | |
//------------------------------------------------------------------------------------------------ | |
// Passthrough methods for HttpClient. | |
//------------------------------------------------------------------------------------------------ | |
/** | |
* Obtains the parameters for this client. | |
* | |
* These parameters will become defaults for all requests being executed with this client, and for the parameters of dependent objects in this client. | |
* | |
* @return The default parameters. | |
* @deprecated Use {@link RequestConfig}. | |
*/ | |
@Deprecated | |
@Override /* HttpClient */ | |
public HttpParams getParams() { | |
return httpClient.getParams(); | |
} | |
/** | |
* Obtains the connection manager used by this client. | |
* | |
* @return The connection manager. | |
* @deprecated Use {@link HttpClientBuilder}. | |
*/ | |
@Deprecated | |
@Override /* HttpClient */ | |
public ClientConnectionManager getConnectionManager() { | |
return httpClient.getConnectionManager(); | |
} | |
/** | |
* Returns the connection manager if one was specified in the client builder. | |
* | |
* @return The connection manager. May be <jk>null</jk>. | |
*/ | |
public HttpClientConnectionManager getHttpClientConnectionManager() { | |
return connectionManager; | |
} | |
/** | |
* Executes HTTP request using the default context. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param request The request to execute. | |
* @return | |
* The response to the request. | |
* <br>This is always a final response, never an intermediate response with an 1xx status code. | |
* <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the | |
* implementation and configuration of this client. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public HttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException { | |
return httpClient.execute(request); | |
} | |
/** | |
* Executes HTTP request using the given context. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param request The request to execute. | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return | |
* The response to the request. | |
* <br>This is always a final response, never an intermediate response with an 1xx status code. | |
* <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the | |
* implementation and configuration of this client. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException, ClientProtocolException { | |
return httpClient.execute(request, context); | |
} | |
/** | |
* Executes HTTP request using the default context. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param target The target host for the request. | |
* <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. | |
* @param request The request to execute. | |
* @return The response to the request. | |
* <br>This is always a final response, never an intermediate response with an 1xx status code. | |
* <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the | |
* implementation and configuration of this client. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException, ClientProtocolException { | |
return httpClient.execute(target, request); | |
} | |
/** | |
* Executes HTTP request using the given context. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* <li>The {@link #run(HttpHost,HttpRequest,HttpContext)} method has been provided as a wrapper around this method. | |
* Subclasses can override these methods for handling requests with and without bodies separately. | |
* <li>The {@link RestCallHandler} interface can also be implemented to intercept this method. | |
* </ul> | |
* | |
* @param target The target host for the request. | |
* <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. | |
* @param request The request to execute. | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return | |
* The response to the request. | |
* <br>This is always a final response, never an intermediate response with an 1xx status code. | |
* <br>Whether redirects or authentication challenges will be returned or handled automatically depends on the | |
* implementation and configuration of this client. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException, ClientProtocolException { | |
return httpClient.execute(target, request, context); | |
} | |
/** | |
* Executes HTTP request using the default context and processes the response using the given response handler. | |
* | |
* <p> | |
* The content entity associated with the response is fully consumed and the underlying connection is released back | |
* to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} | |
* from having to manage resource deallocation internally. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param request The request to execute. | |
* @param responseHandler The response handler. | |
* @return Object returned by response handler. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException { | |
return httpClient.execute(request, responseHandler); | |
} | |
/** | |
* Executes HTTP request using the given context and processes the response using the given response handler. | |
* | |
* <p> | |
* The content entity associated with the response is fully consumed and the underlying connection is released back | |
* to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} | |
* from having to manage resource deallocation internally. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param request The request to execute. | |
* @param responseHandler The response handler. | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return The response object as generated by the response handler. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException, ClientProtocolException { | |
return httpClient.execute(request, responseHandler, context); | |
} | |
/** | |
* Executes HTTP request to the target using the default context and processes the response using the given response handler. | |
* | |
* <p> | |
* The content entity associated with the response is fully consumed and the underlying connection is released back | |
* to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} | |
* from having to manage resource deallocation internally. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param target | |
* The target host for the request. | |
* <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. | |
* @param request The request to execute. | |
* @param responseHandler The response handler. | |
* @return The response object as generated by the response handler. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException { | |
return httpClient.execute(target, request, responseHandler); | |
} | |
/** | |
* Executes a request using the default context and processes the response using the given response handler. | |
* | |
* <p> | |
* The content entity associated with the response is fully consumed and the underlying connection is released back | |
* to the connection manager automatically in all cases relieving individual {@link ResponseHandler ResponseHandlers} | |
* from having to manage resource deallocation internally. | |
* | |
* <ul class='notes'> | |
* <li>This method gets passed on directly to the underlying {@link HttpClient} class. | |
* </ul> | |
* | |
* @param target | |
* The target host for the request. | |
* <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. | |
* @param request The request to execute. | |
* @param responseHandler The response handler. | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return The response object as generated by the response handler. | |
* @throws IOException In case of a problem or the connection was aborted. | |
* @throws ClientProtocolException In case of an http protocol error. | |
*/ | |
@Override /* HttpClient */ | |
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException, ClientProtocolException { | |
return httpClient.execute(target, request, responseHandler, context); | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Other methods. | |
//----------------------------------------------------------------------------------------------------------------- | |
HttpPartSerializerSession getPartSerializerSession() { | |
return partSerializer.createPartSession(null); | |
} | |
HttpPartParserSession getPartParserSession() { | |
return partParser.createPartSession(null); | |
} | |
private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*"); | |
URI toURI(Object x, String rootUri) throws RestCallException { | |
try { | |
if (x instanceof URI) | |
return (URI)x; | |
if (x instanceof URL) | |
((URL)x).toURI(); | |
if (x instanceof URIBuilder) | |
return ((URIBuilder)x).build(); | |
String s = x == null ? "" : x.toString(); | |
if (rootUri != null && ! absUrlPattern.matcher(s).matches()) { | |
if (s.isEmpty()) | |
s = rootUri; | |
else { | |
StringBuilder sb = new StringBuilder(rootUri); | |
if (! s.startsWith("/")) | |
sb.append('/'); | |
sb.append(s); | |
s = sb.toString(); | |
} | |
} | |
s = fixUrl(s); | |
return new URI(s); | |
} catch (URISyntaxException e) { | |
throw new RestCallException(null, e, "Invalid URI encountered: {0}", x); // Shouldn't happen. | |
} | |
} | |
ExecutorService getExecutorService() { | |
if (executorService != null) | |
return executorService; | |
synchronized(this) { | |
executorService = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)); | |
return executorService; | |
} | |
} | |
/* | |
* Returns the serializer that best matches the specified content type. | |
* If no match found or the content type is null, returns the serializer in the list if it's a list of one. | |
* Returns null if no serializers are defined. | |
*/ | |
Serializer getMatchingSerializer(String mediaType) { | |
if (serializers.isEmpty()) | |
return null; | |
if (mediaType != null) { | |
Serializer s = serializers.getSerializer(mediaType); | |
if (s != null) | |
return s; | |
} | |
List<Serializer> l = serializers.getSerializers(); | |
return (l.size() == 1 ? l.get(0) : null); | |
} | |
boolean hasSerializers() { | |
return ! serializers.getSerializers().isEmpty(); | |
} | |
/* | |
* Returns the parser that best matches the specified content type. | |
* If no match found or the content type is null, returns the parser in the list if it's a list of one. | |
* Returns null if no parsers are defined. | |
*/ | |
Parser getMatchingParser(String mediaType) { | |
if (parsers.isEmpty()) | |
return null; | |
if (mediaType != null) { | |
Parser p = parsers.getParser(mediaType); | |
if (p != null) | |
return p; | |
} | |
List<Parser> l = parsers.getParsers(); | |
return (l.size() == 1 ? l.get(0) : null); | |
} | |
boolean hasParsers() { | |
return ! parsers.getParsers().isEmpty(); | |
} | |
@SuppressWarnings("unchecked") | |
<T extends Context> T getInstance(Class<T> c) { | |
Context o = requestContexts.get(c); | |
if (o == null) { | |
o = ContextCache.INSTANCE.create(c, getContextProperties()); | |
requestContexts.put(c, o); | |
} | |
return (T)o; | |
} | |
private RestOperation op(String method, Object url) { | |
return RestOperation.of(method, url); | |
} | |
private RestOperation op(String method, Object url, Object body) { | |
return RestOperation.of(method, url, body); | |
} | |
private Reader stringBody(String body) { | |
return body == null ? null : new StringReader(stringify(body)); | |
} | |
@Override /* Context */ | |
public OMap toMap() { | |
return super.toMap() | |
.a( | |
"RestClient", | |
OMap | |
.create() | |
.filtered() | |
.a("errorCodes", errorCodes) | |
.a("executorService", executorService) | |
.a("executorServiceShutdownOnClose", executorServiceShutdownOnClose) | |
.a("headers", headers) | |
.a("interceptors", interceptors) | |
.a("keepHttpClientOpen", keepHttpClientOpen) | |
.a("partParser", partParser) | |
.a("partSerializer", partSerializer) | |
.a("query", query) | |
.a("rootUri", rootUri) | |
); | |
} | |
} |