Add other APPC-LCM operations 03/103203/4
authorJim Hahn <jrh3@att.com>
Thu, 5 Mar 2020 21:41:15 +0000 (16:41 -0500)
committerJim Hahn <jrh3@att.com>
Fri, 6 Mar 2020 04:06:01 +0000 (23:06 -0500)
Also added legacy ModifyConfig as an operation within APPC-LCM.
Added logging to topic registration keys.

Issue-ID: POLICY-2403
Signed-off-by: Jim Hahn <jrh3@att.com>
Change-Id: Ia54a573fd6218a8afe870184b9a3baebc05b766a

models-interactions/model-actors/actor.appclcm/pom.xml
models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmActorServiceProvider.java
models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmConstants.java [new file with mode: 0644]
models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmOperation.java
models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/ConfigModifyOperation.java [deleted file]
models-interactions/model-actors/actor.appclcm/src/test/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmActorServiceProviderTest.java
models-interactions/model-actors/actor.appclcm/src/test/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmOperationTest.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/topic/Forwarder.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/topic/SelectorKey.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/topic/SelectorKeyTest.java

index 7538d66..01cfd43 100644 (file)
       <version>${project.version}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+     <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+      <artifactId>actor.appc</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
     <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
         <artifactId>commons-lang3</artifactId>
         <version>3.8.1</version>
     </dependency>
+    <dependency>
+     <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+      <artifactId>actor.test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.onap.policy.models.policy-models-interactions</groupId>
       <artifactId>simulators</artifactId>
index 1d04cb5..704c44d 100644 (file)
@@ -1,11 +1,10 @@
 /*-
  * ============LICENSE_START=======================================================
- * AppcLcmActorServiceProvider
+ * ONAP
  * ================================================================================
  * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
  * Modifications copyright (c) 2018 Nokia
  * Modifications Copyright (C) 2019 Nordix Foundation.
- * Modifications Copyright (C) 2020 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.
@@ -31,6 +30,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.onap.policy.appclcm.AppcLcmBody;
 import org.onap.policy.appclcm.AppcLcmCommonHeader;
 import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
@@ -39,6 +39,8 @@ import org.onap.policy.appclcm.AppcLcmOutput;
 import org.onap.policy.appclcm.AppcLcmResponseCode;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.appc.AppcOperation;
+import org.onap.policy.controlloop.actor.appc.ModifyConfigOperation;
 import org.onap.policy.controlloop.actorserviceprovider.impl.BidirectionalTopicActor;
 import org.onap.policy.controlloop.actorserviceprovider.impl.BidirectionalTopicOperator;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicActorParams;
@@ -67,17 +69,16 @@ public class AppcLcmActorServiceProvider extends BidirectionalTopicActor<Bidirec
     private static final String TARGET_VNF = "VNF";
 
     // Strings for recipes
-    private static final String RECIPE_RESTART = "Restart";
-    private static final String RECIPE_REBUILD = "Rebuild";
-    private static final String RECIPE_MIGRATE = "Migrate";
-    private static final String RECIPE_MODIFY = "ConfigModify";
+    private static final String RECIPE_RESTART = AppcLcmConstants.OPERATION_RESTART;
+    private static final String RECIPE_REBUILD = AppcLcmConstants.OPERATION_REBUILD;
+    private static final String RECIPE_MIGRATE = AppcLcmConstants.OPERATION_MIGRATE;
+    private static final String RECIPE_MODIFY = AppcLcmConstants.OPERATION_CONFIG_MODIFY;
 
     /* To be used in future releases when LCM ConfigModify is used */
     private static final String APPC_REQUEST_PARAMS = "request-parameters";
     private static final String APPC_CONFIG_PARAMS = "configuration-parameters";
 
-    private static final ImmutableList<String> recipes =
-            ImmutableList.of(RECIPE_RESTART, RECIPE_REBUILD, RECIPE_MIGRATE, RECIPE_MODIFY);
+    private static final Set<String> recipes = AppcLcmConstants.OPERATION_NAMES;
     private static final ImmutableMap<String, List<String>> targets = new ImmutableMap.Builder<String, List<String>>()
             .put(RECIPE_RESTART, ImmutableList.of(TARGET_VM)).put(RECIPE_REBUILD, ImmutableList.of(TARGET_VM))
             .put(RECIPE_MIGRATE, ImmutableList.of(TARGET_VM)).put(RECIPE_MODIFY, ImmutableList.of(TARGET_VNF)).build();
