Make targetEntity a property
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / main / java / org / onap / policy / controlloop / actorserviceprovider / impl / OperationPartial.java
index 24c7ec8..b5cc15e 100644 (file)
@@ -23,11 +23,14 @@ package org.onap.policy.controlloop.actorserviceprovider.impl;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.UUID;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.Executor;
@@ -37,7 +40,9 @@ import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
+import lombok.AccessLevel;
 import lombok.Getter;
+import lombok.Setter;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
@@ -48,6 +53,7 @@ import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.actorserviceprovider.CallbackManager;
 import org.onap.policy.controlloop.actorserviceprovider.Operation;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.OperatorConfig;
 import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture;
@@ -95,17 +101,31 @@ public abstract class OperationPartial implements Operation {
     @Getter
     private final String fullName;
 
+    @Getter
+    @Setter(AccessLevel.PROTECTED)
+    private String subRequestId;
+
+    @Getter
+    private final List<String> propertyNames;
+
+    /**
+     * Values for the properties identified by {@link #getPropertyNames()}.
+     */
+    private final Map<String, Object> properties = new HashMap<>();
+
 
     /**
      * Constructs the object.
      *
      * @param params operation parameters
      * @param config configuration for this operation
+     * @param propertyNames names of properties required by this operation
      */
-    public OperationPartial(ControlLoopOperationParams params, OperatorConfig config) {
+    public OperationPartial(ControlLoopOperationParams params, OperatorConfig config, List<String> propertyNames) {
         this.params = params;
         this.config = config;
         this.fullName = params.getActor() + "." + params.getOperation();
+        this.propertyNames = propertyNames;
     }
 
     public Executor getBlockingExecutor() {
@@ -120,6 +140,23 @@ public abstract class OperationPartial implements Operation {
         return params.getOperation();
     }
 
+    @Override
+    public boolean containsProperty(String name) {
+        return properties.containsKey(name);
+    }
+
+    @Override
+    public void setProperty(String name, Object value) {
+        logger.info("{}: set property {}={}", getFullName(), name, value);
+        properties.put(name, value);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T getProperty(String name) {
+        return (T) properties.get(name);
+    }
+
     @Override
     public CompletableFuture<OperationOutcome> start() {
         // allocate a controller for the entire operation
@@ -166,7 +203,7 @@ public abstract class OperationPartial implements Operation {
 
         return outcome -> {
 
-            if (outcome != null && isSuccess(outcome)) {
+            if (isSuccess(outcome)) {
                 logger.info("{}: preprocessor succeeded for {}", getFullName(), params.getRequestId());
                 return CompletableFuture.completedFuture(outcome);
             }
@@ -179,7 +216,7 @@ public abstract class OperationPartial implements Operation {
             // propagate "stop" to the callbacks
             controller.add(callbacks);
 
-            final OperationOutcome outcome2 = params.makeOutcome();
+            final OperationOutcome outcome2 = params.makeOutcome(getTargetEntity());
 
             // TODO need a FAILURE_MISSING_DATA (e.g., A&AI)
 
@@ -228,15 +265,12 @@ public abstract class OperationPartial implements Operation {
      *         {@code null} if this operation has no guard
      */
     protected CompletableFuture<OperationOutcome> startGuardAsync() {
-        // get the guard payload
-        Map<String, Object> guardPayload = makeGuardPayload();
-
-        // wrap it in a "resource"
-        Map<String, Object> resource = new LinkedHashMap<>();
-        resource.put("guard", guardPayload);
+        if (params.isPreprocessed()) {
+            return null;
+        }
 
-        Map<String, Object> payload = new LinkedHashMap<>();
-        payload.put("resource", resource);
+        // get the guard payload
+        Map<String, Object> payload = makeGuardPayload();
 
         /*
          * Note: can't use constants from actor.guard, because that would create a
@@ -252,10 +286,11 @@ public abstract class OperationPartial implements Operation {
      * @return a new guard payload
      */
     protected Map<String, Object> makeGuardPayload() {
+        // TODO delete this once preprocessing is done by the application
         Map<String, Object> guard = new LinkedHashMap<>();
         guard.put("actor", params.getActor());
-        guard.put("recipe", params.getOperation());
-        guard.put("target", params.getTargetEntity());
+        guard.put("operation", params.getOperation());
+        guard.put("target", getTargetEntity());
         guard.put("requestId", params.getRequestId());
 
         String clname = params.getContext().getEvent().getClosedLoopControlName();
@@ -277,6 +312,8 @@ public abstract class OperationPartial implements Operation {
     private CompletableFuture<OperationOutcome> startOperationAttempt(
                     PipelineControllerFuture<OperationOutcome> controller, int attempt) {
 
+        generateSubRequestId(attempt);
+
         // propagate "stop" to the operation attempt
         controller.wrap(startAttemptWithoutRetries(attempt)).thenCompose(retryOnFailure(controller, attempt))
                         .whenCompleteAsync(controller.delayedComplete(), params.getExecutor());
@@ -284,6 +321,17 @@ public abstract class OperationPartial implements Operation {
         return controller;
     }
 
+    /**
+     * Generates and sets {@link #subRequestId} to a new subrequest ID.
+     *
+     * @param attempt attempt number, typically starting with 1
+     */
+    public void generateSubRequestId(int attempt) {
+        // Note: this should be "protected", but that makes junits much messier
+
+        setSubRequestId(UUID.randomUUID().toString());
+    }
+
     /**
      * Starts the operation attempt, without doing any retries.
      *
@@ -296,7 +344,7 @@ public abstract class OperationPartial implements Operation {
         logger.info("{}: start operation attempt {} for {}", getFullName(), attempt, params.getRequestId());
 
         final Executor executor = params.getExecutor();
-        final OperationOutcome outcome = params.makeOutcome();
+        final OperationOutcome outcome = params.makeOutcome(getTargetEntity());
         final CallbackManager callbacks = new CallbackManager();
 
         // this operation attempt gets its own controller
@@ -344,7 +392,7 @@ public abstract class OperationPartial implements Operation {
      * @return {@code true} if the outcome was successful
      */
     protected boolean isSuccess(OperationOutcome outcome) {
-        return (outcome.getResult() == PolicyResult.SUCCESS);
+        return (outcome != null && outcome.getResult() == PolicyResult.SUCCESS);
     }
 
     /**
@@ -427,7 +475,7 @@ public abstract class OperationPartial implements Operation {
                 outcome = origOutcome;
             } else {
                 logger.warn("{}: null outcome; treating as a failure for {}", getFullName(), params.getRequestId());
-                outcome = this.setOutcome(params.makeOutcome(), PolicyResult.FAILURE);
+                outcome = this.setOutcome(params.makeOutcome(getTargetEntity()), PolicyResult.FAILURE);
             }
 
             // ensure correct actor/operation
@@ -526,10 +574,16 @@ public abstract class OperationPartial implements Operation {
     private Function<Throwable, OperationOutcome> fromException(String type) {
 
         return thrown -> {
-            OperationOutcome outcome = params.makeOutcome();
+            OperationOutcome outcome = params.makeOutcome(getTargetEntity());
 
-            logger.warn("exception throw by {} {}.{} for {}", type, outcome.getActor(), outcome.getOperation(),
-                            params.getRequestId(), thrown);
+            if (thrown instanceof CancellationException || thrown.getCause() instanceof CancellationException) {
+                // do not include exception in the message, as it just clutters the log
+                logger.warn("{} canceled {}.{} for {}", type, outcome.getActor(), outcome.getOperation(),
+                                params.getRequestId());
+            } else {
+                logger.warn("exception thrown by {} {}.{} for {}", type, outcome.getActor(), outcome.getOperation(),
+                                params.getRequestId(), thrown);
+            }
 
             return setOutcome(outcome, thrown);
         };
@@ -884,11 +938,12 @@ public abstract class OperationPartial implements Operation {
      * @param callbacks used to determine if the start callback can be invoked
      * @return a function that sets the start time and invokes the callback
      */
-    private BiConsumer<OperationOutcome, Throwable> callbackStarted(CallbackManager callbacks) {
+    protected BiConsumer<OperationOutcome, Throwable> callbackStarted(CallbackManager callbacks) {
 
         return (outcome, thrown) -> {
 
             if (callbacks.canStart()) {
+                outcome.setSubRequestId(getSubRequestId());
                 outcome.setStart(callbacks.getStartTime());
                 outcome.setEnd(null);
 
@@ -913,10 +968,11 @@ public abstract class OperationPartial implements Operation {
      * @param callbacks used to determine if the end callback can be invoked
      * @return a function that sets the end time and invokes the callback
      */
-    private BiConsumer<OperationOutcome, Throwable> callbackCompleted(CallbackManager callbacks) {
+    protected BiConsumer<OperationOutcome, Throwable> callbackCompleted(CallbackManager callbacks) {
 
         return (outcome, thrown) -> {
             if (callbacks.canEnd()) {
+                outcome.setSubRequestId(getSubRequestId());
                 outcome.setStart(callbacks.getStartTime());
                 outcome.setEnd(callbacks.getEndTime());
 
@@ -968,30 +1024,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);
@@ -999,6 +1049,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 getCoder().encode(message, true);
+            } catch (CoderException e) {
+                throw new IllegalArgumentException("cannot encode message", e);
+            }
+        }
+    }
+
     // these may be overridden by subclasses or junit tests
 
     /**
@@ -1020,6 +1092,16 @@ public abstract class OperationPartial implements Operation {
         return DEFAULT_RETRY_WAIT_MS;
     }
 
+    /**
+     * Gets the target entity, first trying the properties and then the parameters.
+     *
+     * @return the target entity
+     */
+    protected String getTargetEntity() {
+        String targetEntity = getProperty(OperationProperties.AAI_TARGET_ENTITY);
+        return (targetEntity != null ? targetEntity : params.getTargetEntity());
+    }
+
     /**
      * Gets the operation timeout.
      *
@@ -1034,7 +1116,7 @@ public abstract class OperationPartial implements Operation {
 
     // these may be overridden by junit tests
 
-    protected Coder makeCoder() {
+    protected Coder getCoder() {
         return coder;
     }
 }