Use "coder" to serialize Actor requests 92/108592/2
authorJim Hahn <jrh3@att.com>
Fri, 29 May 2020 20:32:57 +0000 (16:32 -0400)
committerJim Hahn <jrh3@att.com>
Fri, 29 May 2020 20:51:29 +0000 (16:51 -0400)
Modified the Actors to use the "coder" to serialize requests
instead of defaulting to the HttpClient serialization provider.
Decided to just pretty-print the requests since that can be used for
both logging and transmission, which avoids serializing the request twice.

Issue-ID: POLICY-2601
Change-Id: I190ed19dd852a1aa66156b358cbc97c3b121af1f
Signed-off-by: Jim Hahn <jrh3@att.com>
13 files changed:
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java
models-interactions/model-actors/actor.guard/src/main/java/org/onap/policy/controlloop/actor/guard/GuardOperation.java
models-interactions/model-actors/actor.sdnc/src/main/java/org/onap/policy/controlloop/actor/sdnc/SdncOperation.java
models-interactions/model-actors/actor.sdnc/src/test/java/org/onap/policy/controlloop/actor/sdnc/BasicSdncOperation.java
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/VfModuleCreate.java
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/VfModuleDelete.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/VfModuleDeleteTest.java
models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/BidirectionalTopicOperation.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartial.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperationTest.java

index 2cc2a69..718047c 100644 (file)
@@ -117,9 +117,10 @@ public class AaiCustomQueryOperation extends HttpOperation<String> {
 
         String url = str.toString();
 
-        logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+        String strRequest = prettyPrint(request);
+        logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
 
-        Entity<Map<String, String>> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
+        Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
         return handleResponse(outcome, url, callback -> webldr.async().put(entity, callback));
     }
index ca6cfb2..386eb21 100644 (file)
@@ -38,14 +38,11 @@ import java.util.TreeMap;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
-import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.InvocationCallback;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.onap.policy.aai.AaiConstants;
 import org.onap.policy.aai.AaiCqResponse;
@@ -70,9 +67,6 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
     private static final String MY_VSERVER = "my-vserver-name";
     private static final String SIM_VSERVER = "OzVServer";
 
-    @Captor
-    private ArgumentCaptor<Entity<Map<String, String>>> entityCaptor;
-
     @Mock
     private Actor tenantActor;
 
@@ -215,10 +209,13 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
         oper.start();
         executor.runAll(100);
 
-        verify(webAsync).put(entityCaptor.capture(), any(InvocationCallback.class));
+        verify(webAsync).put(requestCaptor.capture(), any(InvocationCallback.class));
+
+        String reqText = requestCaptor.getValue().getEntity();
+        Map<String,String> reqMap = coder.decode(reqText, Map.class);
 
         // sort the request fields so they match the order in cq.json
-        Map<String, String> request = new TreeMap<>(entityCaptor.getValue().getEntity());
+        Map<String, String> request = new TreeMap<>(reqMap);
 
         verifyRequest("cq.json", request);
     }
