com.github.parisoft.resty.client.Client.java Source code

Java tutorial

Introduction

Here is the source code for com.github.parisoft.resty.client.Client.java

Source

/*
 *    Copyright 2013-2014 Parisoft Team
 *
 *   Licensed 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 com.github.parisoft.resty.client;

import static com.github.parisoft.resty.request.RequestMethod.DELETE;
import static com.github.parisoft.resty.request.RequestMethod.GET;
import static com.github.parisoft.resty.request.RequestMethod.HEAD;
import static com.github.parisoft.resty.request.RequestMethod.OPTIONS;
import static com.github.parisoft.resty.request.RequestMethod.POST;
import static com.github.parisoft.resty.request.RequestMethod.PUT;
import static com.github.parisoft.resty.request.RequestMethod.TRACE;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.IdleConnectionEvictor;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;

import com.fasterxml.jackson.core.type.TypeReference;
import com.github.parisoft.resty.request.HttpRequest;
import com.github.parisoft.resty.request.Request;
import com.github.parisoft.resty.request.RequestMethod;
import com.github.parisoft.resty.request.retry.IdempotentRequestMethods;
import com.github.parisoft.resty.request.retry.RequestRetryHandler;
import com.github.parisoft.resty.request.ssl.BypassTrustStrategy;
import com.github.parisoft.resty.response.Response;
import com.github.parisoft.resty.response.ResponseFactory;

/**
 * Class that contains methods to execute a {@link Request}.<br>
 * Also, contains shortcuts to configure a request execution timeout, retries and SSL verification.
 *
 * @author Andre Paris
 *
 */
public class Client {

    private static final HashMap<String, HttpClient> httpClientCache = new HashMap<>();
    private static HttpClientConnectionManager connectionManager;

    private final Request request;
    private int timeout = 0;
    private int retries = 0;
    private boolean bypassSSL = true;

    /**
     * Creates an instance of a Client to execute a given {@link Request}.<br>
     * <br>
     *<i>Note:</i> this is equivalent to {@link Request#client()}
     *
     * @param request The request to be executed by this client
     * @throws IllegalArgumentException If the request is <code>null</code>
     */
    public Client(Request request) {
        if (request == null) {
            throw new IllegalArgumentException("Cannot create client: request cannot be null");
        }

        this.request = request;
    }

    /**
     * Sets the number of attempts that a failed request execution must be retried.<br>
     * <br>
     * Default is 0 (no retries)
     *
     * @param retries The number of requests retries
     * @return this client
     * @see RequestRetryHandler
     * @see IdempotentRequestMethods
     */
    public Client retries(int retries) {
        this.retries = retries;

        return this;
    }

    /**
     * Sets the request execution timeout.<br>
     * Under the hood this is done with these {@link HttpClient} configurations:
     * <ul>
     * <li>{@link SocketConfig#getSoTimeout()}</li>
     * <li>{@link RequestConfig#getConnectionRequestTimeout()}</li>
     * <li>{@link RequestConfig#getConnectTimeout()}</li>
     * <li>{@link RequestConfig#getSocketTimeout()}</li>
     * </ul>
     * Default is 0 (no timeout)
     *
     * @param value The timeout value
     * @param unit The timeout unit
     * @return this client
     */
    public Client timeout(int value, TimeUnit unit) {
        timeout = (int) unit.toMillis(value);

        return this;
    }

    /**
     * Sets the bypass of certificate verification process on SSL contexts.<br>
     * <br>
     * Default is true (bypass the verification)
     *
     * @param bypassSSL True to bypass the verification, false otherwise
     * @return this client
     * @see BypassTrustStrategy
     * @see NoopHostnameVerifier
     */
    public Client bypassSSL(boolean bypassSSL) {
        this.bypassSSL = bypassSSL;

        return this;
    }

