package io.fathom.cloud.openstack.client; import io.fathom.http.HttpClient; import io.fathom.http.HttpMethod; import io.fathom.http.HttpRequest; import io.fathom.http.HttpResponse; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.io.ByteSource; import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import com.google.common.net.HttpHeaders; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; public abstract class SimpleRestClient { private static final Logger log = LoggerFactory.getLogger(SimpleRestClient.class); public static final Charset UTF_8 = Charset.forName("UTF-8"); // public static final DefaultHttpClient DEFAULT_HTTP_CLIENT = // buildDefaultHttpClient(); // // static DefaultHttpClient buildDefaultHttpClient() { // PoolingClientConnectionManager ccm = new // PoolingClientConnectionManager(); // DefaultHttpClient httpClient = new DefaultHttpClient(ccm); // // return httpClient; // } final HttpClient httpConfiguration; protected static final Gson gson = buildGson(); final URI baseUri; public SimpleRestClient(HttpClient httpConfiguration, URI baseUri) { this.httpConfiguration = httpConfiguration; this.baseUri = baseUri; } private static Gson buildGson() { Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateTypeAdapter()).create(); return gson; } // private static ObjectMapper buildObjectMapper() { // ObjectMapper objectMapper = new ObjectMapper(); // objectMapper.setSerializationInclusion(Include.NON_NULL); // // // Use JAXB annotations // AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(); // objectMapper = objectMapper.setAnnotationIntrospector(introspector); // // objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, // false); // // // mapper = mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, // // true); // // mapper = // // mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, // // true); // // mapper = mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, // // true); // // objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, // true); // // return objectMapper; // } protected <T> T doSimpleGet(String relativePath, Class<T> clazz) throws RestClientException { String json = doGet(relativePath); return readSingleValue(clazz, json); } protected <T> T doRequest(HttpRequest request, Class<T> clazz) throws RestClientException { String json = doStringRequest(request); return readSingleValue(clazz, json); } protected <T> List<T> doListRequest(HttpRequest request, Class<T> clazz) throws RestClientException { String json = doStringRequest(request); return readList(json, clazz); } protected <T> T doPost(String relativePath, Object post, Class<T> clazz) throws RestClientException { String json = doPost(relativePath, post); return readSingleValue(clazz, json); } protected <T> T doJsonRequest(HttpRequest request, Class<T> clazz) throws RestClientException { String json = doStringRequest(request); return readSingleValue(clazz, json); } protected <T> T readSingleValue(Class<T> clazz, String json) throws RestClientException { if (json == null) { return null; } // ObjectReader reader = objectMapper.reader(clazz); // T ret; // try { // ret = reader.readValue(json); // } catch (IOException e) { // log.info("Error parsing JSON: " + json); // throw new RestClientException("Error deserializing response", e); // } // // return ret; try { return gson.fromJson(json, clazz); } catch (JsonParseException e) { throw new RestClientException("Error deserializing response", e); } } private <T> List<T> doListGet(String relativePath, Class<T> clazz) throws RestClientException { String json = doGet(relativePath); List<T> ret = readList(json, clazz); // ObjectReader reader = objectMapper.reader(clazz); // List<T> ret = Lists.newArrayList(); // try { // MappingIterator<T> iterator = reader.readValues(json); // while (iterator.hasNext()) { // T next = iterator.next(); // ret.add(next); // } // } catch (IOException e) { // throw new RestClientException("Error deserializing response", e); // } return ret; } private <T> List<T> readList(String json, Class<T> clazz) throws RestClientException { List<T> ret = new ArrayList<T>(); try { JsonParser parser = new JsonParser(); JsonArray array = parser.parse(json).getAsJsonArray(); for (int i = 0; i < array.size(); i++) { T next = gson.fromJson(array.get(i), clazz); ret.add(next); } } catch (JsonParseException e) { throw new RestClientException("Error deserializing response", e); } return ret; } private String doGet(String relativePath) throws RestClientException { HttpRequest request = buildGet(relativePath); return doStringRequest(request); } protected HttpRequest buildGet(String relativePath) throws RestClientException { URI uri = resolve(relativePath); HttpRequest request = httpConfiguration.buildRequest(HttpMethod.GET, uri); addHeaders(request); return request; } protected URI resolve(String relativePath) { return baseUri.resolve(relativePath); } protected HttpRequest buildHead(String relativePath) throws RestClientException { URI uri = resolve(relativePath); HttpRequest request = httpConfiguration.buildRequest(HttpMethod.HEAD, uri); addHeaders(request); return request; } protected HttpRequest buildPut(String relativePath) throws RestClientException { URI uri = resolve(relativePath); HttpRequest request = httpConfiguration.buildRequest(HttpMethod.PUT, uri); addHeaders(request); return request; } protected HttpRequest buildDelete(String relativePath) throws RestClientException { URI uri = resolve(relativePath); HttpRequest request = httpConfiguration.buildRequest(HttpMethod.DELETE, uri); addHeaders(request); return request; } protected void addHeaders(HttpRequest request) throws RestClientException { } protected HttpRequest buildPost(String relativePath) throws RestClientException { URI uri = resolve(relativePath); HttpRequest request = httpConfiguration.buildRequest(HttpMethod.POST, uri); addHeaders(request); return request; } protected String doPost(String relativePath, Object data) throws RestClientException { HttpRequest request = buildPost(relativePath); setEntityJson(request, data); return doStringRequest(request); } protected void setEntityJson(HttpRequest request, Object data) throws RestClientException { String json = asJson(data); request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); setRequestContent(request, ByteSource.wrap(json.getBytes(Charsets.UTF_8))); } protected String doStringRequest(HttpRequest request) throws RestClientException { HttpResponse response = executeRawRequest(request); try { String json = null; try (InputStream is = response.getInputStream()) { if (is != null) { try (InputStreamReader reader = new InputStreamReader(is, Charsets.UTF_8)) { json = CharStreams.toString(reader); } } } catch (IOException e) { throw new RestClientException("Error reading response", e); } return json; } finally { closeQuietly(response); } } protected byte[] doByteArrayRequest(HttpRequest request) throws RestClientException { HttpResponse response = executeRawRequest(request); try { byte[] data = null; try (InputStream is = response.getInputStream()) { if (is != null) { data = ByteStreams.toByteArray(is); } } catch (IOException e) { throw new RestClientException("Error reading response", e); } return data; } finally { closeQuietly(response); } } public static void closeQuietly(HttpResponse response) { if (response == null) { return; } try { response.close(); } catch (IOException e) { log.warn("Error closing response", e); } } /** * Deprecated because you have to close HttpResponse. Use doByteArrayRequest * or doStringRequest instead?? */ @Deprecated protected HttpResponse executeRawRequest(HttpRequest request) throws RestClientException { int attempt = 0; while (true) { attempt++; log.debug("Doing HTTP {} {}", request.getMethod(), request.getUrl()); HttpResponse response; int statusCode; try { response = request.doRequest(); statusCode = response.getHttpResponseCode(); } catch (IOException e) { throw new RestClientException("Error during call", e); } if (!isSuccess(request, statusCode)) { closeQuietly(response); if (shouldRetry(attempt, statusCode)) { continue; } else { throw new RestClientException("Error during API call. StatusCode: " + statusCode, statusCode); } } return response; } } protected boolean isSuccess(HttpRequest request, int statusCode) { switch (statusCode) { case 200: // OK case 201: // Created case 202: // Accepted case 204:// No content return true; case 206: // Partial content // TODO: Check that we passed a range header? return true; default: return false; } } protected boolean shouldRetry(int attempt, int statusCode) { return false; } public static String asJson(Object o) { // ObjectWriter writer = objectMapper.writer(); // String postDataJson; // try { // postDataJson = writer.writeValueAsString(o); // } catch (JsonProcessingException e) { // throw new IllegalStateException("Error serializing value", e); // } // return postDataJson; try { return gson.toJson(o); } catch (JsonParseException e) { throw new IllegalStateException("Error serializing value", e); } } protected static String urlEscape(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(); } } protected void setRequestContent(HttpRequest request, ByteSource entity) throws RestClientException { try { request.setRequestContent(entity); } catch (IOException e) { throw new RestClientException("Error setting request content", e); } } public HttpClient getHttpClient() { return this.httpConfiguration; } public URI getBaseUri() { return baseUri; } }