diff options
Diffstat (limited to 'openstack-client-connectors/jersey-connector/src/main')
4 files changed, 450 insertions, 0 deletions
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 <T> OpenStackResponse request(OpenStackRequest<T> request) { + WebResource target = client.resource(request.endpoint()).path(request.path()); + for(Map.Entry<String, List<Object> > entry : request.queryParams().entrySet()) { + for (Object o : entry.getValue()) { + target = target.queryParam(entry.getKey(), String.valueOf(o)); + } + } + target.addFilter(logger); + MultivaluedMap<String, Object> headers = new OutBoundHeaders(); + for(Map.Entry<String, List<Object>> 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<ObjectMapper> { + 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<String, Object> headers) { + for (Map.Entry<String, List<Object>> e : headers.entrySet()) { + List<Object> 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<String, String> headers) { + for (Map.Entry<String, List<String>> 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> T getEntity(Class<T> 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> T getErrorEntity(Class<T> 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<String, String> headers() {
+ Map<String, String> headers = new HashMap<String, String>();
+ 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 |