    /**
     * Executes the request associated with this client as a GET.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response get() throws IOException {
        return execute(GET);
    }

    /**
     * Executes the request associated with this client as a GET.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T get(Class<T> responseClass) throws IOException {
        return get().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a GET.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * get( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T get(TypeReference<T> responseReference) throws IOException {
        return get().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a HEAD.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response head() throws IOException {
        return execute(HEAD);
    }

    /**
     * Executes the request associated with this client as a HEAD.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T head(Class<T> responseClass) throws IOException {
        return head().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a HEAD.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * head( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T head(TypeReference<T> responseReference) throws IOException {
        return head().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a DELETE.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response delete() throws IOException {
        return execute(DELETE);
    }

    /**
     * Executes the request associated with this client as a DELETE.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T delete(Class<T> responseClass) throws IOException {
        return delete().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a DELETE.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * delete( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T delete(TypeReference<T> responseReference) throws IOException {
        return delete().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a POST.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response post() throws IOException {
        return execute(POST);
    }

    /**
     * Executes the request associated with this client as a POST.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T post(Class<T> responseClass) throws IOException {
        return post().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a POST.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * post( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T post(TypeReference<T> responseReference) throws IOException {
        return post().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a POST.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #post()}.
     *
     * @param entity The entity body to send over request
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response post(Object entity) throws IOException {
        request.entity(entity);

        return post();
    }

    /**
     * Executes the request associated with this client as a POST.<br>
     * The entity of the response is processed and converted into a given type.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #post(Class)}.
     *
     * @param entity The entity body to send over request
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T post(Object entity, Class<T> responseClass) throws IOException {
        return post(entity).getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a POST.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * post( entity, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #post(TypeReference)}.
     *
     * @param entity The entity body to send over request
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T post(Object entity, TypeReference<T> responseReference) throws IOException {
        return post(entity).getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a PUT.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response put() throws IOException {
        return execute(PUT);
    }

    /**
     * Executes the request associated with this client as a PUT.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T put(Class<T> responseClass) throws IOException {
        return put().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a PUT.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * put( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T put(TypeReference<T> responseReference) throws IOException {
        return put().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a PUT.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #put()}.
     *
     * @param entity The entity body to send over request
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response put(Object entity) throws IOException {
        request.entity(entity);

        return put();
    }

    /**
     * Executes the request associated with this client as a PUT.<br>
     * The entity of the response is processed and converted into a given type.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #put(Class)}.
     *
     * @param entity The entity body to send over request
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T put(Object entity, Class<T> responseClass) throws IOException {
        return put(entity).getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a PUT.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * put( entity, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #put(TypeReference)}.
     *
     * @param entity The entity body to send over request
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T put(Object entity, TypeReference<T> responseReference) throws IOException {
        return put(entity).getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a OPTIONS.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response options() throws IOException {
        return execute(OPTIONS);
    }

    /**
     * Executes the request associated with this client as a OPTIONS.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T options(Class<T> responseClass) throws IOException {
        return options().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a OPTIONS.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * options( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T options(TypeReference<T> responseReference) throws IOException {
        return options().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a TRACE.
     *
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response trace() throws IOException {
        return execute(TRACE);
    }

    /**
     * Executes the request associated with this client as a TRACE.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T trace(Class<T> responseClass) throws IOException {
        return trace().getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a TRACE.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * trace( new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T trace(TypeReference<T> responseReference) throws IOException {
        return trace().getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a given method.
     *
     * @param requestMethod The method in which the request is executed
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     * @see RequestMethod
     */
    public Response execute(RequestMethod requestMethod) throws IOException {
        final HttpClient httpClient = toHttpClient();
        final HttpRequest httpRequest = request.toHttpRequest(requestMethod);
        final HttpResponse httpResponse = httpClient.execute(httpRequest);

        return ResponseFactory.newResponse(httpResponse);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param requestMethod The method in which the request is executed
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see RequestMethod
     */
    public <T> T execute(RequestMethod requestMethod, Class<T> responseClass) throws IOException {
        return execute(requestMethod).getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * execute( anyMethod, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param requestMethod The method in which the request is executed
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     * @see RequestMethod
     */
    public <T> T execute(RequestMethod requestMethod, TypeReference<T> responseReference) throws IOException {
        return execute(requestMethod).getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(RequestMethod)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     * @see RequestMethod
     */
    public Response execute(RequestMethod requestMethod, Object entity) throws IOException {
        request.entity(entity);

        return execute(requestMethod);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(RequestMethod, Class)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see RequestMethod
     */
    public <T> T execute(RequestMethod requestMethod, Object entity, Class<T> responseClass) throws IOException {
        request.entity(entity);

        return execute(requestMethod, responseClass);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * execute( anyMethod, entity, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(RequestMethod, TypeReference)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     * @see RequestMethod
     */
    public <T> T execute(RequestMethod requestMethod, Object entity, TypeReference<T> responseReference)
            throws IOException {
        request.entity(entity);

        return execute(requestMethod, responseReference);
    }

    /**
     * Executes the request associated with this client as a given method.
     *
     * @param requestMethod The method in which the request is executed
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response execute(String requestMethod) throws IOException {
        final HttpClient httpClient = toHttpClient();
        final HttpRequest httpRequest = request.toHttpRequest(requestMethod);
        final HttpResponse httpResponse = httpClient.execute(httpRequest);

        return ResponseFactory.newResponse(httpResponse);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type.
     *
     * @param requestMethod The method in which the request is executed
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T execute(String requestMethod, Class<T> responseClass) throws IOException {
        return execute(requestMethod).getEntityAs(responseClass);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * execute( anyMethod, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * @param requestMethod The method in which the request is executed
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T execute(String requestMethod, TypeReference<T> responseReference) throws IOException {
        return execute(requestMethod).getEntityAs(responseReference);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(String)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @return The result of the execution as a {@link Response} instance
     * @throws IOException If any problem occurs during the request
     */
    public Response execute(String requestMethod, Object entity) throws IOException {
        request.entity(entity);

        return execute(requestMethod);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type.<br>
     * <br>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(String, Class)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @param responseClass The type to convert the response entity
     * @return The response entity converted into a given type
     * @throws IOException If any problem occurs during the request or during the entity processing
     */
    public <T> T execute(String requestMethod, Object entity, Class<T> responseClass) throws IOException {
        request.entity(entity);

        return execute(requestMethod, responseClass);
    }

    /**
     * Executes the request associated with this client as a given method.<br>
     * The entity of the response is processed and converted into a given type reference.<br>
     * <br>
     * As example, to convert the response entity into a <code>List{@literal <String>}</code> do
     * <pre>
     * execute( anyMethod, entity, new TypeReference{@literal <List<String>>}(){} )
     * </pre>
     * <i>Note:</i> this is equivalent to invoke {@link Request#entity(Object)} and then {@link #execute(String, TypeReference)}.
     *
     * @param requestMethod The method in which the request is executed
     * @param entity The entity body to send over request
     * @param responseReference The type reference to convert the response entity
     * @return The response entity converted into a given type reference
     * @throws IOException If any problem occurs during the request or during the entity processing
     * @see TypeReference
     */
    public <T> T execute(String requestMethod, Object entity, TypeReference<T> responseReference)
            throws IOException {
        request.entity(entity);

        return execute(requestMethod, responseReference);
    }

    /**
     * Returns an {@link HttpClient} instance configured according to this client.<br>
     * <br>
     * This method is not intend to be invoked outside the RESTy library.<br>
     * For your convenience, to execute a request associated with this client, choose the proper method like {@link #get()}, {@link #post()}, or other.
     *
     * @return A configured instance of {@link HttpClient}
     * @throws IOException If some problem occurs during the HttpClient configuration
     */
    public HttpClient toHttpClient() throws IOException {
        String key = String.format("%s|%s|%s", timeout, retries, bypassSSL);
        HttpClient httpClient = httpClientCache.get(key);

        if (httpClient == null) {
            synchronized (getClass()) {
                httpClient = httpClientCache.get(key);

                if (httpClient == null) {
                    httpClient = newHttpClient();
                    httpClientCache.put(key, httpClient);
                }
            }
        }

        return httpClient;
    }

    private HttpClient newHttpClient() throws IOException {
        final SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(timeout).build();

        final RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout)
                .setConnectTimeout(timeout).setSocketTimeout(timeout).setCookieSpec(CookieSpecs.DEFAULT).build();

        final SSLContext sslContext;
        final HostnameVerifier hostnameVerifier;

        if (bypassSSL) {
            hostnameVerifier = NoopHostnameVerifier.INSTANCE;

            try {
                sslContext = SSLContexts.custom().loadTrustMaterial(new BypassTrustStrategy())
                        .useProtocol(SSLConnectionSocketFactory.TLS).build();
            } catch (Exception e) {
                throw new IOException("Cannot create bypassed SSL context", e);
            }
        } else {
            sslContext = SSLContexts.createSystemDefault();
            hostnameVerifier = null;
        }

        final HttpRequestRetryHandler retryHandler = new RequestRetryHandler(retries);
        final HttpClientConnectionManager connectionManager = getConnectionManager();

        return HttpClientBuilder.create().setConnectionManager(connectionManager).setConnectionManagerShared(true)
                .setRetryHandler(retryHandler).setDefaultSocketConfig(socketConfig)
                .setDefaultRequestConfig(requestConfig).setSSLContext(sslContext)
                .setSSLHostnameVerifier(hostnameVerifier).build();
    }

    public static HttpClientConnectionManager getConnectionManager() {
        if (connectionManager == null) {
            synchronized (Client.class) {
                if (connectionManager == null) {
                    connectionManager = new PoolingHttpClientConnectionManager();
                    ((PoolingHttpClientConnectionManager) connectionManager).setMaxTotal(Integer.MAX_VALUE);
                    ((PoolingHttpClientConnectionManager) connectionManager)
                            .setDefaultMaxPerRoute(Integer.MAX_VALUE);
                    new IdleConnectionEvictor(connectionManager, null, 1, TimeUnit.MINUTES, 0, null).start();
                }
            }
        }

        return connectionManager;
    }

}