com.intuit.data.autumn.client.impl.HttpCallImplWithConnectionPooling.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.data.autumn.client.impl.HttpCallImplWithConnectionPooling.java

Source

/*
 * Copyright 2016 Intuit
 *
 * 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.intuit.data.autumn.client.impl;

import com.google.inject.Inject;
import com.intuit.data.autumn.client.HttpCall;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandler;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.client.apache.ApacheHttpClient;
import com.sun.jersey.client.apache.ApacheHttpClientHandler;
import com.sun.jersey.client.apache.config.DefaultApacheHttpClientConfig;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import com.intuit.data.autumn.client.HttpCallConfig;
import org.slf4j.Logger;

import java.util.Map;

import static com.sun.jersey.client.apache.config.ApacheHttpClientConfig.PROPERTY_PROXY_URI;
import static java.lang.String.format;
import static javax.ws.rs.HttpMethod.*;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * An HTTP/S client implementation that includes connection pooling.
 *
 * @param <T> a typed HTTP/S request response object
 */

// FIXME: merge with HttpCallImpl

public class HttpCallImplWithConnectionPooling<T> implements HttpCall<T> {

    private static final Logger LOGGER = getLogger(HttpCall.class);
    private Client client;

    /**
     * Constructor.
     */

    @Inject
    public HttpCallImplWithConnectionPooling() {
    }

    @Override
    public T makeRequest(final String httpMethod, final String url, final Object data, final Class<T> toMap,
            final Map<String, String> headers, final Map<String, String> queryParams, final String accept,
            final String type, final int expectedStatus) {
        return (T) makeRequest(httpMethod,
                HttpCallConfig.Builder.aHttpCallConfig().withUrl(url).withData(data).withToMap(toMap)
                        .withHeaders(headers).withQueryParams(queryParams).withAccept(accept).withType(type)
                        .withExpectedStatus(expectedStatus).build());
    }

    @Override
    public T makeRequest(final String httpMethod, final HttpCallConfig<T> config) {
        ClientResponse response = getResponse(httpMethod, config);

        //check for a successful verification
        if (response == null) {
            String msg = format("data.autumn: HttpCalls: error for the request with following parameters: %s",
                    config.toString());

            LOGGER.error(msg);

            throw new UnsupportedOperationException(msg);
        }

        if (response.getStatus() != config.getExpectedStatus()) {
            String msg = format(
                    "data.autumn: HttpCalls: error, get call to %s returned status was %s expecting status = %s",
                    config.getUrl(), response.getStatus(), config.getExpectedStatus());

            LOGGER.error(msg);

            // Note that this is a required step to free the thread when there is a response
            response.close();

            throw new HttpCallException(msg);
        }

        // Typically response.getEntity takes care of closing the response and freeing the thread.
        return (ClientResponse.class == config.getToMap() || config.getToMap() == null) ? (T) response
                : response.getEntity(config.getToMap());

    }

    private ClientResponse getResponse(final String httpMethod, final HttpCallConfig<T> config) {
        // Set up the webresource object to make the REST call by obtaining the client object with or without
        // proxy as specified.
        client = getClient(config);

        WebResource webResource = client.resource(config.getUrl());
        webResource = addQueryParams(webResource, config.getQueryParams());
        WebResource.Builder builder = addHeaders(webResource, config.getHeaders(), config.getAccept(),
                config.getType());
        ClientResponse response = null;

        try {
            //make the rest call
            switch (httpMethod) {
            case GET:
                response = builder.get(ClientResponse.class);

                break;
            case POST:
                response = (config.getData().isPresent())
                        ? builder.post(ClientResponse.class, config.getData().get())
                        : builder.post(ClientResponse.class);

                break;
            case PUT:
                response = (config.getData().isPresent())
                        ? builder.put(ClientResponse.class, config.getData().get())
                        : builder.put(ClientResponse.class);

                break;
            case DELETE:
                response = builder.delete(ClientResponse.class);

                break;
            default:
                String msg = format("data.autumn: HttpCalls: error, unsupported HTTP method called %s", httpMethod);

                LOGGER.error(msg);

                throw new UnsupportedOperationException(msg);
            }
        } catch (Exception e) {
            LOGGER.error("data.autumn: HttpCall error", e);
        } finally {
            if (!config.getUseConnectionPooling()) {
                client.destroy();
            }
        }

        return response;
    }