@@ -91,8 +92,15 @@ public class AppcLcmActorServiceProvider extends BidirectionalTopicActor<Bidirec
     public AppcLcmActorServiceProvider() {
         super(NAME, BidirectionalTopicActorParams.class);
 
-        addOperator(new BidirectionalTopicOperator(NAME, ConfigModifyOperation.NAME, this,
-                AppcLcmOperation.SELECTOR_KEYS, ConfigModifyOperation::new));
+        // add LCM operations first as they take precedence
+        for (String opname : AppcLcmConstants.OPERATION_NAMES) {
+            addOperator(new BidirectionalTopicOperator(NAME, opname, this, AppcLcmOperation.SELECTOR_KEYS,
+                            AppcLcmOperation::new));
+        }
+
+        // add legacy operations
+        addOperator(new BidirectionalTopicOperator(NAME, ModifyConfigOperation.NAME, this, AppcOperation.SELECTOR_KEYS,
+                        ModifyConfigOperation::new));
     }
 
     /**
diff --git a/models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmConstants.java b/models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/AppcLcmConstants.java
new file mode 100644 (file)
index 0000000..efcbe49
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.appclcm;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class AppcLcmConstants {
+
+    // Strings for OPERATIONs
+    public static final String OPERATION_RESTART = "Restart";
+    public static final String OPERATION_REBUILD = "Rebuild";
+    public static final String OPERATION_MIGRATE = "Migrate";
+    public static final String OPERATION_CONFIG_MODIFY = "ConfigModify";
+
+    public static final Set<String> OPERATION_NAMES =
+                    Set.of(OPERATION_RESTART, OPERATION_REBUILD, OPERATION_MIGRATE, OPERATION_CONFIG_MODIFY);
+
+    // operations from legacy APPC
+    public static final String LEGACY_MODIFY_CONFIG = "ModifyConfig";
+
+    public static final Set<String> LEGACY_NAMES =
+                    Set.of(LEGACY_MODIFY_CONFIG);
+
+    public static final Set<String> COMBINED_OPERATION_NAMES;
+
+    static {
+        Set<String> set = new HashSet<>(OPERATION_NAMES);
+        set.addAll(LEGACY_NAMES);
+        COMBINED_OPERATION_NAMES = Collections.unmodifiableSet(set);
+    }
+
+    protected static final Set<String> SUPPORTS_PAYLOAD =
+                    Set.of(OPERATION_CONFIG_MODIFY).stream().map(String::toLowerCase).collect(Collectors.toSet());
+
+    private AppcLcmConstants() {
+        // do nothing
+    }
+}
index c0b8331..7496227 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * AppcLcmOperation
+ * ONAP
  * ================================================================================
  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
@@ -24,16 +24,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
-import org.onap.aai.domain.yang.GenericVnf;
-import org.onap.policy.aai.AaiConstants;
-import org.onap.policy.aai.AaiCqResponse;
+import org.apache.commons.lang3.StringUtils;
 import org.onap.policy.appclcm.AppcLcmBody;
 import org.onap.policy.appclcm.AppcLcmCommonHeader;
 import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
 import org.onap.policy.appclcm.AppcLcmInput;
+import org.onap.policy.appclcm.AppcLcmOutput;
 import org.onap.policy.appclcm.AppcLcmResponseCode;
+import org.onap.policy.appclcm.AppcLcmResponseStatus;
 import org.onap.policy.common.utils.coder.CoderException;
-import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.impl.BidirectionalTopicOperation;
@@ -41,23 +40,21 @@ import org.onap.policy.controlloop.actorserviceprovider.parameters.Bidirectional
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.topic.SelectorKey;
 import org.onap.policy.controlloop.policy.PolicyResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcLcmDmaapWrapper, AppcLcmDmaapWrapper> {
+public class AppcLcmOperation extends BidirectionalTopicOperation<AppcLcmDmaapWrapper, AppcLcmDmaapWrapper> {
 
-    private static final Logger logger = LoggerFactory.getLogger(AppcLcmOperation.class);
-    private static final StandardCoder coder = new StandardCoder();
+    private static final String MISSING_STATUS = "APPC-LCM response is missing the response status";
     public static final String VNF_ID_KEY = "vnf-id";
 
     /**
      * Keys used to match the response with the request listener. The sub request ID is a
      * UUID, so it can be used to uniquely identify the response.
      * <p/>
-     * Note: if these change, then {@link #getExpectedKeyValues(int, Request)} must be
-     * updated accordingly.
+     * Note: if these change, then {@link #getExpectedKeyValues(int, AppcLcmDmaapWrapper)}
+     * must be updated accordingly.
      */
-    public static final List<SelectorKey> SELECTOR_KEYS = List.of(new SelectorKey("common-header", "sub-request-id"));
+    public static final List<SelectorKey> SELECTOR_KEYS =
+                    List.of(new SelectorKey("body", "output", "common-header", "sub-request-id"));
 
     /**
      * Constructs the object.
@@ -67,6 +64,10 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
      */
     public AppcLcmOperation(ControlLoopOperationParams params, BidirectionalTopicConfig config) {
         super(params, config, AppcLcmDmaapWrapper.class);
+
+        if (StringUtils.isBlank(params.getTargetEntity())) {
+            throw new IllegalArgumentException("missing targetEntity");
+        }
     }
 
     /**
@@ -74,40 +75,12 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
      * Starts the GUARD using startGuardAsync.
      */
     @Override
-    @SuppressWarnings("unchecked")
     protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
