com.nesscomputing.httpclient.HttpClientRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.nesscomputing.httpclient.HttpClientRequest.java

Source

/**
 * Copyright (C) 2012 Ness Computing, Inc.
 *
 * 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.nesscomputing.httpclient;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.servlet.http.Cookie;

import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.nesscomputing.httpclient.internal.HttpClientBodySource;
import com.nesscomputing.httpclient.internal.HttpClientFactory;
import com.nesscomputing.httpclient.internal.HttpClientHeader;
import com.nesscomputing.httpclient.internal.HttpClientMethod;

/**
 * A request to a remote server. Composed step-by-step using a builder.
 */
public class HttpClientRequest<T> {
    private final HttpClientFactory httpClientFactory;

    private final HttpClientMethod httpMethod;
    private final URI url;
    private final HttpClientResponseHandler<T> httpHandler;
    private List<HttpClientHeader> headers = Collections.emptyList();
    private List<Cookie> cookies = Collections.emptyList();
    private Map<String, Object> parameters = Collections.emptyMap();
    private String virtualHost = null;
    private int virtualPort = -1;
    private HttpClientBodySource httpBodySource = null;
    private Object content = null;
    private String contentType = null;
    private String contentEncoding = null;
    private Boolean followRedirects = null;
    private List<HttpClientAuthProvider> authProviders = null;

    private HttpClientRequest(final HttpClientFactory httpClientFactory, final HttpClientMethod httpMethod,
            final URI url, final HttpClientResponseHandler<T> httpHandler,
            @Nonnull final List<HttpClientHeader> headers, @Nonnull final List<Cookie> cookies,
            @Nonnull final Map<String, Object> parameters, final String virtualHost, final int virtualPort,
            @Nonnull final List<HttpClientAuthProvider> authProviders, final Object content,
            final String contentType, final String contentEncoding, final Boolean followRedirects) {
        Preconditions.checkArgument(headers != null, "headers must not be null!");
        Preconditions.checkArgument(cookies != null, "cookies must not be null!");
        Preconditions.checkArgument(parameters != null, "parameters must not be null!");
        Preconditions.checkArgument(authProviders != null, "authProviders must not be null!");

        this.httpClientFactory = httpClientFactory;

        this.httpMethod = httpMethod;
        this.url = url;
        this.httpHandler = httpHandler;
        this.headers = Collections.unmodifiableList(headers);
        this.cookies = Collections.unmodifiableList(cookies);
        this.parameters = Collections.unmodifiableMap(parameters);

        this.virtualHost = virtualHost;
        this.virtualPort = virtualPort;

        this.authProviders = Collections.unmodifiableList(authProviders);

        this.content = content;
        this.contentType = contentType;
        this.contentEncoding = contentEncoding;

        this.followRedirects = followRedirects;

        if (content != null) {
            httpBodySource = httpClientFactory.getHttpBodySourceFor(content);
        }
        if (httpBodySource != null) {

            if (contentType != null) {
                httpBodySource.setContentType(contentType);
            }

            if (contentEncoding != null) {
                httpBodySource.setContentEncoding(contentEncoding);
            }
        }
    }

    public InputStream getContent() throws IOException {
        // Gee, thanks a million for an incredibly bad named method.
        return httpBodySource.getContent();
    }

    public String getContentType() {
        return contentType;
    }

    /**
     * @return the HTTP method for this request.
     */
    public HttpClientMethod getHttpMethod() {
        return httpMethod;
    }

    /**
     * @return the URI for this request.
     */
    public URI getUri() {
        return url;
    }

    /**
     * @return the Response handler object for this request.
     */
    public HttpClientResponseHandler<T> getHttpHandler() {
        return httpHandler;
    }

    /**
     * @return a list of headers that are sent with this request.
     */
    public List<HttpClientHeader> getHeaders() {
        return headers;
    }

    /**
     * @return a list of Cookies that are sent with this request.
     */
    public List<Cookie> getCookies() {
        return cookies;
    }

    public Map<String, Object> getParameters() {
        return parameters;
    }

    /**
     * @return the virtual host name for this request.
     */
    public String getVirtualHost() {
        return virtualHost;
    }

