From 18914b033f67d25cc9377b2e381648a9e3184968 Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Mon, 21 Jan 2019 12:42:10 -0500 Subject: Add gson support to policy-endpoints Added "provider" property to both http client and server builders. The provider defaults to jackson, to maintain backward compatibility until other policy code has been converted to gson. Removed commented item from pom. Added some comments and re-arranged a few pieces of code. Fixed a few typos and removed spacing at the end of some lines. Reordered imports. Added comments about limitations when using jersey-media-json-jackson. Address ridiculous checkstyle complaint. Support comma-separated list of serialization providers in jersey client. Disabled metainf discovery from jersey client and server so that the media-json dependencies could be re-instated in the pom. Address another ridiculous checkstyle complaint. Change-Id: Ic5a93b475d0ee9b435352b3516de6b865b00a86a Issue-ID: POLICY-1428 Signed-off-by: Jim Hahn --- policy-endpoints/pom.xml | 5 + .../event/comm/bus/internal/BusTopicParams.java | 12 +- .../endpoints/http/client/HttpClientFactory.java | 2 +- .../http/client/IndexedHttpClientFactory.java | 6 +- .../http/client/internal/JerseyClient.java | 38 ++- .../endpoints/http/server/HttpServletServer.java | 9 +- .../server/IndexedHttpServletServerFactory.java | 8 + .../server/internal/GsonMessageBodyHandler.java | 128 +++++++++ .../http/server/internal/JettyJerseyServer.java | 130 +++++---- .../properties/PolicyEndPointProperties.java | 2 + .../server/test/GsonMessageBodyHandlerTest.java | 156 +++++++++++ .../endpoints/http/server/test/HttpClientTest.java | 133 ++++++++- .../endpoints/http/server/test/HttpServerTest.java | 307 ++++++++++++++++++--- .../endpoints/http/server/test/MyGsonProvider.java | 76 +++++ .../http/server/test/MyJacksonProvider.java | 76 +++++ .../http/server/test/RestEchoReqResp.java | 59 ++++ .../http/server/test/RestEchoService.java | 11 +- 17 files changed, 1041 insertions(+), 117 deletions(-) create mode 100644 policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/GsonMessageBodyHandler.java create mode 100644 policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/GsonMessageBodyHandlerTest.java create mode 100644 policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyGsonProvider.java create mode 100644 policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyJacksonProvider.java create mode 100644 policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoReqResp.java diff --git a/policy-endpoints/pom.xml b/policy-endpoints/pom.xml index 202c9c2b..4a7f91fe 100644 --- a/policy-endpoints/pom.xml +++ b/policy-endpoints/pom.xml @@ -139,6 +139,11 @@ jersey-common ${jersey.version} + + + com.google.code.gson + gson + com.fasterxml.jackson.core diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/bus/internal/BusTopicParams.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/bus/internal/BusTopicParams.java index b752d91c..6e3c2632 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/bus/internal/BusTopicParams.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/event/comm/bus/internal/BusTopicParams.java @@ -3,7 +3,7 @@ * policy-endpoints * ================================================================================ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved. - * Modifications Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2018-2019 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. @@ -72,6 +72,7 @@ public class BusTopicParams { private String clientName; private String hostname; private String basePath; + private String serializationProvider; public static TopicParamsBuilder builder() { return new TopicParamsBuilder(); @@ -254,6 +255,10 @@ public class BusTopicParams { return additionalProps != null; } + public String getSerializationProvider() { + return serializationProvider; + } + public static class TopicParamsBuilder { final BusTopicParams params = new BusTopicParams(); @@ -385,6 +390,11 @@ public class BusTopicParams { return this; } + public TopicParamsBuilder serializationProvider(String serializationProvider) { + this.params.serializationProvider = serializationProvider; + return this; + } + } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java index f482eb01..5f4c4c9d 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java @@ -36,7 +36,7 @@ public interface HttpClientFactory { * Build and http client with the following parameters. */ HttpClient build(BusTopicParams busTopicParams) - throws KeyManagementException, NoSuchAlgorithmException; + throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException; /** * Build http client from properties. diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java index 9aef09e4..5cc0071e 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/IndexedHttpClientFactory.java @@ -49,7 +49,7 @@ class IndexedHttpClientFactory implements HttpClientFactory { @Override public synchronized HttpClient build(BusTopicParams busTopicParams) - throws KeyManagementException, NoSuchAlgorithmException { + throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException { if (clients.containsKey(busTopicParams.getClientName())) { return clients.get(busTopicParams.getClientName()); } @@ -109,6 +109,9 @@ class IndexedHttpClientFactory implements HttpClientFactory { String password = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + clientName + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX); + final String classProv = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + + "." + clientName + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER); + String managedString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + clientName + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX); boolean managed = true; @@ -128,6 +131,7 @@ class IndexedHttpClientFactory implements HttpClientFactory { .userName(userName) .password(password) .managed(managed) + .serializationProvider(classProv) .build()); clientList.add(client); } catch (Exception e) { diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java index b55a7bb2..2287486e 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java @@ -22,7 +22,6 @@ package org.onap.policy.common.endpoints.http.client.internal; import com.fasterxml.jackson.annotation.JsonIgnore; - import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -30,7 +29,6 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Map; import java.util.Map.Entry; - import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -39,7 +37,7 @@ import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.core.Response; - +import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; import org.onap.policy.common.endpoints.http.client.HttpClient; @@ -48,6 +46,10 @@ import org.slf4j.LoggerFactory; /** * Http Client implementation using a Jersey Client. + * + *

Note: the serialization provider will be ignored if the maven artifact, + * jersey-media-json-jackson, is included, regardless of whether it's included + * directly or indirectly. */ public class JerseyClient implements HttpClient { @@ -55,6 +57,9 @@ public class JerseyClient implements HttpClient { * Logger. */ private static Logger logger = LoggerFactory.getLogger(JerseyClient.class); + + protected static final String JERSEY_DEFAULT_SERIALIZATION_PROVIDER = + "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"; protected final String name; protected final boolean https; @@ -73,14 +78,17 @@ public class JerseyClient implements HttpClient { /** * Constructor. * - *

name the name https is it https or not selfSignedCerts are there self signed certs hostname - * the hostname port port being used basePath base context userName user password password + *

name the name https is it https or not selfSignedCerts are there self signed certs + * hostname the hostname port port being used basePath base context userName user + * password password * * @param busTopicParams Input parameters object * @throws KeyManagementException key exception * @throws NoSuchAlgorithmException no algorithm exception + * @throws ClassNotFoundException if the serialization provider cannot be found */ - public JerseyClient(BusTopicParams busTopicParams) throws KeyManagementException, NoSuchAlgorithmException { + public JerseyClient(BusTopicParams busTopicParams) + throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException { super(); @@ -147,10 +155,28 @@ public class JerseyClient implements HttpClient { this.client.register(authFeature); } + registerSerProviders(busTopicParams.getSerializationProvider()); + + this.client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true"); + this.baseUrl = tmpBaseUrl.append(this.hostname).append(":").append(this.port).append("/") .append((this.basePath == null) ? "" : this.basePath).toString(); } + /** + * Registers the serialization provider(s) with the client. + * + * @param serializationProvider comma-separated list of serialization providers + * @throws ClassNotFoundException if the serialization provider cannot be found + */ + private void registerSerProviders(String serializationProvider) throws ClassNotFoundException { + String providers = (serializationProvider == null || serializationProvider.isEmpty() + ? JERSEY_DEFAULT_SERIALIZATION_PROVIDER : serializationProvider); + for (String prov : providers.split(",")) { + this.client.register(Class.forName(prov)); + } + } + @Override public Response get(String path) { if (path != null && !path.isEmpty()) { diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java index c4db9fbe..b674e265 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019 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. @@ -61,6 +61,13 @@ public interface HttpServletServer extends Startable { */ boolean isAaf(); + /** + * Sets the serialization provider to be used when classes are added to the service. + * + * @param provider the provider to use for message serialization and de-serialization + */ + void setSerializationProvider(String provider); + /** * Adds a filter at the specified path. * diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java index ad8ef99c..b2c49eae 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java @@ -129,6 +129,9 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { final String restUriPath = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_REST_URIPATH_SUFFIX); + final String classProv = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + + "." + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER); + final String managedString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX); boolean managed = true; @@ -157,9 +160,14 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { aaf = Boolean.parseBoolean(aafString); } + HttpServletServer service = build(serviceName, https, hostName, servicePort, contextUriPath, swagger, managed); + if (classProv != null && !classProv.isEmpty()) { + service.setSerializationProvider(classProv); + } + /* authentication method either AAF or HTTP Basic Auth */ if (aaf) { diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/GsonMessageBodyHandler.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/GsonMessageBodyHandler.java new file mode 100644 index 00000000..a29afef4 --- /dev/null +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/GsonMessageBodyHandler.java @@ -0,0 +1,128 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.internal; + +import com.google.gson.Gson; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +/** + * Provider that serializes and de-serializes JSON via gson. + * + *

Note: jersey will ignore this class if the maven artifact, + * jersey-media-json-jackson, is included, regardless of whether it's included + * directly or indirectly. + */ +@Provider +@Consumes(MediaType.WILDCARD) +@Produces(MediaType.WILDCARD) +public class GsonMessageBodyHandler implements MessageBodyReader, MessageBodyWriter { + + /** + * Object to be used to serialize and de-serialize. + */ + private Gson gson; + + /** + * Constructs the object, using a plain Gson object. + */ + public GsonMessageBodyHandler() { + this(new Gson()); + } + + /** + * Constructs the object. + * + * @param gson the Gson object to be used to serialize and de-serialize + */ + public GsonMessageBodyHandler(Gson gson) { + this.gson = gson; + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return canHandle(mediaType); + } + + @Override + public long getSize(Object object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(Object object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + + try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) { + Type jsonType = (type.equals(genericType) ? type : genericType); + gson.toJson(object, jsonType, writer); + } + } + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return canHandle(mediaType); + } + + /** + * Determines if this provider can handle the given media type. + * + * @param mediaType the media type of interest + * @return {@code true} if this provider handles the given media type, {@code false} + * otherwise + */ + private boolean canHandle(MediaType mediaType) { + if (mediaType == null) { + return true; + } + + String subtype = mediaType.getSubtype(); + + return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json") || "javascript".equals(subtype) + || "x-javascript".equals(subtype) || "x-json".equals(subtype); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + + try (InputStreamReader streamReader = new InputStreamReader(entityStream, StandardCharsets.UTF_8)) { + Type jsonType = (type.equals(genericType) ? type : genericType); + return gson.fromJson(streamReader, jsonType); + } + } +} diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java index a97a9bf5..22fc9ac3 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java @@ -2,14 +2,14 @@ * ============LICENSE_START======================================================= * policy-endpoints * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019 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. @@ -23,12 +23,23 @@ package org.onap.policy.common.endpoints.http.server.internal; import io.swagger.jersey.config.JerseyJaxrsConfig; import java.util.HashMap; import org.eclipse.jetty.servlet.ServletHolder; +import org.glassfish.jersey.server.ServerProperties; import org.onap.policy.common.utils.network.NetworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * REST Jetty Server that uses Jersey Servlets to support JAX-RS Web Services. + * + *

Note: the serialization provider will always be added to the server's class providers, + * as will the swagger providers (assuming swagger has been enabled). This happens whether + * {@link #addServletClass(String, String)} is used or + * {@link #addServletPackage(String, String)} is used. Thus it's possible to have both the + * server's class provider property and the server's package provider property populated. + * + *

Also note: the serialization provider will be ignored if the maven artifact, + * jersey-media-json-jackson, is included, regardless of whether it's included + * directly or indirectly. */ public class JettyJerseyServer extends JettyServletServer { @@ -52,26 +63,6 @@ public class JettyJerseyServer extends JettyServletServer { */ protected static final String SWAGGER_PRETTY_PRINT = "swagger.pretty.print"; - /** - * Swagger Packages. - */ - protected static final String SWAGGER_INIT_PACKAGES_PARAM_VALUE = "io.swagger.jaxrs.listing"; - - /** - * Jersey Packages Init Param Name. - */ - protected static final String JERSEY_INIT_PACKAGES_PARAM_NAME = "jersey.config.server.provider.packages"; - - /** - * Jersey Packages Init Param Value. - */ - protected static final String JERSEY_INIT_PACKAGES_PARAM_VALUE = "com.fasterxml.jackson.jaxrs.json"; - - /** - * Jersey Classes Init Param Name. - */ - protected static final String JERSEY_INIT_CLASSNAMES_PARAM_NAME = "jersey.config.server.provider.classnames"; - /** * Jersey Jackson Classes Init Param Value. */ @@ -98,16 +89,21 @@ public class JettyJerseyServer extends JettyServletServer { */ protected String swaggerId = null; + /** + * The serialization provider to be used when classes are added to the service. + */ + private String classProvider = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE; + /** * Constructor. - * + * * @param name name * @param https enable https? * @param host host server host * @param port port server port * @param swagger support swagger? * @param contextPath context path - * + * * @throws IllegalArgumentException in invalid arguments are provided */ public JettyJerseyServer(String name, boolean https, String host, int port, String contextPath, boolean swagger) { @@ -145,10 +141,10 @@ public class JettyJerseyServer extends JettyServletServer { /** * Retrieves cached server based on servlet path. - * + * * @param servletPath servlet path * @return the jetty servlet holder - * + * * @throws IllegalArgumentException if invalid arguments are provided */ protected synchronized ServletHolder getServlet(String servletPath) { @@ -176,27 +172,17 @@ public class JettyJerseyServer extends JettyServletServer { ServletHolder jerseyServlet = this.getServlet(servPath); - String initClasses = jerseyServlet.getInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME); - if (initClasses != null && !initClasses.isEmpty()) { - logger.warn("Both packages and classes are used in Jetty+Jersey Configuration: {}", restPackage); - } + initStandardParams(jerseyServlet); - String initPackages = jerseyServlet.getInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME); + String initPackages = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_PACKAGES); if (initPackages == null) { - if (this.swaggerId != null) { - initPackages = - JERSEY_INIT_PACKAGES_PARAM_VALUE + "," + SWAGGER_INIT_PACKAGES_PARAM_VALUE + "," + restPackage; - - jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); - jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); - } else { - initPackages = JERSEY_INIT_PACKAGES_PARAM_VALUE + "," + restPackage; - } + initPackages = restPackage; + } else { - initPackages = initPackages + "," + restPackage; + initPackages += "," + restPackage; } - jerseyServlet.setInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME, initPackages); + jerseyServlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, initPackages); if (logger.isDebugEnabled()) { logger.debug("{}: added REST package: {}", this, jerseyServlet.dump()); @@ -216,33 +202,59 @@ public class JettyJerseyServer extends JettyServletServer { ServletHolder jerseyServlet = this.getServlet(servletPath); - String initPackages = jerseyServlet.getInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME); - if (initPackages != null && !initPackages.isEmpty()) { - logger.warn("Both classes and packages are used in Jetty+Jersey Configuration: {}", restClass); - } + initStandardParams(jerseyServlet); - String initClasses = jerseyServlet.getInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME); + String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES); if (initClasses == null) { - if (this.swaggerId != null) { - initClasses = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE + "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE - + "," + restClass; - - jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); - jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); - } else { - initClasses = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE + "," + restClass; - } + initClasses = restClass; + } else { - initClasses = initClasses + "," + restClass; + initClasses += "," + restClass; } - jerseyServlet.setInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME, initClasses); + jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses); if (logger.isDebugEnabled()) { logger.debug("{}: added REST class: {}", this, jerseyServlet.dump()); } } + /** + * Adds "standard" parameters to the initParameter set. Sets swagger parameters, if + * specified, and sets the class provider property. This can be invoked multiple + * times, but only the first actually causes any changes to the parameter set. + * + * @param jerseyServlet servlet into which parameters should be added + */ + private void initStandardParams(ServletHolder jerseyServlet) { + String initClasses = jerseyServlet.getInitParameter(ServerProperties.PROVIDER_CLASSNAMES); + if (initClasses != null) { + return; + } + + initClasses = classProvider; + + if (this.swaggerId != null) { + initClasses += "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE; + + jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); + jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); + } + + jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, initClasses); + + jerseyServlet.setInitParameter(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true"); + } + + /** + * Note: this must be invoked before {@link #addServletClass(String, String)} + * or {@link #addServletPackage(String, String)}. + */ + @Override + public void setSerializationProvider(String provider) { + classProvider = provider; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/properties/PolicyEndPointProperties.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/properties/PolicyEndPointProperties.java index 883ba7d2..2b1a9a3f 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/properties/PolicyEndPointProperties.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/properties/PolicyEndPointProperties.java @@ -94,6 +94,8 @@ public interface PolicyEndPointProperties { String PROPERTY_HTTP_HTTPS_SUFFIX = ".https"; String PROPERTY_HTTP_SWAGGER_SUFFIX = ".swagger"; + String PROPERTY_HTTP_SERIALIZATION_PROVIDER = ".serialization.provider"; + /* HTTP Client Properties */ String PROPERTY_HTTP_CLIENT_SERVICES = "http.client.services"; diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/GsonMessageBodyHandlerTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/GsonMessageBodyHandlerTest.java new file mode 100644 index 00000000..9c6ec80d --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/GsonMessageBodyHandlerTest.java @@ -0,0 +1,156 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import javax.ws.rs.core.MediaType; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.common.endpoints.http.server.internal.GsonMessageBodyHandler; + +public class GsonMessageBodyHandlerTest { + private static final String GEN_TYPE = "some-type"; + private static final String[] subtypes = {"json", "jSoN", "hello+json", "javascript", "x-javascript", "x-json"}; + + @SuppressWarnings("rawtypes") + private static final Class GEN_CLASS = MyObject.class; + + @SuppressWarnings("unchecked") + private static final Class CLASS_OBJ = GEN_CLASS; + + private GsonMessageBodyHandler hdlr; + + @Before + public void setUp() { + hdlr = new GsonMessageBodyHandler(); + } + + @Test + public void testIsWriteable() { + // null media type + assertTrue(hdlr.isWriteable(null, null, null, null)); + + for (String subtype : subtypes) { + assertTrue("writeable " + subtype, hdlr.isWriteable(null, null, null, new MediaType(GEN_TYPE, subtype))); + + } + + // the remaining should be FALSE + + // null subtype + assertFalse(hdlr.isWriteable(null, null, null, new MediaType(GEN_TYPE, null))); + + // text subtype + assertFalse(hdlr.isWriteable(null, null, null, MediaType.TEXT_HTML_TYPE)); + } + + @Test + public void testGetSize() { + assertEquals(-1, hdlr.getSize(null, null, null, null, null)); + } + + @Test + public void testWriteTo_testReadFrom() throws Exception { + ByteArrayOutputStream outstr = new ByteArrayOutputStream(); + MyObject obj1 = new MyObject(10); + hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr); + + Object obj2 = hdlr.readFrom(CLASS_OBJ, CLASS_OBJ, null, null, null, + new ByteArrayInputStream(outstr.toByteArray())); + assertEquals(obj1.toString(), obj2.toString()); + } + + @Test + public void testWriteTo_DifferentTypes() throws Exception { + ByteArrayOutputStream outstr = new ByteArrayOutputStream(); + + // use a derived type, but specify the base type when writing + MyObject obj1 = new MyObject(10) {}; + hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr); + + Object obj2 = hdlr.readFrom(CLASS_OBJ, CLASS_OBJ, null, null, null, + new ByteArrayInputStream(outstr.toByteArray())); + assertEquals(obj1.toString(), obj2.toString()); + } + + @Test + public void testIsReadable() { + // null media type + assertTrue(hdlr.isReadable(null, null, null, null)); + + // null subtype + assertFalse(hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, null))); + + for (String subtype : subtypes) { + assertTrue("readable " + subtype, hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, subtype))); + + } + + // the remaining should be FALSE + + // null subtype + assertFalse(hdlr.isReadable(null, null, null, new MediaType(GEN_TYPE, null))); + + // text subtype + assertFalse(hdlr.isReadable(null, null, null, MediaType.TEXT_HTML_TYPE)); + } + + @Test + public void testReadFrom_DifferentTypes() throws Exception { + ByteArrayOutputStream outstr = new ByteArrayOutputStream(); + MyObject obj1 = new MyObject(10); + hdlr.writeTo(obj1, obj1.getClass(), CLASS_OBJ, null, null, null, outstr); + + // use a derived type, but specify the base type when reading + @SuppressWarnings("rawtypes") + Class clazz = new MyObject() {}.getClass(); + + @SuppressWarnings("unchecked") + Class objclazz = clazz; + + Object obj2 = hdlr.readFrom(objclazz, CLASS_OBJ, null, null, null, + new ByteArrayInputStream(outstr.toByteArray())); + assertEquals(obj1.toString(), obj2.toString()); + } + + public static class MyObject { + private int id; + + public MyObject() { + super(); + } + + public MyObject(int id) { + this.id = id; + } + + @Override + public String toString() { + return "MyObject [id=" + id + "]"; + } + } + +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java index f15d68e9..1f1f117e 100644 --- a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java @@ -22,6 +22,8 @@ package org.onap.policy.common.endpoints.http.server.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.security.KeyManagementException; @@ -34,6 +36,7 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; @@ -53,7 +56,7 @@ public class HttpClientTest { * @throws IOException can have an IO exception */ @BeforeClass - public static void setUp() throws InterruptedException, IOException { + public static void setUpBeforeClass() throws InterruptedException, IOException { /* echo server - http + no auth */ final HttpServletServer echoServerNoAuth = @@ -112,11 +115,22 @@ public class HttpClientTest { } } + /** + * Clear https clients and reset providers. + */ + @Before + public void setUp() { + HttpClient.factory.destroy(); + + MyGsonProvider.resetSome(); + MyJacksonProvider.resetSome(); + } + /** * After the class is created method. */ @AfterClass - public static void tearDown() { + public static void tearDownAfterClass() { HttpServletServer.factory.destroy(); HttpClient.factory.destroy(); @@ -226,6 +240,44 @@ public class HttpClientTest { assertEquals("PUT:hello:{myParameter=myValue}", body); } + @Test + public void testHttpPutAuthClient_JacksonProvider() throws Exception { + final HttpClient client = HttpClient.factory.build(BusTopicParams.builder().clientName("testHttpAuthClient") + .useHttps(true).allowSelfSignedCerts(true).hostname("localhost").port(6667) + .basePath("junit/echo").userName("x").password("y").managed(true) + .serializationProvider(MyJacksonProvider.class.getCanonicalName()).build()); + + Entity entity = Entity.entity(new MyEntity("myValue"), MediaType.APPLICATION_JSON); + final Response response = client.put("hello", entity, Collections.emptyMap()); + final String body = HttpClient.getBody(response, String.class); + + assertEquals(200, response.getStatus()); + assertEquals("PUT:hello:{myParameter=myValue}", body); + + assertTrue(MyJacksonProvider.hasWrittenSome()); + + assertFalse(MyGsonProvider.hasWrittenSome()); + } + + @Test + public void testHttpPutAuthClient_GsonProvider() throws Exception { + final HttpClient client = HttpClient.factory.build(BusTopicParams.builder().clientName("testHttpAuthClient") + .useHttps(true).allowSelfSignedCerts(true).hostname("localhost").port(6667) + .basePath("junit/echo").userName("x").password("y").managed(true) + .serializationProvider(MyGsonProvider.class.getCanonicalName()).build()); + + Entity entity = Entity.entity(new MyEntity("myValue"), MediaType.APPLICATION_JSON); + final Response response = client.put("hello", entity, Collections.emptyMap()); + final String body = HttpClient.getBody(response, String.class); + + assertEquals(200, response.getStatus()); + assertEquals("PUT:hello:{myParameter=myValue}", body); + + assertTrue(MyGsonProvider.hasWrittenSome()); + + assertFalse(MyJacksonProvider.hasWrittenSome()); + } + @Test public void testHttpAuthClient401() throws Exception { final HttpClient client = getNoAuthHttpClient("testHttpAuthClient401", true, @@ -321,16 +373,89 @@ public class HttpClientTest { final HttpClient clientPdp = HttpClient.factory.get("PDP"); final Response response2 = clientPdp.get("test"); assertEquals(500, response2.getStatus()); + + assertFalse(MyJacksonProvider.hasWrittenSome()); + assertFalse(MyGsonProvider.hasWrittenSome()); + } + + @Test + public void testHttpAuthClientProps_MixedProviders() throws Exception { + final Properties httpProperties = new Properties(); + + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES, "GSON,JACKSON"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, "6666"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_HTTP_URL_SUFFIX, "junit/echo"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, "false"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true"); + httpProperties.setProperty( + PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "GSON" + + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, + MyGsonProvider.class.getCanonicalName()); + + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, "localhost"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, "6666"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_HTTP_URL_SUFFIX, "junit/echo"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, "false"); + httpProperties.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true"); + httpProperties.setProperty( + PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "." + "JACKSON" + + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, + MyJacksonProvider.class.getCanonicalName()); + + final List clients = HttpClient.factory.build(httpProperties); + assertEquals(2, clients.size()); + + Entity entity = Entity.entity(new MyEntity("myValue"), MediaType.APPLICATION_JSON); + + // use gson client + MyGsonProvider.resetSome(); + MyJacksonProvider.resetSome(); + HttpClient client = HttpClient.factory.get("GSON"); + + Response response = client.put("hello", entity, Collections.emptyMap()); + String body = HttpClient.getBody(response, String.class); + + assertEquals(200, response.getStatus()); + assertEquals("PUT:hello:{myParameter=myValue}", body); + + assertTrue(MyGsonProvider.hasWrittenSome()); + assertFalse(MyJacksonProvider.hasWrittenSome()); + + // use jackson client + MyGsonProvider.resetSome(); + MyJacksonProvider.resetSome(); + client = HttpClient.factory.get("JACKSON"); + + response = client.put("hello", entity, Collections.emptyMap()); + body = HttpClient.getBody(response, String.class); + + assertEquals(200, response.getStatus()); + assertEquals("PUT:hello:{myParameter=myValue}", body); + + assertTrue(MyJacksonProvider.hasWrittenSome()); + assertFalse(MyGsonProvider.hasWrittenSome()); } - private HttpClient getAuthHttpClient() throws KeyManagementException, NoSuchAlgorithmException { + private HttpClient getAuthHttpClient() + throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException { return HttpClient.factory.build(BusTopicParams.builder().clientName("testHttpAuthClient") .useHttps(true).allowSelfSignedCerts(true).hostname("localhost").port(6667).basePath("junit/echo") .userName("x").password("y").managed(true).build()); } private HttpClient getNoAuthHttpClient(String clientName, boolean https, int port) - throws KeyManagementException, NoSuchAlgorithmException { + throws KeyManagementException, NoSuchAlgorithmException, ClassNotFoundException { return HttpClient.factory.build(BusTopicParams.builder().clientName(clientName) .useHttps(https).allowSelfSignedCerts(https).hostname("localhost").port(port).basePath("junit/echo") .userName(null).password(null).managed(true).build()); diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java index 084847ce..3d80793e 100644 --- a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java @@ -2,14 +2,14 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019 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. @@ -24,14 +24,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.google.gson.Gson; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ConnectException; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; import java.util.UUID; - +import org.apache.commons.io.IOUtils; +import org.junit.AfterClass; +import org.junit.Before; import org.junit.Test; import org.onap.policy.common.endpoints.http.server.HttpServletServer; import org.slf4j.Logger; @@ -47,69 +52,251 @@ public class HttpServerTest { */ private static Logger logger = LoggerFactory.getLogger(HttpServerTest.class); + private static final String LOCALHOST_PREFIX = "http://localhost:"; + + private static final Gson gson = new Gson(); + + /** + * Server port. Incremented by 10 with each test. + */ + private static int port = 5608; + + private String portUrl; + + /** + * Increments the port number, clears the servers, and resets the providers. + */ + @Before + public void setUp() { + port += 10; + portUrl = LOCALHOST_PREFIX + port; + + HttpServletServer.factory.destroy(); + + MyJacksonProvider.resetSome(); + MyGsonProvider.resetSome(); + } + + @AfterClass + public static void tearDownAfterClass() { + HttpServletServer.factory.destroy(); + } + + @Test + public void testDefaultPackageServer() throws Exception { + logger.info("-- testDefaultPackageServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + server.addServletPackage("/*", this.getClass().getPackage().getName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + } + + @Test + public void testJacksonPackageServer() throws Exception { + logger.info("-- testJacksonPackageServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + + server.setSerializationProvider(MyJacksonProvider.class.getCanonicalName()); + server.addServletPackage("/*", this.getClass().getPackage().getName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + + assertTrue(MyJacksonProvider.hasReadSome()); + assertTrue(MyJacksonProvider.hasWrittenSome()); + + assertFalse(MyGsonProvider.hasReadSome()); + assertFalse(MyGsonProvider.hasWrittenSome()); + } + + @Test + public void testGsonPackageServer() throws Exception { + logger.info("-- testGsonPackageServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + + server.setSerializationProvider(MyGsonProvider.class.getCanonicalName()); + server.addServletPackage("/*", this.getClass().getPackage().getName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + + assertTrue(MyGsonProvider.hasReadSome()); + assertTrue(MyGsonProvider.hasWrittenSome()); + + assertFalse(MyJacksonProvider.hasReadSome()); + assertFalse(MyJacksonProvider.hasWrittenSome()); + } + + @Test + public void testDefaultClassServer() throws Exception { + logger.info("-- testDefaultClassServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + server.addServletClass("/*", RestEchoService.class.getCanonicalName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + } + + @Test + public void testJacksonClassServer() throws Exception { + logger.info("-- testJacksonClassServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + server.setSerializationProvider(MyJacksonProvider.class.getCanonicalName()); + server.addServletClass("/*", RestEchoService.class.getCanonicalName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + + assertTrue(MyJacksonProvider.hasReadSome()); + assertTrue(MyJacksonProvider.hasWrittenSome()); + + assertFalse(MyGsonProvider.hasReadSome()); + assertFalse(MyGsonProvider.hasWrittenSome()); + } + + @Test + public void testGsonClassServer() throws Exception { + logger.info("-- testGsonClassServer() --"); + + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); + server.setSerializationProvider(MyGsonProvider.class.getCanonicalName()); + server.addServletClass("/*", RestEchoService.class.getCanonicalName()); + server.addFilterClass("/*", TestFilter.class.getCanonicalName()); + server.waitedStart(5000); + + assertTrue(HttpServletServer.factory.get(port).isAlive()); + + RestEchoReqResp request = new RestEchoReqResp(); + request.setRequestId(100); + request.setText("some text"); + String reqText = gson.toJson(request); + + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/full/request", reqText); + assertEquals(reqText, response); + + assertTrue(MyGsonProvider.hasReadSome()); + assertTrue(MyGsonProvider.hasWrittenSome()); + + assertFalse(MyJacksonProvider.hasReadSome()); + assertFalse(MyJacksonProvider.hasWrittenSome()); + } + @Test public void testSingleServer() throws Exception { logger.info("-- testSingleServer() --"); - HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", 5678, "/", false, true); + HttpServletServer server = HttpServletServer.factory.build("echo", "localhost", port, "/", false, true); server.addServletPackage("/*", this.getClass().getPackage().getName()); server.addFilterClass("/*", TestFilter.class.getCanonicalName()); server.waitedStart(5000); - assertTrue(HttpServletServer.factory.get(5678).isAlive()); - assertFalse(HttpServletServer.factory.get(5678).isAaf()); + assertTrue(HttpServletServer.factory.get(port).isAlive()); + assertFalse(HttpServletServer.factory.get(port).isAaf()); - String response = http(HttpServletServer.factory.get(5678), "http://localhost:5678/junit/echo/hello"); + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello"); assertTrue("hello".equals(response)); response = null; try { - response = http(HttpServletServer.factory.get(5678), "http://localhost:5678/swagger.json"); + response = http(HttpServletServer.factory.get(port), portUrl + "/swagger.json"); } catch (IOException e) { // Expected } assertTrue(response == null); - response = http(HttpServletServer.factory.get(5678), "http://localhost:5678/junit/echo/hello?block=true"); + response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello?block=true"); assertEquals("FILTERED", response); - assertTrue(HttpServletServer.factory.get(5678).isAlive()); - assertTrue(HttpServletServer.factory.inventory().size() == 1); + assertTrue(HttpServletServer.factory.get(port).isAlive()); + assertEquals(1, HttpServletServer.factory.inventory().size()); server.setAafAuthentication("/*"); - assertTrue(HttpServletServer.factory.get(5678).isAaf()); + assertTrue(HttpServletServer.factory.get(port).isAaf()); - HttpServletServer.factory.destroy(5678); - assertTrue(HttpServletServer.factory.inventory().size() == 0); + HttpServletServer.factory.destroy(port); + assertEquals(0, HttpServletServer.factory.inventory().size()); } @Test public void testMultipleServers() throws Exception { logger.info("-- testMultipleServers() --"); - HttpServletServer server1 = HttpServletServer.factory.build("echo-1", false,"localhost", 5688, "/", true, true); + HttpServletServer server1 = HttpServletServer.factory.build("echo-1", false,"localhost", port, "/", true, true); server1.addServletPackage("/*", this.getClass().getPackage().getName()); server1.waitedStart(5000); - HttpServletServer server2 = HttpServletServer.factory.build("echo-2", "localhost", 5689, "/", false, true); + int port2 = port + 1; + + HttpServletServer server2 = HttpServletServer.factory.build("echo-2", "localhost", port2, "/", false, true); server2.addServletPackage("/*", this.getClass().getPackage().getName()); server2.waitedStart(5000); - assertTrue(HttpServletServer.factory.get(5688).isAlive()); - assertTrue(HttpServletServer.factory.get(5689).isAlive()); + assertTrue(HttpServletServer.factory.get(port).isAlive()); + assertTrue(HttpServletServer.factory.get(port2).isAlive()); - String response = http(HttpServletServer.factory.get(5688), "http://localhost:5688/junit/echo/hello"); + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello"); assertTrue("hello".equals(response)); - response = http(HttpServletServer.factory.get(5688), "http://localhost:5688/swagger.json"); + response = http(HttpServletServer.factory.get(port), portUrl + "/swagger.json"); assertTrue(response != null); - response = http(HttpServletServer.factory.get(5689), "http://localhost:5689/junit/echo/hello"); + response = http(HttpServletServer.factory.get(port2), LOCALHOST_PREFIX + port2 + "/junit/echo/hello"); assertTrue("hello".equals(response)); response = null; try { - response = http(HttpServletServer.factory.get(5689), "http://localhost:5689/swagger.json"); + response = http(HttpServletServer.factory.get(port2), LOCALHOST_PREFIX + port2 + "/swagger.json"); } catch (IOException e) { // Expected } @@ -125,16 +312,16 @@ public class HttpServerTest { String randomName = UUID.randomUUID().toString(); - HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", 5668, "/", false, true); + HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", port, "/", false, true); server.addServletPackage("/*", this.getClass().getPackage().getName()); server.waitedStart(5000); - assertTrue(HttpServletServer.factory.get(5668).isAlive()); + assertTrue(HttpServletServer.factory.get(port).isAlive()); - String response = http(HttpServletServer.factory.get(5668), "http://localhost:5668/junit/echo/hello"); + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello"); assertTrue("hello".equals(response)); - response = http(HttpServletServer.factory.get(5668), "http://localhost:5668/junit/endpoints/http/servers"); + response = http(HttpServletServer.factory.get(port), portUrl + "/junit/endpoints/http/servers"); assertTrue(response.contains(randomName)); HttpServletServer.factory.destroy(); @@ -146,13 +333,13 @@ public class HttpServerTest { logger.info("-- testServiceClass() --"); String randomName = UUID.randomUUID().toString(); - HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", 5658, "/", false, true); + HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", port, "/", false, true); server.addServletClass("/*", RestEchoService.class.getCanonicalName()); server.waitedStart(5000); - assertTrue(HttpServletServer.factory.get(5658).isAlive()); + assertTrue(HttpServletServer.factory.get(port).isAlive()); - String response = http(HttpServletServer.factory.get(5658), "http://localhost:5658/junit/echo/hello"); + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello"); assertTrue("hello".equals(response)); HttpServletServer.factory.destroy(); @@ -165,17 +352,17 @@ public class HttpServerTest { String randomName = UUID.randomUUID().toString(); - HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", 5648, "/", false, true); + HttpServletServer server = HttpServletServer.factory.build(randomName, "localhost", port, "/", false, true); server.addServletClass("/*", RestEchoService.class.getCanonicalName()); server.addServletClass("/*", RestEndpoints.class.getCanonicalName()); server.waitedStart(5000); - assertTrue(HttpServletServer.factory.get(5648).isAlive()); + assertTrue(HttpServletServer.factory.get(port).isAlive()); - String response = http(HttpServletServer.factory.get(5648), "http://localhost:5648/junit/echo/hello"); + String response = http(HttpServletServer.factory.get(port), portUrl + "/junit/echo/hello"); assertTrue("hello".equals(response)); - response = http(HttpServletServer.factory.get(5648), "http://localhost:5648/junit/endpoints/http/servers"); + response = http(HttpServletServer.factory.get(port), portUrl + "/junit/endpoints/http/servers"); assertTrue(response.contains(randomName)); HttpServletServer.factory.destroy(); @@ -184,7 +371,7 @@ public class HttpServerTest { /** * performs an http request. - * + * * @throws MalformedURLException make sure URL is good * @throws IOException thrown is IO exception occurs * @throws InterruptedException thrown if thread interrupted occurs @@ -197,7 +384,8 @@ public class HttpServerTest { int maxNumberRetries = 5; while (numRetries <= maxNumberRetries) { try { - response = response(url); + URLConnection conn = url.openConnection(); + response = response(conn); break; } catch (ConnectException e) { logger.warn("http server {} @ {} ({}) - cannot connect yet ..", server, urlString, numRetries, e); @@ -211,16 +399,51 @@ public class HttpServerTest { return response; } + /** + * Performs an http request. + * + * @throws MalformedURLException make sure URL is good + * @throws IOException thrown is IO exception occurs + * @throws InterruptedException thrown if thread interrupted occurs + */ + protected String http(HttpServletServer server, String urlString, String post) + throws MalformedURLException, IOException, InterruptedException { + URL url = new URL(urlString); + String response = null; + int numRetries = 1; + int maxNumberRetries = 5; + while (numRetries <= maxNumberRetries) { + try { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setDoOutput(true); + conn.setRequestProperty("Content-Type", "application/json"); + IOUtils.write(post, conn.getOutputStream()); + response = response(conn); + break; + } catch (ConnectException e) { + logger.warn("http server {} @ {} ({}) - cannot connect yet ..", server, urlString, numRetries, e); + numRetries++; + Thread.sleep(10000L); + } catch (Exception e) { + logger.error("http error", e); + throw e; + } + } + + return response; + } + /** * gets http response. - * - * @param url url - * + * + * @param conn connection from which to read + * * @throws IOException if an I/O error occurs */ - protected String response(URL url) throws IOException { + protected String response(URLConnection conn) throws IOException { String response = ""; - try (BufferedReader ioReader = new BufferedReader(new InputStreamReader(url.openStream()))) { + try (BufferedReader ioReader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { String line; while ((line = ioReader.readLine()) != null) { response += line; @@ -229,6 +452,4 @@ public class HttpServerTest { return response; } - - } diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyGsonProvider.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyGsonProvider.java new file mode 100644 index 00000000..037f6c6f --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyGsonProvider.java @@ -0,0 +1,76 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import org.onap.policy.common.endpoints.http.server.internal.GsonMessageBodyHandler; + +/** + * GsonMessageBodyHandler that tracks activities. + */ +public class MyGsonProvider extends GsonMessageBodyHandler { + private static boolean readSome = false; + private static boolean wroteSome = false; + + /** + * Constructs the object and resets the variables to indicate that no activity has + * occurred yet. + */ + public MyGsonProvider() { + super(); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException { + + readSome = true; + return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream); + } + + @Override + public void writeTo(Object object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { + + wroteSome = true; + super.writeTo(object, type, genericType, annotations, mediaType, httpHeaders, entityStream); + } + + public static boolean hasReadSome() { + return readSome; + } + + public static boolean hasWrittenSome() { + return wroteSome; + } + + public static void resetSome() { + MyGsonProvider.readSome = false; + MyGsonProvider.wroteSome = false; + } + +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyJacksonProvider.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyJacksonProvider.java new file mode 100644 index 00000000..07062451 --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/MyJacksonProvider.java @@ -0,0 +1,76 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.test; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +/** + * JacksonJsonProvider that tracks activities. + */ +public class MyJacksonProvider extends JacksonJsonProvider { + private static boolean readSome = false; + private static boolean wroteSome = false; + + /** + * Constructs the object and resets the variables to indicate that no activity has + * occurred yet. + */ + public MyJacksonProvider() { + super(); + } + + @Override + public Object readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) throws IOException { + + readSome = true; + return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream); + } + + @Override + public void writeTo(Object object, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { + + wroteSome = true; + super.writeTo(object, type, genericType, annotations, mediaType, httpHeaders, entityStream); + } + + public static boolean hasReadSome() { + return readSome; + } + + public static boolean hasWrittenSome() { + return wroteSome; + } + + public static void resetSome() { + MyJacksonProvider.readSome = false; + MyJacksonProvider.wroteSome = false; + } + +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoReqResp.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoReqResp.java new file mode 100644 index 00000000..5778eeeb --- /dev/null +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoReqResp.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.test; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; + +/** + * "ECHO" request and response supporting serialization and de-serialization via + * both jackson and gson. + */ +public class RestEchoReqResp { + @JsonProperty("reqId") + @SerializedName("reqId") + private int requestId; + + @JsonProperty("textValue") + @SerializedName("textValue") + private String text; + + public int getRequestId() { + return requestId; + } + + public void setRequestId(int requestId) { + this.requestId = requestId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public String toString() { + return "RestEchoReqResp [requestId=" + requestId + ", text=" + text + "]"; + } +} diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoService.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoService.java index 5d9b14d3..9db1053f 100644 --- a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoService.java +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/RestEchoService.java @@ -22,7 +22,6 @@ package org.onap.policy.common.endpoints.http.server.test; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; - import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -31,6 +30,8 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; @Api(value = "echo") @@ -53,6 +54,14 @@ public class RestEchoService { return "PUT:" + word + ":" + entity.toString(); } + @POST + @Path("/full/request") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "echoes back the request structure", response = RestEchoReqResp.class) + public Response echoFullyPost(RestEchoReqResp reqResp) { + return Response.status(Status.OK).entity(reqResp).build(); + } + @POST @Path("{word}") @Produces(MediaType.TEXT_PLAIN) -- cgit 1.2.3-korg