From 2e984294ac28c6f2ede290c38164c5d536ccaf4a Mon Sep 17 00:00:00 2001 From: ChrisC Date: Tue, 31 Jan 2017 13:57:24 +0100 Subject: Initial OpenECOMP MSO OpenStack SDK lib commit Change-Id: Ieaacb2b2c0dcc469669880e73f0cda9fa59a6c5a Signed-off-by: ChrisC --- openstack-client-connectors/http-connector/pom.xml | 31 +++ .../openstack/connector/HttpClientConnector.java | 227 +++++++++++++++++ .../openstack/connector/HttpClientException.java | 45 ++++ .../connector/HttpClientRedirectStrategy.java | 109 ++++++++ .../openstack/connector/HttpClientResponse.java | 110 ++++++++ ....openstack.base.client.OpenStackClientConnector | 1 + .../jersey-connector/pom.xml | 25 ++ .../openstack/connector/JerseyConnector.java | 104 ++++++++ .../openstack/connector/JerseyLoggingFilter.java | 279 +++++++++++++++++++++ .../woorea/openstack/connector/JerseyResponse.java | 66 +++++ ....openstack.base.client.OpenStackClientConnector | 1 + .../jersey2-connector/pom.xml | 35 +++ .../openstack/connector/JaxRs20Connector.java | 65 +++++ .../openstack/connector/JaxRs20Response.java | 61 +++++ .../com/woorea/openstack/connector/OpenStack.java | 110 ++++++++ ....openstack.base.client.OpenStackClientConnector | 1 + openstack-client-connectors/pom.xml | 60 +++++ .../resteasy-connector/pom.xml | 30 +++ .../openstack/connector/RESTEasyConnector.java | 125 +++++++++ .../openstack/connector/RESTEasyInputStream.java | 37 +++ .../openstack/connector/RESTEasyResponse.java | 59 +++++ ....openstack.base.client.OpenStackClientConnector | 1 + 22 files changed, 1582 insertions(+) create mode 100644 openstack-client-connectors/http-connector/pom.xml create mode 100644 openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientConnector.java create mode 100644 openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientException.java create mode 100644 openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientRedirectStrategy.java create mode 100644 openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientResponse.java create mode 100644 openstack-client-connectors/http-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector create mode 100644 openstack-client-connectors/jersey-connector/pom.xml create mode 100644 openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyConnector.java create mode 100644 openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyLoggingFilter.java create mode 100644 openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyResponse.java create mode 100644 openstack-client-connectors/jersey-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector create mode 100644 openstack-client-connectors/jersey2-connector/pom.xml create mode 100644 openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Connector.java create mode 100644 openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Response.java create mode 100644 openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/OpenStack.java create mode 100644 openstack-client-connectors/jersey2-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector create mode 100644 openstack-client-connectors/pom.xml create mode 100644 openstack-client-connectors/resteasy-connector/pom.xml create mode 100644 openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyConnector.java create mode 100644 openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyInputStream.java create mode 100644 openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyResponse.java create mode 100644 openstack-client-connectors/resteasy-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector (limited to 'openstack-client-connectors') diff --git a/openstack-client-connectors/http-connector/pom.xml b/openstack-client-connectors/http-connector/pom.xml new file mode 100644 index 0000000..375f579 --- /dev/null +++ b/openstack-client-connectors/http-connector/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + org.openecomp.mso.libs.openstack-java-sdk + client-connectors + 1.0.0-SNAPSHOT + + + org.openecomp.mso.libs.openstack-java-sdk.client-connectors + http-connector + OpenStack HTTP-Client Connector + OpenStack HTTP-Client Connector + + + org.apache.httpcomponents + httpclient + 4.3.5 + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.13 + + + log4j + log4j + 1.2.17 + + + + \ No newline at end of file diff --git a/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientConnector.java b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientConnector.java new file mode 100644 index 0000000..d748a79 --- /dev/null +++ b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientConnector.java @@ -0,0 +1,227 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + * ECOMP and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + * + */ + +package com.woorea.openstack.connector; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +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.client.HttpResponseException; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +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.HttpUriRequest; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.log4j.Logger; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.annotate.JsonRootName; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; + +import com.woorea.openstack.base.client.OpenStackClientConnector; +import com.woorea.openstack.base.client.OpenStackConnectException; +import com.woorea.openstack.base.client.OpenStackRequest; +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class HttpClientConnector implements OpenStackClientConnector { + + public static ObjectMapper DEFAULT_MAPPER; + public static ObjectMapper WRAPPED_MAPPER; + + private static Logger LOGGER = Logger.getLogger(HttpClientConnector.class); + + static { + DEFAULT_MAPPER = new ObjectMapper(); + + DEFAULT_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + DEFAULT_MAPPER.disable(SerializationConfig.Feature.INDENT_OUTPUT); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + DEFAULT_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + + WRAPPED_MAPPER = new ObjectMapper(); + + WRAPPED_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + WRAPPED_MAPPER.disable(SerializationConfig.Feature.INDENT_OUTPUT); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + WRAPPED_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + protected static ObjectMapper getObjectMapper (Class type) { + return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; + } + + public OpenStackResponse request(OpenStackRequest request) { + + CloseableHttpClient httpClient = null; //HttpClients.createDefault(); + 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 > 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) { + // 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())); + + System.out.println("Openstack query JSON:"+entityJson); + 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> h : request.headers().entrySet()) { + StringBuilder sb = new StringBuilder(); + for (Object v : h.getValue()) { + sb.append(String.valueOf(v)); + } + httpRequest.addHeader(h.getKey(), sb.toString()); + } + + LOGGER.debug ("Sending HTTP request: " + httpRequest.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 OpenStackResponseException(e.getMessage(), e.getStatusCode()); + } + catch (UnknownHostException e) { + throw new OpenStackConnectException("Unknown Host: " + e.getMessage()); + } + catch (IOException e) { + // Catch all other IOExceptions and throw as OpenStackConnectException + throw new OpenStackConnectException(e.getMessage()); + } + catch (Exception e) { + // Catchall for anything else, must throw as a RuntimeException + e.printStackTrace(); + throw new RuntimeException("Unexpected client exception", e); + } + finally { + // Have the body. Close the stream + if (httpResponse != null) + try { + httpResponse.close(); + } catch (IOException e) { + LOGGER.warn("Unable to close HTTP Response: " + e); + } + } + + // Get here on an error response (4XX-5XX) + throw new OpenStackResponseException(httpResponse.getStatusLine().getReasonPhrase(), + httpResponse.getStatusLine().getStatusCode(), + httpClientResponse); + } + +} diff --git a/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientException.java b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientException.java new file mode 100644 index 0000000..9f93455 --- /dev/null +++ b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientException.java @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + * ECOMP and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + * + */ + +package com.woorea.openstack.connector; + +/* + * 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; + + 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/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientRedirectStrategy.java b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientRedirectStrategy.java new file mode 100644 index 0000000..88f20bb --- /dev/null +++ b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientRedirectStrategy.java @@ -0,0 +1,109 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + * ECOMP and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + * + */ + +package com.woorea.openstack.connector; + +import java.net.URI; + +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.ProtocolException; +import org.apache.http.annotation.Immutable; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +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). + * + * 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 { + + /** + * Redirectable methods. + */ + 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. + */ + @Override + protected boolean isRedirectable(final String method) { + for (final String m: REDIRECT_METHODS) { + if (m.equalsIgnoreCase(method)) { + return true; + } + } + return false; + } + + /** + * 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 { + + final URI uri = getLocationURI(request, response, context); + final String method = request.getRequestLine().getMethod(); + if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) { + return new HttpHead(uri); + } else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) { + return new HttpGet(uri); + } else { + + final int status = response.getStatusLine().getStatusCode(); + + 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); + } + return newRequest; + } + } +} diff --git a/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientResponse.java b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientResponse.java new file mode 100644 index 0000000..e1850a2 --- /dev/null +++ b/openstack-client-connectors/http-connector/src/main/java/com/woorea/openstack/connector/HttpClientResponse.java @@ -0,0 +1,110 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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. + * ============LICENSE_END============================================ + * + * ECOMP and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + * + */ + +package com.woorea.openstack.connector; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.log4j.Logger; +import org.codehaus.jackson.map.ObjectMapper; + +import com.woorea.openstack.base.client.OpenStackResponse; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class HttpClientResponse implements OpenStackResponse { + + private static Logger LOGGER = Logger.getLogger(HttpClientConnector.class); + + private HttpResponse response = null; + private String entityBody = null; + + 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(); + LOGGER.debug (entityBody); + } + + + @Override + public T getEntity (Class 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 + public T getErrorEntity(Class returnType) { + return getEntity(returnType); + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream (entityBody.getBytes()); + } + + @Override + public String header(String name) { + return response.getFirstHeader(name).getValue(); + } + + @Override + public Map headers() { + Map headers = new HashMap(); + + Header responseHeaders[] = response.getAllHeaders(); + for (Header h : responseHeaders) { + headers.put(h.getName(), h.getValue()); + } + + return headers; + } + +} diff --git a/openstack-client-connectors/http-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/openstack-client-connectors/http-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector new file mode 100644 index 0000000..1281d32 --- /dev/null +++ b/openstack-client-connectors/http-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector @@ -0,0 +1 @@ +com.woorea.openstack.connector.HttpClientConnector \ No newline at end of file diff --git a/openstack-client-connectors/jersey-connector/pom.xml b/openstack-client-connectors/jersey-connector/pom.xml new file mode 100644 index 0000000..24489d9 --- /dev/null +++ b/openstack-client-connectors/jersey-connector/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + org.openecomp.mso.libs.openstack-java-sdk + client-connectors + 1.0.0-SNAPSHOT + + org.openecomp.mso.libs.openstack-java-sdk.client-connectors + jersey-connector + OpenStack Jersey Connector + OpenStack Jersey Connector + + + com.sun.jersey + jersey-client + 1.17.1 + + + org.codehaus.jackson + jackson-jaxrs + 1.9.12 + + + + diff --git a/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyConnector.java b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyConnector.java new file mode 100644 index 0000000..afd7a2b --- /dev/null +++ b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyConnector.java @@ -0,0 +1,104 @@ +package com.woorea.openstack.connector; + +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Provider; + +import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.annotate.JsonRootName; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.client.impl.ClientRequestImpl; +import com.sun.jersey.core.header.OutBoundHeaders; +import com.woorea.openstack.base.client.OpenStackClientConnector; +import com.woorea.openstack.base.client.OpenStackRequest; +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class JerseyConnector implements OpenStackClientConnector { + + protected Client client = null; + protected boolean logPassword; + private JerseyLoggingFilter logger = new JerseyLoggingFilter(Logger.getLogger("os")); + + public JerseyConnector() { + ClientConfig clientConfig = new DefaultClientConfig(); + clientConfig.getClasses().add(JacksonJaxbJsonProvider.class); + clientConfig.getClasses().add(OpenStackObjectMapper.class); + client = Client.create(clientConfig); + } + + @Override + public OpenStackResponse request(OpenStackRequest request) { + WebResource target = client.resource(request.endpoint()).path(request.path()); + for(Map.Entry > entry : request.queryParams().entrySet()) { + for (Object o : entry.getValue()) { + target = target.queryParam(entry.getKey(), String.valueOf(o)); + } + } + target.addFilter(logger); + MultivaluedMap headers = new OutBoundHeaders(); + for(Map.Entry> h : request.headers().entrySet()) { + for(Object v : h.getValue()) { + headers.add(h.getKey(), v); + } + } + if(request.entity() != null && request.entity().getContentType() != null) { + headers.add("Content-Type", request.entity().getContentType()); + } else { + headers.add("Content-Type", "application/json"); + } + try { + ClientResponse response = null; + if (request.entity() != null && request.entity().getEntity() != null) { + response = target.getHeadHandler().handle(new ClientRequestImpl(target.getURI(), request.method().name(), request.entity().getEntity(), headers)); + } else { + response = target.getHeadHandler().handle(new ClientRequestImpl(target.getURI(), request.method().name(), null, headers)); + } + return new JerseyResponse(response); + } catch (UniformInterfaceException e) { + throw new OpenStackResponseException(e.getResponse().getClientResponseStatus().getReasonPhrase(), e.getResponse().getStatus()); + } + } + + @Provider + public static class OpenStackObjectMapper implements ContextResolver { + static ObjectMapper DEFAULT_MAPPER; + static ObjectMapper WRAPPED_MAPPER; + static { + DEFAULT_MAPPER = new ObjectMapper(); + DEFAULT_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + DEFAULT_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + DEFAULT_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + + WRAPPED_MAPPER = new ObjectMapper(); + WRAPPED_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + WRAPPED_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + } + + @Override + public ObjectMapper getContext(Class type) { + return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; + } + } +} diff --git a/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyLoggingFilter.java b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyLoggingFilter.java new file mode 100644 index 0000000..c1877c3 --- /dev/null +++ b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyLoggingFilter.java @@ -0,0 +1,279 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package com.woorea.openstack.connector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import javax.ws.rs.core.MultivaluedMap; + +import com.sun.jersey.api.client.AbstractClientRequestAdapter; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.ClientRequest; +import com.sun.jersey.api.client.ClientRequestAdapter; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.filter.ClientFilter; +import com.sun.jersey.api.client.filter.LoggingFilter; +import com.sun.jersey.core.util.ReaderWriter; + +/** + * A logging filter. + * + */ +public class JerseyLoggingFilter extends ClientFilter { + + private static final Logger LOGGER = Logger.getLogger(LoggingFilter.class.getName()); + + private static final String NOTIFICATION_PREFIX = "* "; + + private static final String REQUEST_PREFIX = "> "; + + private static final String RESPONSE_PREFIX = "< "; + + private static final String PASSWORD_PATTERN = "\"password\".*:.*\"(.*)\""; + + private final class Adapter extends AbstractClientRequestAdapter { + private final StringBuilder b; + + Adapter(ClientRequestAdapter cra, StringBuilder b) { + super(cra); + this.b = b; + } + + public OutputStream adapt(ClientRequest request, OutputStream out) throws IOException { + return new LoggingOutputStream(getAdapter().adapt(request, out), b); + } + + } + + private final class LoggingOutputStream extends OutputStream { + private final OutputStream out; + + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + private final StringBuilder b; + + LoggingOutputStream(OutputStream out, StringBuilder b) { + this.out = out; + this.b = b; + } + + @Override + public void write(byte[] b) throws IOException { + baos.write(b); + out.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + baos.write(b, off, len); + out.write(b, off, len); + } + + @Override + public void write(int b) throws IOException { + baos.write(b); + out.write(b); + } + + @Override + public void close() throws IOException { + printEntity(b, baos.toByteArray()); + log(b); + out.close(); + } + } + + private final PrintStream loggingStream; + + private final Logger logger; + + private long _id = 0; + + /** + * Create a logging filter logging the request and response to + * a default JDK logger, named as the fully qualified class name of this + * class. + */ + public JerseyLoggingFilter() { + this(LOGGER); + } + + /** + * Create a logging filter logging the request and response to + * a JDK logger. + * + * @param logger the logger to log requests and responses. + */ + public JerseyLoggingFilter(Logger logger) { + this.loggingStream = null; + this.logger = logger; + } + + /** + * Create a logging filter logging the request and response to + * print stream. + * + * @param loggingStream the print stream to log requests and responses. + */ + public JerseyLoggingFilter(PrintStream loggingStream) { + this.loggingStream = loggingStream; + this.logger = null; + } + + private void log(StringBuilder b) { + if (logger != null) { + logger.info(b.toString()); + } else { + loggingStream.print(b); + } + } + + private StringBuilder prefixId(StringBuilder b, long id) { + b.append(Long.toString(id)).append(" "); + return b; + } + + @Override + public ClientResponse handle(ClientRequest request) throws ClientHandlerException { + long id = ++this._id; + + logRequest(id, request); + + ClientResponse response = getNext().handle(request); + + logResponse(id, response); + + return response; + } + + private void logRequest(long id, ClientRequest request) { + StringBuilder b = new StringBuilder(); + + printRequestLine(b, id, request); + printRequestHeaders(b, id, request.getHeaders()); + + if (request.getEntity() != null) { + request.setAdapter(new Adapter(request.getAdapter(), b)); + } else { + log(b); + } + } + + private void printRequestLine(StringBuilder b, long id, ClientRequest request) { + prefixId(b, id).append(NOTIFICATION_PREFIX).append("Client out-bound request").append("\n"); + prefixId(b, id).append(REQUEST_PREFIX).append(request.getMethod()).append(" "). + append(request.getURI().toASCIIString()).append("\n"); + } + + private void printRequestHeaders(StringBuilder b, long id, MultivaluedMap headers) { + for (Map.Entry> e : headers.entrySet()) { + List val = e.getValue(); + String header = e.getKey(); + + if(val.size() == 1) { + prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(ClientRequest.getHeaderValue(val.get(0))).append("\n"); + } else { + StringBuilder sb = new StringBuilder(); + boolean add = false; + for(Object o : val) { + if(add) sb.append(','); + add = true; + sb.append(ClientRequest.getHeaderValue(o)); + } + prefixId(b, id).append(REQUEST_PREFIX).append(header).append(": ").append(sb.toString()).append("\n"); + } + } + } + + private void logResponse(long id, ClientResponse response) { + StringBuilder b = new StringBuilder(); + + printResponseLine(b, id, response); + printResponseHeaders(b, id, response.getHeaders()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InputStream in = response.getEntityInputStream(); + try { + ReaderWriter.writeTo(in, out); + + byte[] requestEntity = out.toByteArray(); + printEntity(b, requestEntity); + response.setEntityInputStream(new ByteArrayInputStream(requestEntity)); + } catch (IOException ex) { + throw new ClientHandlerException(ex); + } + log(b); + } + + private void printResponseLine(StringBuilder b, long id, ClientResponse response) { + prefixId(b, id).append(NOTIFICATION_PREFIX). + append("Client in-bound response").append("\n"); + prefixId(b, id).append(RESPONSE_PREFIX). + append(Integer.toString(response.getStatus())). + append("\n"); + } + + private void printResponseHeaders(StringBuilder b, long id, MultivaluedMap headers) { + for (Map.Entry> e : headers.entrySet()) { + String header = e.getKey(); + for (String value : e.getValue()) { + prefixId(b, id).append(RESPONSE_PREFIX).append(header).append(": "). + append(value).append("\n"); + } + } + prefixId(b, id).append(RESPONSE_PREFIX).append("\n"); + } + + private void printEntity(StringBuilder b, byte[] entity) throws IOException { + if (entity.length == 0) + return; + String entityString = new String(entity); + entityString = entityString.replaceAll(PASSWORD_PATTERN, "\"password\" : \"******\""); + b.append(entityString).append("\n"); + } +} \ No newline at end of file diff --git a/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyResponse.java b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyResponse.java new file mode 100644 index 0000000..5e8dc8c --- /dev/null +++ b/openstack-client-connectors/jersey-connector/src/main/java/com/woorea/openstack/connector/JerseyResponse.java @@ -0,0 +1,66 @@ +package com.woorea.openstack.connector; + +/* + * Modifications copyright (c) 2017 AT&T Intellectual Property + */ + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import com.sun.jersey.api.client.ClientResponse; +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class JerseyResponse implements OpenStackResponse { + + private ClientResponse response; + + public JerseyResponse(ClientResponse response) { + this.response = response; + } + + @Override + public T getEntity(Class returnType) { + if(response.getStatus() >= 400) { + throw new OpenStackResponseException(response.getClientResponseStatus().getReasonPhrase(), + response.getStatus(), this); + } + if(response.hasEntity() && returnType != null && Void.class != returnType) { + return response.getEntity(returnType); + } else { + return null; + } + } + + @Override + public T getErrorEntity(Class returnType) { + if(response.getStatus() >= 400 && response.hasEntity()) { + return response.getEntity(returnType); + } + return null; + } + + @Override + public InputStream getInputStream() { + if(response.hasEntity()) { + return response.getEntityInputStream(); + } else { + return null; + } + } + + @Override + public String header(String name) { + return response.getHeaders().getFirst(name); + } + + @Override + public Map headers() { + Map headers = new HashMap(); + for(String k : response.getHeaders().keySet()) { + headers.put(k, response.getHeaders().getFirst(k)); + } + return headers; + } +} diff --git a/openstack-client-connectors/jersey-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/openstack-client-connectors/jersey-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector new file mode 100644 index 0000000..5b9a158 --- /dev/null +++ b/openstack-client-connectors/jersey-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector @@ -0,0 +1 @@ +com.woorea.openstack.connector.JerseyConnector \ No newline at end of file diff --git a/openstack-client-connectors/jersey2-connector/pom.xml b/openstack-client-connectors/jersey2-connector/pom.xml new file mode 100644 index 0000000..4cf7e83 --- /dev/null +++ b/openstack-client-connectors/jersey2-connector/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.openecomp.mso.libs.openstack-java-sdk + client-connectors + 1.0.0-SNAPSHOT + + org.openecomp.mso.libs.openstack-java-sdk.client-connectors + jersey2-connector + OpenStack Jersey2 Connector + OpenStack Jersey2 Connector + http://maven.apache.org + + UTF-8 + + + + org.glassfish.jersey.core + jersey-client + 2.6 + + + org.glassfish.jersey.media + jersey-media-json-jackson + 2.6 + + + org.openecomp.mso.libs.openstack-java-sdk + openstack-client + 1.0.0-SNAPSHOT + + + + diff --git a/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Connector.java b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Connector.java new file mode 100644 index 0000000..2171bea --- /dev/null +++ b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Connector.java @@ -0,0 +1,65 @@ +package com.woorea.openstack.connector; + +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import javax.ws.rs.ClientErrorException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import org.glassfish.jersey.filter.LoggingFilter; + +import com.woorea.openstack.base.client.HttpMethod; +import com.woorea.openstack.base.client.OpenStackClientConnector; +import com.woorea.openstack.base.client.OpenStackRequest; +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class JaxRs20Connector implements OpenStackClientConnector { + + protected Client client = OpenStack.CLIENT; + private LoggingFilter logger = new LoggingFilter(Logger.getLogger("os"), 10000); + + @Override + public OpenStackResponse request(OpenStackRequest request) { + WebTarget target = client.target(request.endpoint()).path(request.path()); + + for(Map.Entry > entry : request.queryParams().entrySet()) { + for (Object o : entry.getValue()) { + target = target.queryParam(entry.getKey(), o); + } + } + target.register(logger); + Invocation.Builder invocation = target.request(); + + for(Map.Entry> h : request.headers().entrySet()) { + StringBuilder sb = new StringBuilder(); + for(Object v : h.getValue()) { + sb.append(String.valueOf(v)); + } + invocation.header(h.getKey(), sb); + } + + Entity entity = (request.entity() == null) ? null : + Entity.entity(request.entity().getEntity(), request.entity().getContentType()); + + try { + if (entity != null) { + return new JaxRs20Response(invocation.method(request.method().name(), entity)); + } else { + if(HttpMethod.PUT == request.method()) { + return new JaxRs20Response(invocation.method(request.method().name(), Entity.entity("", MediaType.APPLICATION_JSON))); + } else { + return new JaxRs20Response(invocation.method(request.method().name())); + } + } + } catch (ClientErrorException e) { + throw new OpenStackResponseException(e.getResponse() + .getStatusInfo().toString(), e.getResponse().getStatus()); + } + } +} diff --git a/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Response.java b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Response.java new file mode 100644 index 0000000..f27f337 --- /dev/null +++ b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/JaxRs20Response.java @@ -0,0 +1,61 @@ +package com.woorea.openstack.connector; + +/* + * Modifications copyright (c) 2017 AT&T Intellectual Property + */ + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class JaxRs20Response implements OpenStackResponse { + + private Response response; + + public JaxRs20Response(Response response) { + this.response = response; + } + + @Override + public T getEntity(Class returnType) { + if(response.getStatus() >= 400) { + throw new OpenStackResponseException(response.getStatusInfo().getReasonPhrase(), + response.getStatusInfo().getStatusCode(), this); + } + return response.readEntity(returnType); + } + + @Override + public T getErrorEntity(Class returnType) { + if(response.getStatus() >= 400 && response.hasEntity()) { + return response.readEntity(returnType); + } + return null; + } + + + @Override + public InputStream getInputStream() { + return (InputStream) response.getEntity(); + } + + @Override + public String header(String name) { + return response.getHeaderString(name); + } + + @Override + public Map headers() { + Map headers = new HashMap(); + for(String k : response.getHeaders().keySet()) { + headers.put(k, response.getHeaderString(k)); + } + return headers; + } + +} diff --git a/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/OpenStack.java b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/OpenStack.java new file mode 100644 index 0000000..f514cc8 --- /dev/null +++ b/openstack-client-connectors/jersey2-connector/src/main/java/com/woorea/openstack/connector/OpenStack.java @@ -0,0 +1,110 @@ +package com.woorea.openstack.connector; + +import java.io.IOException; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.ext.ContextResolver; + +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.annotate.JsonRootName; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; +import org.glassfish.jersey.SslConfigurator; +import org.glassfish.jersey.jackson.JacksonFeature; + +public class OpenStack { + + public static Client CLIENT; + + public static ObjectMapper DEFAULT_MAPPER; + + public static ObjectMapper WRAPPED_MAPPER; + + static { + initialize(); + } + + private static void initialize() { + + /* + //class MyX509TrustManager implements X509TrustManager + TrustManager mytm[] = null; + KeyManager mykm[] = null; + + try { + mytm = new TrustManager[]{new MyX509TrustManager("./truststore_client", "asdfgh".toCharArray())}; + mykm = new KeyManager[]{new MyX509KeyManager("./keystore_client", "asdfgh".toCharArray())}; + } catch (Exception ex) { + + } + + SSLContext context = null; + context = SSLContext.getInstance("SSL"); + context.init(mykm, mytm, null); + + */ + + try { + + SSLContext context = null; + context = SSLContext.getInstance("SSL"); + context.init(null, null, null); + + SslConfigurator sslConfig = SslConfigurator.newInstance(); + /* + .trustStoreFile("./truststore_client") + .trustStorePassword("asdfgh") + + .keyStoreFile("./keystore_client") + .keyPassword("asdfgh"); + */ + //old: CLIENT.property(ClientProperties.SSL_CONFIG, new SslConfig(context)); + + CLIENT = ClientBuilder.newBuilder().sslContext(sslConfig.createSSLContext()).build(); + + DEFAULT_MAPPER = new ObjectMapper(); + + DEFAULT_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + DEFAULT_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + DEFAULT_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + + WRAPPED_MAPPER = new ObjectMapper(); + + WRAPPED_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + WRAPPED_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + + CLIENT.register(new JacksonFeature()).register(new ContextResolver() { + + public ObjectMapper getContext(Class type) { + return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; + } + + }); + + CLIENT.register(new ClientRequestFilter() { + + public void filter(ClientRequestContext requestContext) throws IOException { + requestContext.getHeaders().remove("Content-Language"); + requestContext.getHeaders().remove("Content-Encoding"); + } + }); + + } catch(Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + + } + +} diff --git a/openstack-client-connectors/jersey2-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/openstack-client-connectors/jersey2-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector new file mode 100644 index 0000000..fc24457 --- /dev/null +++ b/openstack-client-connectors/jersey2-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector @@ -0,0 +1 @@ +com.woorea.openstack.connector.JaxRs20Connector \ No newline at end of file diff --git a/openstack-client-connectors/pom.xml b/openstack-client-connectors/pom.xml new file mode 100644 index 0000000..6db50a8 --- /dev/null +++ b/openstack-client-connectors/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + org.openecomp.mso.libs + openstack-java-sdk + 1.0.0-SNAPSHOT + + org.openecomp.mso.libs.openstack-java-sdk + client-connectors + OpenStack Client Connectors + OpenStack Client Connectors + pom + + + jersey + + true + + + jersey-connector + + + + jersey2 + + true + + + jersey2-connector + + + + resteasy + + true + + + resteasy-connector + + + + http + + true + + + http-connector + + + + + + + org.openecomp.mso.libs.openstack-java-sdk + openstack-client + 1.0.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/openstack-client-connectors/resteasy-connector/pom.xml b/openstack-client-connectors/resteasy-connector/pom.xml new file mode 100644 index 0000000..6cbdd3a --- /dev/null +++ b/openstack-client-connectors/resteasy-connector/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + org.openecomp.mso.libs.openstack-java-sdk + client-connectors + 1.0.0-SNAPSHOT + + org.openecomp.mso.libs.openstack-java-sdk.client-connectors + resteasy-connector + OpenStack RESTEasy Connector + OpenStack RESTEasy Connector + + + org.jboss.resteasy + resteasy-jaxrs + 2.3.2.Final + + + org.codehaus.jackson + jackson-jaxrs + 1.9.4 + + + commons-httpclient + commons-httpclient + 3.1 + + + + \ No newline at end of file diff --git a/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyConnector.java b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyConnector.java new file mode 100644 index 0000000..b2ec415 --- /dev/null +++ b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyConnector.java @@ -0,0 +1,125 @@ +package com.woorea.openstack.connector; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.ext.ContextResolver; + +import org.apache.commons.httpclient.HttpStatus; +import org.codehaus.jackson.jaxrs.JacksonJsonProvider; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.annotate.JsonRootName; +import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion; +import org.jboss.resteasy.client.ClientRequest; +import org.jboss.resteasy.client.ClientResponse; +import org.jboss.resteasy.plugins.providers.InputStreamProvider; +import org.jboss.resteasy.spi.ResteasyProviderFactory; + +import com.woorea.openstack.base.client.OpenStackClientConnector; +import com.woorea.openstack.base.client.OpenStackRequest; +import com.woorea.openstack.base.client.OpenStackResponse; +import com.woorea.openstack.base.client.OpenStackResponseException; + +public class RESTEasyConnector implements OpenStackClientConnector { + + public static ObjectMapper DEFAULT_MAPPER; + + public static ObjectMapper WRAPPED_MAPPER; + + static class OpenStackProviderFactory extends ResteasyProviderFactory { + + private JacksonJsonProvider jsonProvider; + private InputStreamProvider streamProvider; + + public OpenStackProviderFactory() { + super(); + + addContextResolver(new ContextResolver() { + public ObjectMapper getContext(Class type) { + return type.getAnnotation(JsonRootName.class) == null ? DEFAULT_MAPPER : WRAPPED_MAPPER; + } + }); + + jsonProvider = new JacksonJsonProvider(); + addMessageBodyReader(jsonProvider); + addMessageBodyWriter(jsonProvider); + + streamProvider = new InputStreamProvider(); + addMessageBodyReader(streamProvider); + addMessageBodyWriter(streamProvider); + } + + } + + private static OpenStackProviderFactory providerFactory; + + static { + DEFAULT_MAPPER = new ObjectMapper(); + + DEFAULT_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + DEFAULT_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + DEFAULT_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + DEFAULT_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + + WRAPPED_MAPPER = new ObjectMapper(); + + WRAPPED_MAPPER.setSerializationInclusion(Inclusion.NON_NULL); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.INDENT_OUTPUT); + WRAPPED_MAPPER.enable(SerializationConfig.Feature.WRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + WRAPPED_MAPPER.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES); + WRAPPED_MAPPER.enable(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); + + providerFactory = new OpenStackProviderFactory(); + } + + public OpenStackResponse request(OpenStackRequest request) { + ClientRequest client = new ClientRequest(UriBuilder.fromUri(request.endpoint() + "/" + request.path()), + ClientRequest.getDefaultExecutor(), providerFactory); + + for(Map.Entry > entry : request.queryParams().entrySet()) { + for (Object o : entry.getValue()) { + client = client.queryParameter(entry.getKey(), String.valueOf(o)); + } + } + + for (Entry> h : request.headers().entrySet()) { + StringBuilder sb = new StringBuilder(); + for (Object v : h.getValue()) { + sb.append(String.valueOf(v)); + } + client.header(h.getKey(), sb); + } + + if (request.entity() != null) { + client.body(request.entity().getContentType(), request.entity().getEntity()); + } + + ClientResponse response; + + try { + response = client.httpMethod(request.method().name(), request.returnType()); + } catch (Exception e) { + throw new RuntimeException("Unexpected client exception", e); + } + + if (response.getStatus() == HttpStatus.SC_OK + || response.getStatus() == HttpStatus.SC_CREATED + || response.getStatus() == HttpStatus.SC_NO_CONTENT + || response.getStatus() == HttpStatus.SC_ACCEPTED) { + return new RESTEasyResponse(client, response); + } + + response.releaseConnection(); + + throw new OpenStackResponseException(response.getResponseStatus() + .getReasonPhrase(), response.getStatus()); + } + +} diff --git a/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyInputStream.java b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyInputStream.java new file mode 100644 index 0000000..913a500 --- /dev/null +++ b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyInputStream.java @@ -0,0 +1,37 @@ +package com.woorea.openstack.connector; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketException; + +import org.jboss.resteasy.client.ClientExecutor; + + +public class RESTEasyInputStream extends FilterInputStream { + + protected ClientExecutor clientExecutor; + + public RESTEasyInputStream(InputStream inputStream, ClientExecutor clientExecutor) { + super(inputStream); + this.clientExecutor = clientExecutor; + } + + @Override + public void close() throws IOException { + try { + clientExecutor.close(); + } catch (Exception e) { + // Silently skip errors in the socket close errors + } + + try { + super.close(); + } catch (SocketException e) { + // We expect this exception because the socket is closed + } catch (IllegalStateException e) { + // We expect this exception because the socket is closed (httpclient 4.2) + } + } + +} diff --git a/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyResponse.java b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyResponse.java new file mode 100644 index 0000000..82e3187 --- /dev/null +++ b/openstack-client-connectors/resteasy-connector/src/main/java/com/woorea/openstack/connector/RESTEasyResponse.java @@ -0,0 +1,59 @@ +package com.woorea.openstack.connector; + +/* + * Modifications copyright (c) 2017 AT&T Intellectual Property + */ + +import org.jboss.resteasy.client.ClientRequest; +import org.jboss.resteasy.client.ClientResponse; +import com.woorea.openstack.base.client.OpenStackResponse; + +import javax.ws.rs.core.MultivaluedMap; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class RESTEasyResponse implements OpenStackResponse { + + private ClientRequest client; + + private ClientResponse response; + + public RESTEasyResponse(ClientRequest client, ClientResponse response) { + this.client = client; + this.response = response; + } + + @Override + public T getEntity(Class returnType) { + return (T) response.getEntity(returnType); + } + + @Override + public T getErrorEntity(Class returnType) { + return (T) response.getEntity(returnType); + } + + @Override + public InputStream getInputStream() { + return new RESTEasyInputStream((InputStream) response.getEntity(InputStream.class), client.getExecutor()); + } + + @Override + public String header(String name) { + return response.getHeaders().getFirst(name).toString(); + } + + @Override + public Map headers() { + Map headers = new HashMap(); + MultivaluedMap responseHeaders = response.getHeaders(); + + for (String key : responseHeaders.keySet()) { + headers.put(key, responseHeaders.getFirst(key).toString()); + } + + return headers; + } + +} diff --git a/openstack-client-connectors/resteasy-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector b/openstack-client-connectors/resteasy-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector new file mode 100644 index 0000000..dbb991d --- /dev/null +++ b/openstack-client-connectors/resteasy-connector/src/main/resources/META-INF/services/com.woorea.openstack.base.client.OpenStackClientConnector @@ -0,0 +1 @@ +com.woorea.openstack.connector.RESTEasyConnector \ No newline at end of file -- cgit 1.2.3-korg