All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.rest.client.RestClient Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * 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.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.collections.JsonMap.*;
import static org.apache.juneau.common.internal.ArgUtils.*;
import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.common.internal.ThrowableUtils.*;
import static org.apache.juneau.http.HttpEntities.*;
import static org.apache.juneau.rest.client.RestOperation.*;
import static java.util.logging.Level.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.StateMachineState.*;
import static java.lang.Character.*;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
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 javax.net.ssl.*;

import org.apache.http.*;
import org.apache.http.Header;
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.CookieStore;
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.config.*;
import org.apache.http.conn.*;
import org.apache.http.conn.routing.*;
import org.apache.http.conn.socket.*;
import org.apache.http.conn.util.*;
import org.apache.http.cookie.*;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.*;
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.html.*;
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.json.*;
import org.apache.juneau.marshaller.*;
import org.apache.juneau.msgpack.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.objecttools.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.parser.ParseException;
import org.apache.juneau.plaintext.*;
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.uon.*;
import org.apache.juneau.urlencoding.*;
import org.apache.juneau.utils.*;
import org.apache.juneau.xml.*;

/**
 * Utility class for interfacing with remote REST interfaces.
 *
 * 

* 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. * *

Example:
*

* // Create a basic REST client with JSON support and download a bean. * MyBean bean = RestClient.create() * .json5() * .build() * .get(URI) * .run() * .assertStatus().asCode().is(200) * .assertHeader("Content-Type").matchesSimple("application/json*") * .getContent().as(MyBean.class); *

* *

* Breaking apart the fluent call, we can see the classes being used: *

* RestClient.Builder builder = RestClient.create().json5(); * RestClient client = builder.build(); * RestRequest req = client.get(URI); * RestResponse res = req.run(); * RestResponseStatusLineAssertion statusLineAssertion = res.assertStatus(); * FluentIntegerAssertion<RestResponse> codeAssertion = statusLineAssertion.asCode(); * res = codeAssertion.is(200); * FluentStringAssertion<RestResponse> headerAssertion = res.assertHeader("Content-Type"); * res = headerAssertion.matchesSimple("application/json*"); * RestResponseBody content = res.getContent(); * MyBean bean = content.as(MyBean.class); *

* *

* It additionally provides support for creating remote proxy interfaces using REST as the transport medium. * *

Example:
*

* // Define a Remote proxy for interacting with a REST interface. * @Remote(path="/petstore") * public interface PetStore { * * @RemotePost("/pets") * Pet addPet( * @Content CreatePet pet, * @Header("E-Tag") UUID etag, * @Query("debug") boolean debug * ); * } * * // Use a RestClient with default JSON 5 support. * RestClient client = RestClient.create().json5().build(); * * PetStore store = client.getRemote(PetStore.class, "http://localhost:10000"); * CreatePet createPet = new CreatePet("Fluffy", 9.99); * Pet pet = store.addPet(createPet, UUID.randomUUID(), true); *

* *

* The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality: *

    *
  • {@link RestClient} extends {@link HttpClient}, creates {@link RestRequest} objects. *
  • {@link RestRequest} extends {@link HttpUriRequest}, creates {@link RestResponse} objects. *
  • {@link RestResponse} creates {@link ResponseContent} and {@link ResponseHeader} objects. *
  • {@link ResponseContent} extends {@link HttpEntity} *
  • {@link ResponseHeader} extends {@link Header} *
* * *

* Instances of this class are built using the {@link Builder} class which can be constructed using * the {@link #create() RestClient.create()} method as shown above. * *

* Clients are typically created with a root URI so that relative URIs can be used when making requests. * This is done using the {@link Builder#rootUrl(Object)} method. * *

Example:
*

* // Create a client where all URIs are relative to localhost. * RestClient client = RestClient.create().json().rootUrl("http://localhost:5000").build(); * * // Use relative paths. * String body = client.get("/subpath").run().getContent().asString(); *

* *

* The {@link RestClient} class creates {@link RestRequest} objects using the following methods: * *

    *
  • {@link RestClient} *
      *
    • {@link RestClient#get(Object) get(uri)} / {@link RestClient#get() get()} *
    • {@link RestClient#put(Object,Object) put(uri,body)} / {@link RestClient#put(Object) put(uri)} *
    • {@link RestClient#post(Object) post(uri,body)} / {@link RestClient#post(Object) post(uri)} *
    • {@link RestClient#patch(Object,Object) patch(uri,body)} / {@link RestClient#patch(Object) patch(uri)} *
    • {@link RestClient#delete(Object) delete(uri)} *
    • {@link RestClient#head(Object) head(uri)} *
    • {@link RestClient#options(Object) options(uri)} *
    • {@link RestClient#formPost(Object,Object) formPost(uri,body)} / {@link RestClient#formPost(Object) formPost(uri)} *
    • {@link RestClient#formPostPairs(Object,String...) formPostPairs(uri,parameters...)} *
    • {@link RestClient#request(String,Object,Object) request(method,uri,body)} *
    *
* *

* The {@link RestRequest} class creates {@link RestResponse} objects using the following methods: * *

    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#run() run()} *
    • {@link RestRequest#complete() complete()} *
    *
* *

* 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 ResponseContent} object are auto-closing once * they are exhausted, so it is often not necessary to explicitly close them. * *

* The following examples show the distinction between the two calls: * *

* // Consuming the response, so use run(). * String body = client.get(URI).run().getContent().asString(); * * // Only interested in response status code, so use complete(). * int status = client.get(URI).complete().getStatusCode(); *

* * *

POJO Marshalling

* *

* By default, JSON support is provided for HTTP request and response bodies. * Other languages can be specified using any of the following builder methods: *

    *
  • {@link Builder} *
      *
    • {@link Builder#json() json()} *
    • {@link Builder#json5() json5()} *
    • {@link Builder#xml() xml()} *
    • {@link Builder#html() html()} *
    • {@link Builder#plainText() plainText()} *
    • {@link Builder#msgPack() msgPack()} *
    • {@link Builder#uon() uon()} *
    • {@link Builder#urlEnc() urlEnc()} *
    • {@link Builder#openApi() openApi()} *
    *
* *
Example:
*

* // Create a basic REST client with JSON 5 support. * // Typically easier to use when performing unit tests. * RestClient client = RestClient.create().json5().build(); *

* *

* Clients can also support multiple languages: * *

Example:
*

* // Create a REST client with support for multiple languages. * RestClient client1 = RestClient.create().json().xml().openApi().build(); * * // Create a REST client with support for all supported languages. * RestClient client2 = RestClient.create().universal().build(); *

* *

* When using clients with multiple language support, you must specify the Content-Type header on requests * with bodies to specify which serializer should be selected. * *

* // Create a REST client with support for multiple languages. * RestClient client = RestClient.create().universal().build(); * * client * .post(URI, myBean) * .contentType("application/json") * .complete() * .assertStatus().is(200); *

* *

* Languages can also be specified per-request. * *

* // Create a REST client with no default languages supported. * RestClient client = RestClient.create().build(); * * // Use JSON for this request. * client * .post(URI, myBean) * .json() * .complete() * .assertStatus().is(200); *

* * *

* The {@link Builder} class provides convenience methods for setting common serializer and parser * settings. * *

Example:
*

* // Create a basic REST client with JSON support. * // Use single-quotes and whitespace. * RestClient client = RestClient.create().json().sq().ws().build(); *

* *

* Other methods are also provided for specifying the serializers and parsers used for lower-level marshalling support: *

    *
  • {@link Builder} *
      *
    • {@link Builder#serializer(Serializer) serializer(Serializer)} *
    • {@link Builder#parser(Parser) parser(Parser)} *
    • {@link Builder#marshaller(Marshaller) marshaller(Marshaller)} *
    *
* *

* 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: *

    *
  • {@link Builder} *
      *
    • {@link Builder#partSerializer(Class) partSerializer(Class<? extends HttpPartSerializer>)} *
    • {@link Builder#partParser(Class) partParser(Class<? extends HttpPartParser>)} *
    *
* * *

Request Headers

*

* Per-client or per-request headers can be specified using the following methods: *

    *
  • {@link Builder} *
      *
    • {@link Builder#headers() headerData()} *
    • {@link Builder#header(String,String) header(String,Object)} *
    • {@link Builder#header(String,Supplier) header(String,Supplier<?>)} *
    • {@link Builder#headers(Header...) headers(Header...)} *
    • {@link Builder#headersDefault(Header...) defaultHeaders(Header...)} *
    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#header(String,Object) header(String,Object)} *
    • {@link RestRequest#headers(Header...) headers(Header...)} *
    • {@link RestRequest#headersBean(Object) headersBean(Object)} *
    • {@link RestRequest#headerPairs(String...) headerPairs(String...)} *
    *
* *

* The supplier methods are particularly useful for header values whose values may change over time (such as Authorization headers * which may need to change every few minutes). *

* *
Example:
*

* // Create a client that adds a dynamic Authorization header to every request. * RestClient client = RestClient.create().header("Authorization", ()->getMyAuthToken()).build(); *

* *

* The {@link HttpPartSchema} API allows you to define OpenAPI schemas to POJO data structures on both requests * and responses. * *

Example:
*

* // Create a client that adds a header "Foo: bar|baz" to every request. * RestClient client = RestClient.create() * .header("Foo", AList.of("bar","baz"), T_ARRAY_PIPES) * .build(); *

* *

* The methods with {@link ListOperation} parameters allow you to control whether new headers get appended, prepended, or * replace existing headers with the same name. * *

Notes:
    *
  • Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in Header or * NameValuePair objects use the values returned by that bean directly. *
* * *

Request Query Parameters

*

* Per-client or per-request query parameters can be specified using the following methods: *

    *
  • {@link Builder} *
      *
    • {@link Builder#queryData() queryData()} *
    • {@link Builder#queryData(String,String) queryData(String,String)} *
    • {@link Builder#queryData(String,Supplier) queryData(String,Supplier<?>)} *
    • {@link Builder#queryData(NameValuePair...) queryData(NameValuePair...)} *
    • {@link Builder#queryDataDefault(NameValuePair...) defaultQueryData(NameValuePair...)} *
    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#queryData(String,Object) queryData(String,Object)} *
    • {@link RestRequest#queryData(NameValuePair...) queryData(NameValuePair...)} *
    • {@link RestRequest#queryDataBean(Object) queryDataBean(Object)} *
    • {@link RestRequest#queryCustom(Object) queryCustom(Object)} *
    • {@link RestRequest#queryDataPairs(String...) queryDataPairs(String...)} *
    *
* *
Example:
*

* // Create a client that adds a ?foo=bar query parameter to every request. * RestClient client = RestClient.create().query("foo", "bar").build(); * * // Or do it on every request. * String response = client.get(URI).query("foo", "bar").run().getContent().asString(); *

* *
Notes:
    *
  • Like header values, dynamic values and OpenAPI schemas are supported. *
  • Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in NameValuePair * objects use the values returned by that bean directly. *
* * *

Request Form Data

* *

* Per-client or per-request form-data parameters can be specified using the following methods: *

    *
  • {@link Builder} *
      *
    • {@link Builder#formData() formData()} *
    • {@link Builder#formData(String,String) formData(String,String)} *
    • {@link Builder#formData(String,Supplier) formData(String,Supplier<?>)} *
    • {@link Builder#formData(NameValuePair...) formDatas(NameValuePair...)} *
    • {@link Builder#formDataDefault(NameValuePair...) defaultFormData(NameValuePair...)} *
    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#formData(String,Object) formData(String,Object)} *
    • {@link RestRequest#formData(NameValuePair...) formData(NameValuePair...)} *
    • {@link RestRequest#formDataBean(Object) formDataBean(Object)} *
    • {@link RestRequest#formDataCustom(Object) formDataCustom(Object)} *
    • {@link RestRequest#formDataPairs(String...) formDataPairs(String...)} *
    *
* *
Notes:
    *
  • Like header values, dynamic values and OpenAPI schemas are supported. *
  • Methods that pass in POJOs convert values to strings using the part serializers. Methods that pass in NameValuePair * objects use the values returned by that bean directly. *
* * *

Request Body

* *

* 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: * *

    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#content(Object) body(Object)} *
    • {@link RestRequest#content(Object,HttpPartSchema) body(Object,HttpPartSchema)} *
    *
* *

* The request body can be any of the following types: *

    *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} defined on the client or request. *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. *
  • * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link PartList} - Converted to a URL-encoded FORM post. *
  • * {@link Supplier} - A supplier of anything on this list. *
* *
Notes:
    *
  • If the serializer on the client or request is explicitly set to null, POJOs will be converted to strings * using the registered part serializer as content type "text/plain. If the part serializer is also null, * POJOs will be converted to strings using {@link ClassMeta#toString(Object)} which typically just calls {@link Object#toString()}. *
* * *

Response Status

* *

* After execution using {@link RestRequest#run()} or {@link RestRequest#complete()}, the following methods can be used * to get the response status: * *

    *
  • {@link RestResponse} *
      *
    • {@link RestResponse#getStatusLine() getStatusLine()} returns {@link StatusLine} *
    • {@link RestResponse#getStatusCode() getStatusCode()} returns int *
    • {@link RestResponse#getReasonPhrase() getReasonPhrase()} returns String *
    • {@link RestResponse#assertStatus() assertStatus()} returns {@link FluentResponseStatusLineAssertion} *
    *
* *
Example:
*

* // Only interested in status code. * int statusCode = client.get(URI).complete().getStatusCode(); *

* *

* Equivalent methods with mutable parameters are provided to allow access to status values without breaking fluent call chains. * *

Example:
*

* // Interested in multiple values. * Value<Integer> statusCode = Value.create(); * Value<String> reasonPhrase = Value.create(); * * client.get(URI).complete().getStatusCode(statusCode).getReasonPhrase(reasonPhrase); * System.err.println("statusCode="+statusCode.get()+", reasonPhrase="+reasonPhrase.get()); *

* *
Notes:
    *
  • 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. *
* *

* The assertion method is provided for quickly asserting status codes in fluent calls. * *

Example:
*

* // Status assertion using a static value. * String body = client.get(URI) * .run() * .assertStatus().asCode().isBetween(200,399) * .getContent().asString(); * * // Status assertion using a predicate. * String body = client.get(URI) * .run() * .assertStatus().asCode().is(x -> x<400) * .getContent().asString(); *

* * *

Response Headers

* *

* Response headers are accessed through the following methods: * *

    *
  • {@link RestResponse} *
      *
    • {@link RestResponse#getHeaders(String) getHeaders(String)} returns {@link ResponseHeader}[] *
    • {@link RestResponse#getFirstHeader(String) getFirstHeader(String)} returns {@link ResponseHeader} *
    • {@link RestResponse#getLastHeader(String) getLastHeader(String)} returns {@link ResponseHeader} *
    • {@link RestResponse#getAllHeaders() getAllHeaders()} returns {@link ResponseHeader}[] *
    • {@link RestResponse#getStringHeader(String) getStringHeader(String)} returns String *
    • {@link RestResponse#containsHeader(String) containsHeader(String)} returns boolean *
    *
* *

* The {@link RestResponse#getFirstHeader(String)} and {@link RestResponse#getLastHeader(String)} methods return an empty {@link ResponseHeader} object instead ofnull. * This allows it to be used more easily in fluent calls. * *

Example:
*

* // See if response contains Location header. * boolean hasLocationHeader = client.get(URI).complete().getLastHeader("Location").exists(); *

* *

* The {@link ResponseHeader} class extends from the HttpClient {@link Header} class and provides several convenience * methods: * *

    *
  • {@link ResponseHeader} *
      *
    • {@link ResponseHeader#isPresent() isPresent()} returns boolean *
    • {@link ResponseHeader#asString() asString()} returns String *
    • {@link ResponseHeader#as(Type,Type...) as(Type,Type...)} returns T *
    • {@link ResponseHeader#as(Class) as(Class<T>)} returns T *
    • {@link ResponseHeader#asMatcher(Pattern) asMatcher(Pattern)} returns {@link Matcher} *
    • {@link ResponseHeader#asMatcher(String) asMatcher(String)} returns {@link Matcher} *
    • {@link ResponseHeader#asHeader(Class) asHeader(Class<T extends BasicHeader> c)} returns {@link BasicHeader} *
    • {@link ResponseHeader#asStringHeader() asStringHeader()} returns {@link BasicStringHeader} *
    • {@link ResponseHeader#asIntegerHeader() asIntegerHeader()} returns {@link BasicIntegerHeader} *
    • {@link ResponseHeader#asLongHeader() asLongHeader()} returns {@link BasicLongHeader} *
    • {@link ResponseHeader#asDateHeader() asDateHeader()} returns {@link BasicDateHeader} *
    • {@link ResponseHeader#asCsvHeader() asCsvHeader()} returns {@link BasicCsvHeader} *
    • {@link ResponseHeader#asEntityTagsHeader() asEntityTagsHeader()} returns {@link BasicEntityTagsHeader} *
    • {@link ResponseHeader#asStringRangesHeader() asStringRangesHeader()} returns {@link BasicStringRangesHeader} *
    • {@link ResponseHeader#asUriHeader() asUriHeader()} returns {@link BasicUriHeader} *
    *
* *

* The {@link ResponseHeader#schema(HttpPartSchema)} method allows you to perform parsing of OpenAPI formats for * header parts. * *

Example:
*

* // Parse the header "Foo: bar|baz". * List<String> fooHeader = client * .get(URI) * .complete() * .getHeader("Foo").schema(T_ARRAY_PIPES).as(List.class, String.class); *

* *

* Assertion methods are also provided for fluent-style calls: * *

    *
  • {@link ResponseHeader} *
      *
    • {@link ResponseHeader#assertValue() assertValue()} returns {@link FluentResponseHeaderAssertion} *
    *
* *

* Note how in the following example, the fluent assertion returns control to the {@link RestResponse} object after * the assertion has been completed: * *

Example:
*

* // Assert the response content type is any sort of JSON. * String body = client.get(URI) * .run() * .getHeader("Content-Type").assertValue().matchesSimple("application/json*") * .getContent().asString(); *

* * *

Response Body

* *

* The response body is accessed through the following method: * *

    *
  • {@link RestResponse} *
      *
    • {@link RestResponse#getContent() getContent()} returns {@link ResponseContent} *
    *
* *

* The {@link ResponseContent} class extends from the HttpClient {@link HttpEntity} class and provides several convenience * methods: * *

    *
  • {@link ResponseContent} *
      *
    • {@link ResponseContent#asInputStream() asInputStream()} returns InputStream *
    • {@link ResponseContent#asReader() asReader()} returns Reader *
    • {@link ResponseContent#asReader(Charset) asReader(Charset)} returns Reader *
    • {@link ResponseContent#pipeTo(OutputStream) pipeTo(OutputStream)} returns {@link RestResponse} *
    • {@link ResponseContent#pipeTo(Writer) pipeTo(Writer)} returns {@link RestResponse} *
    • {@link ResponseContent#as(Type,Type...) as(Type,Type...)} returns T *
    • {@link ResponseContent#as(Class) as(Class<T>)} returns T *
    • {@link ResponseContent#asFuture(Class) asFuture(Class<T>)} returns Future<T> *
    • {@link ResponseContent#asFuture(Type,Type...) asFuture(Type,Type...)} returns Future<T> *
    • {@link ResponseContent#asString() asString()} returns String *
    • {@link ResponseContent#asStringFuture() asStringFuture()} returns Future<String> *
    • {@link ResponseContent#asAbbreviatedString(int) asAbbreviatedString(int)} returns String *
    • {@link ResponseContent#asObjectRest(Class) asObjectRest(Class<?>)} returns {@link ObjectRest} *
    • {@link ResponseContent#asObjectRest() asObjectRest()} returns {@link ObjectRest} *
    • {@link ResponseContent#asMatcher(Pattern) asMatcher(Pattern)} returns {@link Matcher} *
    • {@link ResponseContent#asMatcher(String) asMatcher(String)} returns {@link Matcher} *
    *
* *
* *
Examples:
*

* // Parse into a linked-list of strings. * List<String> list1 = client * .get(URI) * .run() * .getContent().as(LinkedList.class, String.class); * * // Parse into a linked-list of beans. * List<MyBean> list2 = client * .get(URI) * .run() * .getContent().as(LinkedList.class, MyBean.class); * * // Parse into a linked-list of linked-lists of strings. * List<List<String>> list3 = client * .get(URI) * .run() * .getContent().as(LinkedList.class, LinkedList.class, String.class); * * // Parse into a map of string keys/values. * Map<String,String> map1 = client * .get(URI) * .run() * .getContent().as(TreeMap.class, String.class, String.class); * * // Parse into a map containing string keys and values of lists containing beans. * Map<String,List<MyBean>> map2 = client * .get(URI) * .run() * .getContent().as(TreeMap.class, String.class, List.class, MyBean.class); *

* *

* 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 ResponseContent#asString()}. * However, methods that involve reading directly from the input stream cannot be called twice. * In these cases, the {@link RestResponse#cacheContent()} and {@link ResponseContent#cache()} methods are provided * to cache the response body in memory so that you can perform several operations against it. * *

* // Cache the response body so we can access it twice. * InputStream inputStream = client * .get(URI) * .run() * .cacheBody() * .getContent().pipeTo(someOtherStream) * .getContent().asInputStream(); *

* *

* Assertion methods are also provided for fluent-style calls: * *

    *
  • {@link ResponseContent} *
      *
    • {@link ResponseContent#assertValue() assertValue()} returns {@link FluentResponseBodyAssertion} *
    *
* *
* *
Example:
*

* // Assert that the body contains the string "Success". * String body = client * .get(URI) * .run() * .getContent().assertString().contains("Success") * .getContent().asString(); *

* *

* Object assertions allow you to parse the response body into a POJO and then perform various tests on that resulting * POJO. * *

Example:
*

* // Parse bean into POJO and then validate that it was parsed correctly. * MyBean bean = client.get(URI) * .run() * .getContent().assertObject(MyBean.class).asJson().is("{foo:'bar'}") * .getContent().as(MyBean.class); *

* * *

Custom Call Handlers

* *

* The {@link RestCallHandler} interface provides the ability to provide custom handling of requests. * *

    *
  • {@link Builder} *
      *
    • {@link Builder#callHandler() callHandler()} *
    *
  • {@link RestCallHandler} *
      *
    • {@link RestCallHandler#run(HttpHost,HttpRequest,HttpContext) run(HttpHost,HttpRequest,HttpContext)} returns HttpResponse *
    *
* *

* 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. * * *

Interceptors

* *

* The {@link RestCallInterceptor} API provides a quick way of intercepting and manipulating requests and responses beyond * the existing {@link HttpRequestInterceptor} and {@link HttpResponseInterceptor} APIs. * *

    *
  • {@link Builder} *
      *
    • {@link Builder#interceptors(Object...) interceptors(Object...)} *
    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#interceptors(RestCallInterceptor...) interceptors(RestCallInterceptor...)} *
    *
  • {@link RestCallInterceptor} *
      *
    • {@link RestCallInterceptor#onInit(RestRequest) onInit(RestRequest)} *
    • {@link RestCallInterceptor#onConnect(RestRequest,RestResponse) onConnect(RestRequest,RestResponse)} *
    • {@link RestCallInterceptor#onClose(RestRequest,RestResponse) onClose(RestRequest,RestResponse)} *
    *
* * *

Logging / Debugging

* *

* The following methods provide logging of requests and responses: * *

    *
  • {@link Builder} *
      *
    • {@link Builder#logger(Logger) logger(Logger)} *
    • {@link Builder#logToConsole() logToConsole()} *
    • {@link Builder#logRequests(DetailLevel,Level,BiPredicate) logRequests(DetailLevel,Level,BiPredicate)} *
    *
* *

* The following example shows the results of logging all requests that end with /bean. * *

Examples:
*

* MyBean bean = RestClient * .create() * .json5() * .logRequests(DetailLevel.FULL, Level.SEVERE, (req,res)->req.getUri().endsWith("/bean")) * .logToConsole() * .build() * .post("http://localhost/bean", anotherBean) * .run() * .getContent().as(MyBean.class); *

* *

* This produces the following console output: * *

* === HTTP Call (outgoing) ====================================================== * === REQUEST === * POST http://localhost/bean * ---request headers--- * Accept: application/json5 * ---request entity--- * Content-Type: application/json5 * ---request content--- * {f:1} * === RESPONSE === * HTTP/1.1 200 * ---response headers--- * Content-Type: application/json * ---response content--- * {f:1} * === END =======================================================================", *

* * *

* 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. * *

* Additionally, the following method is also provided for enabling debug mode: * *

    *
  • {@link Builder} *
      *
    • {@link Builder#debug() debug()} *
    *
* *

* Enabling debug mode has the following effects: *

    *
  • {@link org.apache.juneau.Context.Builder#debug()} is enabled. *
  • {@link Builder#detectLeaks()} is enabled. *
  • {@link Builder#logToConsole()} is called. *
* * *

REST Proxies

* *

* One of the more powerful features of the REST client class is the ability to produce Java interface proxies against * arbitrary remote REST resources. * *

Example:
*

* // Define a Remote proxy for interacting with a REST interface. * @Remote(path="/petstore") * public interface PetStore { * * @RemotePost("/pets") * Pet addPet( * @Content CreatePet pet, * @Header("E-Tag") UUID etag, * @Query("debug") boolean debug * ); * } * * // Use a RestClient with default JSON 5 support. * RestClient client = RestClient.create().json5().build()) * * PetStore store = client.getRemote(PetStore.class, "http://localhost:10000"); * CreatePet createPet = new CreatePet("Fluffy", 9.99); * Pet pet = store.addPet(createPet, UUID.randomUUID(), true); *

* *

* The methods to retrieve remote interfaces are: * *

    *
  • {@link RestClient} *
      *
    • {@link RestClient#getRemote(Class) getRemote(Class<T>)} returns T *
    • {@link RestClient#getRemote(Class,Object) getRemote(Class<T>,Object)} returns T *
    • {@link RestClient#getRemote(Class,Object,Serializer,Parser) getRemote(Class<T>,Object,Serializer,Parser)} returns T *
    • {@link RestClient#getRrpcInterface(Class) getRrpcInterface(Class<T>)} returns T *
    • {@link RestClient#getRrpcInterface(Class,Object) getRrpcInterface(Class<T>,Object)} returns T *
    • {@link RestClient#getRrpcInterface(Class,Object,Serializer,Parser) getRrpcInterface(Class<T>,Object,Serializer,Parser)} returns T *
    *
* *

* Two basic types of remote interfaces are provided: * *

    *
  • {@link Remote @Remote}-annotated interfaces. These can be defined against arbitrary external REST resources. *
  • RPC-over-REST interfaces. These are Java interfaces that allow you to make method calls on server-side POJOs. *
* *

* Refer to the following documentation on both flavors: * *

* *
*
*

Customizing Apache HttpClient

* *

* Several methods are provided for customizing the underlying HTTP client and client builder classes: *

    *
  • {@link Builder} *
      *
    • {@link Builder#httpClientBuilder(HttpClientBuilder) httpClientBuilder(HttpClientBuilder)} - Set the client builder yourself. *
    • {@link Builder#createHttpClientBuilder() createHttpClientBuilder()} - Override to create the client builder. *
    • {@link Builder#createHttpClient() createHttpClient()} - Override to create the client. *
    • {@link Builder#createConnectionManager() createConnectionManager()} - Override to create the connection management. *
    *
* *

* Additionally, all methods on the HttpClientBuilder class have been extended with fluent setters. * *

Example:
*

* // Create a client with customized HttpClient settings. * MyBean bean = RestClient * .create() * .disableRedirectHandling() * .connectionManager(myConnectionManager) * .addInterceptorFirst(myHttpRequestInterceptor) * .build(); *

* *

* Refer to the {@link HttpClientBuilder HTTP Client Builder API} for more information. * * *

Extending RestClient

* *

* The RestClient 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. * *

Example:
*

* public class MyRestClient extends RestClient { * * // Must provide this constructor! * public MyRestClient(RestClient.Builder builder) { * super(builder); * } * * /** Optionally override to customize builder settings before initialization. * @Override * protected void init(RestClient.Builder) {...} * * /** Optionally override to provide post-initialization (e.g. setting up SAML handshakes, etc...). * @Override * protected void init() {...} * * /** Optionally override to customize requests when they're created (e.g. add headers to each request). * @Override * protected RestRequest request(RestOperation) {...} * * /** Optionally override to implement your own call handling. * @Override * protected HttpResponse run(HttpHost, HttpRequest, HttpContext) {...} * * /** Optionally override to customize requests before they're executed. * @Override * protected void onCallInit(RestRequest) {...} * * /** Optionally override to customize responses as soon as a connection is made. * @Override * protected void onCallConnect(RestRequest, RestResponse) {...} * * /** Optionally override to perform any call cleanup. * @Override * protected void onCallClose(RestRequest, RestResponse) {...} * } * * // Instantiate your client. * MyRestClient client = RestClient.create().json().build(MyRestClient.class); *

* *

* 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. * *

Notes:
    *
  • This class is thread safe and reusable. *
* *
See Also:
*/ public class RestClient extends BeanContextable implements HttpClient, Closeable { //------------------------------------------------------------------------------------------------------------------- // Static //------------------------------------------------------------------------------------------------------------------- private static final RestCallInterceptor[] EMPTY_REST_CALL_INTERCEPTORS = {}; /** * Instantiates a new clean-slate {@link Builder} object. * * @return A new {@link Builder} object. */ public static Builder create() { return new Builder(); } //------------------------------------------------------------------------------------------------------------------- // Builder //------------------------------------------------------------------------------------------------------------------- /** * Builder class. */ @FluentSetters(ignore={"beanMapPutReturnsOldValue","example","exampleJson","debug","mediaType"}) public static class Builder extends BeanContextable.Builder { BeanStore beanStore = BeanStore.create().build(); private HttpClientBuilder httpClientBuilder; private CloseableHttpClient httpClient; private HeaderList headerData; private PartList queryData, formData, pathData; private BeanCreator callHandler; private SerializerSet.Builder serializers; private ParserSet.Builder parsers; private HttpPartSerializer.Creator partSerializer; private HttpPartParser.Creator partParser; private UrlEncodingSerializer.Builder urlEncodingSerializer; private boolean pooled; String rootUrl; boolean skipEmptyHeaderData, skipEmptyFormData, skipEmptyQueryData, executorServiceShutdownOnClose, ignoreErrors, keepHttpClientOpen, detectLeaks, logToConsole; Logger logger; DetailLevel logRequests; Level logRequestsLevel; BiPredicate logRequestsPredicate; Predicate errorCodes = x -> x<=0 || x>=400; HttpClientConnectionManager connectionManager; PrintStream console; ExecutorService executorService; List interceptors; /** * Constructor. */ protected Builder() { } @Override /* Context.Builder */ public Builder copy() { throw new NoSuchMethodError("Not implemented."); } @Override /* Context.Builder */ public RestClient build() { return build(RestClient.class); } //------------------------------------------------------------------------------------------------------------------ // Convenience marshalling support methods. //------------------------------------------------------------------------------------------------------------------ /** * Convenience method for specifying JSON as the marshalling transmission media type. * *

* {@link JsonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link JsonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "application/json" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}}. *

