diff options
Diffstat (limited to 'cloudify-client/src/main/java/org/onap/so/cloudify/connector/http')
4 files changed, 226 insertions, 250 deletions
diff --git a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientConnector.java b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientConnector.java index 247afb3124..e1992d98ca 100644 --- a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientConnector.java +++ b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientConnector.java @@ -30,7 +30,6 @@ import java.net.UnknownHostException; import java.util.List; import java.util.Map; import java.util.Map.Entry; - import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; @@ -55,7 +54,6 @@ import org.onap.so.cloudify.base.client.CloudifyConnectException; import org.onap.so.cloudify.base.client.CloudifyRequest; import org.onap.so.cloudify.base.client.CloudifyResponse; import org.onap.so.cloudify.base.client.CloudifyResponseException; - import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonRootName; import com.fasterxml.jackson.core.JsonProcessingException; @@ -67,183 +65,175 @@ import org.slf4j.LoggerFactory; public class HttpClientConnector implements CloudifyClientConnector { - private static ObjectMapper DEFAULT_MAPPER; - private static ObjectMapper WRAPPED_MAPPER; - + private static ObjectMapper DEFAULT_MAPPER; + private static ObjectMapper WRAPPED_MAPPER; + private static Logger logger = LoggerFactory.getLogger(HttpClientConnector.class); - static { - DEFAULT_MAPPER = new ObjectMapper(); - - DEFAULT_MAPPER.setSerializationInclusion(Include.NON_NULL); - DEFAULT_MAPPER.disable(SerializationFeature.INDENT_OUTPUT); - DEFAULT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); - DEFAULT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - - WRAPPED_MAPPER = new ObjectMapper(); - - WRAPPED_MAPPER.setSerializationInclusion(Include.NON_NULL); - WRAPPED_MAPPER.disable(SerializationFeature.INDENT_OUTPUT); - WRAPPED_MAPPER.enable(SerializationFeature.WRAP_ROOT_VALUE); - WRAPPED_MAPPER.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); - WRAPPED_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); - WRAPPED_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - } - - protected static <T> ObjectMapper getObjectMapper (Class<T> type) { - return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; - } - - public <T> CloudifyResponse request(CloudifyRequest<T> request) { - - CloseableHttpClient httpClient = null; //HttpClients.createDefault(); - - if (request.isBasicAuth()) { - // Use Basic Auth for this request. - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); - credentialsProvider.setCredentials(AuthScope.ANY, - new UsernamePasswordCredentials (request.getUser(), request.getPassword())); - - httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()).setDefaultCredentialsProvider(credentialsProvider).build(); - } - else { - // Don't use basic authentication. The Client will attempt Token-based authentication - httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()).build(); - } - - URI uri = null; - - // Build the URI with query params - try { - URIBuilder uriBuilder = new URIBuilder(request.endpoint() + request.path()); - - for(Map.Entry<String, List<Object> > entry : request.queryParams().entrySet()) { - for (Object o : entry.getValue()) { - uriBuilder.setParameter(entry.getKey(), String.valueOf(o)); - } - } - - uri = uriBuilder.build(); - } catch (URISyntaxException e) { - throw new HttpClientException (e); - } - - HttpEntity entity = null; - if (request.entity() != null) { - // Special handling for streaming input - if (request.entity().getEntity() instanceof InputStream) { - // Entity is an InputStream - entity = new InputStreamEntity ((InputStream) request.entity().getEntity()); - } - else { - // Assume to be JSON. Flatten the entity to a Json string - try { - // Get appropriate mapper, based on existence of a root element in Entity class - ObjectMapper mapper = getObjectMapper (request.entity().getEntity().getClass()); - - String entityJson = mapper.writeValueAsString (request.entity().getEntity()); - entity = new StringEntity(entityJson, ContentType.create(request.entity().getContentType())); - - logger.debug ("Request JSON Body: {}", entityJson.replaceAll("\"password\":\"[^\"]*\"", - "\"password\":\"***\"")); - - } catch (JsonProcessingException e) { - throw new HttpClientException ("Json processing error on request entity", e); - } catch (IOException e) { - throw new HttpClientException ("Json IO error on request entity", e); - } - } - } - - // Determine the HttpRequest class based on the method - HttpUriRequest httpRequest; - - switch (request.method()) { - case POST: - HttpPost post = new HttpPost(uri); - post.setEntity (entity); - httpRequest = post; - break; - - case GET: - httpRequest = new HttpGet(uri); - break; - - case PUT: - HttpPut put = new HttpPut(uri); - put.setEntity (entity); - httpRequest = put; - break; - - case DELETE: - httpRequest = new HttpDelete(uri); - break; - - default: - throw new HttpClientException ("Unrecognized HTTP Method: " + request.method()); - } - - for (Entry<String, List<Object>> h : request.headers().entrySet()) { - StringBuilder sb = new StringBuilder(); - for (Object v : h.getValue()) { - sb.append(String.valueOf(v)); - } - httpRequest.addHeader(h.getKey(), sb.toString()); - } - - // Get the Response. But don't get the body entity yet, as this response - // will be wrapped in an HttpClientResponse. The HttpClientResponse - // buffers the body in constructor, so can close the response here. - HttpClientResponse httpClientResponse = null; - CloseableHttpResponse httpResponse = null; - - // Catch known HttpClient exceptions, and wrap them in OpenStack Client Exceptions - // so calling functions can distinguish. Only RuntimeExceptions are allowed. - try { - httpResponse = httpClient.execute(httpRequest); - - logger.debug ("Response status: {}", httpResponse.getStatusLine().getStatusCode()); - - httpClientResponse = new HttpClientResponse (httpResponse); - - int status = httpResponse.getStatusLine().getStatusCode(); - if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || - status == HttpStatus.SC_NO_CONTENT || status == HttpStatus.SC_ACCEPTED) - { - return httpClientResponse; - } - } - catch (HttpResponseException e) { - // What exactly does this mean? It does not appear to get thrown for - // non-2XX responses as documented. - throw new CloudifyResponseException(e.getMessage(), e.getStatusCode()); - } - catch (UnknownHostException e) { - throw new CloudifyConnectException("Unknown Host: " + e.getMessage()); - } - catch (IOException e) { - // Catch all other IOExceptions and throw as OpenStackConnectException - throw new CloudifyConnectException(e.getMessage()); - } - catch (Exception e) { - // Catchall for anything else, must throw as a RuntimeException - logger.error("Client exception", e); - throw new RuntimeException("Unexpected client exception", e); - } - finally { - // Have the body. Close the stream - if (httpResponse != null) - try { - httpResponse.close(); - } catch (IOException e) { - logger.debug("Unable to close HTTP Response: " + e); - } - } - - // Get here on an error response (4XX-5XX) - throw new CloudifyResponseException(httpResponse.getStatusLine().getReasonPhrase(), - httpResponse.getStatusLine().getStatusCode(), - httpClientResponse); - } + static { + DEFAULT_MAPPER = new ObjectMapper(); + + DEFAULT_MAPPER.setSerializationInclusion(Include.NON_NULL); + DEFAULT_MAPPER.disable(SerializationFeature.INDENT_OUTPUT); + DEFAULT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + DEFAULT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + + WRAPPED_MAPPER = new ObjectMapper(); + + WRAPPED_MAPPER.setSerializationInclusion(Include.NON_NULL); + WRAPPED_MAPPER.disable(SerializationFeature.INDENT_OUTPUT); + WRAPPED_MAPPER.enable(SerializationFeature.WRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + WRAPPED_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + protected static <T> ObjectMapper getObjectMapper(Class<T> type) { + return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; + } + + public <T> CloudifyResponse request(CloudifyRequest<T> request) { + + CloseableHttpClient httpClient = null; // HttpClients.createDefault(); + + if (request.isBasicAuth()) { + // Use Basic Auth for this request. + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(request.getUser(), request.getPassword())); + + httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()) + .setDefaultCredentialsProvider(credentialsProvider).build(); + } else { + // Don't use basic authentication. The Client will attempt Token-based authentication + httpClient = HttpClients.custom().setRedirectStrategy(new HttpClientRedirectStrategy()).build(); + } + + URI uri = null; + + // Build the URI with query params + try { + URIBuilder uriBuilder = new URIBuilder(request.endpoint() + request.path()); + + for (Map.Entry<String, List<Object>> entry : request.queryParams().entrySet()) { + for (Object o : entry.getValue()) { + uriBuilder.setParameter(entry.getKey(), String.valueOf(o)); + } + } + + uri = uriBuilder.build(); + } catch (URISyntaxException e) { + throw new HttpClientException(e); + } + + HttpEntity entity = null; + if (request.entity() != null) { + // Special handling for streaming input + if (request.entity().getEntity() instanceof InputStream) { + // Entity is an InputStream + entity = new InputStreamEntity((InputStream) request.entity().getEntity()); + } else { + // Assume to be JSON. Flatten the entity to a Json string + try { + // Get appropriate mapper, based on existence of a root element in Entity class + ObjectMapper mapper = getObjectMapper(request.entity().getEntity().getClass()); + + String entityJson = mapper.writeValueAsString(request.entity().getEntity()); + entity = new StringEntity(entityJson, ContentType.create(request.entity().getContentType())); + + logger.debug("Request JSON Body: {}", + entityJson.replaceAll("\"password\":\"[^\"]*\"", "\"password\":\"***\"")); + + } catch (JsonProcessingException e) { + throw new HttpClientException("Json processing error on request entity", e); + } catch (IOException e) { + throw new HttpClientException("Json IO error on request entity", e); + } + } + } + + // Determine the HttpRequest class based on the method + HttpUriRequest httpRequest; + + switch (request.method()) { + case POST: + HttpPost post = new HttpPost(uri); + post.setEntity(entity); + httpRequest = post; + break; + + case GET: + httpRequest = new HttpGet(uri); + break; + + case PUT: + HttpPut put = new HttpPut(uri); + put.setEntity(entity); + httpRequest = put; + break; + + case DELETE: + httpRequest = new HttpDelete(uri); + break; + + default: + throw new HttpClientException("Unrecognized HTTP Method: " + request.method()); + } + + for (Entry<String, List<Object>> h : request.headers().entrySet()) { + StringBuilder sb = new StringBuilder(); + for (Object v : h.getValue()) { + sb.append(String.valueOf(v)); + } + httpRequest.addHeader(h.getKey(), sb.toString()); + } + + // Get the Response. But don't get the body entity yet, as this response + // will be wrapped in an HttpClientResponse. The HttpClientResponse + // buffers the body in constructor, so can close the response here. + HttpClientResponse httpClientResponse = null; + CloseableHttpResponse httpResponse = null; + + // Catch known HttpClient exceptions, and wrap them in OpenStack Client Exceptions + // so calling functions can distinguish. Only RuntimeExceptions are allowed. + try { + httpResponse = httpClient.execute(httpRequest); + + logger.debug("Response status: {}", httpResponse.getStatusLine().getStatusCode()); + + httpClientResponse = new HttpClientResponse(httpResponse); + + int status = httpResponse.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT + || status == HttpStatus.SC_ACCEPTED) { + return httpClientResponse; + } + } catch (HttpResponseException e) { + // What exactly does this mean? It does not appear to get thrown for + // non-2XX responses as documented. + throw new CloudifyResponseException(e.getMessage(), e.getStatusCode()); + } catch (UnknownHostException e) { + throw new CloudifyConnectException("Unknown Host: " + e.getMessage()); + } catch (IOException e) { + // Catch all other IOExceptions and throw as OpenStackConnectException + throw new CloudifyConnectException(e.getMessage()); + } catch (Exception e) { + // Catchall for anything else, must throw as a RuntimeException + logger.error("Client exception", e); + throw new RuntimeException("Unexpected client exception", e); + } finally { + // Have the body. Close the stream + if (httpResponse != null) + try { + httpResponse.close(); + } catch (IOException e) { + logger.debug("Unable to close HTTP Response: " + e); + } + } + + // Get here on an error response (4XX-5XX) + throw new CloudifyResponseException(httpResponse.getStatusLine().getReasonPhrase(), + httpResponse.getStatusLine().getStatusCode(), httpClientResponse); + } } diff --git a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientException.java b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientException.java index eb967af996..6f170baef5 100644 --- a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientException.java +++ b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientException.java @@ -21,22 +21,22 @@ package org.onap.so.cloudify.connector.http; /* - * Declare a RuntimeException since the Interface does not declare any - * throwables. Any caught exception will be wrapped in HttpClientException + * Declare a RuntimeException since the Interface does not declare any throwables. Any caught exception will be wrapped + * in HttpClientException */ public class HttpClientException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public HttpClientException (String s) { - super (s); - } - - public HttpClientException (Exception e) { - super ("Caught nested exception in HttpClient", e); - } - - public HttpClientException (String s, Exception e) { - super (s, e); - } + public HttpClientException(String s) { + super(s); + } + + public HttpClientException(Exception e) { + super("Caught nested exception in HttpClient", e); + } + + public HttpClientException(String s, Exception e) { + super(s, e); + } } diff --git a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientRedirectStrategy.java b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientRedirectStrategy.java index a7575ff499..b8b4a5b018 100644 --- a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientRedirectStrategy.java +++ b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientRedirectStrategy.java @@ -21,7 +21,6 @@ package org.onap.so.cloudify.connector.http; import java.net.URI; - import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -36,15 +35,12 @@ import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.protocol.HttpContext; /** - * Custom {@link org.apache.http.client.RedirectStrategy} implementation - * that automatically redirects all HEAD, GET and DELETE requests. - * The {@link org.apache.http.client.DefaultRedirectStrategy} only - * redirects GET and HEAD automatically, per the HTTP specification - * (POST and PUT typically have bodies and thus cannot be redirected). + * Custom {@link org.apache.http.client.RedirectStrategy} implementation that automatically redirects all HEAD, GET and + * DELETE requests. The {@link org.apache.http.client.DefaultRedirectStrategy} only redirects GET and HEAD + * automatically, per the HTTP specification (POST and PUT typically have bodies and thus cannot be redirected). * - * A custom strategy is needed for the Openstack API, which can also send - * 302 on a DELETE (by name) request, expecting the client to follow the - * redirect to perform the actual deletion. + * A custom strategy is needed for the Openstack API, which can also send 302 on a DELETE (by name) request, expecting + * the client to follow the redirect to perform the actual deletion. */ @Immutable public class HttpClientRedirectStrategy extends DefaultRedirectStrategy { @@ -52,20 +48,16 @@ public class HttpClientRedirectStrategy extends DefaultRedirectStrategy { /** * Redirectable methods. */ - private static final String[] REDIRECT_METHODS = new String[] { - HttpGet.METHOD_NAME, - HttpDelete.METHOD_NAME, - HttpHead.METHOD_NAME - }; + private static final String[] REDIRECT_METHODS = + new String[] {HttpGet.METHOD_NAME, HttpDelete.METHOD_NAME, HttpHead.METHOD_NAME}; /** - * Determine if the request should be redirected. - * This may not actually be needed, since the REDIRECT_METHODS - * array has been updated with the DELETE. + * Determine if the request should be redirected. This may not actually be needed, since the REDIRECT_METHODS array + * has been updated with the DELETE. */ @Override protected boolean isRedirectable(final String method) { - for (final String m: REDIRECT_METHODS) { + for (final String m : REDIRECT_METHODS) { if (m.equalsIgnoreCase(method)) { return true; } @@ -74,16 +66,13 @@ public class HttpClientRedirectStrategy extends DefaultRedirectStrategy { } /** - * Override the default redirect handling method. As implemented - * in HttpClient, it does not preserve the method on 301 or 302 - * responses, always redirecting to a GET. + * Override the default redirect handling method. As implemented in HttpClient, it does not preserve the method on + * 301 or 302 responses, always redirecting to a GET. */ @Override - public HttpUriRequest getRedirect( - final HttpRequest request, - final HttpResponse response, - final HttpContext context) throws ProtocolException { - + public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response, final HttpContext context) + throws ProtocolException { + final URI uri = getLocationURI(request, response, context); final String method = request.getRequestLine().getMethod(); if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) { @@ -93,14 +82,14 @@ public class HttpClientRedirectStrategy extends DefaultRedirectStrategy { } else { final int status = response.getStatusLine().getStatusCode(); - - HttpUriRequest newRequest = null; - if (status == HttpStatus.SC_TEMPORARY_REDIRECT || status == HttpStatus.SC_MOVED_TEMPORARILY) { + + HttpUriRequest newRequest = null; + if (status == HttpStatus.SC_TEMPORARY_REDIRECT || status == HttpStatus.SC_MOVED_TEMPORARILY) { newRequest = RequestBuilder.copy(request).setUri(uri).build(); } else { - newRequest = new HttpGet(uri); + newRequest = new HttpGet(uri); } - return newRequest; + return newRequest; } } } diff --git a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientResponse.java b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientResponse.java index b9e7851c98..a96fdb6b50 100644 --- a/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientResponse.java +++ b/cloudify-client/src/main/java/org/onap/so/cloudify/connector/http/HttpClientResponse.java @@ -25,11 +25,9 @@ package org.onap.so.cloudify.connector.http; import org.apache.http.Header; import org.apache.http.HttpResponse; import com.fasterxml.jackson.databind.ObjectMapper; - import org.onap.so.cloudify.base.client.CloudifyResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -40,44 +38,43 @@ import java.util.Map; public class HttpClientResponse implements CloudifyResponse { private static Logger logger = LoggerFactory.getLogger(HttpClientResponse.class); - + private HttpResponse response = null; private String entityBody = null; - public HttpClientResponse(HttpResponse response) - { + public HttpClientResponse(HttpResponse response) { this.response = response; - + // Read the body so InputStream can be closed if (response.getEntity() == null) { // No body logger.debug("No Response Body"); return; } - - ByteArrayOutputStream responseBody = new ByteArrayOutputStream(); - try { - response.getEntity().writeTo(responseBody); - } catch (IOException e) { - throw new HttpClientException ("Error Reading Response Body", e); - } - entityBody = responseBody.toString(); + + ByteArrayOutputStream responseBody = new ByteArrayOutputStream(); + try { + response.getEntity().writeTo(responseBody); + } catch (IOException e) { + throw new HttpClientException("Error Reading Response Body", e); + } + entityBody = responseBody.toString(); logger.debug(entityBody); } - + @Override - public <T> T getEntity (Class<T> returnType) { - // Get appropriate mapper, based on existence of a root element - ObjectMapper mapper = HttpClientConnector.getObjectMapper (returnType); - - T resp = null; - try { - resp = mapper.readValue(entityBody, returnType); - } catch (Exception e) { - throw new HttpClientException ("Caught exception in getEntity", e); - } - return resp; + public <T> T getEntity(Class<T> returnType) { + // Get appropriate mapper, based on existence of a root element + ObjectMapper mapper = HttpClientConnector.getObjectMapper(returnType); + + T resp = null; + try { + resp = mapper.readValue(entityBody, returnType); + } catch (Exception e) { + throw new HttpClientException("Caught exception in getEntity", e); + } + return resp; } @Override @@ -87,7 +84,7 @@ public class HttpClientResponse implements CloudifyResponse { @Override public InputStream getInputStream() { - return new ByteArrayInputStream (entityBody.getBytes()); + return new ByteArrayInputStream(entityBody.getBytes()); } @Override |