    /**
     * @return the virtual host port for this request.
     */
    public int getVirtualPort() {
        return virtualPort;
    }

    /**
     * @return the authentication providers for this request.
     */
    public List<HttpClientAuthProvider> getAuthProviders() {
        return authProviders;
    }

    /**
     * @return the source for the body content for POST and PUT requests.
     */
    public HttpClientBodySource getHttpBodySource() {
        return httpBodySource;
    }

    /**
     * @return true if redirects should be followed automatically.
     */
    public Boolean followRedirects() {
        return followRedirects;
    }

    /**
     * Execute the HTTP request and return the result.
     */
    public T perform() throws IOException {
        return httpClientFactory.performRequest(this);
    }

    //
    // =========================================================
    //
    // Methods for recreating a builder from a request
    //
    // =========================================================
    //

    private Object getContentObject() {
        return content;
    }

    private String getContentEncoding() {
        return contentEncoding;
    }

    private HttpClientFactory getHttpClientFactory() {
        return httpClientFactory;
    }

    public static final class Builder<Type> {
        private final List<HttpClientHeader> headers = Lists.newArrayList();
        private final List<Cookie> cookies = Lists.newArrayList();
        private final Map<String, Object> parameters = Maps.newHashMap();

        private final HttpClientFactory httpClientFactory;
        private final HttpClientResponseHandler<Type> httpHandler;

        private URI url;
        private final HttpClientMethod httpMethod;
        private String virtualHost;
        private int virtualPort;

        private Object content;
        private String contentType;
        private String contentEncoding;

        private Boolean followRedirects;

        private final List<HttpClientAuthProvider> authProviders = Lists.newArrayList();

        public static <T> Builder<T> fromRequest(final HttpClientRequest<T> request) {
            return new Builder<T>(request);
        }

        <T> Builder(final HttpClientFactory httpClientFactory, final HttpClientMethod httpMethod, final URI url,
                final HttpClientResponseHandler<Type> httpHandler) {
            this.httpClientFactory = httpClientFactory;
            this.httpMethod = httpMethod;
            this.url = url;
            this.httpHandler = httpHandler;
        }

        <T> Builder(final HttpClientRequest<Type> request) {
            this(request.getHttpClientFactory(), request.getHttpMethod(), request.getUri(),
                    request.getHttpHandler());

            this.headers.addAll(request.getHeaders());
            this.cookies.addAll(request.getCookies());
            this.parameters.putAll(request.getParameters());
            this.authProviders.addAll(request.getAuthProviders());

            this.virtualHost = request.getVirtualHost();
            this.virtualPort = request.getVirtualPort();
            this.content = request.getContentObject();
            this.contentType = request.getContentType();
            this.contentEncoding = request.getContentEncoding();
            this.followRedirects = request.followRedirects();
        }

        public Builder<Type> setUrl(final URI url) {
            Preconditions.checkArgument(url != null, "URI must not be null!");
            this.url = url;

            return this;
        }

        /**
         * Add a header to the request.
         * @param header the header name
         * @param value the header value
         */
        public Builder<Type> addHeader(final String header, final String value) {
            Preconditions.checkArgument(header != null, "Header name must not be null!");
            Preconditions.checkArgument(value != null, "Header value must not be null!");

            headers.add(new HttpClientHeader(header, value));
            return this;
        }

        /**
         * Add a header to the request and remove all other existing headers.
         * @param header the header name
         * @param value the header value
         */
        public Builder<Type> replaceHeader(final String header, final String value) {
            for (Iterator<HttpClientHeader> it = headers.iterator(); it.hasNext();) {
                final HttpClientHeader oldHeader = it.next();
                if (StringUtils.equals(header, oldHeader.getName())) {
                    it.remove();
                }
            }
            return addHeader(header, value);
        }

        /**
         * @param cookie cookie to add to the request
         */
        public Builder<Type> addCookie(@Nonnull final Cookie cookie) {
            Preconditions.checkArgument(cookie != null, "Cookie must not be null!");

            cookies.add(cookie);
            return this;
        }

