ch.netcetera.eclipse.common.net.AbstractHttpClient.java Source code

Java tutorial

Introduction

Here is the source code for ch.netcetera.eclipse.common.net.AbstractHttpClient.java

Source

/*
 * Copyright (c) 2010 Netcetera AG and others.
 * All rights reserved.
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at
 *
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * - Netcetera AG: initial implementation
 */
package ch.netcetera.eclipse.common.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
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.impl.client.DefaultHttpClient;
import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

/**
 * Base HTTP client for the different ETE subprojects.
 */
public abstract class AbstractHttpClient {

    /** The default buffer size for stream operations. */
    protected static final int DEFAULT_BUFFER_SIZE = 1024 * 8;

    /**
     * Call-back interface for handling HTTP responses.
     *
     * @param <R> the type of result of handling the response.
     */
    public interface IResponseHandler<R> {

        /**
         * Handle a HTTP response.
         *
         * @param response the method that was executed
         * @param monitor the monitor to report progress on
         * @return the result of handling the response
         * @throws IOException if reading the response fails
         */
        R handleResponse(HttpResponse response, IProgressMonitor monitor) throws IOException;

    }

    /**
     * An {@link InputStream} that wraps a {@link IProgressMonitor}. Reading from the stream will
     * report progress.
     */
    protected static final class ProgressReportingInputStream extends InputStream {

        private final InputStream stream;
        private final IProgressMonitor monitor;

        /**
         * Constructor.
         *
         * @param stream the stream to wrap, not {@literal null}
         * @param monitor the monitor to use, not {@literal null}
         */
        ProgressReportingInputStream(InputStream stream, IProgressMonitor monitor) {
            this.stream = stream;
            this.monitor = monitor;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int available() throws IOException {
            return this.stream.available();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void close() throws IOException {
            try {
                this.stream.close();
            } finally {
                this.monitor.done();
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void mark(int readlimit) {
            this.stream.mark(readlimit);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean markSupported() {
            return this.stream.markSupported();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int read() throws IOException {
            int data = this.stream.read();
            this.monitor.worked(1);
            return data;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            int read = this.stream.read(b, off, len);
            this.monitor.worked(read);
            return read;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int read(byte[] b) throws IOException {
            int read = this.stream.read(b);
            this.monitor.worked(read);
            return read;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void reset() throws IOException {
            // screws the monitor
            this.stream.reset();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public long skip(long n) throws IOException {
            long skipped = this.stream.skip(n);
            this.monitor.worked((int) skipped);
            return skipped;
        }
    }

    /**
     * Executes a HTTP get request.
     *
     * @param <R> the return type
     * @param url the url
     * @param handler the response handler
     * @param monitor the progress monitor
     * @return the response
     * @throws CoreException on error
     */
    protected <R> R executeGetRequest(String url, IResponseHandler<R> handler, IProgressMonitor monitor)
            throws CoreException {

        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(url);
        get.addHeader("Accept-Encoding", "gzip");
        configureProxySettings(client, get);
        configureSslHandling(client);

        try {
            HttpResponse response = client.execute(get);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                return handler.handleResponse(response, monitor);
            } else {
                throw convertHttpStatusToException(statusCode, get.getURI());
            }
        } catch (IOException e) {
            throw wrapIoException(e);
        }
    }

    private void configureProxySettings(HttpClient client, HttpGet get) {
        if (getProxyService() != null && getProxyService().getProxyData() != null
                && getProxyService().getProxyData().length > 0) {
            String requestScheme = get.getURI().getScheme();
            for (IProxyData proxyData : getProxyService().getProxyData()) {
                if (proxyData != null && proxyData.getHost() != null
                        && proxyData.getType().equalsIgnoreCase(requestScheme)) {
                    HttpHost proxy = new HttpHost(proxyData.getHost(), proxyData.getPort(), requestScheme);
                    client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
                    break;
                }
            }
        }
    }

    private void configureSslHandling(HttpClient httpClient) throws CoreException {
        Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
        Scheme https = new Scheme("https", 443, SSLSocketFactory.getSocketFactory());
        SchemeRegistry sr = httpClient.getConnectionManager().getSchemeRegistry();
        sr.register(http);
        sr.register(https);
    }

    private CoreException wrapIoException(IOException e) {
        return this.wrapGenericException(e);
    }

    private CoreException wrapGenericException(Exception e) {
        IStatus status = new Status(IStatus.ERROR, getBundleSymbolicName(), e.getLocalizedMessage(), e);
        return new CoreException(status);
    }

    /**
     * @return the bundle symbolic name
     */
    protected abstract String getBundleSymbolicName();

    private CoreException convertHttpStatusToException(int httpStatusCode, URI uri) {
        IStatus status = new Status(IStatus.WARNING, getBundleSymbolicName(),
                "ETE received an unexpected HTTP status code " + httpStatusCode + " while connecting to " + uri);
        return new CoreException(status);
    }

    /**
     * Copy bytes from an {@link InputStream} to an {@link OutputStream}.
     *
     * @param input the {@link InputStream} to read from
     * @param output the {@link OutputStream} to write to
     * @throws NullPointerException if the input or output is null
     * @throws IOException if an I/O error occurs
     */
    protected static void copy(InputStream input, OutputStream output) throws IOException, NullPointerException {
        // taken from Commons IO 1.3
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int n = 0;
        while (-1 != (n = input.read(buffer))) { // NOPMD pellaton 2010-11-20 ok
            output.write(buffer, 0, n);
        }
    }

    private IProxyService proxyService;

    /**
     * Wraps the response stream.
     *
     * @param response the HTTP response
     * @param stream the input stream
     * @param monitor the progress monitor
     * @return the wrapped input stream
     * @throws IOException on error
     */
    protected InputStream wrapResponseStream(HttpResponse response, InputStream stream, IProgressMonitor monitor)
            throws IOException {
        InputStream input = stream;
        Header contentEncoding = response.getFirstHeader("Content-Encoding");
        long contentLength = response.getEntity().getContentLength();
        if (contentLength != -1) {
            monitor.beginTask("Parsing Response", (int) contentLength);
            input = new ProgressReportingInputStream(input, monitor);
        }
        if (contentEncoding != null && "gzip".equalsIgnoreCase(contentEncoding.getValue())) {
            input = new GZIPInputStream(input);
        }
        return input;
    }

    /**
     * Binds the {@link IProxyService} service reference.
     *
     * @param proxyService the {@link IProxyService} service reference to bind
     */
    public void bindProxyService(IProxyService proxyService) {
        this.proxyService = proxyService;
    }

    /**
     * Unbinds the {@link IProxyService} service reference.
     *
     * @param proxyService the {@link IProxyService} service reference to unbind
     */
    public void unbindProxyService(@SuppressWarnings("unused") IProxyService proxyService) {
        this.proxyService = null;
    }

    /**
     * Gets the {@link IProxyService} instance.
     *
     * @return the {@link IProxyService} instance
     */
    protected IProxyService getProxyService() {
        return proxyService;
    }
}