SO poll should not require request ID 15/107715/3
authorJim Hahn <jrh3@att.com>
Thu, 14 May 2020 23:08:02 +0000 (19:08 -0400)
committerJim Hahn <jrh3@att.com>
Thu, 14 May 2020 23:48:51 +0000 (19:48 -0400)
When SO is polled for the result of a previous request, it
does not necessarily include the originally returned request
ID in the response.  This causes the SO actor to generate a
"missing request ID in response" exception.
Modified the actor to only extract the request ID from the
first response and cache it for subsequeent responses.
Testing this required the SO simulator to be modified so that
it would return an INCOMPLETE on the initial request, forcing
the actor to poll until it returns a COMPLETE.  Made this a
settable flag so that it could be enabled just to test the
SO actor without impacting other components (e.g., drools-apps,
CSITs).
Also fixed a couple of checkstyle issues in the simulators.

Issue-ID: POLICY-2568
Change-Id: Ifad8b3c0c2c0b03cb82da693c2cf5ced44ede105
Signed-off-by: Jim Hahn <jrh3@att.com>
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/BasicSoOperation.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/RestManagerResponseTest.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/SoOperationTest.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/VfModuleCreateTest.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/VfModuleDeleteTest.java
models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/GuardSimulatorJaxRs.java
models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/SoSimulatorJaxRs.java
models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/VfcSimulatorJaxRs.java
models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/SoSimulatorTest.java

index 86b9101..a4c802c 100644 (file)
@@ -116,6 +116,7 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
      */
     protected void resetGetCount() {
         getCount = 0;
+        setSubRequestId(null);
     }
 
     /**
@@ -214,7 +215,7 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         // still incomplete
 
         // need a request ID with which to query
-        if (!extractSubRequestId(response)) {
+        if (getSubRequestId() == null && !extractSubRequestId(response)) {
             throw new IllegalArgumentException("missing request ID in response");
         }
 
@@ -227,7 +228,7 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         }
 
         // sleep and then perform a "get" operation
-        Function<Void, CompletableFuture<OperationOutcome>> doGet = unused -> issueGet(outcome, response);
+        Function<Void, CompletableFuture<OperationOutcome>> doGet = unused -> issueGet(outcome);
         return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet);
     }
 
@@ -257,18 +258,16 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
      * Issues a "get" request to see if the original request is complete yet.
      *
      * @param outcome outcome to be populated with the response
-     * @param response previous response
      * @return a future that can be used to cancel the "get" request or await its response
      */
