com.denimgroup.threadfix.service.defects.utils.RestUtilsImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.denimgroup.threadfix.service.defects.utils.RestUtilsImpl.java

Source

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2015 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service.defects.utils;

import com.denimgroup.threadfix.exception.RestException;
import com.denimgroup.threadfix.exception.RestIOException;
import com.denimgroup.threadfix.exception.RestUrlException;
import com.denimgroup.threadfix.logging.SanitizedLogger;
import com.denimgroup.threadfix.service.ProxyService;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import javax.annotation.Nonnull;
import javax.net.ssl.SSLHandshakeException;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * This class holds code for more easily interacting with HTTP-authenticated REST services.
 * So far this is just JIRA but this code could be useful in other places too.
 *
 * WARNING this class throws subclasses of RestException, so our error handler will return REST responses
 * to the user if the exceptions are not caught.
 * 
 * TODO further genericize and move to threadfix common code
 * @author mcollins
 *
 */
public class RestUtilsImpl<T> extends SpringBeanAutowiringSupport implements RestUtils {

    @Autowired(required = false)
    private ProxyService proxyService;

    private RestUtilsImpl() {
    } // intentional, we shouldn't be instantiating this class.

    Class<T> classToProxy = null;

    private static final SanitizedLogger LOG = new SanitizedLogger(RestUtilsImpl.class);
    private String postErrorResponse;

    public static <T> RestUtilsImpl getInstance(Class<T> classToProxy) {
        RestUtilsImpl impl = new RestUtilsImpl();
        impl.classToProxy = classToProxy;
        return impl;
    }

    private int getStatusCode(HttpURLConnection httpConnection) {
        int statusCode = -1;
        if (httpConnection != null) {
            try {
                statusCode = httpConnection.getResponseCode();
            } catch (IOException e1) {
                LOG.error("Encountered IOException while requesting response code from httpconnection object. "
                        + "Re-throwing initial exception.");

            }
        }
        return statusCode;
    }

    @Nonnull
    private InputStream getUrl(String urlString, String username, String password) throws RestException {
        URL url;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException e) {
            throw new RestUrlException(e, "Unable to make request due to malformed URL. Check the code.");
        }