-        if (params != null) {
-            ControlLoopOperationParams cqParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
-                    .operation(AaiCqResponse.OPERATION).payload(null).retry(null).timeoutSec(null).build();
-
-            // run Custom Query and Guard, in parallel
-            return allOf(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams), this::startGuardAsync);
-        }
         return startGuardAsync();
     }
 
     @Override
     protected AppcLcmDmaapWrapper makeRequest(int attempt) {
-        AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
-
-        GenericVnf genvnf = cq.getGenericVnfByModelInvariantId(params.getTarget().getResourceID());
-        if (genvnf == null) {
-            logger.info("{}: target entity could not be found for {}", getFullName(), params.getRequestId());
-            throw new IllegalArgumentException("target vnf-id could not be found");
-        }
-
-        return makeRequest(attempt, genvnf.getVnfId());
-    }
-
-    /**
-     * Makes a request, given the target VNF. This is a support function for
-     * {@link #makeRequest(int)}.
-     *
-     * @param attempt attempt number
-     * @param targetVnf target VNF
-     * @return a new request
-     */
-    protected AppcLcmDmaapWrapper makeRequest(int attempt, String targetVnf) {
         VirtualControlLoopEvent onset = params.getContext().getEvent();
         String subRequestId = UUID.randomUUID().toString();
 
@@ -118,22 +91,21 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
 
         AppcLcmInput inputRequest = new AppcLcmInput();
         inputRequest.setCommonHeader(header);
-        inputRequest.setAction(getName());
+
+        AppcLcmRecipeFormatter recipeFormatter = new AppcLcmRecipeFormatter(getName());
+        inputRequest.setAction(recipeFormatter.getBodyRecipe());
 
         /*
-         * Action Identifiers are required for APPC LCM requests. For R1, the recipes supported by
-         * Policy only require a vnf-id.
+         * Action Identifiers are required for APPC LCM requests. For R1, the recipes
+         * supported by Policy only require a vnf-id.
          */
-        if (inputRequest.getActionIdentifiers() != null) {
-            inputRequest.getActionIdentifiers().put(VNF_ID_KEY, targetVnf);
-        } else {
-            inputRequest.setActionIdentifiers(Map.of(VNF_ID_KEY, targetVnf));
-        }
+        inputRequest.setActionIdentifiers(Map.of(VNF_ID_KEY, params.getTargetEntity()));
 
         /*
-         * For R1, the payloads will not be required for the Restart, Rebuild, or Migrate recipes.
-         * APPC will populate the payload based on A&AI look up of the vnd-id provided in the action
-         * identifiers. The payload is set when converPayload() is called.
+         * For R1, the payloads will not be required for the Restart, Rebuild, or Migrate
+         * recipes. APPC will populate the payload based on A&AI look up of the vnd-id
+         * provided in the action identifiers. The payload is set when converPayload() is
+         * called.
          */
         if (operationSupportsPayload()) {
             convertPayload(params.getPayload(), inputRequest);
@@ -144,9 +116,6 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
         AppcLcmBody body = new AppcLcmBody();
         body.setInput(inputRequest);
 
-        AppcLcmRecipeFormatter recipeFormatter = new AppcLcmRecipeFormatter(getName());
-        inputRequest.setAction(recipeFormatter.getBodyRecipe());
-
         AppcLcmDmaapWrapper dmaapRequest = new AppcLcmDmaapWrapper();
         dmaapRequest.setBody(body);
         dmaapRequest.setVersion("2.0");
@@ -166,14 +135,12 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
      * @param source source from which to get the values
      * @param map where to place the decoded values
      */
-    private static void convertPayload(Map<String, Object> source, AppcLcmInput request) {
-        String encodedPayloadString = null;
+    private void convertPayload(Map<String, Object> source, AppcLcmInput request) {
         try {
-            encodedPayloadString = coder.encode(source);
+            String encodedPayloadString = makeCoder().encode(source);
             request.setPayload(encodedPayloadString);
         } catch (CoderException e) {
-            logger.error("Cannot convert payload. Error encoding source as a string.", e);
-            throw new IllegalArgumentException("Cannot convert payload. Error encoding source as a string.");
+            throw new IllegalArgumentException("Cannot convert payload", e);
         }
     }
 
@@ -187,16 +154,14 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
 
     @Override
     protected Status detmStatus(String rawResponse, AppcLcmDmaapWrapper response) {
-        if (response == null || response.getBody() == null || response.getBody().getOutput() == null
-                || response.getBody().getOutput().getStatus() == null) {
-            throw new IllegalArgumentException("APPC-LCM response is missing the response status");
+        AppcLcmResponseStatus status = getStatus(response);
+        if (status == null) {
+            throw new IllegalArgumentException(MISSING_STATUS);
         }
 
-        String code = AppcLcmResponseCode.toResponseValue(response.getBody().getOutput().getStatus().getCode());
-
+        String code = AppcLcmResponseCode.toResponseValue(status.getCode());
         if (code == null) {
-            throw new IllegalArgumentException(
-                    "unknown APPC-LCM response status code: " + response.getBody().getOutput().getStatus().getCode());
+            throw new IllegalArgumentException("unknown APPC-LCM response status code: " + status.getCode());
         }
 
         switch (code) {
@@ -218,18 +183,52 @@ public abstract class AppcLcmOperation extends BidirectionalTopicOperation<AppcL
      */
     @Override
     public OperationOutcome setOutcome(OperationOutcome outcome, PolicyResult result, AppcLcmDmaapWrapper response) {
-        if (response == null || response.getBody() == null || response.getBody().getOutput() == null
-                || response.getBody().getOutput().getStatus() == null
-                || response.getBody().getOutput().getStatus().getMessage() == null) {
+        AppcLcmResponseStatus status = getStatus(response);
+        if (status == null) {
+            return setOutcome(outcome, result);
+        }
+
+        String message = status.getMessage();
+        if (message == null) {
             return setOutcome(outcome, result);
         }
 
         outcome.setResult(result);
-        outcome.setMessage(response.getBody().getOutput().getStatus().getMessage());
+        outcome.setMessage(message);
         return outcome;
     }
 
+    /**
+     * Gets the status from the response.
+     *
+     * @param response the response from which to extract the status, or {@code null}
+     * @return the status, or {@code null} if it does not exist
+     */
+    protected AppcLcmResponseStatus getStatus(AppcLcmDmaapWrapper response) {
+        if (response == null) {
+            return null;
+        }
+
+        AppcLcmBody body = response.getBody();
+        if (body == null) {
+            return null;
+        }
+
+        AppcLcmOutput output = body.getOutput();
+        if (output == null) {
+            return null;
+        }
+
+        return output.getStatus();
+    }
+
+    /**
+     * Determines if the operation supports a payload.
+     *
+     * @return {@code true} if the operation supports a payload, {@code false} otherwise
+     */
     protected boolean operationSupportsPayload() {
-        return params.getPayload() != null && !params.getPayload().isEmpty();
+        return params.getPayload() != null && !params.getPayload().isEmpty()
+                        && AppcLcmConstants.SUPPORTS_PAYLOAD.contains(params.getOperation().toLowerCase());
     }
 }
diff --git a/models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/ConfigModifyOperation.java b/models-interactions/model-actors/actor.appclcm/src/main/java/org/onap/policy/controlloop/actor/appclcm/ConfigModifyOperation.java
deleted file mode 100644 (file)
index 02645af..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- * AppcLcmOperation
- * ================================================================================
- * Copyright (C) 2020 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.controlloop.actor.appclcm;
-
-import org.onap.policy.controlloop.actorserviceprovider.parameters.BidirectionalTopicConfig;
-import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ConfigModifyOperation extends AppcLcmOperation {
-
-    private static final Logger logger = LoggerFactory.getLogger(ConfigModifyOperation.class);
-
-    public static final String NAME = "ConfigModify";
-
-    /**
-     * Constructs the object.
-     *
-     * @param params operation parameters
-     * @param config configuration for this operation
-     */
-    public ConfigModifyOperation(ControlLoopOperationParams params, BidirectionalTopicConfig config) {
-        super(params, config);
-    }
-}
index 90fc8a5..56ce047 100644 (file)
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * AppcServiceProviderTest
+ * ONAP
  * ================================================================================
- * 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");
@@ -27,7 +27,6 @@ import static org.junit.Assert.assertNull;
 
 import java.time.Instant;
 import java.util.AbstractMap;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -77,7 +76,8 @@ public class AppcLcmActorServiceProviderTest {
 
     static {
         /*
-         * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a target type of VM.
+         * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a
+         * target type of VM.
          */
         onsetEvent = new VirtualControlLoopEvent();
         onsetEvent.setClosedLoopControlName("closedLoopControlName-Test");
@@ -181,7 +181,7 @@ public class AppcLcmActorServiceProviderTest {
         assertEquals(-1, prov.getSequenceNumber());
 
         // verify that it has the operators we expect
-        var expected = Arrays.asList(ConfigModifyOperation.NAME).stream().sorted().collect(Collectors.toList());
+        var expected = AppcLcmConstants.COMBINED_OPERATION_NAMES.stream().sorted().collect(Collectors.toList());
         var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
 
         assertEquals(expected.toString(), actual.toString());
@@ -194,7 +194,7 @@ public class AppcLcmActorServiceProviderTest {
     public void constructRestartRequestTest() {
 
         AppcLcmDmaapWrapper dmaapRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, policy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, policy, VNF01);
 
         /* The service provider must return a non null DMAAP request wrapper */
         assertNotNull(dmaapRequest);
@@ -229,13 +229,14 @@ public class AppcLcmActorServiceProviderTest {
     @Test
     public void processRestartResponseSuccessTest() {
         AbstractMap.SimpleEntry<PolicyResult, String> result =
-                AppcLcmActorServiceProvider.processResponse(dmaapResponse);
+                        AppcLcmActorServiceProvider.processResponse(dmaapResponse);
         assertEquals(PolicyResult.SUCCESS, result.getKey());
         assertEquals("Restart Successful", result.getValue());
     }
 
     /**
-     * A test to assert that a null pointer exception is thrown if the APPC response body is null.
+     * A test to assert that a null pointer exception is thrown if the APPC response body
+     * is null.
      */
     @Test(expected = NullPointerException.class)
     public void processNullBodyResponseTest() {
@@ -243,7 +244,8 @@ public class AppcLcmActorServiceProviderTest {
     }
 
     /**
-     * A test to assert that a null pointer exception is thrown if the APPC response output is null.
+     * A test to assert that a null pointer exception is thrown if the APPC response
+     * output is null.
      */
     @Test(expected = NullPointerException.class)
     public void processNullOutputResponseTest() {
@@ -364,11 +366,11 @@ public class AppcLcmActorServiceProviderTest {
 
         // when
         AppcLcmDmaapWrapper migrateRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, migratePolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, migratePolicy, VNF01);
         AppcLcmDmaapWrapper rebuildRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, rebuildPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, rebuildPolicy, VNF01);
         AppcLcmDmaapWrapper restartRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, restartPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, restartPolicy, VNF01);
 
         // then
         assertNull(migrateRequest.getBody().getInput().getPayload());
@@ -384,9 +386,9 @@ public class AppcLcmActorServiceProviderTest {
 
         // when
         AppcLcmDmaapWrapper noPayloadRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, noPayloadPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, noPayloadPolicy, VNF01);
         AppcLcmDmaapWrapper emptyPayloadRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, emptyPayloadPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, emptyPayloadPolicy, VNF01);
 
         // then
         assertNull(noPayloadRequest.getBody().getInput().getPayload());
@@ -402,11 +404,11 @@ public class AppcLcmActorServiceProviderTest {
 
         // when
         AppcLcmDmaapWrapper dmaapRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, otherPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, otherPolicy, VNF01);
 
         // then
         assertEquals("{\"requestParameters\": {\"host-ip-address\":\"10.183.37.25\"}}",
-                dmaapRequest.getBody().getInput().getPayload());
+                        dmaapRequest.getBody().getInput().getPayload());
     }
 
     @Test
@@ -415,18 +417,18 @@ public class AppcLcmActorServiceProviderTest {
         HashMap<String, String> payload = new HashMap<>();
         payload.put("requestParameters", "{\"host-ip-address\":\"10.183.37.25\"}");
         payload.put("configurationParameters",
-                "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\","
-                        + "\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\","
-                        + "\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]");
+                        "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\","
+                                        + "\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\","
+                                        + "\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]");
         Policy otherPolicy = constructHealthCheckPolicyWithPayload(payload);
 
         // when
         AppcLcmDmaapWrapper dmaapRequest =
-                AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, otherPolicy, VNF01);
+                        AppcLcmActorServiceProvider.constructRequest(onsetEvent, operation, otherPolicy, VNF01);
 
         // then
-        assertEquals(dmaapRequest.getBody().getInput().getPayload(),
-                "{\"requestParameters\": " + "{\"host-ip-address\":\"10.183.37.25\"}," + "\"configurationParameters\": "
+        assertEquals(dmaapRequest.getBody().getInput().getPayload(), "{\"requestParameters\": "
+                        + "{\"host-ip-address\":\"10.183.37.25\"}," + "\"configurationParameters\": "
                         + "[{\"ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[9]\","
                         + "\"oam-ip-addr\":\"$.vf-module-topology.vf-module-parameters.param[16]\","
                         + "\"enabled\":\"$.vf-module-topology.vf-module-parameters.param[23]\"}]" + "}");
index d2af4e7..e94eaec 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * AppcLcmOperation
+ * ONAP
  * ================================================================================
  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
@@ -22,171 +22,291 @@ package org.onap.policy.controlloop.actor.appclcm;
 
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeast;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-import java.util.List;
-import java.util.UUID;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.onap.policy.appclcm.AppcLcmBody;
 import org.onap.policy.appclcm.AppcLcmCommonHeader;
 import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
-import org.onap.policy.appclcm.AppcLcmInput;
 import org.onap.policy.appclcm.AppcLcmOutput;
 import org.onap.policy.appclcm.AppcLcmResponseStatus;
-import org.onap.policy.controlloop.VirtualControlLoopEvent;
+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.ControlLoopOperation;
+import org.onap.policy.controlloop.actor.test.BasicBidirectionalTopicOperation;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
 import org.onap.policy.controlloop.actorserviceprovider.impl.BidirectionalTopicOperation.Status;
-import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.policy.PolicyResult;
-import org.powermock.reflect.Whitebox;
-
-public class AppcLcmOperationTest {
-    private AppcLcmInput mockInput;
-    private AppcLcmOutput mockOutput;
-    private AppcLcmBody mockBody;
-    private AppcLcmDmaapWrapper mockInputWrapper;
-    private AppcLcmDmaapWrapper mockOutputWrapper;
-    private OperationOutcome mockOperationOutcome;
-    private AppcLcmOperation operation;
-    private AppcLcmResponseStatus mockResponseStatus;
-    private AppcLcmCommonHeader mockCommonHeader;
-    private ControlLoopOperationParams mockParams;
-    private ControlLoopEventContext mockContext;
-    private VirtualControlLoopEvent mockEvent;
+import org.onap.policy.controlloop.policy.Target;
+
+public class AppcLcmOperationTest extends BasicBidirectionalTopicOperation {
+
+    private static final String EXPECTED_EXCEPTION = "expected exception";
+    private static final String PAYLOAD_KEY1 = "key-A";
+    private static final String PAYLOAD_VALUE1 = "value-A";
+    private static final String MY_MESSAGE = "my-message";
+    protected static final String MY_VNF = "my-vnf";
+    protected static final String RESOURCE_ID = "my-resource";
+    private static final int SUCCESS_CODE = 400;
+
+    private AppcLcmDmaapWrapper response;
+    private AppcLcmOperation oper;
 
     /**
-     * Setup mocks for testing.
+     * Sets up.
      */
     @Before
-    public void setup() {
-        mockInput = Mockito.mock(AppcLcmInput.class);
-        mockOutput = Mockito.mock(AppcLcmOutput.class);
-        mockBody = Mockito.mock(AppcLcmBody.class);
-        mockContext = Mockito.mock(ControlLoopEventContext.class);
-        mockEvent = Mockito.mock(VirtualControlLoopEvent.class);
-        mockInputWrapper = Mockito.mock(AppcLcmDmaapWrapper.class);
-        mockOutputWrapper = Mockito.mock(AppcLcmDmaapWrapper.class);
-        mockOperationOutcome = Mockito.mock(OperationOutcome.class);
-        mockResponseStatus = Mockito.mock(AppcLcmResponseStatus.class);
-        mockCommonHeader = Mockito.mock(AppcLcmCommonHeader.class);
-        mockParams = Mockito.mock(ControlLoopOperationParams.class);
-        operation = Mockito.mock(AppcLcmOperation.class);
+    public void setUp() {
+        super.setUpBasic();
+
+        response = makeResponse();
+
+        oper = new AppcLcmOperation(params, config);
     }
 
     @Test
-    public void testStartPreprocessorAsync() {
-        Mockito.doCallRealMethod().when(operation).startPreprocessorAsync();
-        assertNull(operation.startPreprocessorAsync());
+    public void testConstructor() {
+        assertEquals(DEFAULT_ACTOR, oper.getActorName());
+        assertEquals(DEFAULT_OPERATION, oper.getName());
+
+        // missing target entity
+        params = params.toBuilder().targetEntity("").build();
+        assertThatIllegalArgumentException().isThrownBy(() -> new AppcLcmOperation(params, config))
+                        .withMessage("missing targetEntity");
+    }
+
+    @Test
+    public void testStartPreprocessorAsync() throws Exception {
+        context = mock(ControlLoopEventContext.class);
+        when(context.getEvent()).thenReturn(event);
+        params = params.toBuilder().context(context).build();
+
+        AtomicBoolean guardStarted = new AtomicBoolean();
+
+        oper = new AppcLcmOperation(params, config) {
+            @Override
+            protected CompletableFuture<OperationOutcome> startGuardAsync() {
+                guardStarted.set(true);
+                return super.startGuardAsync();
+            }
+        };
+
+        CompletableFuture<OperationOutcome> future2 = oper.startPreprocessorAsync();
+        assertNotNull(future2);
+        assertFalse(future.isDone());
+        assertTrue(guardStarted.get());
+
+        assertTrue(executor.runAll(100));
+        assertTrue(future2.isDone());
+        assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
     }
 
-    @Ignore
     @Test
     public void testMakeRequest() {
-        UUID randomId = UUID.randomUUID();
-        Mockito.doCallRealMethod().when(operation).makeRequest(1, "sampleTargetVnf");
-        Mockito.when(mockParams.getRequestId()).thenReturn(randomId);
-        Mockito.when(mockParams.getPayload()).thenReturn(null);
-        Mockito.when(mockParams.getContext()).thenReturn(mockContext);
-        Mockito.when(mockParams.getOperation()).thenReturn("Config-Modify");
-        Mockito.when(mockContext.getEvent()).thenReturn(mockEvent);
-        Mockito.when(mockEvent.getRequestId()).thenReturn(randomId);
-        Whitebox.setInternalState(operation, "params", mockParams);
-        assertNotNull(operation.makeRequest(1, "sampleTargetVnf"));
-        Mockito.verify(mockParams, atLeast(1)).getRequestId();
-        Mockito.verify(mockParams, atLeast(1)).getPayload();
-        Mockito.verify(mockParams, atLeast(1)).getContext();
-        Mockito.verify(mockContext, atLeast(1)).getEvent();
-        Mockito.verify(mockEvent, atLeast(1)).getRequestId();
+        AppcLcmDmaapWrapper request = oper.makeRequest(2);
+        assertEquals("DefaultOperation", request.getBody().getInput().getAction());
+
+        AppcLcmCommonHeader header = request.getBody().getInput().getCommonHeader();
+        assertNotNull(header);
+        assertEquals(params.getRequestId(), header.getRequestId());
+
+        String subreq = header.getSubRequestId();
+        assertNotNull(subreq);
+
+        assertEquals("{vnf-id=my-target}", request.getBody().getInput().getActionIdentifiers().toString());
+
+        // a subsequent request should have a different sub-request id
+        assertNotEquals(subreq, oper.makeRequest(2).getBody().getInput().getCommonHeader().getSubRequestId());
+    }
+
+    @Test
+    public void testConvertPayload() {
+        // only builds a payload for ConfigModify
+        params = params.toBuilder().operation(AppcLcmConstants.OPERATION_CONFIG_MODIFY).build();
+        oper = new AppcLcmOperation(params, config);
+
+        AppcLcmDmaapWrapper req = oper.makeRequest(2);
+        assertEquals("{\"key-A\":\"value-A\"}", req.getBody().getInput().getPayload());
+
+        // coder exception
+        oper = new AppcLcmOperation(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.makeRequest(2))
+                        .withMessage("Cannot convert payload");
     }
 
     @Test
     public void testGetExpectedKeyValues() {
-        Mockito.doCallRealMethod().when(operation).getExpectedKeyValues(1, mockInputWrapper);
-        Mockito.when(mockInputWrapper.getBody()).thenReturn(mockBody);
-        Mockito.when(mockBody.getInput()).thenReturn(mockInput);
-        Mockito.when(mockInput.getCommonHeader()).thenReturn(mockCommonHeader);
-        Mockito.when(mockCommonHeader.getSubRequestId()).thenReturn("sampleSubRequestId");
-
-        List<String> retList = operation.getExpectedKeyValues(1, mockInputWrapper);
-        assertNotNull(retList);
-        assertEquals(1, retList.size());
-
-        Mockito.verify(mockInputWrapper, atLeast(1)).getBody();
-        Mockito.verify(mockBody, atLeast(1)).getInput();
-        Mockito.verify(mockInput, atLeast(1)).getCommonHeader();
-        Mockito.verify(mockCommonHeader, atLeast(1)).getSubRequestId();
+        AppcLcmDmaapWrapper request = oper.makeRequest(2);
+        assertEquals(Arrays.asList(request.getBody().getInput().getCommonHeader().getSubRequestId()),
+                        oper.getExpectedKeyValues(50, request));
     }
 
     @Test
     public void testDetmStatus() {
-        Mockito.doCallRealMethod().when(operation).detmStatus("testResponse", mockOutputWrapper);
-        Mockito.when(mockOutputWrapper.getBody()).thenReturn(mockBody);
-        Mockito.when(mockBody.getOutput()).thenReturn(mockOutput);
-        Mockito.when(mockOutput.getStatus()).thenReturn(mockResponseStatus);
-        Mockito.when(mockResponseStatus.getCode()).thenReturn(100);
-        Status retStatus = operation.detmStatus("testResponse", mockOutputWrapper);
-        assertEquals(Status.STILL_WAITING, retStatus);
-
-        Mockito.when(mockResponseStatus.getCode()).thenReturn(400);
-        retStatus = operation.detmStatus("testResponse", mockOutputWrapper);
-        assertEquals(Status.SUCCESS, retStatus);
-
-        Mockito.when(mockResponseStatus.getCode()).thenReturn(450);
-        retStatus = operation.detmStatus("testResponse", mockOutputWrapper);
-        assertEquals(Status.FAILURE, retStatus);
-
-        Mockito.when(mockOutput.getStatus()).thenReturn(null);
-        assertThatIllegalArgumentException().isThrownBy(() -> operation.detmStatus("testResponse", mockOutputWrapper));
-
-        Mockito.when(mockResponseStatus.getCode()).thenReturn(200);
-        assertThatIllegalArgumentException().isThrownBy(() -> operation.detmStatus("testResponse", mockOutputWrapper));
-
-        Mockito.verify(mockOutputWrapper, atLeast(1)).getBody();
-        Mockito.verify(mockBody, atLeast(1)).getOutput();
-        Mockito.verify(mockOutput, atLeast(1)).getStatus();
-        Mockito.verify(mockResponseStatus, atLeast(1)).getCode();
+        assertEquals(Status.SUCCESS, oper.detmStatus(null, response));
+
+        // failure
+        response.getBody().getOutput().getStatus().setCode(405);
+        assertEquals(Status.FAILURE, oper.detmStatus(null, response));
+
+        // error
+        response.getBody().getOutput().getStatus().setCode(200);
+        assertThatIllegalArgumentException().isThrownBy(() -> oper.detmStatus(null, response));
+
+        // reject
+        response.getBody().getOutput().getStatus().setCode(305);
+        assertThatIllegalArgumentException().isThrownBy(() -> oper.detmStatus(null, response));
+
+        // accepted
+        response.getBody().getOutput().getStatus().setCode(100);
+        assertEquals(Status.STILL_WAITING, oper.detmStatus(null, response));
+
+        // other
+        response.getBody().getOutput().getStatus().setCode(-1);
+        assertThatIllegalArgumentException().isThrownBy(() -> oper.detmStatus(null, response));
+
+        // null status
+        response.getBody().getOutput().setStatus(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> oper.detmStatus(null, response));
     }
 
     @Test
     public void testSetOutcome() {
-        Mockito.doCallRealMethod().when(operation).setOutcome(mockOperationOutcome, PolicyResult.SUCCESS,
-                mockOutputWrapper);
-        Mockito.doCallRealMethod().when(operation).setOutcome(mockOperationOutcome, PolicyResult.FAILURE,
-                mockOutputWrapper);
-
-        Mockito.doCallRealMethod().when(mockOperationOutcome).setResult(any(PolicyResult.class));
-        Mockito.doCallRealMethod().when(mockOperationOutcome).setMessage(any(String.class));
-        Mockito.doCallRealMethod().when(mockOperationOutcome).getResult();
-        Mockito.doCallRealMethod().when(mockOperationOutcome).getMessage();
-
-        Mockito.when(mockOutputWrapper.getBody()).thenReturn(mockBody);
-        Mockito.when(mockBody.getOutput()).thenReturn(mockOutput);
-        Mockito.when(mockOutput.getStatus()).thenReturn(mockResponseStatus);
-        Mockito.when(mockResponseStatus.getMessage()).thenReturn(null);
-
-        OperationOutcome result = operation.setOutcome(mockOperationOutcome, PolicyResult.SUCCESS, mockOutputWrapper);
-        assertNull(result);
-        Mockito.verify(operation).setOutcome(mockOperationOutcome, PolicyResult.SUCCESS, mockOutputWrapper);
-
-        Mockito.when(mockOutput.getStatus()).thenReturn(mockResponseStatus);
-        Mockito.when(mockResponseStatus.getMessage()).thenReturn("sampleMessage");
-        result = operation.setOutcome(mockOperationOutcome, PolicyResult.FAILURE, mockOutputWrapper);
-        assertEquals(PolicyResult.FAILURE, result.getResult());
-        assertNotNull(result.getMessage());
-
-        Mockito.verify(mockOutputWrapper, atLeast(1)).getBody();
-        Mockito.verify(mockBody, atLeast(1)).getOutput();
-        Mockito.verify(mockOutput, atLeast(1)).getStatus();
-        Mockito.verify(mockResponseStatus, atLeast(1)).getMessage();
-        Mockito.verify(operation, atLeast(1)).setOutcome(mockOperationOutcome, PolicyResult.SUCCESS, mockOutputWrapper);
-        Mockito.verify(operation, atLeast(1)).setOutcome(mockOperationOutcome, PolicyResult.FAILURE, mockOutputWrapper);
+        oper.setOutcome(outcome, PolicyResult.SUCCESS, response);
+        assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+        assertEquals(MY_MESSAGE, outcome.getMessage());
+
+        // failure
+        oper.setOutcome(outcome, PolicyResult.FAILURE, response);
+        assertEquals(PolicyResult.FAILURE, outcome.getResult());
+        assertEquals(MY_MESSAGE, outcome.getMessage());
+
+        // null message
+        response.getBody().getOutput().getStatus().setMessage(null);
+        oper.setOutcome(outcome, PolicyResult.SUCCESS, response);
+        assertEquals(ControlLoopOperation.SUCCESS_MSG, outcome.getMessage());
+
+        // null status
+        response.getBody().getOutput().setStatus(null);
+        oper.setOutcome(outcome, PolicyResult.SUCCESS, response);
+        assertEquals(ControlLoopOperation.SUCCESS_MSG, outcome.getMessage());
+    }
+
+    @Test
+    public void testGetStatus() {
+        assertNotNull(oper.getStatus(response));
+
+        // null status
+        response.getBody().getOutput().setStatus(null);
+        assertNull(oper.getStatus(response));
+
+        // null outcome
+        response.getBody().setOutput(null);
+        assertNull(oper.getStatus(response));
+
+        // null body
+        response.setBody(null);
+        assertNull(oper.getStatus(response));
+
+        // null response
+        assertNull(oper.getStatus(null));
+    }
+
+    @Test
+    public void testOperationSupportsPayload() {
+        // these should support a payload
+        Set<String> supported = Set.of(AppcLcmConstants.OPERATION_CONFIG_MODIFY);
+
+        for (String name : supported) {
+            params = params.toBuilder().operation(name).build();
+            oper = new AppcLcmOperation(params, config);
+            assertTrue(name, oper.operationSupportsPayload());
+        }
+
+        // these should NOT support a payload
+        Set<String> unsupported = AppcLcmConstants.OPERATION_NAMES.stream().filter(name -> !supported.contains(name))
+                        .collect(Collectors.toSet());
+
+        for (String name : unsupported) {
+            params = params.toBuilder().operation(name).build();
+            oper = new AppcLcmOperation(params, config);
+            assertFalse(name, oper.operationSupportsPayload());
+        }
+
+        // pick an operation that would ordinarily support payloads
+        String sup = supported.iterator().next();
+
+        // verify that it still supports payload
+        params = params.toBuilder().operation(sup).build();
+        oper = new AppcLcmOperation(params, config);
+        assertTrue(oper.operationSupportsPayload());
+
+        // try with empty payload
+        params = params.toBuilder().payload(Map.of()).build();
+        oper = new AppcLcmOperation(params, config);
+        assertFalse(oper.operationSupportsPayload());
+
+        // try with null payload
+        params = params.toBuilder().payload(null).build();
+        oper = new AppcLcmOperation(params, config);
+        assertFalse(oper.operationSupportsPayload());
+    }
+
+    @Override
+    protected void makeContext() {
+        super.makeContext();
+
+        Target target = new Target();
+        target.setResourceID(RESOURCE_ID);
+
+        params = params.toBuilder().target(target).build();
     }
 
+    @Override
+    protected Map<String, Object> makePayload() {
+        return Map.of(PAYLOAD_KEY1, PAYLOAD_VALUE1);
+    }
+
+    private AppcLcmDmaapWrapper makeResponse() {
+        AppcLcmDmaapWrapper response = new AppcLcmDmaapWrapper();
+
+        AppcLcmBody body = new AppcLcmBody();
+        response.setBody(body);
+
+        AppcLcmOutput output = new AppcLcmOutput();
+        body.setOutput(output);
+
+        AppcLcmResponseStatus status = new AppcLcmResponseStatus();
+        output.setStatus(status);
+        status.setMessage(MY_MESSAGE);
+        status.setCode(SUCCESS_CODE);
+
+        return response;
+    }
 }
index 2d98b66..957185e 100644 (file)
@@ -72,6 +72,8 @@ public class Forwarder {
             throw new IllegalArgumentException("key/value mismatch");
         }
 
+        logger.info("register topic listener for key={} value={}", keys, values);
+
         values2listeners.compute(values, (key, listeners) -> {
             Map<BiConsumer<String, StandardCoderObject>, String> map = listeners;
             if (map == null) {
@@ -90,6 +92,8 @@ public class Forwarder {
      * @param listener listener to unregister
      */
     public void unregister(List<String> values, BiConsumer<String, StandardCoderObject> listener) {
+        logger.info("unregister topic listener for key={} value={}", keys, values);
+
         values2listeners.computeIfPresent(values, (key, listeners) -> {
             listeners.remove(listener);
             return (listeners.isEmpty() ? null : listeners);
@@ -112,6 +116,7 @@ public class Forwarder {
                  * No value for this field, so this message is not relevant to this
                  * forwarder.
                  */
+                logger.info("message has no key={}", keys);
                 return;
             }
 
@@ -122,11 +127,13 @@ public class Forwarder {
         Map<BiConsumer<String, StandardCoderObject>, String> listeners = values2listeners.get(values);
         if (listeners == null) {
             // no listeners for this particular list of values
+            logger.info("no listener registered for key={} value={}", keys, values);
             return;
         }
 
 
         // forward the message to each listener
+        logger.info("forwarding message to listeners for key={} value={}", keys, values);
         for (BiConsumer<String, StandardCoderObject> listener : listeners.keySet()) {
             try {
                 listener.accept(textMessage, scoMessage);
index fc57273..f1ec9e9 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.controlloop.actorserviceprovider.topic;
 
+import java.util.Arrays;
 import lombok.EqualsAndHashCode;
 import org.onap.policy.common.utils.coder.StandardCoderObject;
 
@@ -54,4 +55,9 @@ public class SelectorKey {
     public String extractField(StandardCoderObject object) {
         return object.getString(fieldIdentifiers);
     }
+
+    @Override
+    public String toString() {
+        return Arrays.toString(fieldIdentifiers);
+    }
 }