Java tutorial
// 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; } }