-    private CompletableFuture<OperationOutcome> issueGet(OperationOutcome outcome, SoResponse response) {
-        String path = getPathGet() + response.getRequestReferences().getRequestId();
+    private CompletableFuture<OperationOutcome> issueGet(OperationOutcome outcome) {
+        String path = getPathGet() + getSubRequestId();
         String url = getClient().getBaseUrl() + path;
 
         logger.debug("{}: 'get' count {} for {}", getFullName(), getCount, params.getRequestId());
 
         logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
 
-        // TODO should this use "path" or the full "url"?
         return handleResponse(outcome, url, callback -> getClient().get(callback, path, null));
     }
 
index e6a3615..0232226 100644 (file)
@@ -33,9 +33,11 @@ import org.onap.policy.aai.AaiCqResponse;
 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.gson.GsonMessageBodyHandler;
 import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
 import org.onap.policy.controlloop.policy.Target;
+import org.onap.policy.simulators.SoSimulatorJaxRs;
 import org.onap.policy.so.SoRequest;
 import org.onap.policy.so.SoRequestParameters;
 import org.onap.policy.so.SoRequestReferences;
@@ -89,13 +91,16 @@ public abstract class BasicSoOperation extends BasicHttpOperation<SoRequest> {
     protected static void initBeforeClass() throws Exception {
         org.onap.policy.simulators.Util.buildSoSim();
 
-        BusTopicParams clientParams =
-                        BusTopicParams.builder().clientName(MY_CLIENT).basePath("").hostname("localhost")
-                                        .managed(true).port(org.onap.policy.simulators.Util.SOSIM_SERVER_PORT).build();
+        BusTopicParams clientParams = BusTopicParams.builder().clientName(MY_CLIENT).basePath("").hostname("localhost")
+                        .managed(true).port(org.onap.policy.simulators.Util.SOSIM_SERVER_PORT)
+                        .serializationProvider(GsonMessageBodyHandler.class.getName()).build();
         HttpClientFactoryInstance.getClientFactory().build(clientParams);
+
+        SoSimulatorJaxRs.setYieldIncomplete(true);
     }
 
     protected static void destroyAfterClass() {
+        SoSimulatorJaxRs.setYieldIncomplete(false);
         HttpClientFactoryInstance.getClientFactory().destroy();
         HttpServletServerFactoryInstance.getServerFactory().destroy();
     }
index 7a9541c..a544a62 100644 (file)
@@ -81,6 +81,7 @@ public class RestManagerResponseTest {
         assertThatThrownBy(() -> resp.bufferEntity()).isInstanceOf(UnsupportedOperationException.class);
         assertThatThrownBy(() -> resp.getLength()).isInstanceOf(UnsupportedOperationException.class);
         assertThatThrownBy(() -> resp.readEntity(generic)).isInstanceOf(UnsupportedOperationException.class);
+        assertThatThrownBy(() -> resp.readEntity(String.class, null)).isInstanceOf(UnsupportedOperationException.class);
         assertThatThrownBy(() -> resp.readEntity(generic, null)).isInstanceOf(UnsupportedOperationException.class);
         assertThatThrownBy(() -> resp.getStatusInfo()).isInstanceOf(UnsupportedOperationException.class);
         assertThatThrownBy(() -> resp.getEntity()).isInstanceOf(UnsupportedOperationException.class);
index ce0d2c2..7009396 100644 (file)
@@ -185,6 +185,8 @@ public class SoOperationTest extends BasicSoOperation {
         assertNotNull(oper.getSubRequestId());
 
         // no request id in the response
+        oper.generateSubRequestId(2);
+        assertNull(oper.getSubRequestId());
         response.getRequestReferences().setRequestId(null);
         response.getRequest().getRequestStatus().setRequestState("unknown");
         assertThatIllegalArgumentException()
@@ -231,7 +233,7 @@ public class SoOperationTest extends BasicSoOperation {
 
         CompletableFuture<OperationOutcome> future2 = oper.postProcessResponse(outcome, PATH, rawResponse, response);
 
-        assertSame(outcome, future2.get(500, TimeUnit.SECONDS));
+        assertSame(outcome, future2.get(5, TimeUnit.SECONDS));
         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
         assertEquals(2, oper.getGetCount());
 
@@ -248,6 +250,7 @@ public class SoOperationTest extends BasicSoOperation {
 
         oper.resetGetCount();
         assertEquals(0, oper.getGetCount());
+        assertNull(oper.getSubRequestId());
     }
 
     @Test
index 918559a..def1074 100644 (file)
@@ -88,7 +88,7 @@ public class VfModuleCreateTest extends BasicSoOperation {
     @Test
     public void testSuccess() throws Exception {
         SoParams opParams = SoParams.builder().clientName(MY_CLIENT).path("serviceInstantiation/v7/serviceInstances")
-                        .pathGet("orchestrationRequests/v5/").build();
+                        .pathGet("orchestrationRequests/v5/").maxGets(2).build();
         config = new SoConfig(blockingExecutor, opParams, HttpClientFactoryInstance.getClientFactory());
 
         params = params.toBuilder().retry(0).timeoutSec(5).executor(blockingExecutor).build();
index 16775fe..0ec1a1d 100644 (file)
@@ -122,7 +122,7 @@ public class VfModuleDeleteTest extends BasicSoOperation {
     @Test
     public void testSuccess() throws Exception {
         SoParams opParams = SoParams.builder().clientName(MY_CLIENT).path("serviceInstances/v7")
-                        .pathGet("orchestrationRequests/v5/").build();
+                        .pathGet("orchestrationRequests/v5/").maxGets(2).build();
         config = new SoConfig(blockingExecutor, opParams, HttpClientFactoryInstance.getClientFactory());
 
         params = params.toBuilder().retry(0).timeoutSec(5).executor(blockingExecutor).build();
index 2712475..f915f86 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * simulators
  * ================================================================================
- * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * Modifications Copyright (C) 2019 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,13 +23,11 @@ package org.onap.policy.simulators;
 
 import java.util.Collections;
 import java.util.Map;
-
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-
 import org.onap.policy.models.decisions.concepts.DecisionRequest;
 import org.onap.policy.models.decisions.concepts.DecisionResponse;
 
index ed6bce9..8e787f8 100644 (file)
 
 package org.onap.policy.simulators;
 
-import com.google.gson.Gson;
-
+import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import lombok.Setter;
+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.so.SoRequest;
 import org.onap.policy.so.SoRequestReferences;
 import org.onap.policy.so.SoRequestStatus;
@@ -39,6 +44,25 @@ import org.onap.policy.so.SoResponse;
 
 @Path("/")
 public class SoSimulatorJaxRs {
+    private final Coder coder = new StandardCoder();
+
+    /**
+     * Set of incomplete request IDs. When a POST or DELETE is performed, the new request
+     * ID is added to the set. When the request is polled, the ID is removed and an empty
+     * response is returned. When the request is polled again, it sees that there is no
+     * entry and returns a completion indication.
+     *
+     * <p/>
+     * This is static so request IDs are retained across servlets.
+     */
+    private static final Set<String> incomplete = ConcurrentHashMap.newKeySet();
+
+    /**
+     * {@code True} if the initial request should yield an incomplete, {@code false}
+     * otherwise.  This is used when junit testing the SO actor.
+     */
+    @Setter
+    private static boolean yieldIncomplete = false;
 
     /**
      * SO post query.
@@ -52,8 +76,9 @@ public class SoSimulatorJaxRs {
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces("application/json")
     public String soPostQuery(@PathParam("serviceInstanceId") final String serviceInstanceId,
-                    @PathParam("vnfInstanceId") final String vnfInstanceId) {
-        return makeCompleteSuccess();
+                    @PathParam("vnfInstanceId") final String vnfInstanceId) throws CoderException {
+
+        return coder.encode(yieldIncomplete ? makeIncomplete() : makeComplete(UUID.randomUUID().toString()));
     }
 
     /**
@@ -69,14 +94,52 @@ public class SoSimulatorJaxRs {
     @Produces("application/json")
     public String soDelete(@PathParam("serviceInstanceId") final String serviceInstanceId,
                     @PathParam("vnfInstanceId") final String vnfInstanceId,
-                    @PathParam("vfModuleInstanceId") final String vfModuleInstanceId) {
-        return makeCompleteSuccess();
+                    @PathParam("vfModuleInstanceId") final String vfModuleInstanceId) throws CoderException {
+
+        return coder.encode(yieldIncomplete ? makeIncomplete() : makeComplete(UUID.randomUUID().toString()));
+    }
+
+    /**
+     * Poll SO result.
+     *
+     * @param requestId the ID of the request whose status is to be queried
+     * @return the response
+     */
+    @GET
+    @Path("/orchestrationRequests/v5/{requestId}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces("application/json")
+    public String soGetQuery(@PathParam("requestId") final String requestId) throws CoderException {
+        if (incomplete.remove(requestId)) {
+            // first poll - return empty response
+            return coder.encode(new SoResponse());
+
+        } else {
+            return coder.encode(makeComplete(requestId));
+        }
+    }
+
+    private SoResponse makeIncomplete() {
+        final SoResponse response = makeResponse();
+        response.getRequest().getRequestStatus().setRequestState("INCOMPLETE");
+
+        incomplete.add(response.getRequestReferences().getRequestId());
+
+        return response;
+    }
+
+    private SoResponse makeComplete(String requestId) {
+        final SoResponse response = makeResponse();
+
+        response.getRequest().getRequestStatus().setRequestState("COMPLETE");
+        response.getRequest().setRequestId(UUID.fromString(requestId));
+
+        return response;
     }
 
-    private String makeCompleteSuccess() {
+    private SoResponse makeResponse() {
         final SoRequest request = new SoRequest();
         final SoRequestStatus requestStatus = new SoRequestStatus();
-        requestStatus.setRequestState("COMPLETE");
         request.setRequestStatus(requestStatus);
         request.setRequestId(UUID.randomUUID());
 
@@ -89,6 +152,6 @@ public class SoSimulatorJaxRs {
 
         response.setRequest(request);
 
-        return new Gson().toJson(response);
+        return response;
     }
 }
index 836db86..cf21c7b 100644 (file)
@@ -2,15 +2,15 @@
  * ============LICENSE_START=======================================================
  * simulators
  * ================================================================================
- * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * Modifications Copyright (C) 2019 Nordix Foundation.
  * ================================================================================
  * 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.
@@ -30,7 +30,6 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,7 +38,7 @@ public class VfcSimulatorJaxRs {
 
     /**
      * VFC post query.
-     * 
+     *
      * @param nsInstanceId the NS instance
      * @param response the response
      * @return the response
@@ -64,7 +63,7 @@ public class VfcSimulatorJaxRs {
 
     /**
      * VFC get query.
-     * 
+     *
      * @param jobId tthe job id
      * @return the response
      */
index 723619e..c5f9973 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * simulators
  * ================================================================================
- * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * Modifications Copyright (C) 2019 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,9 @@
 
 package org.onap.policy.simulators;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
 import java.util.HashMap;
@@ -60,6 +62,7 @@ public class SoSimulatorTest {
     @AfterClass
     public static void tearDownSimulator() {
         HttpServletServerFactoryInstance.getServerFactory().destroy();
+        SoSimulatorJaxRs.setYieldIncomplete(false);
     }
 
     /**
@@ -135,25 +138,113 @@ public class SoSimulatorTest {
 
     @Test
     public void testPost() {
-        final String request = Serialization.gsonPretty.toJson(this.createTestRequest());
-        final Pair<Integer, String> httpDetails = new RestManager().post(
+        SoSimulatorJaxRs.setYieldIncomplete(false);
+        String request = Serialization.gsonPretty.toJson(this.createTestRequest());
+        Pair<Integer, String> httpDetails = new RestManager().post(
                         "http://localhost:6667/serviceInstantiation/v7/serviceInstances/12345/vnfs/12345/vfModules/scaleOut",
                         "username",
                         "password", new HashMap<>(), "application/json", request);
         assertNotNull(httpDetails);
-        final SoResponse response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        SoResponse response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
         assertNotNull(response);
+        assertNotNull(response.getRequestReferences());
+        assertNotNull(response.getRequestReferences().getRequestId());
+        assertEquals("COMPLETE", response.getRequest().getRequestStatus().getRequestState());
+
+        /*
+         * Repeat, but set the flag indicating that the request should yield incomplete.
+         */
+        SoSimulatorJaxRs.setYieldIncomplete(true);
+
+        request = Serialization.gsonPretty.toJson(this.createTestRequest());
+        httpDetails = new RestManager().post(
+                        "http://localhost:6667/serviceInstantiation/v7/serviceInstances/12345/vnfs/12345/vfModules/scaleOut",
+                        "username",
+                        "password", new HashMap<>(), "application/json", request);
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNotNull(response.getRequestReferences());
+        assertNotNull(response.getRequestReferences().getRequestId());
+        assertEquals("INCOMPLETE", response.getRequest().getRequestStatus().getRequestState());
+
+        // now poll for the response
+        String reqid = response.getRequestReferences().getRequestId();
+        httpDetails = new RestManager().get(
+                        "http://localhost:6667//orchestrationRequests/v5/" + reqid,
+                        "username",
+                        "password", new HashMap<>());
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNull(response.getRequest());
+
+        // poll again
+        httpDetails = new RestManager().get(
+                        "http://localhost:6667//orchestrationRequests/v5/" + reqid,
+                        "username",
+                        "password", new HashMap<>());
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNotNull(response.getRequest());
+        assertNotNull(response.getRequest().getRequestStatus());
+        assertEquals("COMPLETE", response.getRequest().getRequestStatus().getRequestState());
     }
 
     @Test
     public void testDelete() {
-        final String request = Serialization.gsonPretty.toJson(this.createTestRequest());
-        final Pair<Integer, String> httpDetails = new RestManager().delete(
+        SoSimulatorJaxRs.setYieldIncomplete(false);
+        String request = Serialization.gsonPretty.toJson(this.createTestRequest());
+        Pair<Integer, String> httpDetails = new RestManager().delete(
                         "http://localhost:6667/serviceInstances/v7/12345/vnfs/12345/vfModules/12345",
                         "username",
                         "password", new HashMap<>(), "application/json", request);
         assertNotNull(httpDetails);
-        final SoResponse response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        SoResponse response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNotNull(response.getRequestReferences());
+        assertNotNull(response.getRequestReferences().getRequestId());
+        assertEquals("COMPLETE", response.getRequest().getRequestStatus().getRequestState());
+
+        /*
+         * Repeat, but set the flag indicating that the request should yield incomplete.
+         */
+        SoSimulatorJaxRs.setYieldIncomplete(true);
+
+        request = Serialization.gsonPretty.toJson(this.createTestRequest());
+        httpDetails = new RestManager().delete(
+                        "http://localhost:6667/serviceInstances/v7/12345/vnfs/12345/vfModules/12345",
+                        "username",
+                        "password", new HashMap<>(), "application/json", request);
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNotNull(response.getRequestReferences());
+        assertNotNull(response.getRequestReferences().getRequestId());
+        assertEquals("INCOMPLETE", response.getRequest().getRequestStatus().getRequestState());
+
+        // now poll for the response
+        String reqid = response.getRequestReferences().getRequestId();
+        httpDetails = new RestManager().get(
+                        "http://localhost:6667//orchestrationRequests/v5/" + reqid,
+                        "username",
+                        "password", new HashMap<>());
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
+        assertNotNull(response);
+        assertNull(response.getRequest());
+
+        // poll again
+        httpDetails = new RestManager().get(
+                        "http://localhost:6667//orchestrationRequests/v5/" + reqid,
+                        "username",
+                        "password", new HashMap<>());
+        assertNotNull(httpDetails);
+        response = Serialization.gsonPretty.fromJson(httpDetails.second, SoResponse.class);
         assertNotNull(response);
+        assertNotNull(response.getRequest());
+        assertNotNull(response.getRequest().getRequestStatus());
+        assertEquals("COMPLETE", response.getRequest().getRequestStatus().getRequestState());
     }
 }