        /**
         * @param cookie cookie to add to the request
         */
        public Builder<Type> replaceCookie(@Nonnull final Cookie cookie) {
            for (Iterator<Cookie> it = cookies.iterator(); it.hasNext();) {
                final Cookie oldCookie = it.next();
                if (StringUtils.equals(cookie.getName(), oldCookie.getName())) {
                    it.remove();
                }
            }

            return addCookie(cookie);
        }

        /**
         * Context parameters for the HTTP request.
         */
        public Builder<Type> setParameter(final String key, final Object value) {
            Preconditions.checkArgument(key != null, "key must not be null!");

            parameters.put(key, value);
            return this;
        }

        /**
         * Set the virtual host for this request.
         *
         * @param virtualHost the host
         * @param virtualPort the port. Can be -1 to use the default port.
         */
        public Builder<Type> setVirtualHost(final String virtualHost, final int virtualPort) {
            this.virtualHost = virtualHost;
            this.virtualPort = virtualPort;
            return this;
        }

        /**
         * Set the content type sent with this request if it is a POST or PUT request.
         */
        public Builder<Type> setContentType(@Nonnull final String contentType) {
            if (contentType == null) {
                throw new IllegalArgumentException("Content type can not be null!");
            }

            this.contentType = contentType;

            return this;
        }

        /**
         * Set the content encoding sent with this request if it is a POST or PUT request.
         */
        public Builder<Type> setContentEncoding(@Nonnull final String contentEncoding) {
            if (contentEncoding == null) {
                throw new IllegalArgumentException("Content encoding can not be null!");
            }

            this.contentEncoding = contentEncoding;

            return this;
        }

        public Builder<Type> setContent(Multimap<String, String> kvPairs, String encoding)
                throws UnsupportedEncodingException {
            StringBuilder sb = new StringBuilder();
            boolean first = true;

            for (Map.Entry<String, String> entry : kvPairs.entries()) {
                String key = entry.getKey();
                String value = entry.getValue();

                if (!first) {
                    sb.append('&');
                } else {
                    first = false;
                }

                sb.append(URLEncoder.encode(key, encoding));
                sb.append('=');
                if (key != null) {
                    sb.append(URLEncoder.encode(value, encoding));
                }
            }

            setContentEncoding(encoding);
            setContent(sb.toString());
            return this;
        }

        /**
         * Create the content for a POST or PUT request from a String.
         */
        public Builder<Type> setContent(final String content) {
            this.content = content;
            return this;
        }

        /**
         * Create the content for a POST or PUT request from a byte array.
         */
        public Builder<Type> setContent(final byte[] content) {
            this.content = content;
            return this;
        }

        /**
         * Create the content for a POST or PUT request from an input stream.
         */
        public Builder<Type> setContent(final InputStream content) {
            this.content = content;
            return this;
        }

        /**
         * Add basic authentication information.
         * @param user Username to use when authentication is requested.
         * @param password Password to use when authentication is requested.
         */
        public Builder<Type> addBasicAuth(final String user, final String password) {
            return addAuth(HttpClientDefaultAuthProvider.forUser(user, password));
        }

        /**
         * @param authProvider a custom authentication provider to use when authentication is requested.
         */
        public Builder<Type> addAuth(final HttpClientAuthProvider authProvider) {
            authProviders.add(authProvider);
            return this;
        }

        /**
         * @param followRedirects true if redirects should be followed automatically.
         */
        public Builder<Type> followRedirects(final Boolean followRedirects) {
            this.followRedirects = followRedirects;
            return this;
        }

        /**
         * Create a HttpClientRequest from the builder. The object is disconnected from the builder and the builder can be reused.
         */
        public HttpClientRequest<Type> request() {
            final HttpClientRequest<Type> httpClientRequest = new HttpClientRequest<Type>(httpClientFactory,
                    httpMethod, url, httpHandler, headers, cookies, parameters, virtualHost, virtualPort,
                    authProviders, content, contentType, contentEncoding, followRedirects);

            return httpClientRequest;
        }

        /**
         * Builds a HttpClientRequest from the builder an executes it in one go.
         * @return The return value of the request.
         * @throws IOException
         */
        public Type perform() throws IOException {
            return request().perform();
        }
    }

    @Override
    public String toString() {
        return String.format("HttpClientRequest [%s %s]", httpMethod, url);
    }
}