org.eclipse.andmore.android.common.utilities.HttpUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.andmore.android.common.utilities.HttpUtils.java

Source

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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 org.eclipse.andmore.android.common.utilities;

import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.auth.AuthState;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.eclipse.andmore.android.common.log.AndmoreLogger;
import org.eclipse.andmore.android.common.utilities.i18n.UtilitiesNLS;
import org.eclipse.andmore.android.common.utilities.ui.LoginPasswordDialogCreator;
import org.eclipse.core.internal.net.ProxyManager;
import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.ui.internal.net.auth.NetAuthenticator;

/**
 * Class for opening an input stream with the given URL.
 */
@SuppressWarnings("restriction")
public class HttpUtils {
    /**
     * 1 second if the unit is milliseconds.
     */
    private static final int ONE_SECOND = 1000;

    // map of credentials authentication so the user is not repeatedly asked for
    // them
    private static final Map<String, Credentials> authenticationRealmCache = new HashMap<String, Credentials>();

    private GetMethod getMethod;

    /**
     * Retrieves an open InputStream with the contents of the file pointed by
     * the given url.
     * 
     * @param url
     *            The address from where to retrieve the InputStream
     * @param monitor
     *            The monitor to progress while accessing the file
     * 
     * @return The open InputStream object, or <code>null</code> if no file was
     *         found
     * 
     * @throws IOException
     *             if some error occurs with the network communication
     */
    public InputStream getInputStreamForUrl(String url, IProgressMonitor monitor) throws IOException {
        return getInputStreamForUrl(url, monitor, true);
    }