        HttpURLConnection httpConnection = null;
        try {
            if (proxyService == null) {
                httpConnection = (HttpURLConnection) url.openConnection();
            } else {
                httpConnection = proxyService.getConnectionWithProxyConfig(url, classToProxy);
            }

            setupAuthorization(httpConnection, username, password);

            httpConnection.addRequestProperty("Content-Type", "application/json");
            httpConnection.addRequestProperty("Accept", "application/json");

            InputStream stream = httpConnection.getInputStream();

            return stream;
        } catch (IOException e) {
            LOG.info("Encountered IOException, unable to continue");
            throw new RestIOException(e, "Unable to communicate with the server.", getStatusCode(httpConnection));
        }
    }

    public String getUrlAsString(String urlString, String username, String password) throws RestException {

        LOG.debug("Requesting " + urlString);

        InputStream responseStream = getUrl(urlString, username, password);

        String test = null;
        try {
            test = IOUtils.toString(responseStream);
        } catch (IOException e) {
            throw new RestIOException(e, "Unable to get response from server." + e.toString());
        } finally {
            closeInputStream(responseStream);
        }

        return test;
    }

    public void closeInputStream(InputStream stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException ex) {
                LOG.warn("Closing an input stream failed.", ex);
            }
        }
    }

    @Nonnull
    private InputStream postUrl(String urlString, String data, String username, String password,
            String contentType) {
        URL url;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException e) {
            LOG.warn("URL used for POST was bad: '" + urlString + "'");
            throw new RestUrlException(e, "Received a malformed server URL.");
        }

        HttpURLConnection httpConnection = null;
        OutputStreamWriter outputWriter = null;
        try {
            if (proxyService == null) {
                httpConnection = (HttpURLConnection) url.openConnection();
            } else {
                httpConnection = proxyService.getConnectionWithProxyConfig(url, classToProxy);
            }

            setupAuthorization(httpConnection, username, password);

            httpConnection.addRequestProperty("Content-Type", contentType);
            httpConnection.addRequestProperty("Accept", contentType);

            httpConnection.setDoOutput(true);
            outputWriter = new OutputStreamWriter(httpConnection.getOutputStream());
            outputWriter.write(data);
            outputWriter.flush();

            InputStream is = httpConnection.getInputStream();

            return is;
        } catch (IOException e) {
            LOG.warn("IOException encountered trying to post to URL with message: " + e.getMessage());
            if (httpConnection == null) {
                LOG.warn(
                        "HTTP connection was null so we cannot do further debugging of why the HTTP request failed");
            } else {
                try {
                    InputStream errorStream = httpConnection.getErrorStream();
                    if (errorStream == null) {
                        LOG.warn("Error stream from HTTP connection was null");
                    } else {
                        LOG.warn(
                                "Error stream from HTTP connection was not null. Attempting to get response text.");
                        setPostErrorResponse(IOUtils.toString(errorStream));
                        LOG.warn("Error text in response was '" + getPostErrorResponse() + "'");
                        throw new RestIOException(e, getPostErrorResponse(),
                                "Unable to get response from server. Error text was: " + getPostErrorResponse(),
                                getStatusCode(httpConnection));
                    }
                } catch (IOException e2) {
                    LOG.warn("IOException encountered trying to read the reason for the previous IOException: "
                            + e2.getMessage(), e2);
                    throw new RestIOException(e2, "Unable to read response from server." + e2.getMessage(),
                            getStatusCode(httpConnection));
                }
            }
            throw new RestIOException(e, "Unable to read response from server." + e.toString());
        } finally {
            if (outputWriter != null) {
                try {
                    outputWriter.close();
                } catch (IOException e) {
                    LOG.warn("Failed to close output stream in postUrl.", e);
                }
            }
        }
    }

    public String postUrlAsString(String urlString, String data, String username, String password,
            String contentType) throws RestException {
        InputStream responseStream = postUrl(urlString, data, username, password, contentType);

        String test = null;
        try {
            test = IOUtils.toString(responseStream);
        } catch (IOException e) {
            throw new RestIOException(e, "Unable to parse response from server.");
        } finally {
            closeInputStream(responseStream);
        }

        return test;
    }

    private void setupAuthorization(HttpURLConnection connection, String username, String password) {
        String login = username + ":" + password;

        String encodedLogin = DatatypeConverter.printBase64Binary(login.getBytes());
        connection.setRequestProperty("Authorization", "Basic " + encodedLogin);
    }

    public String getPostErrorResponse() {
        return postErrorResponse;
    }

    private void setPostErrorResponse(String postErrorResponse) {
        this.postErrorResponse = postErrorResponse;
    }

    /**
     *
     * @param urlString JIRA URL to connect to
     * @return VALID if we get a HTTP 200,
     *      UNAUTHORIZED if we get an HTTP 401,
     *      OTHER if we get another HTTP response code,
     *      INVALID if a MalformedURLException or IOException is thrown,
     *      INVALID_CERTIFICATE if a SSLHandshakeException is thrown.
     */
    public ConnectionStatus checkConnectionStatus(String urlString) {
        LOG.info("Checking to see if we get an HTTP 401 error for the URL '" + urlString + "'");

        ConnectionStatus retVal;

        HttpClient httpClient;
        try {
            if (proxyService == null) {
                httpClient = new HttpClient();
            } else {
                httpClient = proxyService.getClientWithProxyConfig(classToProxy);
            }

            GetMethod get = new GetMethod(urlString);

            int responseCode = httpClient.executeMethod(get);
            LOG.info("Got HTTP response code of: " + responseCode);

            if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                retVal = ConnectionStatus.UNAUTHORIZED;
            } else if (responseCode == HttpURLConnection.HTTP_OK) {
                retVal = ConnectionStatus.VALID;
            } else {
                retVal = ConnectionStatus.OTHER;
            }

        } catch (MalformedURLException e) {
            LOG.warn("URL string of '" + urlString + "' is not a valid URL.", e);
            retVal = ConnectionStatus.INVALID;
        } catch (SSLHandshakeException e) {
            LOG.warn("Certificate Error encountered while trying to find the response code.", e);
            retVal = ConnectionStatus.INVALID_CERTIFICATE;
        } catch (IOException e) {
            LOG.warn("IOException encountered while trying to find the response code: " + e.getMessage(), e);
            retVal = ConnectionStatus.INVALID;
        } catch (IllegalArgumentException e) {
            LOG.warn("IllegalArgumentException encountered while trying to find the response code: "
                    + e.getMessage(), e);
            retVal = ConnectionStatus.INVALID;
        }

        LOG.info("Return value will be " + retVal);

        return retVal;
    }

    @Override
    public boolean hasXSeraphLoginReason(String urlString, String username, String password) {
        URL url;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return false;
        }

        try {
            HttpURLConnection httpConnection;

            if (proxyService == null) {
                httpConnection = (HttpURLConnection) url.openConnection();
            } else {
                httpConnection = proxyService.getConnectionWithProxyConfig(url, classToProxy);
            }

            setupAuthorization(httpConnection, username, password);

            httpConnection.addRequestProperty("Content-Type", "application/json");
            httpConnection.addRequestProperty("Accept", "application/json");

            String headerResult = httpConnection.getHeaderField("X-Seraph-LoginReason");

            return headerResult != null && headerResult.equals("AUTHENTICATION_DENIED");
        } catch (IOException e) {
            LOG.warn("IOException encountered while trying to find the response code.", e);
        }
        return false;
    }

}