* Content-Type request header will be set to "application/json" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(JsonSerializer.class).parser(JsonParser.class). * *

Example:
*

* // Construct a client that uses JSON marshalling. * RestClient client = RestClient.create().json().build(); *

* * @return This object. */ @FluentSetter public Builder json() { return serializer(JsonSerializer.class).parser(JsonParser.class); } /** * Convenience method for specifying Simplified JSON as the marshalling transmission media type. * *

* Simplified JSON is typically useful for automated tests because you can do simple string comparison of results * without having to escape lots of quotes. * *

* {@link Json5Serializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link Json5Parser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "application/json" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "application/json5" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(Json5Serializer.class).parser(Json5Parser.class). * *

Example:
*

* // Construct a client that uses Simplified JSON marshalling. * RestClient client = RestClient.create().json5().build(); *

* * @return This object. */ @FluentSetter public Builder json5() { return serializer(Json5Serializer.class).parser(Json5Parser.class); } /** * Convenience method for specifying XML as the marshalling transmission media type. * *

* {@link XmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link XmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/xml" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/xml" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(XmlSerializer.class).parser(XmlParser.class). * *

Example:
*

* // Construct a client that uses XML marshalling. * RestClient client = RestClient.create().xml().build(); *

* * @return This object. */ @FluentSetter public Builder xml() { return serializer(XmlSerializer.class).parser(XmlParser.class); } /** * Convenience method for specifying HTML as the marshalling transmission media type. * *

* POJOs are converted to HTML without any sort of doc wrappers. * *

* {@link HtmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/html" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/html" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(HtmlSerializer.class).parser(HtmlParser.class). * *

Example:
*

* // Construct a client that uses HTML marshalling. * RestClient client = RestClient.create().html().build(); *

* * @return This object. */ @FluentSetter public Builder html() { return serializer(HtmlSerializer.class).parser(HtmlParser.class); } /** * Convenience method for specifying HTML DOC as the marshalling transmission media type. * *

* POJOs are converted to fully renderable HTML pages. * *

* {@link HtmlDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()} or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/html" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/html" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(HtmlDocSerializer.class).parser(HtmlParser.class). * *

Example:
*

* // Construct a client that uses HTML Doc marshalling. * RestClient client = RestClient.create().htmlDoc().build(); *

* * @return This object. */ @FluentSetter public Builder htmlDoc() { return serializer(HtmlDocSerializer.class).parser(HtmlParser.class); } /** * Convenience method for specifying Stripped HTML DOC as the marshalling transmission media type. * *

* Same as {@link #htmlDoc()} but without the header and body tags and page title and description. * *

* {@link HtmlStrippedDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/html+stripped" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/html+stripped" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class). * *

Example:
*

* // Construct a client that uses HTML Stripped Doc marshalling. * RestClient client = RestClient.create().htmlStrippedDoc().build(); *

* * @return This object. */ @FluentSetter public Builder htmlStrippedDoc() { return serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class); } /** * Convenience method for specifying Plain Text as the marshalling transmission media type. * *

* Plain text marshalling typically only works on simple POJOs that can be converted to and from strings using * swaps, swap methods, etc... * *

* {@link PlainTextSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link PlainTextParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/plain" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/plain" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(PlainTextSerializer.class).parser(PlainTextParser.class). * *

Example:
*

* // Construct a client that uses Plain Text marshalling. * RestClient client = RestClient.create().plainText().build(); *

* * @return This object. */ @FluentSetter public Builder plainText() { return serializer(PlainTextSerializer.class).parser(PlainTextParser.class); } /** * Convenience method for specifying MessagePack as the marshalling transmission media type. * *

* MessagePack is a binary equivalent to JSON that takes up considerably less space than JSON. * *

* {@link MsgPackSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link MsgPackParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "octal/msgpack" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "octal/msgpack" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(MsgPackSerializer.class).parser(MsgPackParser.class). * *

Example:
*

* // Construct a client that uses MessagePack marshalling. * RestClient client = RestClient.create().msgPack().build(); *

* * @return This object. */ @FluentSetter public Builder msgPack() { return serializer(MsgPackSerializer.class).parser(MsgPackParser.class); } /** * Convenience method for specifying UON as the marshalling transmission media type. * *

* UON is Url-Encoding Object notation that is equivalent to JSON but suitable for transmission as URL-encoded * query and form post values. * *

* {@link UonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* {@link UonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "text/uon" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/uon" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(UonSerializer.class).parser(UonParser.class). * *

Example:
*

* // Construct a client that uses UON marshalling. * RestClient client = RestClient.create().uon().build(); *

* * @return This object. */ @FluentSetter public Builder uon() { return serializer(UonSerializer.class).parser(UonParser.class); } /** * Convenience method for specifying URL-Encoding as the marshalling transmission media type. * *

* {@link UrlEncodingSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
  • This serializer is NOT used when using the {@link RestRequest#formData(String, Object)} (and related) methods for constructing * the request body. Instead, the part serializer specified via {@link #partSerializer(Class)} is used. *
*

* {@link UrlEncodingParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header will be set to "application/x-www-form-urlencoded" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "application/x-www-form-urlencoded" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class). * *

Example:
*

* // Construct a client that uses URL-Encoded marshalling. * RestClient client = RestClient.create().urlEnc().build(); *

* * @return This object. */ @FluentSetter public Builder urlEnc() { return serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class); } /** * Convenience method for specifying OpenAPI as the marshalling transmission media type. * *

* OpenAPI is a language that allows serialization to formats that use {@link HttpPartSchema} objects to describe their structure. * *

* {@link OpenApiSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
  • Typically the {@link RestRequest#content(Object, HttpPartSchema)} method will be used to specify the body of the request with the * schema describing it's structure. *
*

* {@link OpenApiParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
  • Typically the {@link ResponseContent#schema(HttpPartSchema)} method will be used to specify the structure of the response body. *
*

* Accept request header will be set to "text/openapi" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Content-Type request header will be set to "text/openapi" unless overridden * via {@link #headers()}, or per-request via {@link RestRequest#header(Header)}. *

* Can be combined with other marshaller setters such as {@link #json()} to provide support for multiple languages. *

    *
  • When multiple languages are supported, the Accept and Content-Type headers control which marshallers are used, or uses the * last-enabled language if the headers are not set. *
*

* Identical to calling serializer(OpenApiSerializer.class).parser(OpenApiParser.class). * *

Example:
*

* // Construct a client that uses OpenAPI marshalling. * RestClient client = RestClient.create().openApi().build(); *

* * @return This object. */ @FluentSetter public Builder openApi() { return serializer(OpenApiSerializer.class).parser(OpenApiParser.class); } /** * Convenience method for specifying all available transmission types. * *

* All basic Juneau serializers will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. *

    *
  • The serializers can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* All basic Juneau parsers will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. *

    *
  • The parsers can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
*

* Accept request header must be set via {@link #headers()}, or per-request * via {@link RestRequest#header(Header)} in order for the correct parser to be selected. *

* Content-Type request header must be set via {@link #headers()}, * or per-request via {@link RestRequest#header(Header)} in order for the correct serializer to be selected. *

* Similar to calling json().json5().html().xml().uon().urlEnc().openApi().msgPack().plainText(). * *

Example:
*

* // Construct a client that uses universal marshalling. * RestClient client = RestClient.create().universal().build(); *

* * @return This object. */ @SuppressWarnings("unchecked") public Builder universal() { return serializers( JsonSerializer.class, Json5Serializer.class, HtmlSerializer.class, XmlSerializer.class, UonSerializer.class, UrlEncodingSerializer.class, OpenApiSerializer.class, MsgPackSerializer.class, PlainTextSerializer.class ) .parsers( JsonParser.class, Json5Parser.class, XmlParser.class, HtmlParser.class, UonParser.class, UrlEncodingParser.class, OpenApiParser.class, MsgPackParser.class, PlainTextParser.class ); } //------------------------------------------------------------------------------------------------------------------ // httpClientBuilder //------------------------------------------------------------------------------------------------------------------ /** * Returns the HTTP client builder. * * @return The HTTP client builder. */ public final HttpClientBuilder httpClientBuilder() { if (httpClientBuilder == null) httpClientBuilder = createHttpClientBuilder(); return httpClientBuilder; } /** * Creates an instance of an {@link HttpClientBuilder} to be used to create the {@link HttpClient}. * *

