org.review_board.ereviewboard.core.client.ReviewboardHttpClient.java Source code

Java tutorial

Introduction

Here is the source code for org.review_board.ereviewboard.core.client.ReviewboardHttpClient.java

Source

/*******************************************************************************
 * Copyright (c) 2004 - 2009 Mylyn project committers 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:
 *     Mylyn project committers, Atlassian, Sven Krzyzak
 *******************************************************************************/
/*******************************************************************************
 * Copyright (c) 2009 Markus Knittig
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Contributors:
 *     Markus Knittig - adapted Trac, Redmine & Atlassian implementations for
 *                      Review Board
 *******************************************************************************/
package org.review_board.ereviewboard.core.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.BasicScheme;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.*;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.commons.net.*;
import org.review_board.ereviewboard.core.exception.ReviewboardException;
import org.review_board.ereviewboard.core.exception.ReviewboardResourceNotFoundException;

/**
 * HTTP Client for calling the Review Board API. Handles {@link HttpClient}
 * setup, authentication and GET/POST requests.
 * 
 * @author Markus Knittig
 */
public class ReviewboardHttpClient {

    private final AbstractWebLocation location;

    private final HttpClient httpClient;

    private String sessionCookie;

    public ReviewboardHttpClient(AbstractWebLocation location, String characterEncoding) {
        this.location = location;
        this.httpClient = createAndInitHttpClient(characterEncoding);
    }

    private HttpClient createAndInitHttpClient(String characterEncoding) {
        HttpClient httpClient = new HttpClient(WebUtil.getConnectionManager());
        WebUtil.configureHttpClient(httpClient, "eReviewBoard");
        httpClient.getParams().setContentCharset(characterEncoding);
        return httpClient;
    }

    public boolean apiEntryPointExist(IProgressMonitor monitor) throws ReviewboardException {
        GetMethod getMethod = new GetMethod(location.getUrl() + "/api/");
        int status = executeRequest(getMethod, monitor);
        return status == HttpStatus.SC_OK || status == HttpStatus.SC_UNAUTHORIZED;
    }

    public String login(String username, String password, IProgressMonitor monitor) throws ReviewboardException {

        GetMethod loginRequest = new GetMethod(location.getUrl() + "/api/info/");
        Credentials credentials = new UsernamePasswordCredentials(username, password);

        monitor = Policy.monitorFor(monitor);

        String foundSessionCookie = null;

        try {
            monitor.beginTask("Logging in", IProgressMonitor.UNKNOWN);

            // TODO: this will probably affect existing requests, might have ill side-effects
            httpClient.getState().clearCookies();

            // perform authentication
            String authHeader = new BasicScheme().authenticate(credentials, loginRequest);
            loginRequest.addRequestHeader("Authorization", authHeader);

            // execute and validate call
            int requestStatus = executeRequest(loginRequest, monitor);

            switch (requestStatus) {

            case HttpStatus.SC_OK:
                break;
            case HttpStatus.SC_UNAUTHORIZED:
                throw new ReviewboardException("Authentication failed, please check your username and password");
            default:
                throw new ReviewboardException("Request returned unacceptable status code " + requestStatus);
            }

            // look for session cookie
            for (Cookie cookie : httpClient.getState().getCookies())
                if (cookie.getName().equals("rbsessionid"))
                    foundSessionCookie = cookie.getValue();

            if (foundSessionCookie == null)
                throw new ReviewboardException("Did not find session cookie in response");

            return foundSessionCookie;

        } catch (AuthenticationException e) {
            throw new ReviewboardException(e.getMessage(), e);
        } finally {
            loginRequest.releaseConnection();
            monitor.done();
        }
    }

