com.googlesource.gerrit.plugins.hooks.rtc.network.Transport.java Source code

Java tutorial

Introduction

Here is the source code for com.googlesource.gerrit.plugins.hooks.rtc.network.Transport.java

Source

// Copyright (C) 2013 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 com.googlesource.gerrit.plugins.hooks.rtc.network;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.googlesource.gerrit.plugins.hooks.rtc.api.ResourceNotFoundException;

public class Transport {

    public static final String APP_JSON = "application/json";
    public static final String ANY = "*/*";
    public static final String APP_OSLC = "application/x-oslc-cm-change-request+json";

    private static final Log log = LogFactory.getLog(Transport.class);

    public static final ThreadLocal<String> etag = new ThreadLocal<String>();

    protected HttpClient httpclient;
    protected Gson gson;
    protected String baseUrl;
    private HttpParams httpParams;
    private RTCClient rtcClient;

    public Transport(RTCClient rtcClient, String baseUrl, DefaultHttpClient httpclient, HttpParams httpParams) {
        this.rtcClient = rtcClient;
        this.baseUrl = baseUrl;
        this.httpclient = httpclient;
        this.httpParams = httpParams;
    }

    public void setGson(Gson gson) {
        this.gson = gson;
    }

    public <T> T put(final String path, final Type typeOrClass, JsonObject data, String etag) throws IOException {
        HttpPut request = new HttpPut(toUri(path));
        if (log.isDebugEnabled())
            log.debug("Preparing PUT against " + request.getURI() + " using etag " + etag + " and data " + data);
        request.setEntity(new StringEntity(data.toString(), HTTP.UTF_8));
        if (etag != null)
            request.addHeader("If-Match", etag);
        return invoke(request, typeOrClass, APP_OSLC, APP_OSLC);
    }

    public <T> T patch(final String path, final Type typeOrClass, JsonObject data, String etag) throws IOException {
        HttpPatch request = newHttpPatch(path);
        if (log.isDebugEnabled())
            log.debug("Preparing PATCH against " + request.getURI() + " using etag " + etag + " and data " + data);
        request.setEntity(new StringEntity(data.toString(), HTTP.UTF_8));
        if (etag != null)
            request.addHeader("If-Match", etag);
        return invoke(request, typeOrClass, APP_OSLC, APP_OSLC);
    }