    private InputStream getInputStreamForUrl(String url, IProgressMonitor monitor, boolean returnStream)
            throws IOException {
        SubMonitor subMonitor = SubMonitor.convert(monitor);

        subMonitor.beginTask(UtilitiesNLS.HttpUtils_MonitorTask_PreparingConnection, 300);

        AndmoreLogger.debug(HttpUtils.class, "Verifying proxy usage for opening http connection"); //$NON-NLS-1$

        // Try to retrieve proxy configuration to use if necessary
        IProxyService proxyService = ProxyManager.getProxyManager();
        IProxyData proxyData = null;
        if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled()) {
            Authenticator.setDefault(new NetAuthenticator());
            if (url.startsWith("https")) {
                proxyData = proxyService.getProxyData(IProxyData.HTTPS_PROXY_TYPE);
                AndmoreLogger.debug(HttpUtils.class, "Using https proxy"); //$NON-NLS-1$
            } else if (url.startsWith("http")) {
                proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE);
                AndmoreLogger.debug(HttpUtils.class, "Using http proxy"); //$NON-NLS-1$
            } else {
                AndmoreLogger.debug(HttpUtils.class, "Not using any proxy"); //$NON-NLS-1$
            }
        }

        // Creates the http client and the method to be executed
        HttpClient client = null;
        client = new HttpClient();

        // If there is proxy data, work with it
        if (proxyData != null) {
            if (proxyData.getHost() != null) {
                // Sets proxy host and port, if any
                client.getHostConfiguration().setProxy(proxyData.getHost(), proxyData.getPort());
            }

            if ((proxyData.getUserId() != null) && (proxyData.getUserId().trim().length() > 0)) {
                // Sets proxy user and password, if any
                Credentials cred = new UsernamePasswordCredentials(proxyData.getUserId(),
                        proxyData.getPassword() == null ? "" : proxyData.getPassword()); //$NON-NLS-1$
                client.getState().setProxyCredentials(AuthScope.ANY, cred);
            }
        }

        InputStream streamForUrl = null;
        getMethod = new GetMethod(url);
        getMethod.setFollowRedirects(true);

        // set a 30 seconds timeout
        HttpMethodParams params = getMethod.getParams();
        params.setSoTimeout(15 * ONE_SECOND);
        getMethod.setParams(params);

        boolean trying = true;
        Credentials credentials = null;
        subMonitor.worked(100);
        subMonitor.setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_ContactingSite);
        do {
            AndmoreLogger.info(HttpUtils.class, "Attempting to make a connection"); //$NON-NLS-1$

            // retry to connect to the site once, also set the timeout for 5
            // seconds
            HttpClientParams clientParams = client.getParams();
            clientParams.setIntParameter(HttpClientParams.MAX_REDIRECTS, 1);
            clientParams.setSoTimeout(5 * ONE_SECOND);
            client.setParams(clientParams);

            client.executeMethod(getMethod);
            if (subMonitor.isCanceled()) {
                break;
            } else {
                AuthState authorizationState = getMethod.getHostAuthState();
                String authenticationRealm = authorizationState.getRealm();

                if (getMethod.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    AndmoreLogger.debug(HttpUtils.class, "Client requested authentication; retrieving credentials"); //$NON-NLS-1$

                    credentials = authenticationRealmCache.get(authenticationRealm);

                    if (credentials == null) {
                        AndmoreLogger.debug(HttpUtils.class,
                                "Credentials not found; prompting user for login/password"); //$NON-NLS-1$

                        subMonitor.setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_WaitingAuthentication);

                        LoginPasswordDialogCreator dialogCreator = new LoginPasswordDialogCreator(url);
                        if (dialogCreator.openLoginPasswordDialog() == LoginPasswordDialogCreator.OK) {

                            credentials = new UsernamePasswordCredentials(dialogCreator.getTypedLogin(),
                                    dialogCreator.getTypedPassword());
                        } else {
                            // cancel pressed; stop trying
                            trying = false;

                            // set the monitor canceled to be able to stop
                            // process
                            subMonitor.setCanceled(true);
                        }

                    }

                    if (credentials != null) {
                        AuthScope scope = new AuthScope(null, -1, authenticationRealm);
                        client.getState().setCredentials(scope, credentials);
                    }

                    subMonitor.worked(100);
                } else if (getMethod.getStatusCode() == HttpStatus.SC_OK) {
                    AndmoreLogger.debug(HttpUtils.class, "Http connection suceeded"); //$NON-NLS-1$

                    subMonitor.setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_RetrievingSiteContent);
                    if ((authenticationRealm != null) && (credentials != null)) {
                        authenticationRealmCache.put(authenticationRealm, credentials);
                    } else {
                        // no authentication was necessary, just work the
                        // monitor
                        subMonitor.worked(100);
                    }

                    AndmoreLogger.info(HttpUtils.class, "Retrieving site content"); //$NON-NLS-1$

                    // if the stream should not be returned (ex: only testing
                    // the connection is
                    // possible), then null will be returned
                    if (returnStream) {
                        streamForUrl = getMethod.getResponseBodyAsStream();
                    }

                    // succeeded; stop trying
                    trying = false;

                    subMonitor.worked(100);
                } else {
                    // unhandled return status code
                    trying = false;

                    subMonitor.worked(200);
                }
            }
        } while (trying);

        subMonitor.done();

        return streamForUrl;
    }

    /**
     * Check if a connection with the given URL can be established.
     * 
     * @param url
     *            The URL to test the connection.
     * 
     * @return <code>true</code> if the connection can be established;
     *         <code>false</code> otherwise
     */
    public boolean isConnectionOk(String url) {
        try {
            getInputStreamForUrl(url, null, false);
            // no need to release connection since the stream has not been
            // retrieved
            // if the code above does not throw any exception, the connection is
            // fine
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Release the http connection after users finished reading the InputStream
     * provided by the {@link #getInputStreamForUrl(String, IProgressMonitor)}
     * method.
     */
    public void releaseConnection() {
        if (getMethod != null) {
            Thread t = new Thread() {
                /*
                 * (non-Javadoc)
                 * 
                 * @see java.lang.Thread#run()
                 */
                @Override
                public void run() {
                    getMethod.releaseConnection();
                }
            };
            t.start();

        }
    }
}