index da1f956..a21886b 100644 (file)
@@ -106,14 +106,16 @@ public class GuardOperation extends HttpOperation<DecisionResponse> {
     @Override
     protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
         DecisionRequest request = makeRequest();
-        Entity<DecisionRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
 
         Map<String, Object> headers = makeHeaders();
 
         headers.put("Accept", MediaType.APPLICATION_JSON);
         String url = getUrl();
 
-        logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+        String strRequest = prettyPrint(request);
+        logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
+
+        Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
         // @formatter:off
         return handleResponse(outcome, url,
index ca6d07b..9b63943 100644 (file)
@@ -61,7 +61,6 @@ public abstract class SdncOperation extends HttpOperation<SdncResponse> {
     protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
 
         SdncRequest request = makeRequest(attempt);
-        Entity<SdncRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
 
         Map<String, Object> headers = makeHeaders();
 
@@ -69,7 +68,10 @@ public abstract class SdncOperation extends HttpOperation<SdncResponse> {
         String path = getPath();
         String url = getClient().getBaseUrl() + path;
 
-        logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+        String strRequest = prettyPrint(request);
+        logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
+
+        Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
         // @formatter:off
         return handleResponse(outcome, url,
index 9216942..5cc0956 100644 (file)
@@ -36,6 +36,7 @@ import java.util.concurrent.TimeoutException;
 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
+import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
@@ -114,7 +115,7 @@ public abstract class BasicSdncOperation extends BasicHttpOperation<SdncRequest>
      * @return the request that was posted
      */
     protected SdncRequest verifyOperation(SdncOperation operation)
-                    throws InterruptedException, ExecutionException, TimeoutException {
+                    throws InterruptedException, ExecutionException, TimeoutException, CoderException {
 
         CompletableFuture<OperationOutcome> future2 = operation.start();
         executor.runAll(100);
@@ -132,7 +133,9 @@ public abstract class BasicSdncOperation extends BasicHttpOperation<SdncRequest>
 
         assertNotNull(outcome.getSubRequestId());
 
-        return requestCaptor.getValue().getEntity();
+        String reqText = requestCaptor.getValue().getEntity();
+
+        return coder.decode(reqText, SdncRequest.class);
     }
 
     /**
index 077c857..6a6c792 100644 (file)
@@ -107,10 +107,12 @@ public class VfModuleCreate extends SoOperation {
         String path = getPath() + pair.getLeft();
         SoRequest request = pair.getRight();
 
-        Entity<SoRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
         String url = getClient().getBaseUrl() + path;
 
-        logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+        String strRequest = prettyPrint(request);
+        logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
+
+        Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
         Map<String, Object> headers = createSimpleHeaders();
 
index 5134d58..04f0287 100644 (file)
@@ -45,7 +45,6 @@ import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.endpoints.http.client.HttpClient;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
-import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
@@ -118,13 +117,14 @@ public class VfModuleDelete extends SoOperation {
         SoRequest request = pair.getRight();
         String url = getPath() + pair.getLeft();
 
-        logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+        String strRequest = prettyPrint(request);
+        logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
 
         Map<String, Object> headers = createSimpleHeaders();
 
         // @formatter:off
         return handleResponse(outcome, url,
-            callback -> delete(url, headers, MediaType.APPLICATION_JSON, request, callback));
+            callback -> delete(url, headers, MediaType.APPLICATION_JSON, strRequest, callback));
         // @formatter:on
     }
 
@@ -143,12 +143,9 @@ public class VfModuleDelete extends SoOperation {
      *         future will actually cancel the underlying HTTP request
      */
     protected <Q> CompletableFuture<Response> delete(String uri, Map<String, Object> headers, String contentType,
-                    Q request, InvocationCallback<Response> callback) {
+                    String request, InvocationCallback<Response> callback) {
         // TODO move to HttpOperation
 
-        // make sure we can encode it before going any further
-        final String body = encodeRequest(request);
-
         final String url = getClient().getBaseUrl() + uri;
 
         Builder builder = HttpRequest.newBuilder(URI.create(url));
@@ -161,7 +158,7 @@ public class VfModuleDelete extends SoOperation {
 
         PipelineControllerFuture<Response> controller = new PipelineControllerFuture<>();
 
-        HttpRequest req = builder.method("DELETE", BodyPublishers.ofString(body)).build();
+        HttpRequest req = builder.method("DELETE", BodyPublishers.ofString(request)).build();
 
         CompletableFuture<HttpResponse<String>> future = makeHttpClient().sendAsync(req, BodyHandlers.ofString());
 
@@ -182,26 +179,6 @@ public class VfModuleDelete extends SoOperation {
         return controller;
     }
 
-    /**
-     * Encodes a request.
-     *
-     * @param <Q> request type
-     * @param request request to be encoded
-     * @return the encoded request
-     */
-    protected <Q> String encodeRequest(Q request) {
-        // TODO move to HttpOperation
-        try {
-            if (request instanceof String) {
-                return request.toString();
-            } else {
-                return makeCoder().encode(request);
-            }
-        } catch (CoderException e) {
-            throw new IllegalArgumentException("cannot encode request", e);
-        }
-    }
-
     /**
      * Adds the authorization header to the HTTP request, if configured.
      *
index 86242f0..f5d05a0 100644 (file)
@@ -61,9 +61,7 @@ import org.onap.aai.domain.yang.ServiceInstance;
 import org.onap.aai.domain.yang.Tenant;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
-import org.onap.policy.common.utils.coder.Coder;
 import org.onap.policy.common.utils.coder.CoderException;
-import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
@@ -271,8 +269,10 @@ public class VfModuleDeleteTest extends BasicSoOperation {
 
         Map<String, Object> headers = Map.of("key-A", "value-A");
 
+        String reqText = oper.prettyPrint(req);
+
         final CompletableFuture<Response> delFuture =
-                        oper.delete("my-uri", headers, MediaType.APPLICATION_JSON, req, callback);
+                        oper.delete("my-uri", headers, MediaType.APPLICATION_JSON, reqText, callback);
 
         ArgumentCaptor<HttpRequest> reqCaptor = ArgumentCaptor.forClass(HttpRequest.class);
         verify(javaClient).sendAsync(reqCaptor.capture(), any());
@@ -311,8 +311,10 @@ public class VfModuleDeleteTest extends BasicSoOperation {
         SoRequest req = new SoRequest();
         req.setRequestId(REQ_ID);
 
+        String reqText = oper.prettyPrint(req);
+
         CompletableFuture<Response> delFuture =
-                        oper.delete("/my-uri", Map.of(), MediaType.APPLICATION_JSON, req, callback);
+                        oper.delete("/my-uri", Map.of(), MediaType.APPLICATION_JSON, reqText, callback);
 
         assertTrue(delFuture.isCompletedExceptionally());
 
@@ -321,33 +323,6 @@ public class VfModuleDeleteTest extends BasicSoOperation {
         assertSame(thrown, thrownCaptor.getValue().getCause());
     }
 
-    @Test
-    public void testEncodeBody() {
-        // try when request is already a string
-        assertEquals("hello", oper.encodeRequest("hello"));
-
-        // try with a real request
-        SoRequest req = new SoRequest();
-        req.setRequestId(REQ_ID);
-        assertEquals("{\"requestId\":\"" + REQ_ID.toString() + "\"}", oper.encodeRequest(req));
-
-        // coder throws an exception
-        oper = new MyOperation(params, config) {
-            @Override
-            protected Coder makeCoder() {
-                return new StandardCoder() {
-                    @Override
-                    public String encode(Object object) throws CoderException {
-                        throw new CoderException(EXPECTED_EXCEPTION);
-                    }
-                };
-            }
-        };
-
-        assertThatIllegalArgumentException().isThrownBy(() -> oper.encodeRequest(req))
-                        .withMessage("cannot encode request");
-    }
-
     /**
      * Tests addAuthHeader() when there is a username, but no password.
      */
index 6786b7d..4331edb 100644 (file)
@@ -53,7 +53,7 @@ public class BasicHttpOperation<Q> extends BasicOperation {
     @Captor
     protected ArgumentCaptor<InvocationCallback<Response>> callbackCaptor;
     @Captor
-    protected ArgumentCaptor<Entity<Q>> requestCaptor;
+    protected ArgumentCaptor<Entity<String>> requestCaptor;
     @Captor
     protected ArgumentCaptor<Map<String, Object>> headerCaptor;
 
index 8285a56..2a46066 100644 (file)
@@ -166,18 +166,8 @@ public abstract class BidirectionalTopicOperation<Q, S> extends OperationPartial
      * @param request request to be published
      */
     protected void publishRequest(Q request) {
-        String json;
-        try {
-            if (request instanceof String) {
-                json = request.toString();
-            } else {
-                json = makeCoder().encode(request);
-            }
-        } catch (CoderException e) {
-            throw new IllegalArgumentException("cannot encode request", e);
-        }
-
-        logMessage(EventType.OUT, topicHandler.getSinkTopicCommInfrastructure(), topicHandler.getSinkTopic(), request);
+        String json = prettyPrint(request);
+        logMessage(EventType.OUT, topicHandler.getSinkTopicCommInfrastructure(), topicHandler.getSinkTopic(), json);
 
         if (!topicHandler.send(json)) {
             throw new IllegalStateException("nothing published");
index 1acc1ff..766468c 100644 (file)
@@ -177,7 +177,7 @@ public abstract class HttpOperation<T> extends OperationPartial {
 
         logger.info("{}.{}: response received for {}", params.getActor(), params.getOperation(), params.getRequestId());
 
-        String strResponse = HttpClient.getBody(rawResponse, String.class);
+        String strResponse = rawResponse.readEntity(String.class);
 
         logMessage(EventType.IN, CommInfrastructure.REST, url, strResponse);
 
index 4aa7236..1df3187 100644 (file)
@@ -989,30 +989,24 @@ public abstract class OperationPartial implements Operation {
     }
 
     /**
-     * Logs a response. If the response is not of type, String, then it attempts to
+     * Logs a message. If the message is not of type, String, then it attempts to
      * pretty-print it into JSON before logging.
      *
      * @param direction IN or OUT
      * @param infra communication infrastructure on which it was published
      * @param source source name (e.g., the URL or Topic name)
-     * @param response response to be logged
+     * @param message message to be logged
      * @return the JSON text that was logged
      */
-    public <T> String logMessage(EventType direction, CommInfrastructure infra, String source, T response) {
+    public <T> String logMessage(EventType direction, CommInfrastructure infra, String source, T message) {
         String json;
         try {
-            if (response == null) {
-                json = null;
-            } else if (response instanceof String) {
-                json = response.toString();
-            } else {
-                json = makeCoder().encode(response, true);
-            }
+            json = prettyPrint(message);
 
-        } catch (CoderException e) {
+        } catch (IllegalArgumentException e) {
             String type = (direction == EventType.IN ? "response" : "request");
             logger.warn("cannot pretty-print {}", type, e);
-            json = response.toString();
+            json = message.toString();
         }
 
         logger.info("[{}|{}|{}|]{}{}", direction, infra, source, NetLoggerUtil.SYSTEM_LS, json);
@@ -1020,6 +1014,28 @@ public abstract class OperationPartial implements Operation {
         return json;
     }
 
+    /**
+     * Converts a message to a "pretty-printed" String using the operation's normal
+     * serialization provider (i.e., it's <i>coder</i>).
+     *
+     * @param message response to be logged
+     * @return the JSON text that was logged
+     * @throws IllegalArgumentException if the message cannot be converted
+     */
+    public <T> String prettyPrint(T message) {
+        if (message == null) {
+            return null;
+        } else if (message instanceof String) {
+            return message.toString();
+        } else {
+            try {
+                return makeCoder().encode(message, true);
+            } catch (CoderException e) {
+                throw new IllegalArgumentException("cannot encode message", e);
+            }
+        }
+    }
+
     // these may be overridden by subclasses or junit tests
 
     /**
index eb1aa88..9ac88f4 100644 (file)
@@ -532,14 +532,15 @@ public class HttpOperationTest {
 
             MyRequest request = new MyRequest();
 
-            Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
-
             Map<String, Object> headers = makeHeaders();
 
             headers.put("Accept", MediaType.APPLICATION_JSON);
             String url = getUrl();
 
-            logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+            String strRequest = prettyPrint(request);
+            logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
+
+            Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
             // @formatter:off
             return handleResponse(outcome, url,
@@ -558,14 +559,15 @@ public class HttpOperationTest {
 
             MyRequest request = new MyRequest();
 
-            Entity<MyRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
-
             Map<String, Object> headers = makeHeaders();
 
             headers.put("Accept", MediaType.APPLICATION_JSON);
             String url = getUrl();
 
-            logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
+            String strRequest = prettyPrint(request);
+            logMessage(EventType.OUT, CommInfrastructure.REST, url, strRequest);
+
+            Entity<String> entity = Entity.entity(strRequest, MediaType.APPLICATION_JSON);
 
             // @formatter:off
             return handleResponse(outcome, url,