com.aol.webservice_base.util.http.HttpHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.aol.webservice_base.util.http.HttpHelper.java

Source

/*
    
Copyright (c) 2009-2011, AOL Inc.
All rights reserved.
    
This code is licensed under a BSD license.
    
Howard Uman
    
 */
package com.aol.webservice_base.util.http;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

// TODO: Auto-generated Javadoc
/**
 * The Class HttpHelper.
 */
public class HttpHelper {

    /** The logger. */
    private final Logger logger = Logger.getLogger(HttpHelper.class);

    /** The Constant REQUEST_CHARSET. */
    protected static final String REQUEST_CHARSET = "utf-8";

    /** The Constant REQUEST_CONTENT_TYPE. */
    protected static final String REQUEST_CONTENT_TYPE = "text/html; charset=" + REQUEST_CHARSET;

    /** The Constant X_FORWARDED_FOR. */
    public static final String X_FORWARDED_FOR = "X-Forwarded-For";

    /** The Constant HOST. */
    protected static final String HOST = "host";

    /** The Constant CONTENT_DEBUG_LIMIT. */
    protected static final int CONTENT_DEBUG_LIMIT = 200000;
    // set-able from config
    /** The max connections per host. */
    protected int maxConnectionsPerHost = 5;

    /** The max total connections. */
    protected int maxTotalConnections = 500;

    /** The connection timeout. */
    protected int connectionTimeout = 5000;

    /** The socket timeout. */
    protected int socketTimeout = 5000;

    /** The follow redirect max. */
    protected int followRedirectMax = 3;

    /**
     * Sets the follow redirect max.
     *
     * @param followRedirectMax the new follow redirect max
     */
    public void setFollowRedirectMax(int followRedirectMax) {
        this.followRedirectMax = followRedirectMax;
    }

    // internal state
    /** The inited. */
    protected boolean inited = false;

    /** The http client. */
    protected HttpClient httpClient = null;

    /**
     * Inits the.
     */
    public void init() {
        inited = true;
        ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager();
        connectionManager.setDefaultMaxPerRoute(maxConnectionsPerHost);
        connectionManager.setMaxTotal(maxTotalConnections);

        httpClient = new DefaultHttpClient(connectionManager);
        HttpParams params = httpClient.getParams();
        HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
        HttpConnectionParams.setSoTimeout(params, socketTimeout);
    }

    /**
     * Release response.
     *
     * Convenience method to have the connection released to be reused.
     *
     * @param resp the resp
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void releaseResponse(HttpResponse resp) throws IOException {
        EntityUtils.consume(resp.getEntity());
    }

    /**
     * Gets the data at url
     * 
     * @param url the url
     * @param headers the headers
     * @return the http response data
     * @throws HttpException the http exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public HttpResponseData get(String url, Map<String, String> headers) throws HttpException, IOException {
        return fetchHttpResponseData("GET", url, headers, null);
    }

    /**
     * Post.
     * 
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response data
     * @throws HttpException the http exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public HttpResponseData post(String url, Map<String, String> headers, Object content)
            throws HttpException, IOException {
        return fetchHttpResponseData("POST", url, headers, content);
    }

    /**
     * Delete.
     * 
     * @param url the url
     * @param headers the headers
     * @return the http response data
     * @throws HttpException the http exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public HttpResponseData delete(String url, Map<String, String> headers) throws HttpException, IOException {
        return fetchHttpResponseData("DELETE", url, headers, null);
    }

    /**
     * Put.
     * 
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response data
     * @throws HttpException the http exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public HttpResponseData put(String url, Map<String, String> headers, Object content)
            throws HttpException, IOException {
        return fetchHttpResponseData("PUT", url, headers, content);
    }

    /**
     * Gets the http method.
     *
     * callers to this are responsible for ensuring connection is closed properly - httpHelper.releaseResponse() 
     *
     * @param url the url
     * @param headers the headers
     * @return the http method
     * @throws HttpException the http exception
     */
    public HttpResponse getHttpResponse(String url, Map<String, String> headers) throws HttpException {
        return httpClientCall("GET", url, headers, null);
    }

