/** * This code was initially copied from the egit-github project * * http://git.eclipse.org/c/egit/egit-github.git/ * * and then adjusted for this API. The original contained this copyright: * * Copyright (c) 2011 GitHub Inc. * 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: * Kevin Sawicki (GitHub Inc.) - initial API and implementation * Thomas Hallgren (Cloudsmith Inc.) - Stackhammer changes */ package org.cloudsmith.geppetto.forge.client; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Type; import java.net.URI; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 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.client.utils.URLEncodedUtils; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.message.BasicNameValuePair; import org.cloudsmith.geppetto.forge.client.Authenticator.AuthResponse; import org.cloudsmith.geppetto.forge.model.Constants; import com.google.gson.Gson; import com.google.inject.Inject; import com.google.inject.name.Named; /** * Class responsible for all request and response processing */ public class ForgeHttpClient implements Constants, ForgeClient { public static final Charset UTF_8 = Charset.forName("UTF-8"); static InputStream getStream(HttpEntity entity) throws IOException { if(entity == null) return null; return entity.getContent(); } @Inject @Named(Constants.API_V1_URL_NAME) private String v1URL; @Inject @Named(Constants.API_V2_URL_NAME) private String v2URL; @Inject private Gson gson; @Inject private HttpClient httpClient; @Inject(optional = true) private Authenticator authenticator; private String userAgent = USER_AGENT; private transient String credentials; private transient HttpRequestBase currentRequest; public synchronized void abortCurrentRequest() { if(currentRequest != null) { currentRequest.abort(); currentRequest = null; } } protected void assignJSONContent(HttpEntityEnclosingRequestBase request, Object params) { if(params != null) { request.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON + "; charset=" + UTF_8.name()); //$NON-NLS-1$ byte[] data = toJson(params).getBytes(UTF_8); request.setEntity(new ByteArrayEntity(data)); } } public void authenticate() throws IOException { if(credentials == null && authenticator != null) { AuthResponse auth = authenticator.authenticate(httpClient); this.credentials = "Bearer " + auth.getToken(); } } protected void configureRequest(final HttpRequestBase request) { if(credentials != null) request.addHeader(HttpHeaders.AUTHORIZATION, credentials); else request.addHeader(HttpHeaders.USER_AGENT, userAgent); } private HttpGet createGetRequest(String urlStr, Map<String, String> params, boolean useV1) { StringBuilder bld = new StringBuilder(useV1 ? createV1Uri(urlStr) : createV2Uri(urlStr)); if(params != null && !params.isEmpty()) { List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>(); for(Map.Entry<String, String> param : params.entrySet()) pairs.add(new BasicNameValuePair(param.getKey(), param.getValue())); bld.append('?'); bld.append(URLEncodedUtils.format(pairs, UTF_8.name())); } return new HttpGet(URI.create(bld.toString())); } /** * Create full v1 URI from path * * @param path * @return uri */ protected String createV1Uri(final String path) { return v1URL + path; } /** * Create full v2 URI from path * * @param path * @return uri */ protected String createV2Uri(final String path) { return v2URL + path; } @Override public void delete(final String uri) throws IOException { HttpDelete request = new HttpDelete(createV2Uri(uri)); configureRequest(request); executeRequest(request, null); } @Override public void download(String urlStr, Map<String, String> params, final OutputStream output) throws IOException { HttpGet request = createGetRequest(urlStr, params, false); configureRequest(request); httpClient.execute(request, new ResponseHandler<Void>() { @Override public Void handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine statusLine = response.getStatusLine(); int code = statusLine.getStatusCode(); if(code != HttpStatus.SC_OK) throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); HttpEntity entity = response.getEntity(); entity.writeTo(output); return null; } }); } private synchronized void endRequest() { currentRequest = null; } protected <V> V executeRequest(final HttpRequestBase request, final Type type) throws IOException { startRequest(request); try { return httpClient.execute(request, new JSonResponseHandler<V>(gson, type)); } finally { endRequest(); } } @Override public <V> V get(String urlStr, Map<String, String> params, Type type) throws IOException { HttpGet request = createGetRequest(urlStr, params, false); configureRequest(request); return executeRequest(request, type); } @Override public <V> V getV1(String urlStr, Map<String, String> params, Type type) throws IOException { HttpGet request = createGetRequest(urlStr, params, true); configureRequest(request); return executeRequest(request, type); } @Override public <V> V patch(final String uri, final Object params, final Class<V> type) throws IOException { // HttpPatch is introduced in 4.2. We need to be compatible with 4.1 in order to // play nice with other Juno and Kepler features HttpPost request = new HttpPost(createV2Uri(uri)) { @Override public String getMethod() { return "PATCH"; } }; configureRequest(request); assignJSONContent(request, params); return executeRequest(request, type); } @Override public void post(String uri) throws IOException { postJSON(uri, null, null); } @Override public <V> V postJSON(final String uri, final Object params, final Class<V> type) throws IOException { HttpPost request = new HttpPost(createV2Uri(uri)); configureRequest(request); assignJSONContent(request, params); return executeRequest(request, type); } @Override public <V> V postUpload(String uri, Map<String, String> stringParts, InputStream in, String mimeType, String fileName, final long fileSize, Class<V> type) throws IOException { HttpPost request = new HttpPost(createV2Uri(uri)); configureRequest(request); MultipartEntity entity = new MultipartEntity(); for(Map.Entry<String, String> entry : stringParts.entrySet()) entity.addPart(entry.getKey(), StringBody.create(entry.getValue(), "text/plain", UTF_8)); entity.addPart("file", new InputStreamBody(in, mimeType, fileName) { @Override public long getContentLength() { return fileSize; } }); request.setEntity(entity); return executeRequest(request, type); } @Override public void put(String uri) throws IOException { put(uri, null, null); } @Override public <V> V put(final String uri, final Object params, final Class<V> type) throws IOException { HttpPut request = new HttpPut(createV2Uri(uri)); configureRequest(request); assignJSONContent(request, params); return executeRequest(request, type); } @Override public ForgeClient setUserAgent(final String agent) { if(agent != null && agent.length() > 0) userAgent = agent; else userAgent = USER_AGENT; return this; } private synchronized void startRequest(HttpRequestBase request) { if(currentRequest != null) currentRequest.abort(); currentRequest = request; } /** * Convert object to a JSON string * * @param object * @return JSON string * @throws IOException */ protected String toJson(Object object) { return gson.toJson(object); } }