    private Client getClient(final HttpCallConfig<T> config) {
        // If either use connection pooling
        if (!config.getUseConnectionPooling() || client != null) {
            MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();

            if (config.getMaxConnectionPerHost().or(-1) > 0) {
                connectionManager.getParams()
                        .setDefaultMaxConnectionsPerHost(config.getMaxConnectionPerHost().get());
            } else {
                connectionManager.getParams().setDefaultMaxConnectionsPerHost(20);
            }

            HttpClient httpClient = new HttpClient(connectionManager);
            ApacheHttpClientHandler clientHandler = new ApacheHttpClientHandler(httpClient);
            ClientHandler root = new ApacheHttpClient(clientHandler);
            ClientConfig config1 = new DefaultApacheHttpClientConfig();

            if (config.getProxyURL().isPresent()) {
                String proxyURLWithPort = "http://" + config.getProxyURL() + ':' + config.getProxyPort();

                config1.getProperties().put(PROPERTY_PROXY_URI, proxyURLWithPort);
            }

            client = new Client(root, config1);

            //Setting connection timeouts and read timeouts when present and doing it in the getClient method
            if (config.getConnectionTimeout().or(-1) > 0) {
                client.setConnectTimeout(config.getConnectionTimeout().get());
            }

            if (config.getReadTimeOut().or(-1) > 0) {
                client.setReadTimeout(config.getReadTimeOut().get());
            }

            //Logging the client properties
            LOGGER.info("Created a new client with the following properties set by the application {}",
                    client.getProperties().toString());
        }

        return client;
    }

    @Override
    public WebResource.Builder addHeaders(final WebResource webResource, final Map<String, String> headers,
            final String accept, final String type) {
        WebResource.Builder builder = webResource.getRequestBuilder();

        if (accept != null) {
            builder.accept(accept);
        }

        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                builder.header(entry.getKey(), entry.getValue());
            }
        }

        if (type != null) {
            builder.type(type);
        }

        return builder;
    }

    @Override
    public WebResource addQueryParams(final WebResource webResource, Map<String, String> queryParams) {
        WebResource resource = webResource;

        if (queryParams != null) {
            for (Map.Entry<String, String> entry : queryParams.entrySet()) {
                resource = resource.queryParam(entry.getKey(), entry.getValue());
            }
        }

        return resource;
    }

    @Override
    public T doGet(final String url, final Class<T> toMap, final Map<String, String> headers,
            final Map<String, String> queryParams, final String accept, final int expectedStatus) {
        return makeRequest(GET, url, null, toMap, headers, queryParams, accept, null, expectedStatus);
    }

    @Override
    public T doPost(final String url, final Object data, final Class<T> toMap, final Map<String, String> headers,
            final Map<String, String> queryParams, final String accept, final String type,
            final int expectedStatus) {
        return makeRequest(POST, url, data, toMap, headers, queryParams, accept, type, expectedStatus);
    }

    @Override
    public T doPost(final HttpCallConfig<T> httpCallConfig) {
        return makeRequest(POST, httpCallConfig);
    }

    @Override
    public T doGet(final HttpCallConfig<T> httpCallConfig) {
        return makeRequest(GET, httpCallConfig);
    }

    @Override
    public T doPut(final HttpCallConfig<T> httpCallConfig) {
        return makeRequest(PUT, httpCallConfig);
    }

    @Override
    public T doDelete(final HttpCallConfig<T> httpCallConfig) {
        return makeRequest(DELETE, httpCallConfig);
    }

    @Override
    public T doPut(final String url, final Object data, final Class<T> toMap, final Map<String, String> headers,
            final Map<String, String> queryParams, final String accept, final String type,
            final int expectedStatus) {
        return makeRequest(PUT, url, data, toMap, headers, queryParams, accept, type, expectedStatus);
    }

    @Override
    public T doDelete(final String url, final Class<T> toMap, final Map<String, String> headers,
            final Map<String, String> queryParams, final String accept, final int expectedStatus) {
        return makeRequest(DELETE, url, null, toMap, headers, queryParams, accept, null, expectedStatus);
    }
}