    /**
     * Post http method.
     *
     * callers to this are responsible for ensuring connection is closed properly - httpHelper.releaseResponse() 
     *
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response
     * @throws HttpException the http exception
     */
    public HttpResponse postHttpResponse(String url, Map<String, String> headers, Object content)
            throws HttpException {
        return httpClientCall("POST", url, headers, content);
    }

    /**
     * Delete http method.
     *
     * callers to this are responsible for ensuring connection is closed properly - httpHelper.releaseResponse() 
     *
     * @param url the url
     * @param headers the headers
     * @return the http response
     * @throws HttpException the http exception
     */
    public HttpResponse deleteHttpResponse(String url, Map<String, String> headers) throws HttpException {
        return httpClientCall("DELETE", url, headers, null);
    }

    /**
     * Put http method.
     *
     * callers to this are responsible for ensuring connection is closed properly - httpHelper.releaseResponse() 
     *
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response
     * @throws HttpException the http exception
     */
    public HttpResponse putHttpResponse(String url, Map<String, String> headers, Object content)
            throws HttpException {
        return httpClientCall("PUT", url, headers, content);
    }

    /**
     * Output data.
     *
     * @param in the in
     * @return the byte[]
     * @throws HttpException the http exception
     */
    private byte[] outputData(java.io.InputStream in) throws HttpException {
        final String METHOD = "HttpHelper.outputData()";
        if (logger.isDebugEnabled()) {
            logger.debug(METHOD + ": Enter");
        }
        try {
            ByteArrayOutputStream data = new ByteArrayOutputStream();
            byte[] buf = new byte[4096];
            int len;
            while ((len = in.read(buf, 0, buf.length)) > 0) {
                data.write(buf, 0, len);
                if (logger.isDebugEnabled()) {
                    logger.debug("read size = " + len);
                    // logger.debug("Data size = "+data.size());
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug(METHOD + ": Leave with total data size = [" + data.size() + "]");
            }
            return data.toByteArray();

        } catch (IOException e) {
            logger.error(METHOD, e);
            throw new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        } catch (Throwable t) {
            logger.error(METHOD, t);
            throw new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t);
        }
    }

    /**
     * Url connection call.
     *
     * @param out the out
     * @param url the url
     * @throws HttpException the http exception
     */
    public void urlConnectionCall(BufferedOutputStream out, String url) throws HttpException {
        final String METHOD = "HttpHelper.urlConnectionCall()";
        BufferedInputStream urlData = null;
        try {
            HttpURLConnection urlConn = (HttpURLConnection) (new URL(url)).openConnection();
            urlConn.setRequestMethod("GET");
            urlData = new BufferedInputStream(urlConn.getInputStream());
            byte[] buf = new byte[4096];
            int sizeRead, available = 4096;
            while (available > 0 && (sizeRead = urlData.read(buf, 0, buf.length)) != -1) {
                out.write(buf, 0, Math.min(available, sizeRead));
                available -= sizeRead;
            }
        } catch (Throwable t) {
            throw new HttpException(0, t);
        } finally {
            if (urlData != null) {
                try {
                    urlData.close();
                    urlData = null;
                } catch (IOException e) {
                    logger.debug(METHOD, e);
                }
            }
        }
    }

    /**
     * Checks if is gets the method.
     *
     * @param request the request
     * @return true, if is gets the method
     */
    public static boolean isGetMethod(HttpServletRequest request) {
        return (request == null || "GET".equalsIgnoreCase(request.getMethod()));
    }