    public <T> T post(final String path, final Type typeOrClass, String contentType, final NameValuePair... params)
            throws IOException {
        HttpPost request = newHttpPost(path);
        if (log.isDebugEnabled())
            log.debug("Preparing POST against " + request.getURI() + " using params " + Arrays.asList(params));
        List<NameValuePair> nameValuePairs = Arrays.asList(params);
        request.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));
        return invoke(request, typeOrClass, contentType, null);
    }

    public <T> T get(final String path, final Type typeOrClass) throws IOException, MalformedURLException {
        final HttpGet request = newHttpGet(path);
        if (log.isDebugEnabled())
            log.debug("Preparing GET against " + request.getURI());
        return invoke(request, typeOrClass, APP_JSON, null);
    }

    public String get(final String path) throws IOException, MalformedURLException {
        final HttpGet request = newHttpGet(path);
        if (log.isDebugEnabled())
            log.debug("Preparing GET against " + request.getURI());
        return invoke(request, null, ANY, null);
    }

    @SuppressWarnings("unchecked")
    private synchronized <T> T invoke(HttpRequestBase request, Object typeOrClass, String acceptType,
            String contentType) throws IOException, ClientProtocolException, ResourceNotFoundException {

        if (contentType != null) {
            request.addHeader("Content-Type", contentType);
        }

        if (acceptType != null) {
            request.addHeader("Accept", acceptType);
        }

        HttpResponse response = httpclient.execute(request);
        try {
            final int code = response.getStatusLine().getStatusCode();
            if (code / 100 != 2) {
                if (code == 404) {
                    log.debug("API call failed: " + response.getStatusLine());
                    throw new ResourceNotFoundException(request.getURI());
                } else {
                    log.debug("API call failed: " + response.getStatusLine());
                    throw new IOException("API call failed! " + response.getStatusLine());
                }
            }

            String responseContentTypeString = getResponseContentType(response);
            String entityString = readEntityAsString(response);

            if (!assertValidContentType(acceptType, responseContentTypeString)) {
                log.error("Request to " + request.getURI() + " failed because of an invalid content returned:\n"
                        + entityString);
                rtcClient.setLoggedIn(false);
                throw new InvalidContentTypeException("Wrong content type '" + responseContentTypeString
                        + "' in HTTP response (Expected: " + acceptType + ")");
            }

            if (typeOrClass != null && acceptType.endsWith("json") && responseContentTypeString.endsWith("json")) {
                Transport.etag.set(extractEtag(response));
                if (typeOrClass instanceof ParameterizedType) {
                    return gson.fromJson(entityString, (Type) typeOrClass);
                } else {
                    return gson.fromJson(entityString, (Class<T>) typeOrClass);
                }
            } else if (typeOrClass != null && typeOrClass.equals(String.class)) {
                return (T) entityString;
            } else {
                if (log.isDebugEnabled())
                    log.debug(entityString);
                return null;
            }
        } finally {
            consumeHttpEntity(response.getEntity());
            Transport.etag.set(null);
        }
    }

    private boolean assertValidContentType(String acceptType, String responseContentTypeString) throws IOException {
        if (acceptType == null) {
            return true;
        }
        if (acceptType.endsWith("/*")) {
            return true;
        }
        if (acceptType.split("/")[1].equalsIgnoreCase(responseContentTypeString.split("/")[1])) {
            return true;
        }
        return false;
    }

    public String getResponseContentType(HttpResponse response) {
        Header contentType = response.getEntity().getContentType();
        if (contentType == null) {
            return null;
        }

        String contentTypeValue = contentType.getValue();
        if (contentTypeValue == null) {
            return null;
        }

        for (String contentTypeItem : contentTypeValue.split(";")) {
            if (contentTypeItem.indexOf('/') >= 0) {
                return contentTypeItem;
            }
        }
        return null;
    }

    private String readEntityAsString(HttpResponse response) throws IllegalStateException, IOException {
        String charset = "utf-8";
        Header[] contentTypes = response.getHeaders("Content-Type");
        for (Header header : contentTypes) {
            if (header.getName().equalsIgnoreCase("charset")) {
                charset = header.getValue();
            }
        }

        ByteArrayOutputStream responseOut = new ByteArrayOutputStream();
        try {
            IOUtils.copy(response.getEntity().getContent(), responseOut);
        } finally {
            responseOut.close();
        }

        return new String(responseOut.toByteArray(), Charset.forName(charset));
    }

    // We can't use the HTTP Client 4.2 EntityUtils.consume()
    // because of compatibility issues with gerrit-pgm 2.5
    // that includes httpclient 4.0
    private void consumeHttpEntity(final HttpEntity entity) throws IOException {
        if (entity == null) {
            return;
        }
        if (entity.isStreaming()) {
            InputStream instream = entity.getContent();
            if (instream != null) {
                instream.close();
            }
        }
    }

    private String extractEtag(HttpResponse response) {
        final Header etagHeader = response.getFirstHeader("ETag");
        return etagHeader == null ? null : etagHeader.getValue().substring(1, etagHeader.getValue().length() - 1);
    }

    private HttpGet newHttpGet(final String path) throws MalformedURLException {
        HttpGet get = new HttpGet(toUri(path));
        get.setParams(httpParams);
        return get;
    }

    private HttpPost newHttpPost(final String path) throws MalformedURLException {
        return new HttpPost(toUri(path));
    }

    private HttpPatch newHttpPatch(final String path) throws MalformedURLException {
        return new HttpPatch(toUri(path));
    }

    private String toUri(final String path) throws MalformedURLException {
        if (path.startsWith(baseUrl))
            return path;
        else
            return baseUrl + path;
    }
}