fr.jayasoft.ivy.url.HttpClientHandler.java Source code

Java tutorial

Introduction

Here is the source code for fr.jayasoft.ivy.url.HttpClientHandler.java

Source

/*
 * This file is subject to the license found in LICENCE.TXT in the root directory of the project.
 * 
 * version 1.4
 */
package fr.jayasoft.ivy.url;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;

import fr.jayasoft.ivy.util.CopyProgressListener;
import fr.jayasoft.ivy.util.Credentials;
import fr.jayasoft.ivy.util.FileUtil;
import fr.jayasoft.ivy.util.Message;

/**
 * @author Xavier Hanin
 *
 */
public class HttpClientHandler extends AbstractURLHandler {
    private static final SimpleDateFormat LAST_MODIFIED_FORMAT = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z",
            Locale.US);

    // proxy configuration: obtain from system properties
    private int _proxyPort;

    private String _proxyRealm = null;
    private String _proxyHost = null;
    private String _proxyUserName = null;
    private String _proxyPasswd = null;

    private HttpClientHelper _httpClientHelper;

    public HttpClientHandler() {
        configureProxy();
    }

    private void configureProxy() {
        _proxyRealm = null;
        //no equivalent for realm in jdk proxy support ?
        _proxyHost = System.getProperty("http.proxyHost");
        //TODO constant is better ...
        if (useProxy()) {
            _proxyPort = Integer.parseInt(System.getProperty("http.proxyPort", "80"));
            _proxyUserName = System.getProperty("http.proxyUser");
            _proxyPasswd = System.getProperty("http.proxyPassword");
            //It seems there is no equivalent in HttpClient for
            // 'http.nonProxyHosts' property
            Message.verbose(
                    "proxy configured: host=" + _proxyHost + " port=" + _proxyPort + " user=" + _proxyUserName);
        } else {
            Message.verbose("no proxy configured");
        }
    }

    public InputStream openStream(URL url) throws IOException {
        GetMethod get = doGet(url);
        return new GETInputStream(get);
    }

    public void download(URL src, File dest, CopyProgressListener l) throws IOException {
        GetMethod get = doGet(src);
        FileUtil.copy(get.getResponseBodyAsStream(), dest, l);
        get.releaseConnection();
    }

    public URLInfo getURLInfo(URL url) {
        return getURLInfo(url, 0);
    }

    public URLInfo getURLInfo(URL url, int timeout) {
        HeadMethod head = null;
        try {
            head = doHead(url, timeout);
            int status = head.getStatusCode();
            head.releaseConnection();
            if (status == HttpStatus.SC_OK) {
                return new URLInfo(true, getResponseContentLength(head), getLastModified(head));
            }
            if (status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
                Message.error("Your proxy requires authentication.");
            } else if (String.valueOf(status).startsWith("4")) {
                Message.verbose("CLIENT ERROR: " + head.getStatusText() + " url=" + url);
            } else if (String.valueOf(status).startsWith("5")) {
                Message.warn("SERVER ERROR: " + head.getStatusText() + " url=" + url);
            }
            Message.debug("HTTP response status: " + status + "=" + head.getStatusText() + " url=" + url);
        } catch (HttpException e) {
            Message.error("HttpClientHandler: " + e.getMessage() + ":" + e.getReasonCode() + "=" + e.getReason()
                    + " url=" + url);
        } catch (UnknownHostException e) {
            Message.warn("Host " + e.getMessage() + " not found. url=" + url);
            Message.info(
                    "You probably access the destination server through a proxy server that is not well configured.");
        } catch (IOException e) {
            Message.error("HttpClientHandler: " + e.getMessage() + " url=" + url);
        } finally {
            if (head != null) {
                head.releaseConnection();
            }
        }
        return UNAVAILABLE;
    }

    private long getLastModified(HeadMethod head) {
        Header header = head.getResponseHeader("last-modified");
        if (header != null) {
            String lastModified = header.getValue();
            try {
                return LAST_MODIFIED_FORMAT.parse(lastModified).getTime();
            } catch (ParseException e) {
            }
            return System.currentTimeMillis();
        } else {
            return System.currentTimeMillis();
        }
    }

    private long getResponseContentLength(HeadMethod head) {
        return getHttpClientHelper().getResponseContentLength(head);
    }

    private HttpClientHelper getHttpClientHelper() {
        if (_httpClientHelper == null) {
            // use commons httpclient 3.0 if available
            try {
                HttpMethodBase.class.getMethod("getResponseContentLength", new Class[0]);
                _httpClientHelper = new HttpClientHelper3x();
                Message.verbose("using commons httpclient 3.x helper");
            } catch (SecurityException e) {
                Message.verbose(
                        "unable to get access to getResponseContentLength of commons-httpclient HeadMethod. Please use commons-httpclient 3.0 or use ivy with sufficient security permissions.");
                Message.verbose("exception: " + e.getMessage());
                _httpClientHelper = new HttpClientHelper2x();
                Message.verbose("using commons httpclient 2.x helper");
            } catch (NoSuchMethodException e) {
                _httpClientHelper = new HttpClientHelper2x();
                Message.verbose("using commons httpclient 2.x helper");
            }
        }
        return _httpClientHelper;
    }

    public int getHttpClientMajorVersion() {
        HttpClientHelper helper = getHttpClientHelper();
        return helper.getHttpClientMajorVersion();
    }

    private GetMethod doGet(URL url) throws IOException, HttpException {
        HttpClient client = getClient(url);

        GetMethod get = new GetMethod(url.toExternalForm());
        get.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
        client.executeMethod(get);
        return get;
    }

    private HeadMethod doHead(URL url, int timeout) throws IOException, HttpException {
        HttpClient client = getClient(url);
        client.setTimeout(timeout);

        HeadMethod head = new HeadMethod(url.toExternalForm());
        head.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
        client.executeMethod(head);
        return head;
    }

    private HttpClient getClient(URL url) {
        HttpClient client = new HttpClient();

        List authPrefs = new ArrayList(2);
        authPrefs.add(AuthPolicy.DIGEST);
        authPrefs.add(AuthPolicy.BASIC);
        // Exclude the NTLM authentication scheme because it is not supported by this class
        client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);

        if (useProxy()) {
            client.getHostConfiguration().setProxy(_proxyHost, _proxyPort);
            if (useProxyAuthentication()) {
                client.getState().setProxyCredentials(_proxyRealm, _proxyHost,
                        new UsernamePasswordCredentials(_proxyUserName, _proxyPasswd));
            }
        }
        Credentials c = getCredentials(url);
        if (c != null) {
            Message.debug("found credentials for " + url + ": " + c);
            client.getState().setCredentials(c.getRealm(), c.getHost(),
                    new UsernamePasswordCredentials(c.getUserName(), c.getPasswd()));
        }
        return client;
    }

    private boolean useProxy() {
        return _proxyHost != null && _proxyHost.trim().length() > 0;
    }

    private boolean useAuthentication(URL url) {
        return getCredentials(url) != null;
    }

    private Credentials getCredentials(URL url) {
        return CredentialsStore.INSTANCE.getCredentials(null, url.getHost());
    }

    private boolean useProxyAuthentication() {
        return (_proxyUserName != null && _proxyUserName.trim().length() > 0);
    }

    private static final class GETInputStream extends InputStream {
        private InputStream _is;
        private GetMethod _get;

        private GETInputStream(GetMethod get) throws IOException {
            _get = get;
            _is = get.getResponseBodyAsStream();
        }

        public int available() throws IOException {
            return _is.available();
        }

        public void close() throws IOException {
            _is.close();
            _get.releaseConnection();
        }

        public boolean equals(Object obj) {
            return _is.equals(obj);
        }

        public int hashCode() {
            return _is.hashCode();
        }

        public void mark(int readlimit) {
            _is.mark(readlimit);
        }

        public boolean markSupported() {
            return _is.markSupported();
        }

        public int read() throws IOException {
            return _is.read();
        }

        public int read(byte[] b, int off, int len) throws IOException {
            return _is.read(b, off, len);
        }

        public int read(byte[] b) throws IOException {
            return _is.read(b);
        }

        public void reset() throws IOException {
            _is.reset();
        }

        public long skip(long n) throws IOException {
            return _is.skip(n);
        }

        public String toString() {
            return _is.toString();
        }
    }

    private static final class HttpClientHelper3x implements HttpClientHelper {
        private HttpClientHelper3x() {
        }

        public long getResponseContentLength(HeadMethod head) {
            return head.getResponseContentLength();
        }

        /**
         * {@inheritDoc}
         */
        public int getHttpClientMajorVersion() {
            return 3;
        }
    }

    private static final class HttpClientHelper2x implements HttpClientHelper {
        private HttpClientHelper2x() {
        }

        public long getResponseContentLength(HeadMethod head) {
            Header header = head.getResponseHeader("Content-Length");
            if (header != null) {
                try {
                    return Integer.parseInt(header.getValue());
                } catch (NumberFormatException e) {
                    Message.verbose("Invalid content-length value: " + e.getMessage());
                }
            }
            return 0;
        }

        /**
         * {@inheritDoc}
         */
        public int getHttpClientMajorVersion() {
            return 2;
        }
    }

    public interface HttpClientHelper {
        long getResponseContentLength(HeadMethod head);

        int getHttpClientMajorVersion();
    }
}