org.attribyte.api.http.impl.commons.Commons4Client.java Source code

Java tutorial

Introduction

Here is the source code for org.attribyte.api.http.impl.commons.Commons4Client.java

Source

/*
 * Copyright 2014 Attribyte, LLC
 * 
 * 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 org.attribyte.api.http.impl.commons;

import com.google.common.collect.Lists;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.attribyte.api.InitializationException;
import org.attribyte.api.Logger;
import org.attribyte.api.http.ClientOptions;
import org.attribyte.api.http.Header;
import org.attribyte.api.http.Parameter;
import org.attribyte.api.http.Request;
import org.attribyte.api.http.RequestOptions;
import org.attribyte.api.http.Response;
import org.attribyte.api.http.ResponseBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * An HTTP client based on Apache commons HTTP.
 */
public class Commons4Client implements org.attribyte.api.http.Client {

    /**
     * Creates an <em>uninitialized</em> client.
     */
    public Commons4Client() {

    }

    private void initFromOptions(final ClientOptions options) {

        if (options != ClientOptions.IMPLEMENTATION_DEFAULT) {

            HttpClientBuilder builder = HttpClients.custom();
            builder.setMaxConnTotal(options.maxConnectionsTotal);
            builder.setMaxConnPerRoute(options.maxConnectionsPerDestination);
            builder.setUserAgent(options.userAgent);
            if (options.proxyHost != null) {
                builder.setProxy(new HttpHost(options.proxyHost, options.proxyPort));
            }

            this.defaultRequestConfig = RequestConfig.custom().setConnectTimeout(options.connectionTimeoutMillis)
                    .setConnectionRequestTimeout(options.requestTimeoutMillis)
                    .setRedirectsEnabled(RequestOptions.DEFAULT_FOLLOW_REDIRECTS)
                    .setMaxRedirects(RequestOptions.DEFAULT_FOLLOW_REDIRECTS ? 5 : 0)
                    .setAuthenticationEnabled(false).setCircularRedirectsAllowed(false)
                    .setSocketTimeout(options.socketTimeoutMillis).build();
            builder.setDefaultRequestConfig(defaultRequestConfig);

            ConnectionConfig connectionConfig = ConnectionConfig.custom()
                    .setBufferSize(
                            options.requestBufferSize > options.responseBufferSize ? options.requestBufferSize
                                    : options.responseBufferSize)
                    .build();
            builder.setDefaultConnectionConfig(connectionConfig);

            this.httpClient = builder.build();
        } else {
            this.defaultRequestConfig = RequestConfig.DEFAULT;
            this.httpClient = HttpClients.createDefault();
        }
    }

    /**
     * Creates a client with specified options.
     * @param options The options.
     */
    public Commons4Client(final ClientOptions options) {
        initFromOptions(options);
    }

    @Override
    /**
     * Initializes the client from properties.
     * <p>
     *   The following properties are available. <b>Bold</b> properties are required.
     *
     *   <h2>HTTP Client</h2>
     *   <dl>
     *     <dt><b>User-Agent</b></dt>
     *     <dd>The default User-Agent string sent with requests. Added only if
     *     request has no <code>User-Agent</code> header.</dd>
     *     <dt><b>connectionTimeoutMillis</b></dt>
     *     <dd>The HTTP connection timeout in milliseconds.</dd>
     *     <dt><b>socketTimeoutMillis</b></dt>
     *     <dd>The HTTP client socket timeout in milliseconds.</dd>
     *     <dt>proxyHost</dt>
     *     <dd>The HTTP proxy host. If specified, all client requests will use this proxy.</dd>
     *     <dt>proxyPort</dt>
     *     <dd>The HTTP proxy port. Required when <code>proxyHost</code> is specified</dd>
     *   </dl>
     * </p>
     * @param prefix The prefix for all properties (e.g. 'client.').
     * @param props The properties.
     * @param logger The logger. If unspecified, messages are logged to the console.
     * @throws org.attribyte.api.InitializationException on initialization error.
     */
    public void init(String prefix, Properties props, Logger logger) throws InitializationException {
        if (isInit.compareAndSet(false, true)) {
            ClientOptions options = new ClientOptions(prefix, props);
            initFromOptions(options);
        }
    }

    @Override
    public Response send(Request request) throws IOException {
        return send(request, RequestOptions.DEFAULT);
    }

    @Override
    public Response send(Request request, RequestOptions options) throws IOException {

        HttpUriRequest commonsRequest = null;

        switch (request.getMethod()) {
        case GET:
            commonsRequest = new HttpGet(request.getURI());
            break;
        case DELETE:
            commonsRequest = new HttpDelete(request.getURI());
            break;
        case HEAD:
            commonsRequest = new HttpHead(request.getURI());
            break;
        case POST: {
            HttpEntityEnclosingRequestBase entityEnclosingRequest = new HttpPost(request.getURI());
            commonsRequest = entityEnclosingRequest;
            EntityBuilder entityBuilder = EntityBuilder.create();
            if (request.getBody() != null) {
                entityBuilder.setBinary(request.getBody().toByteArray());
            } else {
                Collection<Parameter> parameters = request.getParameters();
                List<NameValuePair> nameValuePairs = Lists.newArrayListWithExpectedSize(parameters.size());
                for (Parameter parameter : parameters) {
                    String[] values = parameter.getValues();
                    for (String value : values) {
                        nameValuePairs.add(new BasicNameValuePair(parameter.getName(), value));
                    }
                }
            }
            entityEnclosingRequest.setEntity(entityBuilder.build());
            break;
        }
        case PUT: {
            HttpEntityEnclosingRequestBase entityEnclosingRequest = new HttpPut(request.getURI());
            commonsRequest = entityEnclosingRequest;
            EntityBuilder entityBuilder = EntityBuilder.create();
            if (request.getBody() != null) {
                entityBuilder.setBinary(request.getBody().toByteArray());
            }
            entityEnclosingRequest.setEntity(entityBuilder.build());
            break;
        }
        }

        Collection<Header> headers = request.getHeaders();
        for (Header header : headers) {
            String[] values = header.getValues();
            for (String value : values) {
                commonsRequest.setHeader(header.getName(), value);
            }
        }

        ResponseBuilder builder = new ResponseBuilder();
        CloseableHttpResponse response = null;
        InputStream is = null;
        try {
            if (options.followRedirects != RequestOptions.DEFAULT_FOLLOW_REDIRECTS) {
                RequestConfig localConfig = RequestConfig.copy(defaultRequestConfig)
                        .setRedirectsEnabled(options.followRedirects)
                        .setMaxRedirects(options.followRedirects ? 5 : 0).build();
                HttpClientContext localContext = HttpClientContext.create();
                localContext.setRequestConfig(localConfig);
                response = httpClient.execute(commonsRequest, localContext);
            } else {
                response = httpClient.execute(commonsRequest);
            }

            builder.setStatusCode(response.getStatusLine().getStatusCode());
            for (org.apache.http.Header header : response.getAllHeaders()) {
                builder.addHeader(header.getName(), header.getValue());

            }
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                is = entity.getContent();
                if (is != null) {
                    builder.setBody(Request.bodyFromInputStream(is, options.maxResponseBytes));
                }
            }

        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ioe) {
                    //TODO?
                }
            }
            if (response != null) {
                response.close();
            }
        }

        return builder.create();
    }

    @Override
    public void shutdown() {
        try {
            httpClient.close();
        } catch (IOException ioe) {
            //TODO?
        }
    }

    private CloseableHttpClient httpClient;
    private RequestConfig defaultRequestConfig;
    private final AtomicBoolean isInit = new AtomicBoolean(false);
}