/** * Copyright (c) 2012 Cloudsmith Inc. and other contributors, as listed below. * 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: * Cloudsmith * */ package org.cloudsmith.geppetto.forge.client; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.util.List; import org.apache.http.HttpEntity; 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.HttpResponseException; import org.apache.http.client.ResponseHandler; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.annotations.Expose; /** * A response handler that expects JSON content. * * @param <V> */ public class JSonResponseHandler<V> implements ResponseHandler<V> { public static class ErrorResponse { @Expose private List<String> errors; public List<String> getErrors() { return errors; } @Override public String toString() { if(errors == null || errors.isEmpty()) return "unknown reason"; int top = errors.size(); if(top == 1) return errors.get(0); StringBuilder bld = new StringBuilder(); bld.append("Multiple errors ["); bld.append(errors.get(0)); for(int idx = 1; idx < top; ++idx) { bld.append(", "); bld.append(errors.get(idx)); } bld.append(']'); return bld.toString(); } } protected static <T> T parseJson(Gson gson, InputStream stream, Type type) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(stream, ForgeHttpClient.UTF_8), 2048); StringBuilder bld = new StringBuilder(); String line; while((line = reader.readLine()) != null) { bld.append(line); bld.append('\n'); } try { return gson.fromJson(bld.toString(), type); } catch(JsonSyntaxException jpe) { throw new ForgeException("Parse exception converting JSON to object", jpe); //$NON-NLS-1$ } finally { try { reader.close(); } catch(IOException ignored) { // Ignored } } } private final Gson gson; private final Type type; public JSonResponseHandler(Gson gson, Type type) { this.gson = gson; this.type = type; } /** * Create exception from response * * @param response * @param code * @param status * @return non-null newly {@link IOException} */ protected HttpResponseException createException(InputStream response, int code, String status) { String message; if(status != null && status.length() > 0) message = status; else message = "Unknown error occurred"; return new HttpResponseException(code, message); } @Override public V handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine statusLine = response.getStatusLine(); int code = statusLine.getStatusCode(); if(code >= 300) { String msg; try { ErrorResponse errors = parseJson( gson, ForgeHttpClient.getStream(response.getEntity()), ErrorResponse.class); if(errors == null) msg = statusLine.getReasonPhrase(); else { msg = statusLine.getReasonPhrase() + ": " + errors; } } catch(Exception e) { // Just skip msg = statusLine.getReasonPhrase(); } throw new HttpResponseException(statusLine.getStatusCode(), msg); } HttpEntity entity = response.getEntity(); if(isOk(code)) { if(type == null) return null; return parseJson(ForgeHttpClient.getStream(entity), type); } throw createException(ForgeHttpClient.getStream(entity), code, statusLine.getReasonPhrase()); } /** * Does status code denote a non-error response? * * @param code * @return true if okay, false otherwise */ protected boolean isOk(final int code) { switch(code) { case HttpStatus.SC_OK: case HttpStatus.SC_CREATED: case HttpStatus.SC_ACCEPTED: case HttpStatus.SC_NO_CONTENT: // weird, but returned by DELETE calls return true; default: return false; } } /** * Parse JSON to specified type * * @param <V> * @param stream * @param type * @return parsed type * @throws IOException */ protected V parseJson(InputStream stream, Type type) throws IOException { return parseJson(gson, stream, type); } }