* Subclasses can override this method to provide their own client builder. * The builder can also be specified using the {@link #httpClientBuilder(HttpClientBuilder)} method. * *

Example:
*

* // A Builder that provides it's own customized HttpClientBuilder. * public class MyBuilder extends Builder { * @Override * protected HttpClientBuilder createHttpClientBuilder() { * return HttpClientBuilder.create(); * } * } * * // Instantiate. * RestClient client = new MyBuilder().build(); *

* * @return The HTTP client builder to use to create the HTTP client. */ protected HttpClientBuilder createHttpClientBuilder() { return HttpClientBuilder.create(); } /** * Sets the {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. * *

* This can be used to bypass the builder created by {@link #createHttpClientBuilder()} method. * *

Example:
*

* // Construct a client that uses a customized HttpClientBuilder. * RestClient client = RestClient * .create() * .httpClientBuilder(HttpClientBuilder.create()) * .build(); *

* * @param value The {@link HttpClientBuilder} that will be used to create the {@link HttpClient} used by {@link RestClient}. * @return This object. */ @FluentSetter public Builder httpClientBuilder(HttpClientBuilder value) { this.httpClientBuilder = value; return this; } //------------------------------------------------------------------------------------------------------------------ // httpClient //------------------------------------------------------------------------------------------------------------------ /** * Creates an instance of an {@link HttpClient} to be used to handle all HTTP communications with the target server. * *

* This HTTP client is used when the HTTP client is not specified through one of the constructors or the * {@link #httpClient(CloseableHttpClient)} method. * *

* Subclasses can override this method to provide specially-configured HTTP clients to handle stuff such as * SSL/TLS certificate handling, authentication, etc. * *

* The default implementation returns an instance of {@link HttpClient} using the client builder returned by * {@link #createHttpClientBuilder()}. * *

Example:
*

* // A Builder that provides it's own customized HttpClient. * public class MyBuilder extends Builder { * @Override * protected HttpClientBuilder createHttpClient() { * return HttpClientBuilder.create().build(); * } * } * * // Instantiate. * RestClient client = new MyBuilder().build(); *

* * @return The HTTP client to use. */ protected CloseableHttpClient createHttpClient() { if (connectionManager == null) connectionManager = createConnectionManager(); httpClientBuilder().setConnectionManager(connectionManager); return httpClientBuilder().build(); } /** * Sets the {@link HttpClient} to be used to handle all HTTP communications with the target server. * *

* This can be used to bypass the client created by {@link #createHttpClient()} method. * *

Example:
*

* // Construct a client that uses a customized HttpClient. * RestClient client = RestClient * .create() * .httpClient(HttpClientBuilder.create().build()) * .build(); *

* * @param value The {@link HttpClient} to be used to handle all HTTP communications with the target server. * @return This object. */ @FluentSetter public Builder httpClient(CloseableHttpClient value) { this.httpClient = value; return this; } final CloseableHttpClient getHttpClient() { return httpClient != null ? httpClient : createHttpClient(); } //------------------------------------------------------------------------------------------------------------------ // serializers //------------------------------------------------------------------------------------------------------------------ /** * Returns the serializer group sub-builder. * * @return The serializer group sub-builder. */ public final SerializerSet.Builder serializers() { if (serializers == null) serializers = createSerializers(); return serializers; } /** * Instantiates the serializer group sub-builder. * * @return A new serializer group sub-builder. */ protected SerializerSet.Builder createSerializers() { return SerializerSet.create().beanContext(beanContext()); } /** * Serializer. * *

* Associates the specified {@link Serializer Serializer} with the HTTP client. * *

* The serializer is used to serialize POJOs into the HTTP request body. * *

Notes:
    *
  • When using this method that takes in a class, the serializer can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
* *
Example:
*

* // Create a client that uses JSON transport for request bodies. * RestClient client = RestClient * .create() * .serializer(JsonSerializer.class) * .sortCollections() // Sort any collections being serialized. * .build(); *

* * @param value * The new value for this setting. *
The default is {@link JsonSerializer}. * @return This object. */ @SuppressWarnings("unchecked") @FluentSetter public Builder serializer(Class value) { return serializers(value); } /** * Serializer. * *

* Associates the specified {@link Serializer Serializer} with the HTTP client. * *

* The serializer is used to serialize POJOs into the HTTP request body. * *

Notes:
    *
  • When using this method that takes in a pre-instantiated serializer, the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined * on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses a predefined JSON serializer request bodies. * RestClient client = RestClient * .create() * .serializer(JsonSerializer.DEFAULT_READABLE) * .build(); *

* * @param value * The new value for this setting. *
The default is {@link JsonSerializer}. * @return This object. */ @FluentSetter public Builder serializer(Serializer value) { return serializers(value); } /** * Serializers. * *

* Associates the specified {@link Serializer Serializers} with the HTTP client. * *

* The serializer is used to serialize POJOs into the HTTP request body. * *

* The serializer that best matches the Content-Type header will be used to serialize the request body. *
If no Content-Type header is specified, the first serializer in the list will be used. * *

Notes:
    *
  • When using this method that takes in classes, the serializers can be configured using any of the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
* *
Example:
*

* // Create a client that uses JSON and XML transport for request bodies. * RestClient client = RestClient * .create() * .serializers(JsonSerializer.class, XmlSerializer.class) * .sortCollections() // Sort any collections being serialized. * .build(); *

* * @param value * The new value for this setting. *
The default is {@link JsonSerializer}. * @return This object. */ @SuppressWarnings("unchecked") @FluentSetter public Builder serializers(Class...value) { serializers().add(value); return this; } /** * Serializers. * *

* Associates the specified {@link Serializer Serializers} with the HTTP client. * *

* The serializer is used to serialize POJOs into the HTTP request body. * *

* The serializer that best matches the Content-Type header will be used to serialize the request body. *
If no Content-Type header is specified, the first serializer in the list will be used. * *

Notes:
    *
  • When using this method that takes in a pre-instantiated serializers, the serializer property setters (e.g. {@link #sortCollections()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined * on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses predefined JSON and XML serializers for request bodies. * RestClient client = RestClient * .create() * .serializers(JsonSerializer.DEFAULT_READABLE, XmlSerializer.DEFAULT_READABLE) * .build(); *

* * @param value * The new value for this setting. *
The default is {@link JsonSerializer}. * @return This object. */ @FluentSetter public Builder serializers(Serializer...value) { serializers().add(value); return this; } //------------------------------------------------------------------------------------------------------------------ // parsers //------------------------------------------------------------------------------------------------------------------ /** * Returns the parser group sub-builder. * * @return The parser group sub-builder. */ public final ParserSet.Builder parsers() { if (parsers == null) parsers = createParsers(); return parsers; } /** * Instantiates the parser group sub-builder. * * @return A new parser group sub-builder. */ protected ParserSet.Builder createParsers() { return ParserSet.create().beanContext(beanContext()); } /** * Parser. * *

* Associates the specified {@link Parser Parser} with the HTTP client. * *

* The parser is used to parse the HTTP response body into a POJO. * *

Notes:
    *
  • When using this method that takes in a class, the parser can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
* *
Example:
*

* // Create a client that uses JSON transport for response bodies. * RestClient client = RestClient * .create() * .parser(JsonParser.class) * .strict() // Enable strict mode on JsonParser. * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link JsonParser#DEFAULT}. * @return This object. */ @SuppressWarnings("unchecked") @FluentSetter public Builder parser(Class value) { return parsers(value); } /** * Parser. * *

* Associates the specified {@link Parser Parser} with the HTTP client. * *

* The parser is used to parse the HTTP response body into a POJO. * *

Notes:
    *
  • When using this method that takes in a pre-instantiated parser, the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined * on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses a predefined JSON parser for response bodies. * RestClient client = RestClient * .create() * .parser(JsonParser.DEFAULT_STRICT) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link JsonParser#DEFAULT}. * @return This object. */ @FluentSetter public Builder parser(Parser value) { return parsers(value); } /** * Parsers. * *

* Associates the specified {@link Parser Parsers} with the HTTP client. * *

* The parsers are used to parse the HTTP response body into a POJO. * *

* The parser that best matches the Accept header will be used to parse the response body. *
If no Accept header is specified, the first parser in the list will be used. * *

Notes:
    *
  • When using this method that takes in classes, the parsers can be configured using any of the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class. *
* *
Example:
*

* // Create a client that uses JSON and XML transport for response bodies. * RestClient client = RestClient * .create() * .parser(JsonParser.class, XmlParser.class) * .strict() // Enable strict mode on parsers. * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link JsonParser#DEFAULT}. * @return This object. */ @SuppressWarnings("unchecked") @FluentSetter public Builder parsers(Class...value) { parsers().add(value); return this; } /** * Parsers. * *

* Associates the specified {@link Parser Parsers} with the HTTP client. * *

* The parsers are used to parse the HTTP response body into a POJO. * *

* The parser that best matches the Accept header will be used to parse the response body. *
If no Accept header is specified, the first parser in the list will be used. * *

Notes:
    *
  • When using this method that takes in pre-instantiated parsers, the parser property setters (e.g. {@link #strict()}) or * bean context property setters (e.g. {@link #swaps(Class...)}) defined * on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses JSON and XML transport for response bodies. * RestClient client = RestClient * .create() * .parser(JsonParser.DEFAULT_STRICT, XmlParser.DEFAULT) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link JsonParser#DEFAULT}. * @return This object. */ @FluentSetter public Builder parsers(Parser...value) { parsers().add(value); return this; } //------------------------------------------------------------------------------------------------------------------ // partSerializer //------------------------------------------------------------------------------------------------------------------ /** * Returns the part serializer sub-builder. * * @return The part serializer sub-builder. */ public final HttpPartSerializer.Creator partSerializer() { if (partSerializer == null) partSerializer = createPartSerializer(); return partSerializer; } /** * Instantiates the part serializer sub-builder. * * @return A new part serializer sub-builder. */ protected HttpPartSerializer.Creator createPartSerializer() { return HttpPartSerializer.creator().type(OpenApiSerializer.class).beanContext(beanContext()); } /** * Part serializer. * *

* The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. * *

* The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. * *

Example:
*

* // Create a client that uses UON format by default for outgoing HTTP parts. * RestClient client = RestClient * .create() * .partSerializer(UonSerializer.class) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link OpenApiSerializer}. * @return This object. */ @FluentSetter public Builder partSerializer(Class value) { partSerializer().type(value); return this; } /** * Part serializer. * *

* The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables. * *

* The default part serializer is {@link OpenApiSerializer} which allows for schema-driven marshalling. * *

Example:
*

* // Create a client that uses UON format by default for outgoing HTTP parts. * RestClient client = RestClient * .create() * .partSerializer(UonSerializer.DEFAULT) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link OpenApiSerializer}. * @return This object. */ @FluentSetter public Builder partSerializer(HttpPartSerializer value) { partSerializer().impl(value); return this; } //------------------------------------------------------------------------------------------------------------------ // partParser //------------------------------------------------------------------------------------------------------------------ /** * Returns the part parser sub-builder. * * @return The part parser sub-builder. */ public final HttpPartParser.Creator partParser() { if (partParser == null) partParser = createPartParser(); return partParser; } /** * Instantiates the part parser sub-builder. * * @return A new part parser sub-builder. */ protected HttpPartParser.Creator createPartParser() { return HttpPartParser.creator().type(OpenApiParser.class).beanContext(beanContext()); } /** * Part parser. * *

* The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. * *

* The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. * *

Example:
*

* // Create a client that uses UON format by default for incoming HTTP parts. * RestClient client = RestClient * .create() * .partParser(UonParser.class) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link OpenApiParser}. * @return This object. */ @FluentSetter public Builder partParser(Class value) { partParser().type(value); return this; } /** * Part parser. * *

* The parser to use for parsing POJOs from form data, query parameters, headers, and path variables. * *

* The default part parser is {@link OpenApiParser} which allows for schema-driven marshalling. * *

Example:
*

* // Create a client that uses UON format by default for incoming HTTP parts. * RestClient client = RestClient * .create() * .partParser(UonParser.DEFAULT) * .build(); *

* * @param value * The new value for this setting. *
The default value is {@link OpenApiParser}. * @return This object. */ @FluentSetter public Builder partParser(HttpPartParser value) { partParser().impl(value); return this; } //------------------------------------------------------------------------------------------------------------------ // urlEncodingSerializer //------------------------------------------------------------------------------------------------------------------ /** * Returns the URL-encoding serializer sub-builder. * * @return The URL-encoding serializer sub-builder. */ public final UrlEncodingSerializer.Builder urlEncodingSerializer() { if (urlEncodingSerializer == null) urlEncodingSerializer = createUrlEncodingSerializer(); return urlEncodingSerializer; } /** * Instantiates the URL-encoding serializer sub-builder. * * @return A new URL-encoding serializer sub-builder. */ protected UrlEncodingSerializer.Builder createUrlEncodingSerializer() { return UrlEncodingSerializer.create().beanContext(beanContext()); } //------------------------------------------------------------------------------------------------------------------ // headerData //------------------------------------------------------------------------------------------------------------------ /** * Returns the builder for the list of headers that get applied to all requests created by this builder. * *

* This is the primary method for accessing the request header list. * On first call, the builder is created via the method {@link #createHeaderData()}. * *

Example:
*

* // Create a client that adds a "Foo: bar" header on every request. * RestClient.Builder builder = RestClient.create(); * builder.headerData().setDefault("Foo", "bar")); * RestClient client = builder.build(); *

* *

* The following convenience methods are also provided for updating the headers: *

    *
  • {@link #headers(Header...)} *
  • {@link #headersDefault(Header...)} *
  • {@link #header(String,String)} *
  • {@link #header(String,Supplier)} *
  • {@link #mediaType(String)} *
  • {@link #mediaType(MediaType)} *
  • {@link #accept(String)} *
  • {@link #acceptCharset(String)} *
  • {@link #clientVersion(String)} *
  • {@link #contentType(String)} *
  • {@link #debug()} *
  • {@link #noTrace()} *
* * @return The header list builder. */ public final HeaderList headers() { if (headerData == null) headerData = createHeaderData(); return headerData; } /** * Creates the builder for the header list. * *

* Subclasses can override this method to provide their own implementation. * *

* The default behavior creates an empty builder. * * @return The header list builder. * @see #headers() */ protected HeaderList createHeaderData() { return HeaderList.create(); } /** * Appends multiple headers to all requests. * *

Example:
*

* import static org.apache.juneau.http.HttpHeaders.*; * * RestClient client = RestClient * .create() * .headers( * ACCEPT_TEXT_XML, * stringHeader("Foo", "bar") * ) * .build(); *

* *

* This is a shortcut for calling headerData().append(parts). * * @param parts * The header to set. * @return This object. * @see #headers() */ @FluentSetter public Builder headers(Header...parts) { headers().append(parts); return this; } /** * Sets default header values. * *

* Uses default values for specified headers if not otherwise specified on the outgoing requests. * *

Example:
*

* RestClient client = RestClient * .create() * .headersDefault(stringHeader("Foo", ()->"bar")); * .build(); *

* *

* This is a shortcut for calling headerData().setDefault(parts). * * @param parts The header values. * @return This object. * @see #headers() */ public Builder headersDefault(Header...parts) { headers().setDefault(parts); return this; } /** * Appends a header to all requests. * *

Example:
*

* RestClient client = RestClient * .create() * .header("Foo", "bar"); * .build(); *

* *

* This is a shortcut for calling headerData().append(name,value). * * @param name The header name. * @param value The header value. * @return This object. * @see #headers() */ @FluentSetter public Builder header(String name, String value) { headers().append(name, value); return this; } /** * Appends a header to all requests using a dynamic value. * *

Example:
*

* RestClient client = RestClient * .create() * .header("Foo", ()->"bar"); * .build(); *

* *

* This is a shortcut for calling headerData().append(name,value). * * @param name The header name. * @param value The header value supplier. * @return This object. * @see #headers() */ @FluentSetter public Builder header(String name, Supplier value) { headers().append(name, value); return this; } /** * Appends the Accept and Content-Type headers on all requests made by this client. * *

* Headers are appended to the end of the current header list. * *

* This is a shortcut for calling headerData().append(Accept.of(value), ContentType.of(value)). * * @param value The new header values. * @return This object. * @see #headers() */ @FluentSetter public Builder mediaType(String value) { super.mediaType(MediaType.of(value)); return headers(Accept.of(value), ContentType.of(value)); } /** * Appends the Accept and Content-Type headers on all requests made by this client. * *

* Headers are appended to the end of the current header list. * *

* This is a shortcut for calling headerData().append(Accept.of(value), ContentType.of(value)). * * @param value The new header values. * @return This object. * @see #headers() */ @Override @FluentSetter public Builder mediaType(MediaType value) { super.mediaType(value); return headers(Accept.of(value), ContentType.of(value)); } /** * Appends an Accept header on this request. * *

* This is a shortcut for calling headerData().append(Accept.of(value)). * * @param value * The new header value. * @return This object. * @see #headers() */ @FluentSetter public Builder accept(String value) { return headers(Accept.of(value)); } /** * Sets the value for the Accept-Charset request header on all requests. * *

* This is a shortcut for calling headerData().append(AcceptCharset.of(value)). * * @param value The new header value. * @return This object. * @see #headers() */ @FluentSetter public Builder acceptCharset(String value) { return headers(AcceptCharset.of(value)); } /** * Sets the client version by setting the value for the "Client-Version" header. * *

* This is a shortcut for calling headerData().append(ClientVersion.of(value)). * * @param value The version string (e.g. "1.2.3") * @return This object. * @see #headers() */ @FluentSetter public Builder clientVersion(String value) { return headers(ClientVersion.of(value)); } /** * Sets the value for the Content-Type request header on all requests. * *

* This is a shortcut for calling headerData().append(ContentType.of(value)). * *

* This overrides the media type specified on the serializer. * * @param value The new header value. * @return This object. * @see #headers() */ @FluentSetter public Builder contentType(String value) { return headers(ContentType.of(value)); } /** * Sets the value for the Debug request header on all requests. * *

* This is a shortcut for calling headerData().append(Debug.of(value)). * * @return This object. * @see #headers() */ @Override @FluentSetter public Builder debug() { super.debug(); serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::debug); return headers(Debug.TRUE); } /** * When called, No-Trace: true is added to requests. * *

* This gives the opportunity for the servlet to not log errors on invalid requests. * This is useful for testing purposes when you don't want your log file to show lots of errors that are simply the * results of testing. * *

* It's up to the server to decide whether to allow for this. * The BasicTestRestLogger class watches for this header and prevents logging of status 400+ responses to * prevent needless logging of test scenarios. * * @return This object. * @see #headers() */ @FluentSetter public Builder noTrace() { return headers(NoTrace.of(true)); } //------------------------------------------------------------------------------------------------------------------ // queryData //------------------------------------------------------------------------------------------------------------------ /** * Returns the builder for the list of query parameters that get applied to all requests created by this builder. * *

* This is the primary method for accessing the query parameter list. * On first call, the builder is created via the method {@link #createQueryData()}. * *

Example:
*

* // Create a client that adds a "foo=bar" query parameter on every request. * RestClient.Builder builder = RestClient.create(); * builder.queryData().setDefault("foo", "bar")); * RestClient client = builder.build(); *

* *

* The following convenience methods are also provided for updating the parameters: *

    *
  • {@link #queryData(NameValuePair...)} *
  • {@link #queryDataDefault(NameValuePair...)} *
  • {@link #queryData(String,String)} *
  • {@link #queryData(String,Supplier)} *
* * @return The query data list builder. */ public final PartList queryData() { if (queryData == null) queryData = createQueryData(); return queryData; } /** * Creates the builder for the query data list. * *

* Subclasses can override this method to provide their own implementation. * *

* The default behavior creates an empty builder. * * @return The query data list builder. * @see #queryData() */ protected PartList createQueryData() { return PartList.create(); } /** * Appends multiple query parameters to the URI of all requests. * *

Example:
*

* import static org.apache.juneau.http.HttpParts.*; * * RestClient client = RestClient * .create() * .queryData( * stringPart("foo", "bar"), * booleanPart("baz", true) * ) * .build(); *

* *

* This is a shortcut for calling queryData().append(parts). * * @param parts * The query parameters. * @return This object. * @see #queryData() */ @FluentSetter public Builder queryData(NameValuePair...parts) { queryData().append(parts); return this; } /** * Sets default query parameter values. * *

* Uses default values for specified parameters if not otherwise specified on the outgoing requests. * *

Example:
*

* RestClient client = RestClient * .create() * .queryDataDefault(stringPart("foo", ()->"bar")); * .build(); *

* *

* This is a shortcut for calling queryData().setDefault(parts). * * @param parts The parts. * @return This object. * @see #queryData() */ public Builder queryDataDefault(NameValuePair...parts) { queryData().setDefault(parts); return this; } /** * Appends a query parameter to the URI. * *

Example:
*

* RestClient client = RestClient * .create() * .queryData("foo", "bar") * .build(); *

* *

* This is a shortcut for calling queryData().append(name,value). * * @param name The parameter name. * @param value The parameter value. * @return This object. * @see #queryData() */ @FluentSetter public Builder queryData(String name, String value) { queryData().append(name, value); return this; } /** * Appends a query parameter with a dynamic value to the URI. * *

Example:
*

* RestClient client = RestClient * .create() * .queryData("foo", ()->"bar") * .build(); *

* *

* This is a shortcut for calling queryData().append(name,value). * * @param name The parameter name. * @param value The parameter value supplier. * @return This object. * @see #queryData() */ @FluentSetter public Builder queryData(String name, Supplier value) { queryData().append(name, value); return this; } //------------------------------------------------------------------------------------------------------------------ // formData //------------------------------------------------------------------------------------------------------------------ /** * Returns the builder for the list of form data parameters that get applied to all requests created by this builder. * *

* This is the primary method for accessing the form data parameter list. * On first call, the builder is created via the method {@link #createFormData()}. * *

Example:
*

* // Create a client that adds a "foo=bar" form-data parameter on every request. * RestClient.Builder builder = RestClient.create(); * builder.formData().setDefault("foo", "bar")); * RestClient client = builder.build(); *

* *

* The following convenience methods are also provided for updating the parameters: *

    *
  • {@link #formData(NameValuePair...)} *
  • {@link #formDataDefault(NameValuePair...)} *
  • {@link #formData(String,String)} *
  • {@link #formData(String,Supplier)} *
* * @return The form data list builder. */ public final PartList formData() { if (formData == null) formData = createFormData(); return formData; } /** * Creates the builder for the form data list. * *

* Subclasses can override this method to provide their own implementation. * *

* The default behavior creates an empty builder. * * @return The query data list builder. * @see #formData() */ protected PartList createFormData() { return PartList.create(); } /** * Appends multiple form-data parameters to the request bodies of all URL-encoded form posts. * *

Example:
*

* import static org.apache.juneau.http.HttpParts.*; * * RestClient client = RestClient * .create() * .formData( * stringPart("foo", "bar"), * booleanPart("baz", true) * ) * .build(); *

* *

* This is a shortcut for calling formData().append(parts). * * @param parts * The form-data parameters. * @return This object. * @see #formData() */ @FluentSetter public Builder formData(NameValuePair...parts) { formData().append(parts); return this; } /** * Sets default form-data parameter values. * *

* Uses default values for specified parameters if not otherwise specified on the outgoing requests. * *

Example:
*

* RestClient client = RestClient * .create() * .formDataDefault(stringPart("foo", ()->"bar")); * .build(); *

* *

* This is a shortcut for calling formData().setDefault(parts). * * @param parts The parts. * @return This object. * @see #formData() */ public Builder formDataDefault(NameValuePair...parts) { formData().setDefault(parts); return this; } /** * Appends a form-data parameter to all request bodies. * *

Example:
*

* RestClient client = RestClient * .create() * .formData("foo", "bar") * .build(); *

* *

* This is a shortcut for calling formData().append(name,value). * * @param name The parameter name. * @param value The parameter value. * @return This object. * @see #formData() */ @FluentSetter public Builder formData(String name, String value) { formData().append(name, value); return this; } /** * Appends a form-data parameter with a dynamic value to all request bodies. * *

Example:
*

* RestClient client = RestClient * .create() * .formData("foo", ()->"bar") * .build(); *

* *

* This is a shortcut for calling formData().append(name,value). * * @param name The parameter name. * @param value The parameter value supplier. * @return This object. * @see #formData() */ @FluentSetter public Builder formData(String name, Supplier value) { formData().append(name, value); return this; } //------------------------------------------------------------------------------------------------------------------ // pathData //------------------------------------------------------------------------------------------------------------------ /** * Returns the builder for the list of path data parameters that get applied to all requests created by this builder. * *

* This is the primary method for accessing the path data parameter list. * On first call, the builder is created via the method {@link #createFormData()}. * *

Example:
*

* // Create a client that uses "bar" for the "{foo}" path variable on every request. * RestClient.Builder builder = RestClient.create(); * builder.pathData().setDefault("foo", "bar")); * RestClient client = builder.build(); *

* *

* The following convenience methods are also provided for updating the parameters: *

    *
  • {@link #pathData(NameValuePair...)} *
  • {@link #pathDataDefault(NameValuePair...)} *
  • {@link #pathData(String,String)} *
  • {@link #pathData(String,Supplier)} *
* * @return The form data list builder. */ public final PartList pathData() { if (pathData == null) pathData = createPathData(); return pathData; } /** * Creates the builder for the path data list. * *

* Subclasses can override this method to provide their own implementation. * *

* The default behavior creates an empty builder. * * @return The query data list builder. * @see #pathData() */ protected PartList createPathData() { return PartList.create(); } /** * Sets multiple path parameters on all requests. * *

Example:
*

* import static org.apache.juneau.http.HttpParts.*; * * RestClient client = RestClient * .create() * .pathData( * stringPart("foo", "bar"), * booleanPart("baz", true) * ) * .build(); *

* *

* This is a shortcut for calling pathData().append(parts). * * @param parts * The path parameters. * @return This object. * @see #pathData() */ @FluentSetter public Builder pathData(NameValuePair...parts) { pathData().append(parts); return this; } /** * Sets default path parameter values. * *

* Uses default values for specified parameters if not otherwise specified on the outgoing requests. * *

Example:
*

* RestClient client = RestClient * .create() * .pathDataDefault(stringPart("foo", ()->"bar")); * .build(); *

* *

* This is a shortcut for calling pathData().setDefault(parts). * * @param parts The parts. * @return This object. * @see #pathData() */ public Builder pathDataDefault(NameValuePair...parts) { pathData().setDefault(parts); return this; } /** * Appends a path parameter to all request bodies. * *

Example:
*

* RestClient client = RestClient * .create() * .pathData("foo", "bar") * .build(); *

* *

* This is a shortcut for calling pathData().append(name,value). * * @param name The parameter name. * @param value The parameter value. * @return This object. * @see #pathData() */ @FluentSetter public Builder pathData(String name, String value) { pathData().append(name, value); return this; } /** * Sets a path parameter with a dynamic value to all request bodies. * *

Example:
*

* RestClient client = RestClient * .create() * .pathData("foo", ()->"bar") * .build(); *

* *

* This is a shortcut for calling pathData().append(name,value). * * @param name The parameter name. * @param value The parameter value supplier. * @return This object. * @see #pathData() */ @FluentSetter public Builder pathData(String name, Supplier value) { pathData().set(name, value); return this; } //------------------------------------------------------------------------------------------------------------------ // callHandler //------------------------------------------------------------------------------------------------------------------ /** * Returns the creator for the rest call handler. * *

* Allows you to provide a custom handler for making HTTP calls. * *

Example:
*

* // Create a client that handles processing of requests using a custom handler. * public class MyRestCallHandler implements RestCallHandler { * * @Override * public HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws IOException { * // Custom handle requests. * } * } * * RestClient client = RestClient * .create() * .callHandler(MyRestCallHandler.class) * .build(); *

* *
Notes:
    *
  • * The {@link RestClient#run(HttpHost, HttpRequest, HttpContext)} method can also be overridden to produce the same results. *
  • * Use {@link BeanCreator#impl(Object)} to specify an already instantiated instance. *
  • * Use {@link BeanCreator#type(Class)} to specify a subtype to instantiate. *
    Subclass must have a public constructor that takes in any args available * in the bean store of this builder (including {@link RestClient} itself). *
* *
See Also:
    *
  • {@link RestCallHandler} *
* * @return The creator for the rest call handler. */ public final BeanCreator callHandler() { if (callHandler == null) callHandler = createCallHandler(); return callHandler; } /** * Creates the creator for the rest call handler. * *

* Subclasses can override this method to provide their own implementation. * *

* The default behavior creates a bean creator initialized to return a {@link BasicRestCallHandler}. * * @return The creator for the rest call handler. * @see #callHandler() */ protected BeanCreator createCallHandler() { return beanStore.createBean(RestCallHandler.class).type(BasicRestCallHandler.class); } /** * REST call handler class. * *

* Specifies a custom handler for making HTTP calls. * *

* This is a shortcut for callHandler().type(value). * * @param value * The new value for this setting. * @return This object. * @see #callHandler() */ @FluentSetter public Builder callHandler(Class value) { callHandler().type(value); return this; } //------------------------------------------------------------------------------------------------------------------ // errorCodes //------------------------------------------------------------------------------------------------------------------ /** * Errors codes predicate. * *

* Defines a predicate to test for error codes. * *

Example:
*

* // Create a client that considers any 300+ responses to be errors. * RestClient client = RestClient * .create() * .errorCodes(x -> x>=300) * .build(); *

* * @param value * The new value for this setting. *
The default value is x -> x >= 400. * @return This object. */ @FluentSetter public Builder errorCodes(Predicate value) { errorCodes = assertArgNotNull("value", value); return this; } //------------------------------------------------------------------------------------------------------------------ // Logging. //------------------------------------------------------------------------------------------------------------------ /** * Logger. * *

* Specifies the logger to use for logging. * *

* If not specified, uses the following logger: *

* Logger.getLogger(RestClient.class.getName()); *

* *
Example:
*

* // Construct a client that logs messages to a special logger. * RestClient client = RestClient * .create() * .logger(Logger.getLogger("MyLogger")) // Log to MyLogger logger. * .logToConsole() // Also log to console. * .logRequests(FULL, WARNING) // Log requests with full detail at WARNING level. * .build(); *

* * @param value The logger to use for logging. * @return This object. */ @FluentSetter public Builder logger(Logger value) { logger = value; return this; } /** * Log to console. * *

* Specifies to log messages to the console. * *

Example:
*

* // Construct a client that logs messages to a special logger. * RestClient client = RestClient * .create() * .logToConsole() * .logRequests(FULL, INFO) // Level is ignored when logging to console. * .build(); *

* * @return This object. */ @FluentSetter public Builder logToConsole() { logToConsole = true; return this; } /** * Log requests. * *

* Causes requests/responses to be logged at the specified log level at the end of the request. * *

* SIMPLE detail produces a log message like the following: *

* POST http://localhost:10000/testUrl, HTTP/1.1 200 OK *

* *

* FULL detail produces a log message like the following: *

* === 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 ======================================================================== *

* *

* By default, the message is logged to the default logger. It can be logged to a different logger via the * {@link #logger(Logger)} method or logged to the console using the * {@link #logToConsole()} method. * * @param detail The detail level of logging. * @param level The log level. * @param test A predicate to use per-request to see if the request should be logged. If null, always logs. * @return This object. */ @FluentSetter public Builder logRequests(DetailLevel detail, Level level, BiPredicate test) { logRequests = detail; logRequestsLevel = level; logRequestsPredicate = test; return this; } //------------------------------------------------------------------------------------------------------------------ // HttpClientConnectionManager methods. //------------------------------------------------------------------------------------------------------------------ /** * Creates the {@link HttpClientConnectionManager} returned by {@link #createConnectionManager()}. * *

* Subclasses can override this method to provide their own connection manager. * *

* The default implementation returns an instance of a {@link PoolingHttpClientConnectionManager} if {@link #pooled()} * was called or {@link BasicHttpClientConnectionManager} if not.. * *

Example:
*

* // A Builder that provides it's own customized HttpClientConnectionManager. * public class MyBuilder extends Builder { * @Override * protected HttpClientConnectionManager createConnectionManager() { * return new PoolingHttpClientConnectionManager(); * } * } * * // Instantiate. * RestClient client = new MyBuilder().build(); *

* * @return The HTTP client builder to use to create the HTTP client. */ protected HttpClientConnectionManager createConnectionManager() { return (pooled ? new PoolingHttpClientConnectionManager() : new BasicHttpClientConnectionManager()); } /** * When called, the {@link #createConnectionManager()} method will return a {@link PoolingHttpClientConnectionManager} * instead of a {@link BasicHttpClientConnectionManager}. * *
Example:
*

* // Construct a client that uses pooled connections. * RestClient client = RestClient * .create() * .pooled() * .build(); *

* * @return This object. */ @FluentSetter public Builder pooled() { this.pooled = true; return this; } /** * Assigns {@link HttpClientConnectionManager} instance. * * @param value New property value. * @return This object. * @see HttpClientBuilder#setConnectionManager(HttpClientConnectionManager) */ @FluentSetter public Builder connectionManager(HttpClientConnectionManager value) { connectionManager = value; httpClientBuilder().setConnectionManager(value); return this; } /** * Defines the connection manager is to be shared by multiple client instances. * *
Notes:
    *
  • If the connection manager is shared its life-cycle is expected to be managed by the caller and it will not be shut down if the client is closed. *
* * @param shared New property value. * @return This object. * @see HttpClientBuilder#setConnectionManagerShared(boolean) */ @FluentSetter public Builder connectionManagerShared(boolean shared) { httpClientBuilder().setConnectionManagerShared(shared); return this; } /** * Set up this client to use BASIC auth. * *
Example:
*

* // Construct a client that uses BASIC authentication. * RestClient client = RestClient * .create() * .basicAuth("http://localhost", 80, "me", "mypassword") * .build(); *

* * @param host The auth scope hostname. * @param port The auth scope port. * @param user The username. * @param pw The password. * @return This object. */ @FluentSetter public Builder basicAuth(String host, int port, String user, String pw) { AuthScope scope = new AuthScope(host, port); Credentials up = new UsernamePasswordCredentials(user, pw); CredentialsProvider p = new BasicCredentialsProvider(); p.setCredentials(scope, up); defaultCredentialsProvider(p); return this; } //----------------------------------------------------------------------------------------------------------------- // Properties //----------------------------------------------------------------------------------------------------------------- /** * Console print stream * *

* Allows you to redirect the console output to a different print stream. * * @param value * The new value for this setting. * @return This object. */ @FluentSetter public Builder console(PrintStream value) { console = value; return this; } /** * RestClient configuration property:  Executor service. * *

* Defines the executor service to use when calling future methods on the {@link RestRequest} class. * *

* This executor service is used to create {@link Future} objects on the following methods: *

    *
  • {@link RestRequest#runFuture()} *
  • {@link RestRequest#completeFuture()} *
  • {@link ResponseContent#asFuture(Class)} (and similar methods) *
* *

* The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout * and a queue size of 10. * *

Example:
*

* // Create a client with a customized executor service. * RestClient client = RestClient * .create() * .executorService(new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)), true) * .build(); * * // Use it to asynchronously run a request. * Future<RestResponse> responseFuture = client.get(URI).runFuture(); * * // Do some other stuff. * * // Now read the response. * String body = responseFuture.get().getContent().asString(); * * // Use it to asynchronously retrieve a response. * Future<MyBean> myBeanFuture = client * .get(URI) * .run() * .getContent().asFuture(MyBean.class); * * // Do some other stuff. * * // Now read the response. * MyBean bean = myBeanFuture.get(); *

* * @param executorService The executor service. * @param shutdownOnClose Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called. * @return This object. */ @FluentSetter public Builder executorService(ExecutorService executorService, boolean shutdownOnClose) { this.executorService = executorService; this.executorServiceShutdownOnClose = shutdownOnClose; return this; } /** * RestClient configuration property:  Keep HttpClient open. * *

* Don't close this client when the {@link RestClient#close()} method is called. * *

Example:
*

* // Create a client with a customized client and don't close the client service. * RestClient client = RestClient * .create() * .httpClient(myHttpClient) * .keepHttpClientOpen() * .build(); * * client.closeQuietly(); // Customized HttpClient won't be closed. *

* * @return This object. */ @FluentSetter public Builder keepHttpClientOpen() { keepHttpClientOpen = true; return this; } /** * Ignore errors. * *

* When enabled, HTTP error response codes (e.g. >=400) will not cause a {@link RestCallException} to * be thrown. *

* Note that this is equivalent to builder.errorCodes(x -> false); * *

Example:
*

* // Create a client that doesn't throws a RestCallException when a 500 error occurs. * RestClient * .create() * .ignoreErrors() * .build() * .get("/error") // Throws a 500 error * .run() * .assertStatus().is(500); *

* * @return This object. */ @FluentSetter public Builder ignoreErrors() { ignoreErrors = true; return this; } /** * RestClient configuration property:  Call interceptors. * *

* Adds an interceptor that can be called to hook into specified events in the lifecycle of a single request. * *

Example:
*

* // Customized interceptor (note you can also extend from BasicRestCallInterceptor as well. * public class MyRestCallInterceptor implements RestCallInterceptor { * * @Override * public void onInit(RestRequest req) throws Exception { * // Intercept immediately after RestRequest object is created and all headers/query/form-data has been * // set on the request from the client. * } * * @Override * public void onConnect(RestRequest req, RestResponse res) throws Exception { * // Intercept immediately after an HTTP response has been received. * } * * @Override * public void onClose(RestRequest req, RestResponse res) throws Exception { * // Intercept when the response body is consumed. * } * } * * // Create a client with a customized interceptor. * RestClient client = RestClient * .create() * .interceptors(MyRestCallInterceptor.class) * .build(); *

* *
Notes:
    *
  • The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. *
* * @param values * The values to add to this setting. *
Can be implementations of any of the following: *
    *
  • {@link RestCallInterceptor} *
  • {@link HttpRequestInterceptor} *
  • {@link HttpResponseInterceptor} *
* @return This object. * @throws Exception If one or more interceptors could not be created. */ @FluentSetter public Builder interceptors(Class...values) throws Exception { for (Class c : values) { ClassInfo ci = ClassInfo.of(c); if (ci != null) { if (ci.isChildOfAny(RestCallInterceptor.class, HttpRequestInterceptor.class, HttpResponseInterceptor.class)) interceptors(ci.newInstance()); else throw new ConfigException("Invalid class of type ''{0}'' passed to interceptors().", ci.getName()); } } return this; } /** * Call interceptors. * *

* Adds an interceptor that gets called immediately after a connection is made. * *

Example:
*

* // Create a client with a customized interceptor. * RestClient client = RestClient * .create() * .interceptors( * new RestCallInterceptor() { * * @Override * public void onInit(RestRequest req) throws Exception { * // Intercept immediately after RestRequest object is created and all headers/query/form-data has been * // set on the request from the client. * } * * @Override * public void onConnect(RestRequest req, RestResponse res) throws Exception { * // Intercept immediately after an HTTP response has been received. * } * * @Override * public void onClose(RestRequest req, RestResponse res) throws Exception { * // Intercept when the response body is consumed. * } * } * ) * .build(); *

* *
Notes:
    *
  • The {@link RestClient#onCallInit(RestRequest)}, {@link RestClient#onCallConnect(RestRequest,RestResponse)}, and * {@link RestClient#onCallClose(RestRequest,RestResponse)} methods can also be overridden to produce the same results. *
* * @param value * The values to add to this setting. *
Can be implementations of any of the following: *
    *
  • {@link RestCallInterceptor} *
  • {@link HttpRequestInterceptor} *
  • {@link HttpResponseInterceptor} *
* @return This object. */ @FluentSetter public Builder interceptors(Object...value) { List l = list(); for (Object o : value) { ClassInfo ci = ClassInfo.of(o); if (ci != null) { if (! ci.isChildOfAny(HttpRequestInterceptor.class, HttpResponseInterceptor.class, RestCallInterceptor.class)) throw new ConfigException("Invalid object of type ''{0}'' passed to interceptors().", ci.getName()); if (o instanceof HttpRequestInterceptor) addInterceptorLast((HttpRequestInterceptor)o); if (o instanceof HttpResponseInterceptor) addInterceptorLast((HttpResponseInterceptor)o); if (o instanceof RestCallInterceptor) l.add((RestCallInterceptor)o); } } if (interceptors == null) interceptors = l; else interceptors.addAll(0, l); return this; } /** * RestClient configuration property:  Enable leak detection. * *

* Enable client and request/response leak detection. * *

* Causes messages to be logged to the console if clients or request/response objects are not properly closed * when the finalize methods are invoked. * *

* Automatically enabled with {@link org.apache.juneau.Context.Builder#debug()}. * *

Example:
*

* // Create a client that logs a message if * RestClient client = RestClient * .create() * .detectLeaks() * .logToConsole() // Also log the error message to System.err * .build(); * * client.closeQuietly(); // Customized HttpClient won't be closed. *

* * @return This object. */ @FluentSetter public Builder detectLeaks() { detectLeaks = true; return this; } /** * RestClient configuration property:  Marshaller * *

* Shortcut for specifying the serializers and parsers * using the serializer and parser defined in a marshaller. * *

Notes:
    *
  • When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), * parser property setters (e.g. {@link #strict()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses Simplified-JSON transport using an existing marshaller. * RestClient client = RestClient * .create() * .marshaller(Json5.DEFAULT_READABLE) * .build(); *

* * @param value The values to add to this setting. * @return This object. */ @FluentSetter public Builder marshaller(Marshaller value) { if (value != null) serializer(value.getSerializer()).parser(value.getParser()); return this; } /** * RestClient configuration property:  Marshalls * *

* Shortcut for specifying the serializers and parsers * using the serializer and parser defined in a marshaller. * *

Notes:
    *
  • When using this method that takes in a pre-instantiated serializers and parsers, the serializer property setters (e.g. {@link #sortCollections()}), * parser property setters (e.g. {@link #strict()}), or bean context property setters (e.g. {@link #swaps(Class...)}) defined on this builder class have no effect. *
* *
Example:
*

* // Create a client that uses JSON and XML transport using existing marshalls. * RestClient client = RestClient * .create() * .marshaller(Json.DEFAULT_READABLE, Xml.DEFAULT_READABLE) * .build(); *

* * @param value The values to add to this setting. * @return This object. */ @FluentSetter public Builder marshallers(Marshaller...value) { for (Marshaller m : value) if (m != null) serializer(m.getSerializer()).parser(m.getParser()); return this; } /** * RestClient configuration property:  Root URI. * *

* 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. *
This root URI is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URI string. * *

Example:
*

* // Create a client that uses UON format by default for HTTP parts. * RestClient client = RestClient * .create() * .rootUrl("http://localhost:10000/foo") * .build(); * * Bar bar = client * .get("/bar") // Relative to http://localhost:10000/foo * .run() * .getContent().as(Bar.class); *

* * @param value * The root URI to prefix to relative URI strings. *
Trailing slashes are trimmed. *
Usually a String but you can also pass in URI and URL objects as well. * @return This object. */ @FluentSetter public Builder rootUrl(Object value) { String s = stringify(value); if (! isEmpty(s)) s = s.replaceAll("\\/$", ""); if (isEmpty(s)) rootUrl = null; else if (s.indexOf("://") == -1) throw new BasicRuntimeException("Invalid rootUrl value: ''{0}''. Must be a valid absolute URL.", value); else rootUrl = s; return this; } /** * Returns the root URI defined for this client. * *

* Returns null in leu of an empty string. * Trailing slashes are trimmed. * * @return The root URI defined for this client. */ public String getRootUri() { return rootUrl; } /** * Skip empty form data. * *

* When enabled, form data consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @param value * The new value for this setting. *
The default is false. * @return This object. */ @FluentSetter public Builder skipEmptyFormData(boolean value) { skipEmptyFormData = true; return this; } /** * Skip empty form data. * *

* When enabled, form data consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @return This object. */ @FluentSetter public Builder skipEmptyFormData() { return skipEmptyFormData(true); } /** * Skip empty header data. * *

* When enabled, headers consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @param value * The new value for this setting. *
The default is false. * @return This object. */ @FluentSetter public Builder skipEmptyHeaderData(boolean value) { skipEmptyHeaderData = true; return this; } /** * Skip empty header data. * *

* When enabled, headers consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @return This object. */ @FluentSetter public Builder skipEmptyHeaderData() { return skipEmptyHeaderData(true); } /** * Skip empty query data. * *

* When enabled, query parameters consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @param value * The new value for this setting. *
The default is false. * @return This object. */ @FluentSetter public Builder skipEmptyQueryData(boolean value) { skipEmptyQueryData = true; return this; } /** * Skip empty query data. * *

* When enabled, query parameters consisting of empty strings will be skipped on requests. * Note that null values are already skipped. * *

* The {@link Schema#skipIfEmpty()} annotation overrides this setting. * * @return This object. */ @FluentSetter public Builder skipEmptyQueryData() { return skipEmptyQueryData(true); } //----------------------------------------------------------------------------------------------------------------- // BeanTraverse Properties //----------------------------------------------------------------------------------------------------------------- /** * BeanTraverse configuration property:  Automatically detect POJO recursions. * *

* When enabled, specifies that recursions should be checked for during traversal. * *

* Recursions can occur when traversing models that aren't true trees but rather contain loops. *
In general, unchecked recursions cause stack-overflow-errors. *
These show up as {@link BeanRecursionException BeanRecursionException} with the message "Depth too deep. Stack overflow occurred.". * *

Notes:
    *
  • * Checking for recursion can cause a small performance penalty. *
* *
Example:
*

* // Create a JSON client that automatically checks for recursions. * RestClient client = RestClient * .create() * .json() * .detectRecursions() * .build(); * * // Create a POJO model with a recursive loop. * public class A { * public Object f; * } * A a = new A(); * a.f = a; * * try { * // Throws a RestCallException with an inner SerializeException and not a StackOverflowError * client * .post("http://localhost:10000/foo", a) * .run(); * } catch (RestCallException e} { * // Handle exception. * } *

* *
See Also:
    *
  • {@link org.apache.juneau.BeanTraverseContext.Builder#detectRecursions()} *
* * @return This object. */ @FluentSetter public Builder detectRecursions() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::detectRecursions); return this; } /** * BeanTraverse configuration property:  Ignore recursion errors. * *

* When enabled, when we encounter the same object when traversing a tree, we set the value to null. * *

* For example, if a model contains the links A->B->C->A, then the JSON generated will look like * the following when BEANTRAVERSE_ignoreRecursions is true... * *

* {A:{B:{C:null}}} *

* *
Notes:
    *
  • * Checking for recursion can cause a small performance penalty. *
* *
Example:
*

* // Create a JSON client that ignores recursions. * RestClient client = RestClient * .create() * .json() * .ignoreRecursions() * .build(); * * // Create a POJO model with a recursive loop. * public class A { * public Object f; * } * A a = new A(); * a.f = a; * * // Produces request body "{f:null}" * client * .post("http://localhost:10000/foo", a) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.BeanTraverseContext.Builder#ignoreRecursions()} *
* * @return This object. */ @FluentSetter public Builder ignoreRecursions() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::ignoreRecursions); return this; } /** * BeanTraverse configuration property:  Initial depth. * *

* The initial indentation level at the root. * *

* Useful when constructing document fragments that need to be indented at a certain level when whitespace is enabled. * *

Example:
*

* // Create a REST client with JSON serializer with whitespace enabled and an initial depth of 2. * RestClient client = RestClient * .create() * .json() * .ws() * .initialDepth(2) * .build(); * * // Our bean to serialize. * public class MyBean { * public String foo = null; * } * * // Produces request body "\t\t{\n\t\t\t'foo':'bar'\n\t\t}\n" * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.BeanTraverseContext.Builder#initialDepth(int)} *
* * @param value * The new value for this property. *
The default is 0. * @return This object. */ @FluentSetter public Builder initialDepth(int value) { serializers().forEach(x -> x.initialDepth(value)); return this; } /** * BeanTraverse configuration property:  Max serialization depth. * *

* When enabled, abort traversal if specified depth is reached in the POJO tree. * *

* If this depth is exceeded, an exception is thrown. * *

* This prevents stack overflows from occurring when trying to traverse models with recursive references. * *

Example:
*

* // Create a REST client with JSON serializer that throws an exception if the depth reaches greater than 20. * RestClient client = RestClient * .create() * .json() * .maxDepth(20) * .build(); *

* *
See Also:
    *
  • {@link org.apache.juneau.BeanTraverseContext.Builder#maxDepth(int)} *
* * @param value * The new value for this property. *
The default is 100. * @return This object. */ @FluentSetter public Builder maxDepth(int value) { serializers().forEach(x -> x.maxDepth(value)); return this; } //----------------------------------------------------------------------------------------------------------------- // Serializer Properties //----------------------------------------------------------------------------------------------------------------- /** * Serializer configuration property:  Add "_type" properties when needed. * *

* When enabled, "_type" properties will be added to beans if their type cannot be inferred * through reflection. * *

* This is used to recreate the correct objects during parsing if the object types cannot be inferred. *
For example, when serializing a Map<String,Object> field where the bean class cannot be determined from * the type of the values. * *

* Note the differences between the following settings: *

    *
  • {@link #addRootType()} - Affects whether '_type' is added to root node. *
  • {@link #addBeanTypes()} - Affects whether '_type' is added to any nodes. *
* *
Example:
*

* // Create a JSON client that adds _type to nodes in the request body. * RestClient client = RestClient * .create() * .json() * .addBeanTypes() * .build(); * * // Our map of beans to serialize. * @Bean(typeName="mybean") * public class MyBean { * public String foo = "bar"; * } * * AMap map = AMap.of("foo", new MyBean()); * * // Request body will contain: {"foo":{"_type":"mybean","foo":"bar"}} * client * .post("http://localhost:10000/foo", map) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} *
* * @return This object. */ @FluentSetter public Builder addBeanTypes() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::addBeanTypes); return this; } /** * Serializer configuration property:  Add type attribute to root nodes. * *

* When enabled, "_type" properties will be added to top-level beans. * *

* When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level * type information that might normally be included to determine the data type will not be serialized. * *

* For example, when serializing a top-level POJO with a {@link Bean#typeName() @Bean(typeName)} value, a * '_type' attribute will only be added when this setting is enabled. * *

* Note the differences between the following settings: *

    *
  • {@link #addRootType()} - Affects whether '_type' is added to root node. *
  • {@link #addBeanTypes()} - Affects whether '_type' is added to any nodes. *
* *
Example:
*

* // Create a JSON client that adds _type to root node. * RestClient client = RestClient * .create() * .json() * .addRootType() * .build(); * * // Our bean to serialize. * @Bean(typeName="mybean") * public class MyBean { * public String foo = "bar"; * } * * // Request body will contain: {"_type":"mybean","foo":"bar"} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#addRootType()} *
* * @return This object. */ @FluentSetter public Builder addRootType() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::addRootType); return this; } /** * Serializer configuration property:  Don't trim null bean property values. * *

* When enabled, null bean values will be serialized to the output. * *

Notes:
    *
  • Not enabling this setting will cause Maps with null values to be lost during parsing. *
* *
Example:
*

* // Create a REST client with JSON serializer that serializes null properties. * RestClient client = RestClient * .create() * .json() * .keepNullProperties() * .build(); * * // Our bean to serialize. * public class MyBean { * public String foo = null; * } * * // Request body will contain: {foo:null} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#keepNullProperties()} *
* * @return This object. */ @FluentSetter public Builder keepNullProperties() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::keepNullProperties); return this; } /** * Serializer configuration property:  Sort arrays and collections alphabetically. * *

* When enabled, copies and sorts the contents of arrays and collections before serializing them. * *

* Note that this introduces a performance penalty since it requires copying the existing collection. * *

Example:
*

* // Create a REST client with JSON serializer that sorts arrays and collections before serialization. * RestClient client = RestClient * .create() * .json() * .sortCollections() * .build(); * * // An unsorted array * String[] array = {"foo","bar","baz"} * * // Request body will contain: ["bar","baz","foo"] * client * .post("http://localhost:10000/foo", array) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#sortCollections()} *
* * @return This object. */ @FluentSetter public Builder sortCollections() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::sortCollections); return this; } /** * Serializer configuration property:  Sort maps alphabetically. * *

* When enabled, copies and sorts the contents of maps by their keys before serializing them. * *

* Note that this introduces a performance penalty. * *

Example:
*

* // Create a REST client with JSON serializer that sorts maps before serialization. * RestClient client = RestClient * .create() * .json() * .sortMaps() * .build(); * * // An unsorted map. * AMap map = AMap.of("foo",1,"bar",2,"baz",3); * * // Request body will contain: {"bar":2,"baz":3,"foo":1} * client * .post("http://localhost:10000/foo", map) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#sortMaps()} *
* * @return This object. */ @FluentSetter public Builder sortMaps() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::sortMaps); return this; } /** * Serializer configuration property:  Trim empty lists and arrays. * *

* When enabled, empty lists and arrays will not be serialized. * *

* Note that enabling this setting has the following effects on parsing: *

    *
  • * Map entries with empty list values will be lost. *
  • * Bean properties with empty list values will not be set. *
* *
Example:
*

* // Create a serializer that skips empty arrays and collections. * WriterSerializer serializer = JsonSerializer * .create() * .trimEmptyCollections() * .build(); * * // A bean with a field with an empty array. * public class MyBean { * public String[] foo = {}; * } * * // Request body will contain: {} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#trimEmptyCollections()} *
* * @return This object. */ @FluentSetter public Builder trimEmptyCollections() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimEmptyCollections); return this; } /** * Serializer configuration property:  Trim empty maps. * *

* When enabled, empty map values will not be serialized to the output. * *

* Note that enabling this setting has the following effects on parsing: *

    *
  • * Bean properties with empty map values will not be set. *
* *
Example:
*

* // Create a REST client with JSON serializer that skips empty maps. * RestClient client = RestClient * .create() * .json() * .trimEmptyMaps() * .build(); * * // A bean with a field with an empty map. * public class MyBean { * public AMap foo = AMap.of(); * } * * // Request body will contain: {} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#trimEmptyMaps()} *
* * @return This object. */ @FluentSetter public Builder trimEmptyMaps() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimEmptyMaps); return this; } /** * Serializer configuration property:  Trim strings. * *

* When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized. * *

Example:
*

* // Create a REST client with JSON serializer that trims strings before serialization. * RestClient client = RestClient * .create() * .json() * .trimStrings() * .build(); * * // A map with space-padded keys/values * AMap map = AMap.of(" foo ", " bar "); * * // Request body will contain: {"foo":"bar"} * client * .post("http://localhost:10000/foo", map) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#trimStrings()} *
* * @return This object. */ @FluentSetter public Builder trimStringsOnWrite() { serializers().forEach(org.apache.juneau.serializer.Serializer.Builder::trimStrings); return this; } /** * Serializer configuration property:  URI context bean. * *

* Bean used for resolution of URIs to absolute or root-relative form. * *

Example:
*

* // Our URI contextual information. * String authority = "http://localhost:10000"; * String contextRoot = "/myContext"; * String servletPath = "/myServlet"; * String pathInfo = "/foo"; * * // Create a UriContext object. * UriContext uriContext = new UriContext(authority, contextRoot, servletPath, pathInfo); * * // Create a REST client with JSON serializer and associate our context. * RestClient client = RestClient * .create() * .json() * .uriContext(uriContext) * .uriRelativity(RESOURCE) // Assume relative paths are relative to servlet. * .uriResolution(ABSOLUTE) // Serialize URIs as absolute paths. * .build(); * * // A relative URI * URI uri = new URI("bar"); * * // Request body will contain: "http://localhost:10000/myContext/myServlet/foo/bar" * client * .post("http://localhost:10000/foo", uri) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#uriContext(UriContext)} *
* * @param value The new value for this property. * @return This object. */ @FluentSetter public Builder uriContext(UriContext value) { serializers().forEach(x -> x.uriContext(value)); return this; } /** * Serializer configuration property:  URI relativity. * *

* Defines what relative URIs are relative to when serializing any of the following: *

    *
  • {@link java.net.URI} *
  • {@link java.net.URL} *
  • Properties and classes annotated with {@link Uri @Uri} *
* *

* See {@link #uriContext(UriContext)} for examples. * *

    *
  • {@link org.apache.juneau.UriRelativity#RESOURCE} * - Relative URIs should be considered relative to the servlet URI. *
  • {@link org.apache.juneau.UriRelativity#PATH_INFO} * - Relative URIs should be considered relative to the request URI. *
* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#uriRelativity(UriRelativity)} *
* * @param value * The new value for this property. *
The default is {@link UriRelativity#RESOURCE} * @return This object. */ @FluentSetter public Builder uriRelativity(UriRelativity value) { serializers().forEach(x -> x.uriRelativity(value)); return this; } /** * Serializer configuration property:  URI resolution. * *

* Defines the resolution level for URIs when serializing any of the following: *

    *
  • {@link java.net.URI} *
  • {@link java.net.URL} *
  • Properties and classes annotated with {@link Uri @Uri} *
* *

* See {@link #uriContext(UriContext)} for examples. * *

    *
  • {@link UriResolution#ABSOLUTE} * - Resolve to an absolute URI (e.g. "http://host:port/context-root/servlet-path/path-info"). *
  • {@link UriResolution#ROOT_RELATIVE} * - Resolve to a root-relative URI (e.g. "/context-root/servlet-path/path-info"). *
  • {@link UriResolution#NONE} * - Don't do any URI resolution. *
* *
See Also:
    *
  • {@link org.apache.juneau.serializer.Serializer.Builder#uriResolution(UriResolution)} *
* * @param value * The new value for this property. *
The default is {@link UriResolution#NONE} * @return This object. */ @FluentSetter public Builder uriResolution(UriResolution value) { serializers().forEach(x -> x.uriResolution(value)); return this; } //----------------------------------------------------------------------------------------------------------------- // WriterSerializer Properties //----------------------------------------------------------------------------------------------------------------- /** * WriterSerializer configuration property:  Maximum indentation. * *

* Specifies the maximum indentation level in the serialized document. * *

Notes:
    *
  • This setting does not apply to the RDF serializers. *
* *
Example:
*

* // Create a REST client with JSON serializer that indents a maximum of 20 tabs. * RestClient client = RestClient * .create() * .json() * .ws() // Enable whitespace * .maxIndent(20) * .build(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.WriterSerializer.Builder#maxIndent(int)} *
* * @param value * The new value for this property. *
The default is 100. * @return This object. */ @FluentSetter public Builder maxIndent(int value) { serializers().forEachWS(x -> x.maxIndent(value)); return this; } /** * WriterSerializer configuration property:  Quote character. * *

* Specifies the character to use for quoting attributes and values. * *

Notes:
    *
  • This setting does not apply to the RDF serializers. *
* *
Example:
*

* // Create a REST client with JSON serializer that uses single quotes. * RestClient client = RestClient * .create() * .json() * .quoteChar('\'') * .build(); * * // A bean with a single property * public class MyBean { * public String foo = "bar"; * } * * // Request body will contain: {'foo':'bar'} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.WriterSerializer.Builder#quoteChar(char)} *
* * @param value * The new value for this property. *
The default is '"'. * @return This object. */ @FluentSetter public Builder quoteChar(char value) { serializers().forEachWS(x -> x.quoteChar(value)); return this; } /** * WriterSerializer configuration property:  Quote character. * *

* Specifies to use single quotes for quoting attributes and values. * *

Notes:
    *
  • This setting does not apply to the RDF serializers. *
* *
Example:
*

* // Create a REST client with JSON serializer that uses single quotes. * RestClient client = RestClient * .create() * .json() * .sq() * .build(); * * // A bean with a single property * public class MyBean { * public String foo = "bar"; * } * * // Request body will contain: {'foo':'bar'} * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.WriterSerializer.Builder#quoteChar(char)} *
* * @return This object. */ @FluentSetter public Builder sq() { serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::sq); return this; } /** * WriterSerializer configuration property:  Use whitespace. * *

* When enabled, whitespace is added to the output to improve readability. * *

Example:
*

* // Create a REST client with JSON serializer with whitespace enabled. * RestClient client = RestClient * .create() * .json() * .useWhitespace() * .build(); * * // A bean with a single property * public class MyBean { * public String foo = "bar"; * } * * // Request body will contain: {\n\t"foo": "bar"\n\}\n * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.WriterSerializer.Builder#useWhitespace()} *
* @return This object. */ @FluentSetter public Builder useWhitespace() { serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::useWhitespace); return this; } /** * WriterSerializer configuration property:  Use whitespace. * *

* When enabled, whitespace is added to the output to improve readability. * *

Example:
*

* // Create a REST client with JSON serializer with whitespace enabled. * RestClient client = RestClient * .create() * .json() * .ws() * .build(); * * // A bean with a single property * public class MyBean { * public String foo = "bar"; * } * * // Request body will contain: {\n\t"foo": "bar"\n\}\n * client * .post("http://localhost:10000/foo", new MyBean()) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.serializer.WriterSerializer.Builder#useWhitespace()} *
* * @return This object. */ @FluentSetter public Builder ws() { serializers().forEachWS(org.apache.juneau.serializer.WriterSerializer.Builder::ws); return this; } //----------------------------------------------------------------------------------------------------------------- // OutputStreamSerializer Properties //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- // Parser Properties //----------------------------------------------------------------------------------------------------------------- /** * Parser configuration property:  Debug output lines. * *

* When parse errors occur, this specifies the number of lines of input before and after the * error location to be printed as part of the exception message. * *

Example:
*

* // Create a parser whose exceptions print out 100 lines before and after the parse error location. * RestClient client = RestClient * .create() * .json() * .debug() // Enable debug mode to capture Reader contents as strings. * .debugOuputLines(100) * .build(); * * // Try to parse some bad JSON. * try { * client * .get("/pathToBadJson") * .run() * .getContent().as(Object.class); // Try to parse it. * } catch (RestCallException e) { * System.err.println(e.getMessage()); // Will display 200 lines of the output. * } *

* *
See Also:
    *
  • {@link org.apache.juneau.parser.Parser.Builder#debugOutputLines(int)} *
* * @param value * The new value for this property. *
The default value is 5. * @return This object. */ @FluentSetter public Builder debugOutputLines(int value) { parsers().forEach(x -> x.debugOutputLines(value)); return this; } /** * Parser configuration property:  Strict mode. * *

* When enabled, strict mode for the parser is enabled. * *

* Strict mode can mean different things for different parsers. * *

* * * * * * * * * *
Parser classStrict behavior
All reader-based parsers * When enabled, throws {@link ParseException ParseExceptions} on malformed charset input. * Otherwise, malformed input is ignored. *
{@link JsonParser} * When enabled, throws exceptions on the following invalid JSON syntax: *
    *
  • Unquoted attributes. *
  • Missing attribute values. *
  • Concatenated strings. *
  • Javascript comments. *
  • Numbers and booleans when Strings are expected. *
  • Numbers valid in Java but not JSON (e.g. octal notation, etc...) *
*
* *
Example:
*

* // Create a REST client with JSON parser using strict mode. * RestClient client = RestClient * .create() * .json() * .strict() * .build(); * * // Try to parse some bad JSON. * try { * client * .get("/pathToBadJson") * .run() * .getContent().as(Object.class); // Try to parse it. * } catch (RestCallException e) { * // Handle exception. * } *

* *
See Also:
    *
  • {@link org.apache.juneau.parser.Parser.Builder#strict()} *
* * @return This object. */ @FluentSetter public Builder strict() { parsers().forEach(org.apache.juneau.parser.Parser.Builder::strict); return this; } /** * Parser configuration property:  Trim parsed strings. * *

* When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being added to * the POJO. * *

Example:
*

* // Create a REST client with JSON parser with trim-strings enabled. * RestClient client = RestClient * .create() * .json() * .trimStringsOnRead() * .build(); * * // Try to parse JSON containing {" foo ":" bar "}. * Map<String,String> map = client * .get("/pathToJson") * .run() * .getContent().as(HashMap.class, String.class, String.class); * * // Make sure strings are trimmed. * assertEquals("bar", map.get("foo")); *

* *
See Also:
    *
  • {@link org.apache.juneau.parser.Parser.Builder#trimStrings()} *
* * @return This object. */ @FluentSetter public Builder trimStringsOnRead() { parsers().forEach(org.apache.juneau.parser.Parser.Builder::trimStrings); return this; } //----------------------------------------------------------------------------------------------------------------- // ReaderParser Properties //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- // InputStreamParser Properties //----------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------- // OpenApi Properties //----------------------------------------------------------------------------------------------------------------- /** * OpenApiCommon configuration property:  Default OpenAPI format for HTTP parts. * *

* Specifies the format to use for HTTP parts when not otherwise specified via {@link org.apache.juneau.annotation.Schema#format()} for * the OpenAPI serializer and parser on this client. * *

Example:
*

* // Create a REST client with UON part serialization and parsing. * RestClient client = RestClient * .create() * .oapiFormat(UON) * .build(); * * // Set a header with a value in UON format. * client * .get("/uri") * .header("Foo", "bar baz") // Will be serialized as: 'bar baz' * .run(); *

* *
    *
  • {@link org.apache.juneau.httppart.HttpPartFormat} *
      *
    • {@link org.apache.juneau.httppart.HttpPartFormat#UON UON} - UON notation (e.g. "'foo bar'"). *
    • {@link org.apache.juneau.httppart.HttpPartFormat#INT32 INT32} - Signed 32 bits. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#INT64 INT64} - Signed 64 bits. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#FLOAT FLOAT} - 32-bit floating point number. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#DOUBLE DOUBLE} - 64-bit floating point number. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#BYTE BYTE} - BASE-64 encoded characters. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#BINARY BINARY} - Hexadecimal encoded octets (e.g. "00FF"). *
    • {@link org.apache.juneau.httppart.HttpPartFormat#BINARY_SPACED BINARY_SPACED} - Spaced-separated hexadecimal encoded octets (e.g. "00 FF"). *
    • {@link org.apache.juneau.httppart.HttpPartFormat#DATE DATE} - An RFC3339 full-date. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#DATE_TIME DATE_TIME} - An RFC3339 date-time. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#PASSWORD PASSWORD} - Used to hint UIs the input needs to be obscured. *
    • {@link org.apache.juneau.httppart.HttpPartFormat#NO_FORMAT NO_FORMAT} - (default) Not specified. *
    *
* *
See Also:
    *
  • {@link org.apache.juneau.oapi.OpenApiSerializer.Builder#format(HttpPartFormat)} *
  • {@link org.apache.juneau.oapi.OpenApiParser.Builder#format(HttpPartFormat)} *
* * @param value * The new value for this property. *
The default value is {@link HttpPartFormat#NO_FORMAT}. * @return This object. */ @FluentSetter public Builder oapiFormat(HttpPartFormat value) { serializers().forEach(OpenApiSerializer.Builder.class, x -> x.format(value)); parsers().forEach(OpenApiParser.Builder.class, x -> x.format(value)); partSerializer().builder(OpenApiSerializer.Builder.class).ifPresent(x -> x.format(value)); partParser().builder(OpenApiParser.Builder.class).ifPresent(x -> x.format(value)); return this; } /** * OpenApiCommon configuration property:  Default collection format for HTTP parts. * *

* Specifies the collection format to use for HTTP parts when not otherwise specified via {@link org.apache.juneau.annotation.Schema#collectionFormat()} for the * OpenAPI serializer and parser on this client. * *

Example:
*

* // Create a REST client with CSV format for http parts. * RestClient client = RestClient * .create() * .collectionFormat(CSV) * .build(); * * // An arbitrary data structure. * AList list = AList.of( * "foo", * "bar", * AMap.of( * "baz", AList.of("qux","true","123") * ) * ); * * // Set a header with a comma-separated list. * client * .get("/uri") * .header("Foo", list) // Will be serialized as: foo=bar,baz=qux\,true\,123 * .run(); *

* *
    *
  • {@link HttpPartCollectionFormat} *
      *
    • {@link HttpPartCollectionFormat#CSV CSV} - (default) Comma-separated values (e.g. "foo,bar"). *
    • {@link HttpPartCollectionFormat#SSV SSV} - Space-separated values (e.g. "foo bar"). *
    • {@link HttpPartCollectionFormat#TSV TSV} - Tab-separated values (e.g. "foo\tbar"). *
    • {@link HttpPartCollectionFormat#PIPES PIPES} - Pipe-separated values (e.g. "foo|bar"). *
    • {@link HttpPartCollectionFormat#MULTI MULTI} - Corresponds to multiple parameter instances instead of multiple values for a single instance (e.g. "foo=bar&foo=baz"). *
    • {@link HttpPartCollectionFormat#UONC UONC} - UON collection notation (e.g. "@(foo,bar)"). *
    *
* *
See Also:
    *
  • {@link org.apache.juneau.oapi.OpenApiSerializer.Builder#collectionFormat(HttpPartCollectionFormat)} *
  • {@link org.apache.juneau.oapi.OpenApiParser.Builder#collectionFormat(HttpPartCollectionFormat)} *
* * @param value * The new value for this property. *
The default value is {@link HttpPartCollectionFormat#NO_COLLECTION_FORMAT}. * @return This object. */ @FluentSetter public Builder oapiCollectionFormat(HttpPartCollectionFormat value) { serializers().forEach(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); parsers().forEach(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); partSerializer().builder(OpenApiSerializer.Builder.class, x -> x.collectionFormat(value)); partParser().builder(OpenApiParser.Builder.class, x -> x.collectionFormat(value)); return this; } //----------------------------------------------------------------------------------------------------------------- // UON Properties //----------------------------------------------------------------------------------------------------------------- /** * UonSerializer configuration property:  Parameter format. * *

* Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. * *

* Specifies the format to use for GET parameter keys and values. * *

Example:
*

* // Create a REST client with URL-Encoded serializer that serializes values in plain-text format. * RestClient client = RestClient * .create() * .urlEnc() * .paramFormat(PLAINTEXT) * .build(); * * // An arbitrary data structure. * AMap map = AMap.of( * "foo", "bar", * "baz", new String[]{"qux", "true", "123"} * ); * * // Request body will be serialized as: foo=bar,baz=qux,true,123 * client * .post("/uri", map) * .run(); *

* *
    *
  • {@link ParamFormat#UON} (default) - Use UON notation for parameters. *
  • {@link ParamFormat#PLAINTEXT} - Use plain text for parameters. *
* *
See Also:
    *
  • {@link org.apache.juneau.uon.UonSerializer.Builder#paramFormat(ParamFormat)} *
* * @param value The new value for this property. * @return This object. */ @FluentSetter public Builder paramFormat(ParamFormat value) { serializers().forEach(UonSerializer.Builder.class, x -> x.paramFormat(value)); return this; } /** * UonSerializer configuration property:  Parameter format. * *

* Specifies the format of parameters when using the {@link UrlEncodingSerializer} to serialize Form Posts. * *

* Specifies plaintext as the format to use for GET parameter keys and values. * *

Example:
*

* // Create a REST client with URL-Encoded serializer that serializes values in plain-text format. * RestClient client = RestClient * .create() * .urlEnc() * .build(); * * // An arbitrary data structure. * AMap map = AMap.of( * "foo", "bar", * "baz", new String[]{"qux", "true", "123"} * ); * * // Request body will be serialized as: foo=bar,baz=qux,true,123 * client * .post("/uri", map) * .run(); *

* *
See Also:
    *
  • {@link org.apache.juneau.uon.UonSerializer.Builder#paramFormatPlain()} *
* * @return This object. */ @FluentSetter public Builder paramFormatPlain() { serializers().forEach(UonSerializer.Builder.class, org.apache.juneau.uon.UonSerializer.Builder::paramFormatPlain); return this; } // @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder annotations(Annotation...values) { super.annotations(values); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder apply(AnnotationWorkList work) { super.apply(work); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(java.lang.Class...fromClasses) { super.applyAnnotations(fromClasses); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(Method...fromMethods) { super.applyAnnotations(fromMethods); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder cache(Cache value) { super.cache(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder impl(Context value) { super.impl(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder type(Class value) { super.type(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanClassVisibility(Visibility value) { super.beanClassVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanConstructorVisibility(Visibility value) { super.beanConstructorVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanContext(BeanContext value) { super.beanContext(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanContext(BeanContext.Builder value) { super.beanContext(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanDictionary(java.lang.Class...values) { super.beanDictionary(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanFieldVisibility(Visibility value) { super.beanFieldVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanInterceptor(Class on, Class> value) { super.beanInterceptor(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanMethodVisibility(Visibility value) { super.beanMethodVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(Map values) { super.beanProperties(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(Class beanClass, String properties) { super.beanProperties(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(String beanClassName, String properties) { super.beanProperties(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(Map values) { super.beanPropertiesExcludes(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(Class beanClass, String properties) { super.beanPropertiesExcludes(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(String beanClassName, String properties) { super.beanPropertiesExcludes(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(Map values) { super.beanPropertiesReadOnly(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(Class beanClass, String properties) { super.beanPropertiesReadOnly(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(String beanClassName, String properties) { super.beanPropertiesReadOnly(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(Map values) { super.beanPropertiesWriteOnly(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(Class beanClass, String properties) { super.beanPropertiesWriteOnly(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { super.beanPropertiesWriteOnly(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireDefaultConstructor() { super.beansRequireDefaultConstructor(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireSerializable() { super.beansRequireSerializable(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireSettersForGetters() { super.beansRequireSettersForGetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder dictionaryOn(Class on, java.lang.Class...values) { super.dictionaryOn(on, values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableBeansRequireSomeProperties() { super.disableBeansRequireSomeProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreMissingSetters() { super.disableIgnoreMissingSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreTransientFields() { super.disableIgnoreTransientFields(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreUnknownNullBeanProperties() { super.disableIgnoreUnknownNullBeanProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableInterfaceProxies() { super.disableInterfaceProxies(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder findFluentSetters() { super.findFluentSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder findFluentSetters(Class on) { super.findFluentSetters(on); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreInvocationExceptionsOnGetters() { super.ignoreInvocationExceptionsOnGetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreInvocationExceptionsOnSetters() { super.ignoreInvocationExceptionsOnSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreUnknownBeanProperties() { super.ignoreUnknownBeanProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreUnknownEnumValues() { super.ignoreUnknownEnumValues(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder implClass(Class interfaceClass, Class implClass) { super.implClass(interfaceClass, implClass); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder implClasses(Map,Class> values) { super.implClasses(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder interfaceClass(Class on, Class value) { super.interfaceClass(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder interfaces(java.lang.Class...value) { super.interfaces(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder locale(Locale value) { super.locale(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder notBeanClasses(java.lang.Class...values) { super.notBeanClasses(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder notBeanPackages(String...values) { super.notBeanPackages(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder propertyNamer(Class value) { super.propertyNamer(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder propertyNamer(Class on, Class value) { super.propertyNamer(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder sortProperties() { super.sortProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder sortProperties(java.lang.Class...on) { super.sortProperties(on); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder stopClass(Class on, Class value) { super.stopClass(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swap(Class normalClass, Class swappedClass, ThrowingFunction swapFunction) { super.swap(normalClass, swappedClass, swapFunction); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swap(Class normalClass, Class swappedClass, ThrowingFunction swapFunction, ThrowingFunction unswapFunction) { super.swap(normalClass, swappedClass, swapFunction, unswapFunction); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swaps(java.lang.Class...values) { super.swaps(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder timeZone(TimeZone value) { super.timeZone(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typeName(Class on, String value) { super.typeName(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typePropertyName(String value) { super.typePropertyName(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typePropertyName(Class on, String value) { super.typePropertyName(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder useEnumNames() { super.useEnumNames(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder useJavaBeanIntrospector() { super.useJavaBeanIntrospector(); return this; } // //------------------------------------------------------------------------------------------------ // Passthrough methods for HttpClientBuilder. //------------------------------------------------------------------------------------------------ /** * Disables automatic redirect handling. * * @return This object. * @see HttpClientBuilder#disableRedirectHandling() */ @FluentSetter public Builder disableRedirectHandling() { httpClientBuilder().disableRedirectHandling(); return this; } /** * Assigns {@link RedirectStrategy} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #disableRedirectHandling()} method. *
* * @param redirectStrategy New property value. * @return This object. * @see HttpClientBuilder#setRedirectStrategy(RedirectStrategy) */ @FluentSetter public Builder redirectStrategy(RedirectStrategy redirectStrategy) { httpClientBuilder().setRedirectStrategy(redirectStrategy); return this; } /** * Assigns default {@link CookieSpec} registry which will be used for request execution if not explicitly set in the client execution context. * * @param cookieSpecRegistry New property value. * @return This object. * @see HttpClientBuilder#setDefaultCookieSpecRegistry(Lookup) */ @FluentSetter public Builder defaultCookieSpecRegistry(Lookup cookieSpecRegistry) { httpClientBuilder().setDefaultCookieSpecRegistry(cookieSpecRegistry); return this; } /** * Assigns {@link HttpRequestExecutor} instance. * * @param requestExec New property value. * @return This object. * @see HttpClientBuilder#setRequestExecutor(HttpRequestExecutor) */ @FluentSetter public Builder requestExecutor(HttpRequestExecutor requestExec) { httpClientBuilder().setRequestExecutor(requestExec); return this; } /** * Assigns {@link javax.net.ssl.HostnameVerifier} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} * and the {@link #sslSocketFactory(LayeredConnectionSocketFactory)} methods. *
* * @param hostnameVerifier New property value. * @return This object. * @see HttpClientBuilder#setSSLHostnameVerifier(HostnameVerifier) */ @FluentSetter public Builder sslHostnameVerifier(HostnameVerifier hostnameVerifier) { httpClientBuilder().setSSLHostnameVerifier(hostnameVerifier); return this; } /** * Assigns file containing public suffix matcher. * *
Notes:
    *
  • Instances of this class can be created with {@link PublicSuffixMatcherLoader}. *
* * @param publicSuffixMatcher New property value. * @return This object. * @see HttpClientBuilder#setPublicSuffixMatcher(PublicSuffixMatcher) */ @FluentSetter public Builder publicSuffixMatcher(PublicSuffixMatcher publicSuffixMatcher) { httpClientBuilder().setPublicSuffixMatcher(publicSuffixMatcher); return this; } /** * Assigns {@link SSLContext} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} * and the {@link #sslSocketFactory(LayeredConnectionSocketFactory)} methods. *
* * @param sslContext New property value. * @return This object. * @see HttpClientBuilder#setSSLContext(SSLContext) */ @FluentSetter public Builder sslContext(SSLContext sslContext) { httpClientBuilder().setSSLContext(sslContext); return this; } /** * Assigns {@link LayeredConnectionSocketFactory} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param sslSocketFactory New property value. * @return This object. * @see HttpClientBuilder#setSSLSocketFactory(LayeredConnectionSocketFactory) */ @FluentSetter public Builder sslSocketFactory(LayeredConnectionSocketFactory sslSocketFactory) { httpClientBuilder().setSSLSocketFactory(sslSocketFactory); return this; } /** * Assigns maximum total connection value. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param maxConnTotal New property value. * @return This object. * @see HttpClientBuilder#setMaxConnTotal(int) */ @FluentSetter public Builder maxConnTotal(int maxConnTotal) { httpClientBuilder().setMaxConnTotal(maxConnTotal); return this; } /** * Assigns maximum connection per route value. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param maxConnPerRoute New property value. * @return This object. * @see HttpClientBuilder#setMaxConnPerRoute(int) */ @FluentSetter public Builder maxConnPerRoute(int maxConnPerRoute) { httpClientBuilder().setMaxConnPerRoute(maxConnPerRoute); return this; } /** * Assigns default {@link SocketConfig}. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param config New property value. * @return This object. * @see HttpClientBuilder#setDefaultSocketConfig(SocketConfig) */ @FluentSetter public Builder defaultSocketConfig(SocketConfig config) { httpClientBuilder().setDefaultSocketConfig(config); return this; } /** * Assigns default {@link ConnectionConfig}. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param config New property value. * @return This object. * @see HttpClientBuilder#setDefaultConnectionConfig(ConnectionConfig) */ @FluentSetter public Builder defaultConnectionConfig(ConnectionConfig config) { httpClientBuilder().setDefaultConnectionConfig(config); return this; } /** * Sets maximum time to live for persistent connections. * *
Notes:
    *
  • This value can be overridden by the {@link #connectionManager(HttpClientConnectionManager)} method. *
* * @param connTimeToLive New property value. * @param connTimeToLiveTimeUnit New property value. * @return This object. * @see HttpClientBuilder#setConnectionTimeToLive(long,TimeUnit) */ @FluentSetter public Builder connectionTimeToLive(long connTimeToLive, TimeUnit connTimeToLiveTimeUnit) { httpClientBuilder().setConnectionTimeToLive(connTimeToLive, connTimeToLiveTimeUnit); return this; } /** * Assigns {@link ConnectionReuseStrategy} instance. * * @param reuseStrategy New property value. * @return This object. * @see HttpClientBuilder#setConnectionReuseStrategy(ConnectionReuseStrategy) */ @FluentSetter public Builder connectionReuseStrategy(ConnectionReuseStrategy reuseStrategy) { httpClientBuilder().setConnectionReuseStrategy(reuseStrategy); return this; } /** * Assigns {@link ConnectionKeepAliveStrategy} instance. * * @param keepAliveStrategy New property value. * @return This object. * @see HttpClientBuilder#setKeepAliveStrategy(ConnectionKeepAliveStrategy) */ @FluentSetter public Builder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { httpClientBuilder().setKeepAliveStrategy(keepAliveStrategy); return this; } /** * Assigns {@link AuthenticationStrategy} instance for target host authentication. * * @param targetAuthStrategy New property value. * @return This object. * @see HttpClientBuilder#setTargetAuthenticationStrategy(AuthenticationStrategy) */ @FluentSetter public Builder targetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy) { httpClientBuilder().setTargetAuthenticationStrategy(targetAuthStrategy); return this; } /** * Assigns {@link AuthenticationStrategy} instance for proxy authentication. * * @param proxyAuthStrategy New property value. * @return This object. * @see HttpClientBuilder#setProxyAuthenticationStrategy(AuthenticationStrategy) */ @FluentSetter public Builder proxyAuthenticationStrategy(AuthenticationStrategy proxyAuthStrategy) { httpClientBuilder().setProxyAuthenticationStrategy(proxyAuthStrategy); return this; } /** * Assigns {@link UserTokenHandler} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #disableConnectionState()} method. *
* * @param userTokenHandler New property value. * @return This object. * @see HttpClientBuilder#setUserTokenHandler(UserTokenHandler) */ @FluentSetter public Builder userTokenHandler(UserTokenHandler userTokenHandler) { httpClientBuilder().setUserTokenHandler(userTokenHandler); return this; } /** * Disables connection state tracking. * * @return This object. * @see HttpClientBuilder#disableConnectionState() */ @FluentSetter public Builder disableConnectionState() { httpClientBuilder().disableConnectionState(); return this; } /** * Assigns {@link SchemePortResolver} instance. * * @param schemePortResolver New property value. * @return This object. * @see HttpClientBuilder#setSchemePortResolver(SchemePortResolver) */ @FluentSetter public Builder schemePortResolver(SchemePortResolver schemePortResolver) { httpClientBuilder().setSchemePortResolver(schemePortResolver); return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @param itcp New property value. * @return This object. * @see HttpClientBuilder#addInterceptorFirst(HttpResponseInterceptor) */ @FluentSetter public Builder addInterceptorFirst(HttpResponseInterceptor itcp) { httpClientBuilder().addInterceptorFirst(itcp); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @param itcp New property value. * @return This object. * @see HttpClientBuilder#addInterceptorLast(HttpResponseInterceptor) */ @FluentSetter public Builder addInterceptorLast(HttpResponseInterceptor itcp) { httpClientBuilder().addInterceptorLast(itcp); return this; } /** * Adds this protocol interceptor to the head of the protocol processing list. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @param itcp New property value. * @return This object. * @see HttpClientBuilder#addInterceptorFirst(HttpRequestInterceptor) */ @FluentSetter public Builder addInterceptorFirst(HttpRequestInterceptor itcp) { httpClientBuilder().addInterceptorFirst(itcp); return this; } /** * Adds this protocol interceptor to the tail of the protocol processing list. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @param itcp New property value. * @return This object. * @see HttpClientBuilder#addInterceptorLast(HttpRequestInterceptor) */ @FluentSetter public Builder addInterceptorLast(HttpRequestInterceptor itcp) { httpClientBuilder().addInterceptorLast(itcp); return this; } /** * Disables state (cookie) management. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @return This object. * @see HttpClientBuilder#disableCookieManagement() */ @FluentSetter public Builder disableCookieManagement() { httpClientBuilder().disableCookieManagement(); return this; } /** * Disables automatic content decompression. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @return This object. * @see HttpClientBuilder#disableContentCompression() */ @FluentSetter public Builder disableContentCompression() { httpClientBuilder().disableContentCompression(); return this; } /** * Disables authentication scheme caching. * *
Notes:
    *
  • This value can be overridden by the {@link #httpProcessor(HttpProcessor)} method. *
* * @return This object. * @see HttpClientBuilder#disableAuthCaching() */ @FluentSetter public Builder disableAuthCaching() { httpClientBuilder().disableAuthCaching(); return this; } /** * Assigns {@link HttpProcessor} instance. * * @param httpprocessor New property value. * @return This object. * @see HttpClientBuilder#setHttpProcessor(HttpProcessor) */ @FluentSetter public Builder httpProcessor(HttpProcessor httpprocessor) { httpClientBuilder().setHttpProcessor(httpprocessor); return this; } /** * Assigns {@link HttpRequestRetryHandler} instance. * *
Notes:
    *
  • This value can be overridden by the {@link #disableAutomaticRetries()} method. *
* * @param retryHandler New property value. * @return This object. * @see HttpClientBuilder#setRetryHandler(HttpRequestRetryHandler) */ @FluentSetter public Builder retryHandler(HttpRequestRetryHandler retryHandler) { httpClientBuilder().setRetryHandler(retryHandler); return this; } /** * Disables automatic request recovery and re-execution. * * @return This object. * @see HttpClientBuilder#disableAutomaticRetries() */ @FluentSetter public Builder disableAutomaticRetries() { httpClientBuilder().disableAutomaticRetries(); return this; } /** * Assigns default proxy value. * *
Notes:
    *
  • This value can be overridden by the {@link #routePlanner(HttpRoutePlanner)} method. *
* * @param proxy New property value. * @return This object. * @see HttpClientBuilder#setProxy(HttpHost) */ @FluentSetter public Builder proxy(HttpHost proxy) { httpClientBuilder().setProxy(proxy); return this; } /** * Assigns {@link HttpRoutePlanner} instance. * * @param routePlanner New property value. * @return This object. * @see HttpClientBuilder#setRoutePlanner(HttpRoutePlanner) */ @FluentSetter public Builder routePlanner(HttpRoutePlanner routePlanner) { httpClientBuilder().setRoutePlanner(routePlanner); return this; } /** * Assigns {@link ConnectionBackoffStrategy} instance. * * @param connectionBackoffStrategy New property value. * @return This object. * @see HttpClientBuilder#setConnectionBackoffStrategy(ConnectionBackoffStrategy) */ @FluentSetter public Builder connectionBackoffStrategy(ConnectionBackoffStrategy connectionBackoffStrategy) { httpClientBuilder().setConnectionBackoffStrategy(connectionBackoffStrategy); return this; } /** * Assigns {@link BackoffManager} instance. * * @param backoffManager New property value. * @return This object. * @see HttpClientBuilder#setBackoffManager(BackoffManager) */ @FluentSetter public Builder backoffManager(BackoffManager backoffManager) { httpClientBuilder().setBackoffManager(backoffManager); return this; } /** * Assigns {@link ServiceUnavailableRetryStrategy} instance. * * @param serviceUnavailStrategy New property value. * @return This object. * @see HttpClientBuilder#setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy) */ @FluentSetter public Builder serviceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy serviceUnavailStrategy) { httpClientBuilder().setServiceUnavailableRetryStrategy(serviceUnavailStrategy); return this; } /** * Assigns default {@link CookieStore} instance which will be used for request execution if not explicitly set in the client execution context. * * @param cookieStore New property value. * @return This object. * @see HttpClientBuilder#setDefaultCookieStore(CookieStore) */ @FluentSetter public Builder defaultCookieStore(CookieStore cookieStore) { httpClientBuilder().setDefaultCookieStore(cookieStore); return this; } /** * Assigns default {@link CredentialsProvider} instance which will be used for request execution if not explicitly set in the client execution context. * * @param credentialsProvider New property value. * @return This object. * @see HttpClientBuilder#setDefaultCredentialsProvider(CredentialsProvider) */ @FluentSetter public Builder defaultCredentialsProvider(CredentialsProvider credentialsProvider) { httpClientBuilder().setDefaultCredentialsProvider(credentialsProvider); return this; } /** * Assigns default {@link org.apache.http.auth.AuthScheme} registry which will be used for request execution if not explicitly set in the client execution context. * * @param authSchemeRegistry New property value. * @return This object. * @see HttpClientBuilder#setDefaultAuthSchemeRegistry(Lookup) */ @FluentSetter public Builder defaultAuthSchemeRegistry(Lookup authSchemeRegistry) { httpClientBuilder().setDefaultAuthSchemeRegistry(authSchemeRegistry); return this; } /** * Assigns a map of {@link org.apache.http.client.entity.InputStreamFactory InputStreamFactories} to be used for automatic content decompression. * * @param contentDecoderMap New property value. * @return This object. * @see HttpClientBuilder#setContentDecoderRegistry(Map) */ @FluentSetter public Builder contentDecoderRegistry(Map contentDecoderMap) { httpClientBuilder().setContentDecoderRegistry(contentDecoderMap); return this; } /** * Assigns default {@link RequestConfig} instance which will be used for request execution if not explicitly set in the client execution context. * * @param config New property value. * @return This object. * @see HttpClientBuilder#setDefaultRequestConfig(RequestConfig) */ @FluentSetter public Builder defaultRequestConfig(RequestConfig config) { httpClientBuilder().setDefaultRequestConfig(config); return this; } /** * Use system properties when creating and configuring default implementations. * * @return This object. * @see HttpClientBuilder#useSystemProperties() */ @FluentSetter public Builder useSystemProperties() { httpClientBuilder().useSystemProperties(); return this; } /** * Makes this instance of {@link HttpClient} proactively evict expired connections from the connection pool using a background thread. * *
Notes:
    *
  • One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. *
  • This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. *
  • This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. *
* * @return This object. * @see HttpClientBuilder#evictExpiredConnections() */ @FluentSetter public Builder evictExpiredConnections() { httpClientBuilder().evictExpiredConnections(); return this; } /** * Makes this instance of {@link HttpClient} proactively evict idle connections from the connection pool using a background thread. * *
Notes:
    *
  • One MUST explicitly close HttpClient with {@link CloseableHttpClient#close()} in order to stop and release the background thread. *
  • This method has no effect if the instance of {@link HttpClient} is configured to use a shared connection manager. *
  • This method may not be used when the instance of {@link HttpClient} is created inside an EJB container. *
* * @param maxIdleTime New property value. * @param maxIdleTimeUnit New property value. * @return This object. * @see HttpClientBuilder#evictIdleConnections(long,TimeUnit) */ @FluentSetter public Builder evictIdleConnections(long maxIdleTime, TimeUnit maxIdleTimeUnit) { httpClientBuilder().evictIdleConnections(maxIdleTime, maxIdleTimeUnit); return this; } } //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- final HeaderList headerData; final PartList queryData, formData, pathData; final CloseableHttpClient httpClient; private final HttpClientConnectionManager connectionManager; private final boolean keepHttpClientOpen, detectLeaks, skipEmptyHeaderData, skipEmptyQueryData, skipEmptyFormData; private final BeanStore beanStore; private final UrlEncodingSerializer urlEncodingSerializer; // Used for form posts only. final HttpPartSerializer partSerializer; final HttpPartParser partParser; private final RestCallHandler callHandler; private final String rootUrl; private volatile boolean isClosed = false; private final StackTraceElement[] creationStack; private final Logger logger; final DetailLevel logRequests; final BiPredicate logRequestsPredicate; final Level logRequestsLevel; final boolean ignoreErrors; private final boolean logToConsole; private final PrintStream console; private StackTraceElement[] closedStack; private static final ConcurrentHashMap,Context> requestContexts = new ConcurrentHashMap<>(); // These are read directly by RestCall. final SerializerSet serializers; final ParserSet parsers; Predicate errorCodes; final RestCallInterceptor[] interceptors; private final Map, HttpPartParser> partParsers = new ConcurrentHashMap<>(); private final Map, HttpPartSerializer> partSerializers = new ConcurrentHashMap<>(); // This is lazy-created. private volatile ExecutorService executorService; private final boolean executorServiceShutdownOnClose; private static final BiPredicate LOG_REQUESTS_PREDICATE_DEFAULT = (req,res) -> true; /** * Constructor. * * @param builder The builder for this client. */ public RestClient(Builder builder) { super(builder); beanStore = builder.beanStore .addBean(RestClient.class, this); httpClient = builder.getHttpClient(); headerData = builder.headers().copy(); queryData = builder.queryData().copy(); formData = builder.formData().copy(); pathData = builder.pathData().copy(); callHandler = builder.callHandler().run(); skipEmptyHeaderData = builder.skipEmptyHeaderData; skipEmptyQueryData = builder.skipEmptyQueryData; skipEmptyFormData = builder.skipEmptyFormData; rootUrl = builder.rootUrl; errorCodes = builder.errorCodes; connectionManager = builder.connectionManager; console = builder.console != null ? builder.console : System.err; executorService = builder.executorService; executorServiceShutdownOnClose = builder.executorServiceShutdownOnClose; ignoreErrors = builder.ignoreErrors; keepHttpClientOpen = builder.keepHttpClientOpen; detectLeaks = builder.detectLeaks; logger = builder.logger != null ? builder.logger : Logger.getLogger(RestClient.class.getName()); logToConsole = builder.logToConsole || isDebug(); logRequests = builder.logRequests != null ? builder.logRequests : isDebug() ? DetailLevel.FULL : DetailLevel.NONE; logRequestsLevel = builder.logRequestsLevel != null ? builder.logRequestsLevel : isDebug() ? Level.WARNING : Level.OFF; logRequestsPredicate = builder.logRequestsPredicate != null ? builder.logRequestsPredicate : LOG_REQUESTS_PREDICATE_DEFAULT; interceptors = builder.interceptors != null ? builder.interceptors.toArray(EMPTY_REST_CALL_INTERCEPTORS) : EMPTY_REST_CALL_INTERCEPTORS; serializers = builder.serializers().build(); parsers = builder.parsers().build(); partSerializer = builder.partSerializer().create(); partParser = builder.partParser().create(); urlEncodingSerializer = builder.urlEncodingSerializer().build(); creationStack = isDebug() ? Thread.currentThread().getStackTrace() : null; init(); } @Override /* Context */ public Builder copy() { throw new NoSuchMethodError("Not implemented."); } /** * Perform optional initialization on builder before it is used. * *

* Default behavior is a no-op. * * @param builder The builder to initialize. */ protected void init(RestClient.Builder builder) {} /** * Gets called add the end of the constructor call to perform any post-initialization. */ protected void init() { } /** * Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}. * *

* 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. * *

* Subclasses can override this method to provide specialized handling. * *

* The behavior of this method can also be modified by specifying a different {@link RestCallHandler}. * *

See Also:
    *
  • {@link Builder#callHandler()} *
* * @param target The target host for the request. *
Implementations may accept null 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 null to use the default context. * @return * The response to the request. *
This is always a final response, never an intermediate response with an 1xx status code. *
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. */ protected HttpResponse run(HttpHost target, HttpRequest request, HttpContext context) throws ClientProtocolException, IOException { return callHandler.run(target, request, context); } /** * Perform a GET request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a GET 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, NO_BODY)); } /** * Perform a PUT request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param body * The object to serialize and transmit to the URI as the body of the request. * Can be of the following types: *
    *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the * {@link RestClient}. *
  • * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link PartList} - Converted to a URL-encoded FORM post. *
  • * {@link Supplier} - A supplier of anything on this list. *
* @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 PUT request against the specified URI using a plain text body bypassing the serializer. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, ContentType contentType) throws RestCallException { return request(op("PUT", uri, stringBody(body))).header(contentType); } /** * Same as {@link #put(Object, Object)} but don't specify the input yet. * *

* You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} * to set the contents on the result object. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *

    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a POST request against the specified URI. * *
Notes:
    *
  • Use {@link #formPost(Object, Object)} for application/x-www-form-urlencoded form posts. *
* * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param body * The object to serialize and transmit to the URI as the body of the request. * Can be of the following types: *
    *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the * {@link RestClient}. *
  • * {@link HttpEntity} / {@link HttpResource} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link PartList} - Converted to a URL-encoded FORM post. *
  • * {@link Supplier} - A supplier of anything on this list. *
* @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 POST request against the specified URI as a plain text body bypassing the serializer. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, ContentType contentType) throws RestCallException { return request(op("POST", uri, stringBody(body))).header(contentType); } /** * Same as {@link #post(Object, Object)} but don't specify the input yet. * *

* You must call either {@link RestRequest#content(Object)} or {@link RestRequest#formData(String, Object)} to set the * contents on the result object. * *

Notes:
    *
  • Use {@link #formPost(Object, Object)} for application/x-www-form-urlencoded form posts. *
* * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a DELETE request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform an OPTIONS request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a HEAD request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a POST request with a content type of application/x-www-form-urlencoded * against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param body * The object to serialize and transmit to the URI as the body of the request. *
    *
  • {@link NameValuePair} - URL-encoded as a single name-value pair. *
  • {@link NameValuePair} array - URL-encoded as name value pairs. *
  • {@link PartList} - URL-encoded as name value pairs. *
  • {@link Reader}/{@link InputStream}- Streamed directly and Content-Type set to "application/x-www-form-urlencoded" *
  • {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. *
  • {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • {@link Object} - Converted to a {@link SerializedEntity} using {@link UrlEncodingSerializer} to serialize. *
  • {@link Supplier} - A supplier of anything on this list. *
* @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, NO_BODY)); try { if (body instanceof Supplier) body = ((Supplier)body).get(); if (body instanceof NameValuePair) return req.content(new UrlEncodedFormEntity(alist((NameValuePair)body))); if (body instanceof NameValuePair[]) return req.content(new UrlEncodedFormEntity(alist((NameValuePair[])body))); if (body instanceof PartList) return req.content(new UrlEncodedFormEntity(((PartList)body))); 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.header(ContentType.APPLICATION_FORM_URLENCODED); return req.content(e); } if (body instanceof Reader || body instanceof InputStream) return req.header(ContentType.APPLICATION_FORM_URLENCODED).content(body); return req.content(serializedEntity(body, urlEncodingSerializer, null)); } 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. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a POST request with a content type of application/x-www-form-urlencoded * against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param parameters * The parameters of the form post. *
The parameters represent name/value pairs and must be an even number of arguments. *
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, String...parameters) throws RestCallException { return formPost(uri, partList(parameters)); } /** * Perform a PATCH request against the specified URI. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param body * The object to serialize and transmit to the URI as the body of the request. * Can be of the following types: *
    *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. *
  • * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the * {@link RestClient}. *
  • * {@link PartList} - Converted to a URL-encoded FORM post. *
  • * {@link Supplier} - A supplier of anything on this list. *
* @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 PATCH request against the specified URI as a plain text body bypassing the serializer. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, ContentType contentType) throws RestCallException { return request(op("PATCH", uri, stringBody(body))).header(contentType); } /** * Same as {@link #patch(Object, Object)} but don't specify the input yet. * *

* You must call {@link RestRequest#content(Object)} to set the contents on the result object. * * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *

    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Performs a REST call where the entire call is specified in a simple string. * *

* 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. * *

* The call string can be any of the following formats: *

    *
  • * "[method] [uri]" - e.g. "GET http://localhost/callback" *
  • * "[method] [uri] [payload]" - e.g. "POST http://localhost/callback some text payload" *
  • * "[method] [headers] [uri] [payload]" - e.g. "POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}" *
*

* 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) JsonMap.ofJson(headers).forEach((k,v) -> req.header(stringHeader(k, stringify(v)))); if (isNotEmpty(content)) req.contentString(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. *
Can be any of the following types: *

    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @param body * The HTTP body content. * Can be of the following types: *
    *
  • * {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. *
  • * {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. *
  • * {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. *
  • * {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. *
  • * {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the * {@link RestClient}. *
  • * {@link PartList} - Converted to a URL-encoded FORM post. *
  • * {@link Supplier} - A supplier of anything on this list. *
* 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. *
Can be any of the following types: *
    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY)); } /** * Perform a generic REST call. * *

* 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. "GET", "OPTIONS"). * @param uri * The URI of the remote REST resource. *
Can be any of the following types: *

    *
  • {@link URIBuilder} *
  • {@link URI} *
  • {@link URL} *
  • {@link String} *
  • {@link Object} - Converted to String using toString() *
* @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, NO_BODY).hasContent(hasBody)); } /** * Perform an arbitrary request against the specified URI. * *

* All requests feed through this method so it can be used to intercept request creations and make modifications * (such as add headers). * * @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. */ protected 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(), rootUrl), op.getMethod(), op.hasContent()); onCallInit(req); req.content(op.getContent()); return req; } /** * Creates a {@link RestRequest} object from the specified {@link HttpRequest} object. * *

* 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. * *

* 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. * *

* The URI to the REST interface is based on the following values: *

    *
  • The {@link Remote#path() @Remote(path)} annotation on the interface (remote-path). *
  • The {@link Builder#rootUrl(Object) rootUrl} on the client (root-url). *
  • The fully-qualified class name of the interface (class-name). *
* *

* The URI calculation is as follows: *

    *
  • remote-path - If remote path is absolute. *
  • root-uri/remote-path - If remote path is relative and root-uri has been specified. *
  • root-uri/class-name - If remote path is not specified. *
* *

* If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. * *

Examples:
*

* package org.apache.foo; * * @RemoteResource(path="http://hostname/resturi/myinterface1") * public interface MyInterface1 { ... } * * @RemoteResource(path="/myinterface2") * public interface MyInterface2 { ... } * * public interface MyInterface3 { ... } * * // Resolves to "http://localhost/resturi/myinterface1" * MyInterface1 interface1 = RestClient * .create() * .build() * .getRemote(MyInterface1.class); * * // Resolves to "http://hostname/resturi/myinterface2" * MyInterface2 interface2 = RestClient * .create() * .rootUrl("http://hostname/resturi") * .build() * .getRemote(MyInterface2.class); * * // Resolves to "http://hostname/resturi/org.apache.foo.MyInterface3" * MyInterface3 interface3 = RestClient * .create() * .rootUrl("http://hostname/resturi") * .build() * .getRemote(MyInterface3.class); *

* *
Notes:
    *
  • * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying * pooling client connection manager. *
* *
See Also:
* * @param The interface to create a proxy for. * @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 getRemote(Class interfaceClass) { return getRemote(interfaceClass, null); } /** * Same as {@link #getRemote(Class)} except explicitly specifies the URI of the REST interface. * *
See Also:
* * @param The interface to create a proxy for. * @param interfaceClass The interface to create a proxy for. * @param rootUrl The URI of the REST interface. * @return The new proxy interface. */ public T getRemote(Class interfaceClass, Object rootUrl) { return getRemote(interfaceClass, rootUrl, null, null); } /** * Same as {@link #getRemote(Class, Object)} but allows you to override the serializer and parser used. * *
See Also:
* @param The interface to create a proxy for. * @param interfaceClass The interface to create a proxy for. * @param rootUrl 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 getRemote(final Class interfaceClass, Object rootUrl, final Serializer serializer, final Parser parser) { if (rootUrl == null) rootUrl = this.rootUrl; final String restUrl2 = trimSlashes(emptyIfNull(rootUrl)); 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(); RestRequest rc = request(httpMethod, uri, hasContent(httpMethod)); rc.serializer(serializer); rc.parser(parser); rm.getHeaders().forEach(x -> rc.header(x)); rom.forEachPathArg(a -> rc.pathArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer().orElse(partSerializer))); rom.forEachQueryArg(a -> rc.queryArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty())); rom.forEachFormDataArg(a -> rc.formDataArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty())); rom.forEachHeaderArg(a -> rc.headerArg(a.getName(), args[a.getIndex()], a.getSchema(), a.getSerializer().orElse(partSerializer), a.isSkipIfEmpty())); RemoteOperationArg ba = rom.getContentArg(); if (ba != null) rc.content(args[ba.getIndex()], ba.getSchema()); rom.forEachRequestArg(rmba -> { RequestBeanMeta rbm = rmba.getMeta(); Object bean = args[rmba.getIndex()]; if (bean != null) { for (RequestBeanPropertyMeta p : rbm.getProperties()) { Object val = safeSupplier(()->p.getGetter().invoke(bean)); HttpPartType pt = p.getPartType(); String pn = p.getPartName(); HttpPartSchema schema = p.getSchema(); if (pt == PATH) rc.pathArg(pn, val, schema, p.getSerializer().orElse(partSerializer)); else if (val != null) { if (pt == QUERY) rc.queryArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); else if (pt == FORMDATA) rc.formDataArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); else if (pt == HEADER) rc.headerArg(pn, val, schema, p.getSerializer().orElse(partSerializer), schema.isSkipIfEmpty()); else /* (pt == HttpPartType.BODY) */ rc.content(val, schema); } } } }); RemoteOperationReturn ror = rom.getReturns(); if (ror.isFuture()) { return getExecutorService().submit(() -> { try { return executeRemote(interfaceClass, rc, method, rom); } catch (Exception e) { throw e; } catch (Throwable e) { throw asRuntimeException(e); } }); } else if (ror.isCompletableFuture()) { CompletableFuture cf = new CompletableFuture<>(); getExecutorService().submit(() -> { try { cf.complete(executeRemote(interfaceClass, rc, method, rom)); } catch (Throwable e) { cf.completeExceptionally(e); } return null; }); return cf; } return executeRemote(interfaceClass, rc, method, rom); } }); } 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); rom.forEachException(x -> rc.rethrow(x)); 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.getContent().as(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 asRuntimeException(e); } } /** * Create a new proxy interface against an RRPC-style service. * *

* Remote interfaces are interfaces exposed on the server side using either the RrpcServlet * or RRPC REST methods. * *

* The URI to the REST interface is based on the following values: *

    *
  • The {@link Remote#path() @Remote(path)} annotation on the interface (remote-path). *
  • The {@link Builder#rootUrl(Object) rootUrl} on the client (root-url). *
  • The fully-qualified class name of the interface (class-name). *
* *

* The URI calculation is as follows: *

    *
  • remote-path - If remote path is absolute. *
  • root-url/remote-path - If remote path is relative and root-url has been specified. *
  • root-url/class-name - If remote path is not specified. *
* *

* If the information is not available to resolve to an absolute URI, a {@link RemoteMetadataException} is thrown. * *

Notes:
    *
  • * If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying * pooling client connection manager. *
* *
See Also:
* * @param The interface to create a proxy for. * @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 getRrpcInterface(final Class interfaceClass) { return getRrpcInterface(interfaceClass, null); } /** * Same as {@link #getRrpcInterface(Class)} except explicitly specifies the URI of the REST interface. * *
See Also:
* * @param The interface to create a proxy for. * @param interfaceClass The interface to create a proxy for. * @param uri The URI of the REST interface. * @return The new proxy interface. */ public T getRrpcInterface(final Class 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. * *
See Also:
* * @param The interface to create a proxy for. * @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 getRrpcInterface(final Class 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(rootUrl)) throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified. Cannot construct absolute path to remote interface."); path = trimSlashes(rootUrl) + '/' + 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) .content(args) .rethrow(RuntimeException.class) .rethrow(method.getExceptionTypes()); res = rc.run(); Object v = res.getContent().as(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 asRuntimeException(e); } } }); } @Override protected void finalize() throws Throwable { if (detectLeaks && ! 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 null. * @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 msg(String msg, Object...args) { return ()->args.length == 0 ? msg : MessageFormat.format(msg, args); } /** * Returns the part serializer associated with this client. * * @return The part serializer associated with this client. */ protected HttpPartSerializer getPartSerializer() { return partSerializer; } /** * Returns the part parser associated with this client. * * @return The part parser associated with this client. */ protected HttpPartParser getPartParser() { return partParser; } /** * Returns the part serializer instance of the specified type. * * @param c The part serializer class. * @return The part serializer. */ protected HttpPartSerializer getPartSerializer(Class c) { HttpPartSerializer x = partSerializers.get(c); if (x == null) { try { x = beanStore.createBean(c).run(); } catch (ExecutableException e) { throw asRuntimeException(e); } partSerializers.put(c, x); } return x; } /** * Returns the part parser instance of the specified type. * * @param c The part parser class. * @return The part parser. */ protected HttpPartParser getPartParser(Class c) { HttpPartParser x = partParsers.get(c); if (x == null) { try { x = beanStore.createBean(c).run(); } catch (ExecutableException e) { throw asRuntimeException(e); } partParsers.put(c, x); } return x; } /** * Returns true if empty request header values should be ignored. * * @return true if empty request header values should be ignored. */ protected boolean isSkipEmptyHeaderData() { return skipEmptyHeaderData; } /** * Returns true if empty request query parameter values should be ignored. * * @return true if empty request query parameter values should be ignored. */ protected boolean isSkipEmptyQueryData() { return skipEmptyQueryData; } /** * Returns true if empty request form-data parameter values should be ignored. * * @return true if empty request form-data parameter values should be ignored. */ protected boolean isSkipEmptyFormData() { return skipEmptyFormData; } //----------------------------------------------------------------------------------------------------------------- // Part list builders methods //----------------------------------------------------------------------------------------------------------------- /** * Creates a mutable copy of the header data defined on this client. * *

* Used during the construction of {@link RestRequest} objects. * *

* Subclasses can override this method to provide their own builder. * * @return A new builder. */ public HeaderList createHeaderData() { return headerData.copy(); } /** * Creates a mutable copy of the query data defined on this client. * *

* Used during the construction of {@link RestRequest} objects. * *

* Subclasses can override this method to provide their own builder. * * @return A new builder. */ public PartList createQueryData() { return queryData.copy(); } /** * Creates a mutable copy of the form data defined on this client. * *

* Used during the construction of {@link RestRequest} objects. * *

* Subclasses can override this method to provide their own builder. * * @return A new builder. */ public PartList createFormData() { return formData.copy(); } /** * Creates a mutable copy of the path data defined on this client. * *

* Used during the construction of {@link RestRequest} objects. * *

* Subclasses can override this method to provide their own builder. * * @return A new builder. */ public PartList createPathData() { return pathData.copy(); } //----------------------------------------------------------------------------------------------------------------- // RestCallInterceptor methods //----------------------------------------------------------------------------------------------------------------- /** * Interceptor method called immediately after the RestRequest object is created and all headers/query/form-data has been copied from the client. * *

* Subclasses can override this method to intercept the request and perform special modifications. * *

See Also:
    *
  • {@link Builder#interceptors(Object...)} *
* * @param req The HTTP request. * @throws RestCallException If any of the interceptors threw an exception. */ protected void onCallInit(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. * *

* Subclasses can override this method to intercept the response and perform special modifications. * *

See Also:
    *
  • {@link Builder#interceptors(Object...)} *
* * @param req The HTTP request. * @param res The HTTP response. * @throws RestCallException If any of the interceptors threw an exception. */ protected void onCallConnect(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. * *

* Subclasses can override this method to handle any cleanup operations. * *

See Also:
    *
  • {@link Builder#interceptors(Object...)} *
* * @param req The HTTP request. * @param res The HTTP response. * @throws RestCallException If any of the interceptors threw an exception. */ protected void onCallClose(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 null. */ public HttpClientConnectionManager getHttpClientConnectionManager() { return connectionManager; } /** * Executes HTTP request using the default context. * *
Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param request The request to execute. * @return * The response to the request. *
This is always a final response, never an intermediate response with an 1xx status code. *
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. * *
Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param request The request to execute. * @param context The context to use for the execution, or null to use the default context. * @return * The response to the request. *
This is always a final response, never an intermediate response with an 1xx status code. *
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. * *
Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param target The target host for the request. *
Implementations may accept null 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. *
This is always a final response, never an intermediate response with an 1xx status code. *
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. * *
Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
  • 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. *
  • The {@link RestCallHandler} interface can also be implemented to intercept this method. *
* * @param target The target host for the request. *
Implementations may accept null 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 null to use the default context. * @return * The response to the request. *
This is always a final response, never an intermediate response with an 1xx status code. *
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. * *

* 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. * *

Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @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 execute(HttpUriRequest request, ResponseHandler 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. * *

* 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. * *

Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param request The request to execute. * @param responseHandler The response handler. * @param context The context to use for the execution, or null 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 execute(HttpUriRequest request, ResponseHandler 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. * *

* 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. * *

Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param target * The target host for the request. *
Implementations may accept null 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 execute(HttpHost target, HttpRequest request, ResponseHandler 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. * *

* 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. * *

Notes:
    *
  • This method gets passed on directly to the underlying {@link HttpClient} class. *
* * @param target * The target host for the request. *
Implementations may accept null 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 null 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 execute(HttpHost target, HttpRequest request, ResponseHandler responseHandler, HttpContext context) throws IOException, ClientProtocolException { return httpClient.execute(target, request, responseHandler, context); } //----------------------------------------------------------------------------------------------------------------- // Other methods //----------------------------------------------------------------------------------------------------------------- private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*"); URI toURI(Object x, String rootUrl) 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 (rootUrl != null && ! absUrlPattern.matcher(s).matches()) { if (s.isEmpty()) s = rootUrl; else { StringBuilder sb = new StringBuilder(rootUrl); 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<>(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 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 l = parsers.getParsers(); return (l.size() == 1 ? l.get(0) : null); } boolean hasParsers() { return ! parsers.getParsers().isEmpty(); } @SuppressWarnings("unchecked") T getInstance(Class c) { Context o = requestContexts.get(c); if (o == null) { if (Serializer.class.isAssignableFrom(c)) { o = Serializer.createSerializerBuilder((Class)c).beanContext(getBeanContext()).build(); } else if (Parser.class.isAssignableFrom(c)) { o = Parser.createParserBuilder((Class)c).beanContext(getBeanContext()).build(); } requestContexts.put(c, o); } return (T)o; } 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 */ protected JsonMap properties() { return filteredMap() .append("errorCodes", errorCodes) .append("executorService", executorService) .append("executorServiceShutdownOnClose", executorServiceShutdownOnClose) .append("headerData", headerData) .append("interceptors", interceptors) .append("keepHttpClientOpen", keepHttpClientOpen) .append("partParser", partParser) .append("partSerializer", partSerializer) .append("queryData", queryData) .append("rootUrl", rootUrl); } }