    // WARN: content should be byte[] or String.
    /**
     * Gets the http request.
     *
     * @param requestMethod the request method
     * @param url the url
     * @param content the content
     * @return the http request
     * @throws HttpException the http exception
     * @throws UnsupportedEncodingException the unsupported encoding exception
     */
    private HttpRequestBase getHttpRequest(String requestMethod, String url, Object content)
            throws HttpException, UnsupportedEncodingException {
        HttpRequestBase httpRequest = null;

        if (requestMethod.equalsIgnoreCase("GET")) {
            httpRequest = new HttpGet(url);
        } else if (requestMethod.equalsIgnoreCase("POST")) {
            HttpPost postMethod = new HttpPost(url);
            if (content != null) {
                if (content instanceof byte[]) {
                    postMethod.setEntity(new ByteArrayEntity((byte[]) content));
                } else if (content instanceof String) {
                    postMethod.setEntity(new StringEntity((String) content, REQUEST_CONTENT_TYPE, REQUEST_CHARSET));
                } else {
                    throw new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "content must be String or byte[]");
                }
                httpRequest = postMethod;
            }
        } else if (requestMethod.equalsIgnoreCase("DELETE")) {
            httpRequest = new HttpDelete(url);
        } else if (requestMethod.equalsIgnoreCase("PUT")) {
            HttpPut putMethod = new HttpPut(url);
            if (content != null) {
                if (content instanceof byte[]) {
                    putMethod.setEntity(new ByteArrayEntity((byte[]) content));
                } else if (content instanceof String) {
                    putMethod.setEntity(new StringEntity((String) content, REQUEST_CONTENT_TYPE, REQUEST_CHARSET));
                } else {
                    throw new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "content must be String or byte[]");

                }
                httpRequest = putMethod;
            }
        } else {
            throw new HttpException(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
                    "Unsupported request method [" + requestMethod + "]");
        }
        return httpRequest;

    }

    /**
     * Fetch http response data.
     *
     * @param requestMethod the request method
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response data
     * @throws HttpException the http exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    protected HttpResponseData fetchHttpResponseData(String requestMethod, String url, Map<String, String> headers,
            Object content) throws HttpException, IOException {
        HttpResponse httpResponse = null;

        try {
            httpResponse = httpClientCall(requestMethod, url, headers, content);
            return new HttpResponseData(httpResponse.getAllHeaders(),
                    outputData(httpResponse.getEntity().getContent()));
        } finally {
            if (httpResponse != null)
                releaseResponse(httpResponse);
        }
    }

    /**
     * Http client call.
     *
     * callers to this are responsible for ensuring connection is closed properly - httpHelper.releaseResponse() 
     *
     * @param requestMethod the request method
     * @param url the url
     * @param headers the headers
     * @param content the content
     * @return the http response
     * @throws HttpException the http exception
     */
    protected HttpResponse httpClientCall(String requestMethod, String url, Map<String, String> headers,
            Object content) throws HttpException {
        HttpResponse response = null;
        if (!inited) {
            throw new Error("HttpHelper used when not initialized (call init) for " + url);
        }

        final String METHOD = "HttpHelper.httpClientCall()";
        HttpRequestBase httpRequest = null;
        boolean success = false;

        int iter = 0;
        int status;
        String statusMsg;
        do {
            try {
                long begin = System.currentTimeMillis();
                new URL(url);
                httpRequest = getHttpRequest(requestMethod, url, content);

                if (headers != null && headers.size() > 0) {
                    for (Map.Entry<String, String> headerSet : headers.entrySet()) {
                        httpRequest.addHeader(headerSet.getKey(), headerSet.getValue());
                    }
                }
                Header[] requestHeaders = httpRequest.getAllHeaders();
                for (int i = 0; i < requestHeaders.length; i++) {
                    String name = requestHeaders[i].getName();
                    String value = requestHeaders[i].getValue();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Request header " + name + " = [" + value + "]");
                    }
                }

                // make the request
                //httpRequest.setFollowRedirects(false);
                response = httpClient.execute(httpRequest);
                status = response.getStatusLine().getStatusCode();
                statusMsg = response.getStatusLine().getReasonPhrase();
                if (logger.isDebugEnabled()) {
                    logger.debug(METHOD + " status=" + status + " status desc: [" + statusMsg + "] url=[" + url
                            + "] took=" + (System.currentTimeMillis() - begin) + " ms");
                }
                if (status == 302 || status == 301) {
                    Header loc = httpRequest.getFirstHeader("Location");
                    if (loc != null) {
                        String origUrl = url;
                        url = loc.getValue();
                        if (!url.startsWith("http")) {
                            url = addHost(origUrl, url);
                        }
                        continue;
                    }
                }
                if (status != 200 /* && status != 304 */) {
                    throw new HttpException(status, statusMsg);
                }

                if (logger.isDebugEnabled()) {
                    Header[] responseHeaders = response.getAllHeaders();
                    for (int i = 0; i < responseHeaders.length; i++) {
                        String name = responseHeaders[i].getName();
                        String value = responseHeaders[i].getValue();
                        logger.debug("Response header " + name + " = [" + value + "]");
                    }
                }

                success = true;
                return response;
            } catch (MalformedURLException e) {
                String msg = "target URL = [" + url + "] is invalid.";
                logger.error(msg);
                throw new HttpException(HttpServletResponse.SC_NOT_FOUND, msg);
            } catch (HttpException e) {
                logger.error("HttpException " + METHOD + " url=" + url + " exception=" + e.getMessage());
                throw e;
            } catch (java.net.SocketTimeoutException e) {
                logger.error("SocketTimeoutException " + METHOD + " url=" + url + " exception=" + e.getMessage());
                throw new HttpException(HttpServletResponse.SC_GATEWAY_TIMEOUT,
                        "Connection or Read timeout exception: " + e);
            } catch (Throwable t) {
                logger.error("HttpException " + METHOD + " url=" + url + " exception=" + t.getMessage());
                throw new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t);
            } finally {
                // release the connection whenever we are not successful
                if ((!success) && (response != null)) {
                    try {
                        releaseResponse(response);
                        response = null;
                    } catch (IOException e) {
                        logger.error("HttpHelper - problem releasing connection", e);
                    }
                }
            }
        } while (!success && (++iter <= this.followRedirectMax));

        throw new HttpException(status, statusMsg);
    }

    /* (non-Javadoc)
     * @see java.lang.Object#finalize()
     */
    protected void finalize() {
        ClientConnectionManager connectionManager = httpClient.getConnectionManager();
        if (connectionManager != null) {
            connectionManager.shutdown();
        }
        httpClient.getConnectionManager().shutdown();
    }

    /**
     * Sets the max connections per host.
     *
     * @param maxConnectionsPerHost the new max connections per host
     */
    public void setMaxConnectionsPerHost(int maxConnectionsPerHost) {
        this.maxConnectionsPerHost = maxConnectionsPerHost;
    }

    /**
     * Sets the max total connections.
     *
     * @param maxTotalConnections the new max total connections
     */
    public void setMaxTotalConnections(int maxTotalConnections) {
        this.maxTotalConnections = maxTotalConnections;
    }

    /**
     * Sets the connection timeout.
     *
     * @param connectionTimeout the new connection timeout
     */
    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    /**
     * Sets the socket timeout.
     *
     * @param socketTimeout the new socket timeout
     */
    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    /**
     * Adds the host.
     *
     * @param url the url
     * @param path the path
     * @return the string
     */
    private String addHost(String url, String path) {

        int start = url.indexOf("://");
        if (start == -1) {
            return path;
        }
        start += 3;
        if (path.startsWith("/")) {
            int i = url.indexOf("/", start);
            if (i == -1) {
                path = url + path;
            } else {
                path = url.substring(0, i) + path;
            }
        } else {
            int i = url.lastIndexOf("/");
            if ((i == -1) || (i < start)) {
                path = url + "/" + path;
            } else {
                path = url.substring(0, i + 1) + path;
            }
        }

        return path;
    }
}