    private void ensureIsLoggedIn(IProgressMonitor monitor) throws ReviewboardException {

        if (sessionCookie != null)
            return;

        AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY);
        sessionCookie = login(credentials.getUserName(), credentials.getPassword(), monitor);
    }

    private String stripSlash(String url) {
        if (url.endsWith("/")) {
            return url.substring(0, url.lastIndexOf("/"));
        }
        return url;
    }

    public String executeGet(String url, IProgressMonitor monitor) throws ReviewboardException {
        GetMethod getRequest = new GetMethod(stripSlash(location.getUrl()) + url);
        configureRequestForJson(getRequest);

        return executeMethod(getRequest, monitor);
    }

    private void configureRequestForJson(HttpMethodBase request) {

        request.addRequestHeader("Accept", "application/json");

        if (request instanceof PutMethod)
            request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }

    public byte[] executeGetForBytes(String url, String acceptHeaderValue, IProgressMonitor monitor)
            throws ReviewboardException {

        GetMethod getRequest = new GetMethod(stripSlash(location.getUrl()) + url);
        getRequest.addRequestHeader("Accept", acceptHeaderValue);

        return executeMethodForBytes(getRequest, monitor);
    }

    public String executePost(String url, IProgressMonitor monitor) throws ReviewboardException {
        return executePost(url, new HashMap<String, String>(), monitor);
    }

    public String executePost(String url, Map<String, String> parameters, IProgressMonitor monitor)
            throws ReviewboardException {
        PostMethod postRequest = new PostMethod(stripSlash(location.getUrl()) + url);
        configureRequestForJson(postRequest);

        for (Map.Entry<String, String> entry : parameters.entrySet())
            postRequest.setParameter(entry.getKey(), entry.getValue());

        return executeMethod(postRequest, monitor);
    }

    public String executePost(String url, Map<String, String> parameters, List<UploadItem> uploadItems,
            IProgressMonitor monitor) throws ReviewboardException {

        PostMethod postRequest = new PostMethod(stripSlash(location.getUrl()) + url);
        configureRequestForJson(postRequest);
        List<Part> parts = new ArrayList<Part>();

        for (Map.Entry<String, String> paramEntry : parameters.entrySet())
            parts.add(new StringPart(paramEntry.getKey(), paramEntry.getValue()));

        for (UploadItem uploadItem : uploadItems) {

            String partName = uploadItem.getName();

            parts.add(new FilePart(partName,
                    new ByteArrayPartSource(uploadItem.getFileName(), uploadItem.getContent())));
        }

        postRequest.setRequestEntity(
                new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), postRequest.getParams()));

        return executeMethod(postRequest, monitor);
    }

    private String executeMethod(HttpMethodBase request, IProgressMonitor monitor) throws ReviewboardException {

        monitor = Policy.monitorFor(monitor);

        ensureIsLoggedIn(monitor);

        try {
            monitor.beginTask("Executing request", IProgressMonitor.UNKNOWN);

            int statusCode = executeRequest(request, monitor);

            if (statusCode == HttpStatus.SC_NOT_FOUND)
                throw new ReviewboardResourceNotFoundException(request.getPath());

            return getResponseBodyAsString(request, monitor);
        } finally {
            request.releaseConnection();
            monitor.done();
        }
    }

    public String executePut(String url, Map<String, String> parameters, IProgressMonitor monitor)
            throws ReviewboardException {

        PutMethod putMethod = new PutMethod(stripSlash(location.getUrl()) + url);
        configureRequestForJson(putMethod);

        List<NameValuePair> pairs = new ArrayList<NameValuePair>();

        for (Map.Entry<String, String> entry : parameters.entrySet())
            pairs.add(new NameValuePair(entry.getKey(), entry.getValue()));

        putMethod.setQueryString(pairs.toArray(new NameValuePair[0]));
        String queryString = putMethod.getQueryString();
        putMethod.setQueryString("");

        try {
            putMethod.setRequestEntity(new StringRequestEntity(queryString, null, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new ReviewboardException(e.getMessage(), e);
        }

        return executeMethod(putMethod, monitor);
    }

    public void executeDelete(String url, IProgressMonitor monitor) throws ReviewboardException {

        executeMethod(new DeleteMethod(stripSlash(location.getUrl()) + url), monitor);
    }

    private byte[] executeMethodForBytes(HttpMethodBase request, IProgressMonitor monitor)
            throws ReviewboardException {

        monitor = Policy.monitorFor(monitor);

        ensureIsLoggedIn(monitor);

        try {
            monitor.beginTask("Executing request", IProgressMonitor.UNKNOWN);

            executeRequest(request, monitor);
            return getResponseBodyAsByteArray(request, monitor);
        } finally {
            request.releaseConnection();
            monitor.done();
        }
    }

    private int executeRequest(HttpMethodBase request, IProgressMonitor monitor) throws ReviewboardException {
        HostConfiguration hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor);
        try {
            return WebUtil.execute(httpClient, hostConfiguration, request, monitor);
        } catch (IOException e) {
            throw new ReviewboardException("Exception executing " + request.getClass().getSimpleName() + " on "
                    + constructUri(request) + " : " + e.getMessage(), e);
        }
    }

    private static String constructUri(HttpMethodBase request) {
        try {
            return request.getURI().toString();
        } catch (URIException e) {
            return request.getPath();
        }
    }

    private String getResponseBodyAsString(HttpMethodBase request, IProgressMonitor monitor)
            throws ReviewboardException {

        InputStream stream = null;
        try {
            stream = getResponseBodyAsStream(request, monitor);
            return IOUtils.toString(stream);
        } catch (IOException e) {
            throw new ReviewboardException("Exception reading the response body : " + e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(stream);
        }
    }

    private InputStream getResponseBodyAsStream(HttpMethodBase request, IProgressMonitor monitor)
            throws IOException {

        return WebUtil.getResponseBodyAsStream(request, monitor);
    }

    private byte[] getResponseBodyAsByteArray(HttpMethodBase request, IProgressMonitor monitor) {

        InputStream stream = null;
        try {
            stream = getResponseBodyAsStream(request, monitor);
            return IOUtils.toByteArray(stream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(stream);
        }
    }

    public static class UploadItem {

        private final String name;
        private final String fileName;
        private final byte[] content;

        public UploadItem(String name, String fileName, byte[] content) {
            this.name = name;
            this.fileName = fileName;
            this.content = content;
        }

        public String getName() {
            return name;
        }

        public String getFileName() {
            return fileName;
        }

        public byte[] getContent() {
            return content;
        }
    }
}