net.paissad.waqtsalat.service.utils.HttpUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.paissad.waqtsalat.service.utils.HttpUtils.java

Source

/*
 * This file is part of WaqtSalat-Service.
 * 
 * WaqtSalat-Service is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * WaqtSalat-Service is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with WaqtSalat-Service. If not, see <http://www.gnu.org/licenses/>.
 */

package net.paissad.waqtsalat.service.utils;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NoHttpResponseException;
import org.apache.http.auth.params.AuthPNames;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.paissad.waqtsalat.service.WSConstants;
import net.paissad.waqtsalat.service.exception.WSException;

/**
 * @author paissad
 * 
 */
public class HttpUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private static enum RequestType {
        HEAD, GET;
    }

    private HttpUtils() {
    }

    /**
     * <p>
     * <b>Example</b>: <i>getFilenameFromURL(http://domain.com/aaa/bbb.zip)</i>
     * would return '<i>bbb.zip</i>'
     * </p>
     * <p>
     * <b>Note</b>:
     * <ul>
     * <li>This method will follow the url redirections and will return the name
     * of the file of the last URL which is among all redirections.</li>
     * <li>If the URL has no filename (Ex: <i>http://domain.com)</i>, then an
     * empty string is returned.</li>
     * </ul>
     * </p>
     * 
     * @param url
     * @return The name of the file from the URL.
     * @throws WSException
     */
    public static String getFilenameFromURL(final String url) throws WSException {
        HttpHead request = null;
        HttpClient client = null;
        try {
            request = (HttpHead) getNewHttpRequest(url, RequestType.HEAD, null);
            client = getNewHttpClient();
            client.execute(request);
            String resourcePath = request.getURI().getPath();
            return resourcePath.substring(resourcePath.lastIndexOf("/") + 1);

        } catch (Exception e) {
            throw new WSException("Error while retrieving the ressource name of the URL.", e);

        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }
    }

    /**
     * Downloads a file from a specified URL.
     * 
     * @param url - The url from where to download the file.
     * @param destinationFile - Where to save the file.
     * @throws WSException
     */
    public static void downloadFile(final String url, final File destinationFile) throws WSException {

        if (url == null) {
            throw new IllegalArgumentException("The URL of download may not be null.");
        }
        if (destinationFile == null) {
            throw new IllegalArgumentException("The destination file may not be null.");
        }
        InputStream in = null;
        OutputStream out = null;

        try {
            in = sendRequest(url, RequestType.GET);
            out = new BufferedOutputStream(new FileOutputStream(destinationFile));
            if (in != null) {
                IOUtils.copy(in, out);
            }

        } catch (Exception e) {
            throw new WSException("Error while downloading file", e);

        } finally {
            CommonUtils.closeAllStreamsQuietly(in, out);
        }
    }

    private static InputStream sendRequest(final String url, final RequestType requestType) throws WSException {

        InputStream in = null;
        HttpClient client = null;
        HttpRequestBase request = null;
        boolean errorOccured = false;

        try {
            request = getNewHttpRequest(url, requestType, null);
            client = getNewHttpClient();
            HttpResponse resp = client.execute(request);

            int statusCode = resp.getStatusLine().getStatusCode();

            if (statusCode == HttpStatus.SC_OK) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                IOUtils.copy(resp.getEntity().getContent(), baos);
                baos.flush();
                in = new ByteArrayInputStream(baos.toByteArray());

            } else {
                // The response code is not OK.
                StringBuilder errMsg = new StringBuilder();
                errMsg.append("HTTP ").append(requestType).append(" failed => ").append(resp.getStatusLine());
                if (request != null) {
                    errMsg.append(" : ").append(request.getURI());
                }
                throw new WSException(errMsg.toString());
            }

            return in;

        } catch (Exception e) {
            errorOccured = true;
            throw new WSException("Error during file HTTP request.", e);

        } finally {
            if (errorOccured) {
                if (request != null) {
                    request.abort();
                }
            }
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }
    }

    private static HttpRequestBase getNewHttpRequest(final String url, final RequestType requestType,
            final Header[] additionalRequestHeaders) {

        HttpRequestBase request = null;
        if (requestType == RequestType.HEAD) {
            request = new HttpHead(url);
        } else if (requestType == RequestType.GET) {
            request = new HttpGet(url);
        } else {
            throw new IllegalStateException("The 'RequestType' " + requestType + " is not supported yet !");
        }
        request.addHeader("User-Agent", WSConstants.WS_USER_AGENT);
        if (additionalRequestHeaders != null && additionalRequestHeaders.length > 0) {
            for (Header aHeader : additionalRequestHeaders) {
                request.setHeader(aHeader);
            }
        }

        return request;
    }

    private static HttpClient getNewHttpClient() throws WSException {

        try {
            TrustStrategy trustStrategy = new TrustStrategy() {

                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            };
            SSLSocketFactory sf = new CustomSSLFactory(trustStrategy);

            HttpParams params = new BasicHttpParams();
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

            SchemeRegistry sr = new SchemeRegistry();
            sr.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
            sr.register(new Scheme("https", 443, sf));

            ClientConnectionManager ccm = new ThreadSafeClientConnManager(sr);

            DefaultHttpClient client = new DefaultHttpClient(ccm);
            client.setHttpRequestRetryHandler(new CustomHttpRequestRetryHandler());
            client.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, true);
            client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);

            List<String> authpref = new ArrayList<String>();
            // Choose BASIC over DIGEST for proxy authentication
            authpref.add(AuthPolicy.BASIC);
            authpref.add(AuthPolicy.DIGEST);
            client.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);

            client.addRequestInterceptor(new CustomHttpRequestInterceptor());
            client.addResponseInterceptor(new CustomHttpResponseInterceptor());

            return client;

        } catch (Exception e) {
            throw new WSException("Error while creating a HTTP client.", e);
        }
    }

    // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d4e292
    private static class CustomHttpRequestRetryHandler implements HttpRequestRetryHandler {

        private static final int MAX_RETRIES = 5;

        @Override
        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            if (executionCount >= MAX_RETRIES) {
                return false; // Do not retry if over max retry count
            }
            if (exception instanceof NoHttpResponseException) {
                return true; // Retry if the server dropped connection on us
            }
            if (exception instanceof SSLHandshakeException) {
                return false; // Do not retry on SSL handshake exception
            }
            HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
            boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
            if (idempotent) {
                return true; // Retry if the request is considered idempotent
            }
            return false;
        }
    }

    private static class CustomSSLFactory extends SSLSocketFactory {

        SSLContext sslContext;

        public CustomSSLFactory(TrustStrategy trustStrategy) throws NoSuchAlgorithmException,
                KeyManagementException, KeyStoreException, UnrecoverableKeyException {

            super(trustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            TrustManager tm = new X509TrustManager() {

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }
            };

            this.sslContext = SSLContext.getInstance("TLS");
            this.sslContext.init(null, new TrustManager[] { tm }, null);
        }
    }

    private static class CustomHttpRequestInterceptor implements HttpRequestInterceptor {

        @Override
        public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
            // logger.trace("------------- REQUEST PARAMS ---------------");
            // for (Header aHeader : request.getAllHeaders()) {
            // logger.trace(aHeader.toString());
            // }
            // logger.trace("--------------------------------------------");
        }
    }

    private static class CustomHttpResponseInterceptor implements HttpResponseInterceptor {

        @Override
        public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                Header redirectHeader = response.getFirstHeader("Location");
                if (redirectHeader != null) {
                    logger.debug("Redirecting to {}", redirectHeader.getValue());
                    return;
                }
                logger.warn("------------- RESPONSE PARAMS ---------------");
                for (Header aHeader : response.getAllHeaders()) {
                    logger.warn(aHeader.toString());
                }
                logger.warn("---------------------------------------------");
            }
        }
    }
}