transaction records cleanup
[policy/drools-applications.git] / controlloop / common / controller-usecases / src / main / resources / usecases.drl
index 692c743..0e1f7de 100644 (file)
@@ -2,8 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2019 Bell Canada.
+ * Copyright (C) 2020-2022 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.
 
 package org.onap.policy.controlloop;
 
-import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput;
-import org.onap.policy.controlloop.params.ControlLoopParams;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import org.onap.policy.controlloop.CanonicalOnset;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.VirtualControlLoopNotification;
-import org.onap.policy.controlloop.ControlLoopEventStatus;
-import org.onap.policy.controlloop.ControlLoopException;
 import org.onap.policy.controlloop.ControlLoopNotificationType;
-import org.onap.policy.controlloop.ControlLoopResponse;
-import org.onap.policy.controlloop.policy.PolicyResult;
-import org.onap.policy.controlloop.policy.ControlLoopPolicy;
-import org.onap.policy.controlloop.policy.Policy;
-import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
-import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager.NewEventStatus;
-import org.onap.policy.controlloop.eventmanager.ControlLoopOperationManager;
-import org.onap.policy.controlloop.eventmanager.LockCallbackWorkingMemory;
+import org.onap.policy.controlloop.actor.xacml.XacmlActor;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationFinalResult;
+import org.onap.policy.controlloop.actorserviceprovider.OperationProperties;
+import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.eventmanager.ActorConstants;
+import org.onap.policy.controlloop.eventmanager.Step;
 import org.onap.policy.controlloop.utils.ControlLoopUtils;
-import org.onap.policy.controlloop.actor.so.SoActorServiceProvider;
-import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider;
-import org.onap.policy.controlloop.actor.cds.CdsActorServiceProvider.CdsActorServiceManager;
-import org.onap.policy.controlloop.actor.cds.constants.CdsActorConstants;
-import org.onap.policy.aai.AaiCqResponse;
-import org.onap.policy.appc.Request;
-import org.onap.policy.appc.Response;
-import org.onap.policy.appc.CommonHeader;
-import org.onap.policy.appclcm.AppcLcmDmaapWrapper;
-import org.onap.policy.cds.CdsResponse;
-import org.onap.policy.cds.client.CdsProcessorGrpcClient;
-import org.onap.policy.cds.properties.CdsServerProperties;
-import org.onap.policy.drools.utils.Pair;
-import org.onap.policy.sdnr.PciRequestWrapper;
-import org.onap.policy.sdnr.PciResponseWrapper;
-import org.onap.policy.sdnr.PciRequest;
-import org.onap.policy.sdnr.PciResponse;
-import org.onap.policy.vfc.VfcRequest;
-import org.onap.policy.vfc.VfcResponse;
-import org.onap.policy.vfc.VfcManager;
-import org.onap.policy.so.SoManager;
-import org.onap.policy.so.SoRequest;
-import org.onap.policy.so.SoResponseWrapper;
-import org.onap.policy.sdnc.SdncRequest;
-import org.onap.policy.sdnc.SdncManager;
-import org.onap.policy.sdnc.SdncResponse;
-import org.onap.policy.drools.core.lock.Lock;
-import org.onap.policy.guard.PolicyGuardRequest;
-import org.onap.policy.guard.PolicyGuardResponse;
-import org.onap.policy.guard.PolicyGuardXacmlRequestAttributes;
-import org.onap.policy.guard.PolicyGuardXacmlHelper;
+import org.onap.policy.drools.apps.controller.usecases.UsecasesEventManager;
+import org.onap.policy.controlloop.eventmanager.ClEventManagerWithSteps.State;
+import org.onap.policy.controlloop.eventmanager.ClEventManagerWithOutcome.OperationOutcome2;
+import org.onap.policy.controlloop.eventmanager.ClEventManagerWithEvent.NewEventStatus;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
+import org.onap.policy.controlloop.eventmanager.EventManagerServices;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 
-import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Constructor;
-
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
-import java.time.Instant;
-import java.util.LinkedList;
-import java.util.Iterator;
-
-
 import org.onap.policy.drools.system.PolicyEngineConstants;
 
+
 /*
- * This object is to provide support for timeouts
- * due to a bug in drools' built-in timers
- */
-declare ControlLoopTimer
-    closedLoopControlName : String
-    requestId : String
-    delay : String
-    expired : boolean
-    //timerType is the type of timer: either "ClosedLoop" or "Operation"
-    timerType : String
+* Called at initial start-up, to create the event services that will be used by all
+* event managers in this rule engine instance.
+*/
+rule "CREATE.EVENT.SERVICES"
+    when
+        not (EventManagerServices())
+    then
+
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}", drools.getRule().getName());
+
+    try {
+        insert(new EventManagerServices("event-manager"));
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: cannot create event services", drools.getRule().getName(), e);
+    }
 end
 
 /*
 *
-* Called when the ControlLoopParams object has been inserted into working memory from the BRMSGW.
+* Called when the ControlLoopParams object has been inserted into working memory from the PAP.
 *
 */
 rule "INSERT.PARAMS"
@@ -107,8 +82,8 @@ rule "INSERT.PARAMS"
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), $params.getPolicyName() + "."
-        + drools.getRule().getName(), $params.getControlLoopYaml());
+    logger.info("{}: {} : TOSCA-POLICY=[{}]", $params.getClosedLoopControlName(), $params.getPolicyName() + "."
+        + drools.getRule().getName(), $params.getToscaPolicy());
 end
 
 /*
@@ -157,1562 +132,977 @@ end
 rule "EVENT"
     when
         $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        not ( ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() ) )
+        $event : CanonicalOnset( closedLoopControlName == $clName )
+        $services : EventManagerServices()
+        not ( UsecasesEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
+            getEvent() == $event ) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
+    logger.info("{}: {}.{}: event={}",
+                $clName, $params.getPolicyName(), drools.getRule().getName(),
+                $event);
+    //
+    // Retract the event from memory; it will be managed by the manager from now on
+    //
+    retract($event);
 
-    try {
+    VirtualControlLoopNotification notification = null;
 
+    try {
         //
         // Check the event, because we need it to not be null when
-        // we create the ControlLoopEventManager. The ControlLoopEventManager
-        // will do extra syntax checking as well check if the closed loop is disabled.
+        // we create the UsecasesEventManager. The UsecasesEventManager
+        // will do extra syntax checking as well as check if the closed loop is disabled.
         //
         if ($event.getRequestId() == null) {
-            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+            notification = new VirtualControlLoopNotification($event);
             notification.setNotification(ControlLoopNotificationType.REJECTED);
             notification.setFrom("policy");
             notification.setMessage("Missing requestId");
-            notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
+            notification.setPolicyScope(drools.getRule().getName());
+            notification.setPolicyName($params.getPolicyName());
             notification.setPolicyVersion($params.getPolicyVersion());
 
-            //
-            // Let interested parties know
-            //
-            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-
-            //
-            // Retract it from memory
-            //
-            retract($event);
-        } else if ($event.getClosedLoopEventStatus() != ControlLoopEventStatus.ONSET) {
-            throw new ControlLoopException($event.getClosedLoopEventStatus() + " received with no prior onset");
         } else {
-            //
-            // Create an EventManager
-            //
-            ControlLoopEventManager manager = new ControlLoopEventManager($clName, $event.getRequestId());
-            //
-            // Determine if EventManager can actively process the event
-            // (i.e. syntax, is_closed_loop_disabled checks etc.)
-            //
-            VirtualControlLoopNotification notification = manager.activate($params.getControlLoopYaml(), $event);
-            notification.setFrom("pdp-0001-controller=controlloop"); // Engine.getInstanceName()
-            notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
-            notification.setPolicyVersion($params.getPolicyVersion());
-            //
-            // Are we actively pursuing this event?
-            //
-            if (notification.getNotification() == ControlLoopNotificationType.ACTIVE) {
-                //
-                // Insert Event Manager into memory, this will now kick off processing.
-                //
-                insert(manager);
-                //
-                // Let interested parties know
-                //
-                PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-                //
-                // Setup the Overall Control Loop timer
-                //
-                ControlLoopTimer clTimer = new ControlLoopTimer();
-                clTimer.setTimerType("ClosedLoop");
-                clTimer.setClosedLoopControlName($event.getClosedLoopControlName());
-                clTimer.setRequestId($event.getRequestId().toString());
-                clTimer.setDelay(manager.getControlLoopTimeout(1500) + "s");
-                //
-                // Insert it
-                //
-                insert(clTimer);
-            } else {
-                //
-                // Let interested parties know
-                //
-                PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-                //
-                // Retract it from memory
-                //
-                retract($event);
-            }
+            UsecasesEventManager manager =
+                new UsecasesEventManager($services, $params, $event, drools.getWorkingMemory());
+            insert(manager);
+            try {
+                // load the first policy/step
+                manager.start();
 
-            //
-            // Now that the manager is inserted into Drools working memory, we'll wait for
-            // another rule to fire in order to continue processing. This way we can also
-            // then screen for additional ONSET and ABATED events for this RequestId.
-            //
+                if (manager.getSteps().isEmpty()) {
+                    // no steps implies no policies, thus go straight to DONE state
+                    manager.setState(State.DONE);
+
+                    manager.setAccepted(true);
+
+                    notification = manager.makeNotification();
+                    notification.setNotification(ControlLoopNotificationType.ACTIVE);
+                    notification.setPolicyScope(drools.getRule().getName());
+                    notification.setPolicyName($params.getPolicyName());
+                    notification.setPolicyVersion($params.getPolicyVersion());
+
+                } else {
+                    // Note: the notification will be generated lazily
+                    manager.setState(State.POLICY_LOADED);
+                }
+
+            } catch(Exception e) {
+                retract(manager);
+                throw e;
+            }
         }
     } catch (Exception e) {
-        logger.warn("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName(), e);
-
-        VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
+        logger.warn("{}: {}.{}: error starting manager", $clName, $params.getPolicyName(),
+                        drools.getRule().getName(), e);
+        notification = new VirtualControlLoopNotification($event);
         notification.setNotification(ControlLoopNotificationType.REJECTED);
         notification.setMessage("Exception occurred: " + e.getMessage());
-        notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-        notification.setPolicyScope($params.getPolicyScope());
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($params.getPolicyName());
         notification.setPolicyVersion($params.getPolicyVersion());
-        //
-        //
-        //
-        PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-        //
-        // Retract the event
-        //
-        retract($event);
+    }
+    //
+    // Generate notification
+    //
+    try {
+        if (notification != null) {
+            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+        }
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: event={} exception generating notification",
+                $clName, $params.getPolicyName(), drools.getRule().getName(),
+                $event, e);
     }
 end
 
 /*
 *
-* This rule happens when we got a valid ONSET, closed loop is enabled and an Event Manager
-* is now created. We can start processing the yaml specification via the Event Manager.
+* This rule fires when we get a subsequent event.
 *
 */
-rule "EVENT.MANAGER"
+rule "EVENT.MANAGER.NEW.EVENT"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "ClosedLoop", !expired )
+        $event : VirtualControlLoopEvent( )
+        $manager : UsecasesEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
+            getEvent() == $event )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}: event={} manager={} clTimer={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $clTimer);
+    logger.info("{}: {}.{}: event={} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $event, $manager);
+    //
+    // Remove the event from memory
+    //
+    retract($event);
 
-    try {
-        //
-        // Check which event this is.
-        //
-        ControlLoopEventManager.NewEventStatus eventStatus = $manager.onNewEvent($event);
-        //
-        // Check what kind of event this is
-        //
-        if (eventStatus == NewEventStatus.SUBSEQUENT_ONSET) {
-            //
-            // We don't care about subsequent onsets
-            //
-            logger.info("{}: {}: subsequent onset",
-                        $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-            retract($event);
-            return;
-        }
-        if (eventStatus == NewEventStatus.SYNTAX_ERROR) {
+    //
+    // Check what kind of event this is
+    //
+    switch($manager.onNewEvent($event)) {
+        case SYNTAX_ERROR:
             //
             // Ignore any bad syntax events
             //
-            logger.warn("{}: {}: syntax error",
-                        $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-            retract($event);
-            return;
-        }
-        //
-        // We only want the initial ONSET event in memory,
-        // all the other events need to be retracted to support
-        // cleanup and avoid the other rules being fired for this event.
-        //
-        if (eventStatus != NewEventStatus.FIRST_ONSET) {
-            logger.warn("{}: {}: no first onset",
-                        $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-            retract($event);
-        }
+            logger.warn("{}: {}.{}: syntax error",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName());
+            break;
 
-        logger.debug("{}: {}: target={}", $clName,
-                     $params.getPolicyName() + "." + drools.getRule().getName(), $event.getTarget());
-        //
-        // Now start seeing if we need to process this event
-        //
-
-        //
-        // Check if this is a Final Event
-        //
-        VirtualControlLoopNotification notification = $manager.isControlLoopFinal();
-
-
-        if (notification != null) {
+        case FIRST_ABATEMENT:
+        case SUBSEQUENT_ABATEMENT:
             //
-            // Its final, but are we waiting for abatement?
+            // TODO: handle the abatement.  Currently, it's just discarded.
             //
-            if ($manager.getNumAbatements() > 0) {
-                logger.info("{}: {}: abatement received for {}.  Closing the control loop",
-                            $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                            $event.getRequestId());
-                notification.setFrom("policy");
-                notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-                notification.setPolicyScope($params.getPolicyScope());
-                notification.setPolicyVersion($params.getPolicyVersion());
-                //
-                // In this case, we are done
-                //
-                PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-                //
-                // Unlock the target
-                //
-                Lock lock = $manager.unlockCurrentOperation();
-                if(lock != null) {
-                    logger.debug("{}: {}.{}: retracting lock={}", $clName,
-                                 $params.getPolicyName(), drools.getRule().getName(), lock);
-                    retract(lock);
-                }
-                //
-                // Retract everything from memory
-                //
-                logger.info("{}: {}: retracting onset, manager, and timer",
-                            $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-
-                retract($manager.getOnsetEvent());
-
-                // don't retract manager, etc. - a clean-up rule will do that
-
-                //
-                // TODO - what if we get subsequent Events for this RequestId?
-                // By default, it will all start over again. May be confusing for Ruby.
-                // Or, we could track this and then subsequently ignore the events
-                //
-            } else {
-                //
-                // Check whether we need to wait for abatement
-                //
-                if ($manager.getProcessor().getControlLoop().getAbatement() == true && notification.getNotification() == ControlLoopNotificationType.FINAL_SUCCESS) {
-                  logger.info("{}: {}: waiting for abatement ..",
-                              $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-                } else {
-                  logger.info("{}: {}: no abatement expect for {}.  Closing the control loop",
-                              $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                              $event.getRequestId());
-
-                  notification.setFrom("policy");
-                  notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-                  notification.setPolicyScope($params.getPolicyScope());
-                  notification.setPolicyVersion($params.getPolicyVersion());
-
-                  //
-                  // In this case, we are done
-                  //
-                  PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-                  //
-                  // Unlock the target
-                  //
-                  Lock lock = $manager.unlockCurrentOperation();
-                  if(lock != null) {
-                      logger.debug("{}: {}.{}: retracting lock={}", $clName,
-                                  $params.getPolicyName(), drools.getRule().getName(), lock);
-                      retract(lock);
-                  }
-                  //
-                  // Retract everything from memory
-                  //
-                  logger.info("{}: {}: retracting onset, manager, and timer",
-                              $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-
-                  retract($manager.getOnsetEvent());
-
-                  // don't retract manager, etc. - a clean-up rule will do that
-                }
-            }
-        } else {
+            logger.info("{}: {}.{}: abatement",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName());
+            break;
+
+        case FIRST_ONSET:
+        case SUBSEQUENT_ONSET:
+        default:
             //
-            // NOT final, so let's ask for the next operation
+            // We don't care about subsequent onsets
             //
-            ControlLoopOperationManager operation = $manager.processControlLoop();
-            if (operation != null) {
-              //
-              // Let's ask for a lock right away
-              //
-              logger.info("{}: {}.{}: requesting lock for operation={}",
-                            $clName, $params.getPolicyName(), drools.getRule().getName(),
-                            operation);
-
-              Pair<Lock,Lock> oldNew = $manager.lockCurrentOperation(
-                        new LockCallbackWorkingMemory($params.getClosedLoopControlName(), drools.getWorkingMemory()));
-              if(oldNew.first() != null) {
-                  logger.debug("{}: {}.{}: retracting lock={}", $clName,
-                              $params.getPolicyName(), drools.getRule().getName(), oldNew.first());
-                  retract(oldNew.first());
-              }
-              if(oldNew.second() != null) {
-                  logger.debug("{}: {}.{}: inserting lock={}", $clName,
-                              $params.getPolicyName(), drools.getRule().getName(), oldNew.second());
-                  insert(oldNew.second());
-              }
-
-              //
-              // insert the operation into memory
-              //
-              insert(operation);
-
-              //
-              // insert operation timeout object
-              //
-              ControlLoopTimer opTimer = new ControlLoopTimer();
-              opTimer.setTimerType("Operation");
-              opTimer.setClosedLoopControlName($event.getClosedLoopControlName());
-              opTimer.setRequestId($event.getRequestId().toString());
-              Integer timeout = operation.getOperationTimeout();
-              opTimer.setDelay(timeout > 0 ? timeout.toString() + "s" : $clTimer.getDelay());
-              insert(opTimer);
-
-            } else {
-              //
-              // Probably waiting for abatement
-              //
-              logger.info("{}: {}: no operation, probably waiting for abatement",
-                          $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-            }
-        }
-    } catch (Exception e) {
-        logger.warn("{}: {}: unexpected",
-                   $clName,
-                   $params.getPolicyName() + "." + drools.getRule().getName(), e);
-
-        VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-        notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
-        notification.setMessage(e.getMessage());
-        notification.setFrom("policy");
-        notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-        notification.setPolicyScope($params.getPolicyScope());
-        notification.setPolicyVersion($params.getPolicyVersion());
-
-        PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-
-        retract($event);
-
-        // don't retract manager, etc. - a clean-up rule will do that
+            logger.warn("{}: {}.{}: subsequent onset",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName());
+            break;
     }
-
 end
 
 /*
 *
-* Lock denied
+* All steps have been executed, load the next policy.
 *
 */
-rule "EVENT.MANAGER.OPERATION.LOCK.DENIED"
+rule "EVENT.MANAGER.LOAD.NEXT.POLICY"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId(), "None".equalsIgnoreCase(getGuardApprovalStatus()) )
-        $lock : Lock (ownerKey == $event.getRequestId().toString(), isUnavailable())
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.POLICY_LOADED,
+                        getSteps().isEmpty() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}.{}: event={} manager={} operation={} lock={}",
-            $clName, $params.getPolicyName(), drools.getRule().getName(),
-            $event, $manager, $operation, $lock);
+    logger.info("{}: {}.{}: manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $manager);
 
-    logger.debug("The target resource {} is already processing", $event.getAai().get($event.getTarget()));
-    VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-    notification.setNotification(ControlLoopNotificationType.REJECTED);
-    notification.setMessage("The target " + $event.getAai().get($event.getTarget()) + " is already locked");
-    notification.setFrom("policy");
-    notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-    notification.setPolicyScope($params.getPolicyScope());
-    notification.setPolicyVersion($params.getPolicyVersion());
+    try {
+        $manager.loadNextPolicy($manager.getResult());
 
-    PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+        if ($manager.getSteps().isEmpty()) {
+            // no steps - must be the final policy
+            $manager.setState(State.DONE);
+        }
 
-    retract($event);
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception loading next policy",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to load next policy");
+    }
 
-    // don't retract manager, etc. - a clean-up rule will do that
+    update($manager);
 end
 
 /*
 *
-* Guard Permitted, let's send request to the actor.
+* Policy loaded, identify any preprocessor steps that need to be run first.
 *
 */
-rule "EVENT.MANAGER.OPERATION.LOCKED.GUARD_PERMITTED"
+rule "EVENT.MANAGER.PREPROCESS"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId(), "Permit".equalsIgnoreCase(getGuardApprovalStatus()) )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.POLICY_LOADED,
+                        $step : getSteps().peek(),
+                        $step != null,
+                        !$step.isPreprocessed() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}: event={} manager={} operation={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation);
-
-    Object request = null;
-    boolean caughtException = false;
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
     try {
-        request = $operation.startOperation($event);
-
-        if (request != null) {
-          logger.debug("{}: {}: starting operation ..",
-                       $clName,
-                       $params.getPolicyName() + "." + drools.getRule().getName());
-          //
-          // Tell interested parties we are performing this Operation
-          //
-          VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-          notification.setNotification(ControlLoopNotificationType.OPERATION);
-          notification.setMessage($operation.getOperationMessage());
-          notification.setHistory($operation.getHistory());
-          notification.setFrom("policy");
-          notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-          notification.setPolicyScope($params.getPolicyScope());
-          notification.setPolicyVersion($params.getPolicyVersion());
-
-          PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-
-          switch ($operation.policy.getActor()){
-
-              case "APPC":
-
-                  if (request instanceof Request) {
-                      PolicyEngineConstants.getManager().deliver("APPC-CL", request);
-                  }
-                  else if (request instanceof AppcLcmDmaapWrapper) {
-                      PolicyEngineConstants.getManager().deliver("APPC-LCM-READ", request);
-                  }
-                  break;
-              case "SO":
-                  // at this point the AAI named query request should have already been made, the response
-                  // recieved and used in the construction of the SO Request which is stored in operationRequest
-
-                  if(request instanceof SoRequest) {
-                      // Call SO. The response will be inserted into memory once it's received
-                      class mySoCallback implements SoManager.SoCallback {
-                          public void onSoResponseWrapper(SoResponseWrapper wrapper) {
-                              drools.getWorkingMemory().insert(wrapper);
-                          }
-                      }
-                      SoActorServiceProvider.sendRequest($event.getRequestId().toString(),
-                          new mySoCallback(),
-                          request,
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("so.url"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("so.username"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("so.password"));
-                  }
-                  break;
-              case "VFC":
-                  if (request instanceof VfcRequest) {
-                      class myVfcCallback implements VfcManager.VfcCallback {
-
-                          public void onResponse(VfcResponse responseError) {
-                              drools.getWorkingMemory().insert(responseError);
-                          }
-                      };
-                      // Start VFC thread
-                      Thread t = new Thread(new VfcManager(new myVfcCallback(),
-                          (VfcRequest)request,
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.url"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.username"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("vfc.password")));
-                      t.start();
-                  }
-                  break;
-              case "SDNR":
-                  if (request instanceof PciRequestWrapper) {
-                      PolicyEngineConstants.getManager().deliver("SDNR-CL", request);
-                  }
-                  break;
-
-              case "SDNC":
-                  if (request instanceof SdncRequest) {
-                      class mySdncCallback implements SdncManager.SdncCallback {
-                          public void onCallback(SdncResponse response) {
-                              drools.getWorkingMemory().insert(response);
-                          }
-                      }
-                      // Start SDNC thread
-                      Thread t = new Thread(new SdncManager(new mySdncCallback(),
-                          (SdncRequest)request,
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.url"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.username"),
-                          PolicyEngineConstants.getManager().getEnvironmentProperty("sdnc.password")));
-                      t.start();
-                  }
-                  break;
-
-              case "CDS":
-
-                  if(request instanceof ExecutionServiceInput) {
-
-                      // Instantiate cds actor, service manager and grpc properties
-
-                      CdsActorServiceProvider cdsActor = new CdsActorServiceProvider();
-                      CdsActorServiceProvider.CdsActorServiceManager cdsActorServiceManager = cdsActor.new CdsActorServiceManager();
-
-                      CdsServerProperties props = new CdsServerProperties();
-                      props.setHost(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcHost"));
-                      props.setPort(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPort")));
-                      props.setUsername(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcUsername"));
-                      props.setPassword(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcPassword"));
-                      props.setTimeout(Integer.parseInt(PolicyEngineConstants.getManager().getEnvironmentProperty("cds.grpcTimeout")));
-
-                      // Send cds grpc request
-                      try (CdsProcessorGrpcClient client = new CdsProcessorGrpcClient(cdsActorServiceManager, props)) {
-                          CdsResponse response =
-                                cdsActorServiceManager.sendRequestToCds(client, props, (ExecutionServiceInput) request);
-                          logger.info("CDS response: {}", response);
-                          insert(response);
-                      }
-                  }
-                  break;
-          }
-        } else {
-          //
-          // What happens if its null?
-          //
-            logger.warn("{}: {}: unexpected null operation request",
-                      $clName,
-                      $params.getPolicyName() + "." + drools.getRule().getName());
-            if ("SO".equals($operation.policy.getActor())) {
-                retract($opTimer);
-                retract($operation);
-                modify($manager) {finishOperation($operation)};
-            }
-            else if ("vfc".equalsIgnoreCase($operation.policy.getActor())) {
-                retract($opTimer);
-                retract($operation);
-                modify($manager) {finishOperation($operation)};
-            }
-            else if ("sdnc".equalsIgnoreCase($operation.policy.getActor())) {
-                retract($opTimer);
-                retract($operation);
-                modify($manager) {finishOperation($operation)};
-            }
-        }
-
-    } catch (Exception e) {
-        String msg = e.getMessage();
-        logger.warn("{}: {}: operation={}:  AAI failure: {}",
-                    $clName,
-                    $params.getPolicyName() + "." + drools.getRule().getName(),
-                    $operation, msg, e);
-        $operation.setOperationHasException(msg);
-
-        if(request != null) {
-            //
-            // Create a notification for it ("DB Write - end operation")
-            //
-            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-            notification.setFrom("policy");
-            notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
-            notification.setPolicyVersion($params.getPolicyVersion());
-            notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
-            notification.setMessage($operation.getOperationHistory());
-            notification.setHistory($operation.getHistory());
-
-            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-        }
-
-        retract($opTimer);
-        retract($operation);
-        caughtException = true;
+        /*
+         * Load any preprocessor steps.
+         *
+         * Note: this will not change the state of the manager, but it may change the
+         * state of the step.
+         */
+        $step.setPreprocessed(true);
+        $manager.loadPreprocessorSteps();
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception loading preprocessor steps",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to load preprocessing steps");
     }
 
-    // Having the modify statement in the catch clause doesn't work for whatever reason
-    if (caughtException) {
-        modify($manager) {finishOperation($operation)};
-    }
+    update($manager);
 end
 
-
 /*
 *
-* We were able to acquire a lock so now let's ask Xacml Guard whether
-* we are allowed to proceed with the request to the actor.
+* Accepts the event, when ready to execute a step of interest.  Does not change the
+* state of the manager, leaving that to be done by rule "EVENT.MANAGER.EXECUTE.STEP".
 *
 */
-rule "EVENT.MANAGER.OPERATION.LOCKED.GUARD_NOT_YET_QUERIED"
+rule "EVENT.MANAGER.ACCEPT"
+    salience 200
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId(), "None".equalsIgnoreCase(getGuardApprovalStatus()) )
-        $lock : Lock (ownerKey == $event.getRequestId().toString(), isActive())
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        !isAccepted(),
+                        getState() == State.POLICY_LOADED,
+                        $step : getSteps().peek(),
+                        $step != null,
+                        $step.isPreprocessed(),
+                        $step.acceptsEvent() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}: event={} manager={} operation={} lock={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation, $lock);
-
-    //
-    // Sending notification that we are about to query Guard ("DB write - start operation")
-    //
-    VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-    notification.setNotification(ControlLoopNotificationType.OPERATION);
-    notification.setMessage("Sending guard query for " + $operation.policy.getActor() + " "
-        + $operation.policy.getRecipe());
-    notification.setHistory($operation.getHistory());
-    notification.setFrom("policy");
-    notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-    notification.setPolicyScope($params.getPolicyScope());
-    notification.setPolicyVersion($params.getPolicyVersion());
-
-    PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+    logger.info("{}: {}.{}: manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-    //
-    // Now send Guard Request to XACML Guard. In order to bypass the call to Guard,
-    // just change guardEnabled to false.
-    //
-    // In order to use REST XACML, provide a URL instead of "" as a second argument
-    // to the CallGuardTask() and set the first argument to null
-    // (instead of XacmlPdpEngine).
-    //
-
-    // NOTE: The environment properties uses "guard.disabled" but the boolean is guardEnabled
-    boolean guardEnabled = "false".equalsIgnoreCase(PolicyEngineConstants.getManager().getEnvironmentProperty("guard.disabled"));
-
-    if(guardEnabled){
-
-        Thread t = new Thread(new org.onap.policy.guard.CallGuardTask(
-                                                        drools.getWorkingMemory(),
-                                                        $event.getClosedLoopControlName(),
-                                                        $operation.policy.getActor().toString(),
-                                                        $operation.policy.getRecipe(),
-                                                        $operation.getTargetEntity(),
-                                                        $event.getRequestId().toString(),
-                                                        () -> {
-                                                                 try {
-                                                                     AaiCqResponse resp_cq  = $manager.getCqResponse($event);
-                                                                     if (resp_cq == null){
-                                                                         return null;
-                                                                     } else {
-                                                                         String custId = $operation.policy.getTarget().getModelCustomizationId();
-                                                                         String invId = $operation.policy.getTarget().getModelInvariantId();
-                                                                         String verId = $operation.policy.getTarget().getModelVersionId();
-                                                                         return resp_cq.getVfModuleCount(custId, invId, verId);
-                                                                     }
-                                                                 } catch (Exception e){
-                                                                     logger.warn("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName(), e);
-                                                                 }
-                                                                 return null;
-                                                        }));
-        t.start();
-    }
-    else{
-        insert(new PolicyGuardResponse("Permit", $event.getRequestId(), $operation.policy.getRecipe()));
+    try {
+        $manager.setAccepted(true);
+
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.ACTIVE);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to accept the event");
     }
 
+    update($manager);
 end
 
-//
-// This rule will be triggered when a thread talking to the XACML Guard inserts a
-// guardResponse object into the working memory
-//
-rule "GUARD.RESPONSE"
+/*
+*
+* Ready to execute the step.
+*
+*/
+rule "EVENT.MANAGER.EXECUTE.STEP"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $guardResponse : PolicyGuardResponse(requestId == $event.getRequestId(), $operation.policy.recipe == operation)
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.POLICY_LOADED,
+                        $step : getSteps().peek(),
+                        $step != null,
+                        $step.isPreprocessed() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}: event={} manager={} operation={} opTimer={} guardResponse={}",
-                 $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                 $event, $manager, $operation, $opTimer, $guardResponse);
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
+    try {
+        $step.init();
+        $step.setProperties();
 
-    //we will permit the operation if there was no Guard for it
-    if("Indeterminate".equalsIgnoreCase($guardResponse.getResult())){
-        $guardResponse.setResult("Permit");
-    }
+        boolean guardDisabled = "true".equalsIgnoreCase(
+                    PolicyEngineConstants.getManager().getEnvironmentProperty(
+                        ControlLoopEventManager.GUARD_DISABLED_PROPERTY));
 
-    //
-    // This notification has Guard result in "message". ("DB write - end operation in case of Guard Deny")
-    //
-    VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-    notification.setNotification(ControlLoopNotificationType.OPERATION);
-    notification.setMessage("Guard result for " + $operation.policy.getActor() + " " + $operation.policy.getRecipe()
-        + " is " + $guardResponse.getResult());
-    notification.setHistory($operation.getHistory());
-    notification.setFrom("policy");
-    notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-    notification.setPolicyScope($params.getPolicyScope());
-    notification.setPolicyVersion($params.getPolicyVersion());
+        if (guardDisabled && XacmlActor.NAME.equals($step.getActorName())) {
+            // guard is disabled - just enqueue a "SUCCESS" (i.e., "Permit")
+            OperationOutcome outcome = $step.getParams().makeOutcome();
+            outcome.setStart(Instant.now());
+            outcome.setEnd(outcome.getStart());
 
-    PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+            $manager.getOutcomes().add(outcome);
+            $manager.setState(State.AWAITING_OUTCOME);
 
-    if("Permit".equalsIgnoreCase($guardResponse.getResult())){
+        } else if ($manager.executeStep()) {
+            $manager.setState(State.AWAITING_OUTCOME);
 
-        modify($operation){setGuardApprovalStatus($guardResponse.getResult())};
-    }
-    else {
-        //This is the Deny case
-        $operation.startOperation($event);
-        $operation.setOperationHasGuardDeny();
-        retract($opTimer);
-        retract($operation);
-        modify($manager) {finishOperation($operation)};
-    }
+        } else {
+            // this step is no longer necessary - try the next one
+            $manager.nextStep();
+        }
 
-    retract($guardResponse);
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception executing a step",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to execute the next step");
+    }
 
+    update($manager);
 end
 
 /*
 *
-* This rule responds to APPC Response Events
-*
-* I would have like to be consistent and write the Response like this:
-* $response : Response( CommonHeader.RequestId == $onset.getRequestId() )
-*
-* However, no compile error was given. But a runtime error was given. I think
-* because drools is confused between the classname CommonHeader vs the property CommonHeader.
+* Generate SDNR notification.  Does not discard the outcome from the queue, leaving it to be
+* handled by rule "EVENT.MANAGER.PROCESS.OUTCOME".
 *
 */
-rule "APPC.RESPONSE"
+rule "EVENT.MANAGER.GENERATE.SDNR.NOTIFICATION"
+    // this should fire BEFORE the "EVENT.MANAGER.PROCESS.OUTCOME" rule
+    salience 100
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : Response( getCommonHeader().RequestId == $event.getRequestId() )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        $outcome.getEnd() != null,
+                        !isAbort($outcome),
+                        $step : getSteps().peek(),
+                        "SDNR".equals($step.getActorName()),
+                        $outcome.isFor("SDNR", $step.getOperationName()) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                 $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                 $event, $manager, $operation, $opTimer, $response);
-    //
-    // Get the result of the operation
-    //
-    PolicyResult policyResult = $operation.onResponse($response);
-    if (policyResult != null) {
-        logger.debug("{}: {}: operation finished - result={}",
-                    $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                    policyResult);
-        //
-        // This Operation has completed, construct a notification showing our results. (DB write - end operation)
-        //
-        VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-        notification.setFrom("policy");
-        notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-        notification.setPolicyScope($params.getPolicyScope());
-        notification.setPolicyVersion($params.getPolicyVersion());
-        notification.setMessage($operation.getOperationHistory());
-        notification.setHistory($operation.getHistory());
-        if (policyResult.equals(PolicyResult.SUCCESS)) {
-            notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
-            //
-            // Let interested parties know
-            //
-            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-        } else {
-            notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
-            //
-            // Let interested parties know
-            //
-            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-        }
-        //
-        // Ensure the operation is complete
-        //
-        if ($operation.isOperationComplete() == true) {
-            //
-            // It is complete, remove it from memory
-            //
-            retract($operation);
-            //
-            // We must also retract the timer object
-            // NOTE: We could write a Rule to do this
-            //
-            retract($opTimer);
-            //
-            // Complete the operation
-            //
-            modify($manager) {finishOperation($operation)};
-        } else {
-            //
-            // Just doing this will kick off the LOCKED rule again
-            //
-            modify($operation) {};
-        }
-    } else {
-        //
-        // Its not finished yet (i.e. expecting more Response objects)
-        //
-        // Or possibly it is a leftover response that we timed the request out previously
-        //
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
+
+    try {
+        ControlLoopResponse clResponse = $manager.makeControlLoopResponse($outcome);
+        $manager.deliver("DCAE_CL_RSP", clResponse, "SDNR notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception generating SDNR Response notification",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
     }
-    //
-    // We are going to retract these objects from memory
-    //
-    retract($response);
 end
 
 /*
 *
-* The problem with Responses is that they don't have a controlLoopControlName
-* field in them, so the only way to attach them is via RequestId. If we have multiple
-* control loop .drl's loaded in the same container, we need to be sure the cleanup
-* rules don't remove Responses for other control loops.
+* Process a GUARD outcome.  Does not discard the outcome from the queue, leaving it to be
+* handled by rule "EVENT.MANAGER.PROCESS.OUTCOME".
 *
 */
-rule "APPC.RESPONSE.CLEANUP"
+rule "EVENT.MANAGER.PROCESS.GUARD.OUTCOME"
+    salience 100
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $response : Response($id : getCommonHeader().RequestId )
-        not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        !isAbort($outcome),
+                        $step : getSteps().peek(),
+                        XacmlActor.NAME.equals($step.getActorName()),
+                        $outcome.isFor($step.getActorName(), $step.getOperationName()) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: orphan appc response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id);
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-    //
-    // Retract it
-    //
-    retract($response);
+    try {
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.OPERATION);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory(Collections.emptyList());
+
+        // get actor/operation name from the policy step, not from the guard step
+        Step step2 = $step.getParentStep();
+
+        if ($outcome.getEnd() == null) {
+            // it's a "start" operation
+            notification.setMessage("Sending guard query for " + step2.getActorName() + " " + step2.getOperationName());
+
+        } else if ($outcome.getResult() == OperationResult.SUCCESS) {
+            notification.setMessage("Guard result for " + step2.getActorName() + " " + step2.getOperationName()
+                                        + " is Permit");
+        } else {
+            // it's a failure
+            notification.setMessage("Guard result for " + step2.getActorName() + " " + step2.getOperationName()
+                                        + " is Deny");
+        }
+
+        $manager.deliver("POLICY-CL-MGT", notification, "GUARD notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception generating GUARD notification",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+    }
 end
 
 /*
 *
-* This rule responds to APPC Response Events using the new LCM interface provided by appc
+* Process an outcome when the policy's operation starts.
 *
 */
-rule "APPC.LCM.RESPONSE"
+rule "EVENT.MANAGER.PROCESS.POLICY.STARTED"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : AppcLcmDmaapWrapper( getBody().getOutput().getCommonHeader().getRequestId() == $event.getRequestId() )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        $outcome.getEnd() == null,
+                        $step : getSteps().peek(),
+                        $step.isPolicyStep(),
+                        $outcome.isFor($step.getActorName(), $step.getOperationName()) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation, $operation, $opTimer, $response);
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-    //
-    // Get the result of the operation
-    //
-    PolicyResult policyResult = $operation.onResponse($response);
-    if (policyResult != null) {
-      logger.debug("{}: {}: operation finished - result={}",
-                  $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                  policyResult);
-
-      //
-      // This Operation has completed, construct a notification showing our results. (DB write - end operation)
-      //
-      VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-      notification.setFrom("policy");
-      notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-      notification.setPolicyScope($params.getPolicyScope());
-      notification.setPolicyVersion($params.getPolicyVersion());
-      notification.setMessage($operation.getOperationHistory());
-      notification.setHistory($operation.getHistory());
-      if (policyResult.equals(PolicyResult.SUCCESS)) {
-          notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
-      } else {
-          notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
-      }
-      PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-      //
-      // Ensure the operation is complete
-      //
-      if ($operation.isOperationComplete() == true) {
-          //
-          // It is complete, remove it from memory
-          //
-          retract($operation);
-          //
-          // We must also retract the timer object
-          // NOTE: We could write a Rule to do this
-          //
-          retract($opTimer);
-          //
-          // Complete the operation
-          //
-          modify($manager) {finishOperation($operation)};
-      } else {
-          //
-          // Just doing this will kick off the LOCKED rule again
-          //
-          modify($operation) {};
-      }
-    } else {
-        //
-        // Its not finished yet (i.e. expecting more Response objects)
-        //
-        // Or possibly it is a leftover response that we timed the request out previously
-        //
+    try {
+        $manager.getOutcomes().remove();
+
+        // it's a "start" operation for the step
+        $manager.bumpAttempts();
+
+        $manager.addToHistory($outcome);
+        $manager.storeInDataBase($manager.getPartialHistory().peekLast());
+
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.OPERATION);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory(Collections.emptyList());
+        notification.setMessage($manager.getOperationMessage());
+
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'start' outcome");
     }
-    //
-    // We are going to retract these objects from memory
-    //
-    retract($response);
+
+    update($manager);
 end
 
 /*
 *
-* Clean Up any lingering LCM reponses
+* Process an outcome when an arbitrary Preprocessor step starts.
 *
 */
-rule "APPC.LCM.RESPONSE.CLEANUP"
+rule "EVENT.MANAGER.PROCESS.PREPROCESSOR.STARTED"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $response : AppcLcmDmaapWrapper($id : getBody().getOutput().getCommonHeader().getRequestId() )
-        not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        $outcome.getEnd() == null,
+                        $step : getSteps().peek(),
+                        !$step.isPolicyStep(),
+                        $outcome.isFor($step.getActorName(), $step.getOperationName()) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: orphan appc response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id);
-    //
-    // Retract it
-    //
-    retract($response);
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
+
+    try {
+        $manager.getOutcomes().remove();
+
+        // it's a "start" operation for the step
+        $manager.bumpAttempts();
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle 'start' outcome");
+    }
+
+    update($manager);
 end
 
 /*
 *
-* This rule responds to SO Response Events
+* Process an outcome when the policy's operation succeeds.
 *
 */
-rule "SO.RESPONSE"
+rule "EVENT.MANAGER.PROCESS.POLICY.SUCCESS"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : SoResponseWrapper(requestId.toString() == $event.getRequestId().toString() )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        $outcome.getEnd() != null,
+                        $outcome.getResult() == OperationResult.SUCCESS,
+                        $step : getSteps().peek(),
+                        $step.isPolicyStep(),
+                        $outcome.isFor($step.getActorName(), $step.getOperationName()) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation, $operation, $opTimer, $response);
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-    // Get the result of the operation
-    //
-    PolicyResult policyResult = $operation.onResponse($response);
-    if (policyResult != null) {
-        logger.debug("{}: {}: operation finished - result={}",
-                    $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                    policyResult);
+    try {
+        $manager.getOutcomes().remove();
 
-        //
-        // This Operation has completed, construct a notification showing our results
-        //
-        VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-        notification.setFrom("policy");
-        notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-        notification.setPolicyScope($params.getPolicyScope());
-        notification.setPolicyVersion($params.getPolicyVersion());
-        notification.setMessage($operation.getOperationHistory());
-        notification.setHistory($operation.getHistory());
-        if (policyResult.equals(PolicyResult.SUCCESS)) {
-            notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
-        } else {
-            notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+        // let the step record the response that's contained within the outcome
+        $step.success($outcome);
 
-        }
-        PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-        //
-        // Ensure the operation is complete
-        //
-        if ($operation.isOperationComplete() == true) {
-            //
-            // It is complete, remove it from memory
-            //
-            retract($operation);
-            //
-            // We must also retract the timer object
-            // NOTE: We could write a Rule to do this
-            //
-            retract($opTimer);
-            //
-            // Complete the operation
-            //
-            modify($manager) {finishOperation($operation)};
-        } else {
-            //
-            // Just doing this will kick off the LOCKED rule again
-            //
-            modify($operation) {};
-        }
-    } else {
-        //
-        // Its not finished yet (i.e. expecting more Response objects)
-        //
-        // Or possibly it is a leftover response that we timed the request out previously
-        //
+        $manager.addToHistory($outcome);
+        $manager.storeInDataBase($manager.getPartialHistory().peekLast());
+
+        $manager.setResult($outcome.getResult());
+
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation)
+                                .collect(Collectors.toList()));
+
+        // this step is complete - discard it
+        $manager.getSteps().remove();
+
+        $manager.setState(State.POLICY_LOADED);
+
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'success' outcome");
     }
-    //
-    // We are going to retract these objects from memory
-    //
-    retract($response);
 
+    update($manager);
 end
 
 /*
 *
-* This rule responds to VFC Response Events
+* Process a final failure outcome, when the event has been accepted.
 *
 */
-rule "VFC.RESPONSE"
+rule "EVENT.MANAGER.PROCESS.FINAL.FAILURE.ACCEPTED"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : VfcResponse( requestId.toString() == $event.getRequestId().toString() )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        isAccepted(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        !isAbort($outcome),
+                        $outcome.getEnd() != null,
+                        $outcome.isFinalOutcome(),
+                        $outcome.getResult() != OperationResult.SUCCESS,
+                        $step : getSteps().peek() )
     then
-        Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-        logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-        logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                    $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                    $event, $manager, $operation, $operation, $opTimer, $response);
 
-        // Get the result of the operation
-        //
-        PolicyResult policyResult = $operation.onResponse($response);
-        if (policyResult != null) {
-            //
-            // This Operation has completed, construct a notification showing our results
-            //
-            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-            notification.setFrom("policy");
-            notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
-            notification.setPolicyVersion($params.getPolicyVersion());
-            notification.setMessage($operation.getOperationHistory());
-            notification.setHistory($operation.getHistory());
-            //
-            // Ensure the operation is complete
-            //
-            if ($operation.isOperationComplete() == true) {
-                //
-                // It is complete, remove it from memory
-                //
-                retract($operation);
-                //
-                // We must also retract the timer object
-                // NOTE: We could write a Rule to do this
-                //
-                retract($opTimer);
-                //
-                // Complete the operation
-                //
-                modify($manager) {finishOperation($operation)};
-            } else {
-                //
-                // Just doing this will kick off the LOCKED rule again
-                //
-                modify($operation) {};
-            }
-        } else {
-            //
-            // Its not finished yet (i.e. expecting more Response objects)
-            //
-            // Or possibly it is a leftover response that we timed the request out previously
-            //
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
+
+    try {
+        $manager.getOutcomes().remove();
+
+        if (!$outcome.isFor($step.getActorName(), $step.getOperationName())) {
+            $outcome.setResult(OperationResult.FAILURE_GUARD);
+            $outcome.setMessage("Operation denied by " + $outcome.getActor());
         }
-        //
-        // We are going to retract these objects from memory
-        //
-        retract($response);
 
+        // final failure for this policy
+        $manager.addToHistory($outcome);
+        $manager.storeInDataBase($manager.getPartialHistory().peekLast());
+
+        $manager.setResult($outcome.getResult());
+
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation)
+                                .collect(Collectors.toList()));
+
+        // trigger move to the next policy - clear all steps
+        $manager.getSteps().clear();
+        $manager.setState(State.POLICY_LOADED);
+
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'failure' outcome");
+    }
+
+    update($manager);
 end
 
 /*
 *
-* This rule responds to SDNC Response Events
+* Process a final failure outcome, when the event has NOT been accepted.  This typically
+* occurs when an A&AI query fails BEFORE the first lock has been requested (and thus
+* before the first policy's operation has been started).
 *
 */
-rule "SDNC.RESPONSE"
+rule "EVENT.MANAGER.PROCESS.FINAL.FAILURE.REJECTED"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName, closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(), requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(), onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : SdncResponse( requestId.toString() == $event.getRequestId().toString() )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        !isAccepted(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        !isAbort($outcome),
+                        $outcome.getEnd() != null,
+                        $outcome.isFinalOutcome(),
+                        $outcome.getResult() != OperationResult.SUCCESS,
+                        $step : getSteps().peek() )
     then
-        Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-        logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-        logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                       $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                       $event, $manager, $operation, $operation, $opTimer, $response);
-
-        // Get the result of the operation
-        //
-        PolicyResult policyResult = $operation.onResponse($response);
-        if (policyResult != null) {
-            //
-            // This Operation has completed, construct a notification showing our results
-            //
-            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-            notification.setFrom("policy");
-            notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
-            notification.setPolicyVersion($params.getPolicyVersion());
-            notification.setMessage($operation.getOperationHistory());
-            notification.setHistory($operation.getHistory());
-            //
-            // Ensure the operation is complete
-            //
-            if ($operation.isOperationComplete()) {
-                //
-                // It is complete, remove it from memory
-                //
-                retract($operation);
-                //
-                // We must also retract the timer object
-                // NOTE: We could write a Rule to do this
-                //
-                retract($opTimer);
-                //
-                // Complete the operation
-                //
-                modify($manager) {finishOperation($operation)};
-            } else {
-                //
-                // Just doing this will kick off the LOCKED rule again
-                //
-                modify($operation) {};
-            }
-        } else {
-            //
-            // Its not finished yet (i.e. expecting more Response objects)
-            //
-            // Or possibly it is a leftover response that we timed the request out previously
-            //
-        }
-        //
-        // We are going to retract these objects from memory
-        //
-        retract($response);
 
-end
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
+    retract($manager);
 
-/**
- * This rule responds to CDS Response Events
- */
-rule "${policyName}.CDS.RESPONSE"
-    when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $params.getClosedLoopControlName(),
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : CdsResponse( requestId == $event.getRequestId().toString() )
+    try {
+        // final failure for this policy
+        $manager.addToHistory($outcome);
 
-    then
+        $manager.setResult($outcome.getResult());
 
-        Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-        logger.info("{}: {}", $params.getClosedLoopControlName(), drools.getRule().getName());
-        logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                    $params.getClosedLoopControlName(), drools.getRule().getName(),
-                    $event, $manager, $operation, $operation, $opTimer, $response);
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.REJECTED);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory($manager.getPartialHistory().stream().map(OperationOutcome2::getClOperation)
+                                .collect(Collectors.toList()));
+
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to reject event");
+    }
 
-        // Get the result of the operation
-        PolicyResult policyResult = $operation.onResponse($response);
+    $manager.destroy();
+end
 
-        if (policyResult != null) {
-            logger.debug("{}: {}: operation finished - result={}",
-                    $params.getClosedLoopControlName(), drools.getRule().getName(),
-                    policyResult);
+/*
+*
+* Process an outcome when the policy's operation fails.
+*
+*/
+rule "EVENT.MANAGER.PROCESS.POLICY.FAILURE"
+    when
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        !isAbort($outcome),
+                        $outcome.getEnd() != null,
+                        !$outcome.isFinalOutcome(),
+                        $outcome.getResult() != OperationResult.SUCCESS,
+                        $step : getSteps().peek(),
+                        $step.isPolicyStep(),
+                        $outcome.isFor($step.getActorName(), $step.getOperationName()) )
+    then
 
-            // The operation has completed, construct a notification showing our results
-            VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-            notification.setFrom("policy");
-            notification.setPolicyName(drools.getRule().getName());
-            notification.setPolicyScope($params.getPolicyScope());
-            notification.setPolicyVersion($params.getPolicyVersion());
-            notification.setMessage($operation.getOperationHistory());
-            notification.setHistory($operation.getHistory());
-            notification.setNotification(
-                ($response != null && CdsActorConstants.SUCCESS.equals($response.getStatus()))
-                    ? ControlLoopNotificationType.OPERATION_SUCCESS : ControlLoopNotificationType.OPERATION_FAILURE);
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-            // Send the notification
-            PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+    try {
+        // not a final failure, thus it will be retried automatically
 
-            // Ensure the operation is complete
-            if ($operation.isOperationComplete()) {
+        $manager.getOutcomes().remove();
 
-                // It is complete, remove it from memory
-                retract($operation);
+        // do NOT invoke manager.setResult()
 
-                // We must also retract the timer object
-                // NOTE: We could write a Rule to do this
-                retract($opTimer);
+        $manager.addToHistory($outcome);
+        $manager.storeInDataBase($manager.getPartialHistory().peekLast());
 
-                // Complete the operation
-                modify($manager) {finishOperation($operation)};
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
 
-            } else {
-                // Just doing this will kick off the LOCKED rule again
-                modify($operation) {};
-            }
-        } else {
-            // Its not finished yet (i.e. expecting more Response objects)
-            // Or possibly it is a leftover response that we timed the request out previously
-            logger.info(
-                "policyResult is null"
-                    + "\nIt's not finished yet (i.e. expecting more Response objects)"
-                    + "\nOr possibly it is a leftover response that we timed the request out previously");
-        }
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
 
-        // We are going to retract these objects from memory
-        retract($response);
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle policy 'failure' outcome");
+    }
 
+    update($manager);
 end
 
-/*
-/*
-*
-* This manages a single timer.
-* Due to a bug in the drools code, the drools timer needed to be split from most of the objects in the when clause
-*
-*/
-rule "TIMER.FIRED"
-    timer (expr: $timeout)
-    when
-        $timer : ControlLoopTimer($timeout : delay, !expired)
-    then
-        Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-        logger.info("This is TIMER.FIRED");
-        modify($timer){setExpired(true)};
-    end
-
 /*
 *
-* This is the timer that manages the timeout for an individual operation.
+* Discard an outcome that was not handled by any other rule.
 *
 */
-rule "EVENT.MANAGER.OPERATION.TIMEOUT"
+rule "EVENT.MANAGER.DISCARD.OUTCOME"
+    salience -10
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", expired )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() == State.AWAITING_OUTCOME,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        $step : getSteps().peek() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: event={} manager={} operation={} opTimer={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation, $operation, $opTimer);
+    logger.info("{}: {}.{}: {} {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $outcome.getResult(), $manager);
 
-    //
-    // Tell it its timed out
-    //
-    $operation.setOperationHasTimedOut();
-    //
-    // Create a notification for it ("DB Write - end operation")
-    //
-    VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-    notification.setFrom("policy");
-    notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-    notification.setPolicyScope($params.getPolicyScope());
-    notification.setPolicyVersion($params.getPolicyVersion());
-    notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
-    notification.setMessage($operation.getOperationHistory());
-    notification.setHistory($operation.getHistory());
-    //
-    // Let interested parties know
-    //
-    PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-    //
-    // Get rid of the timer
-    //
-    retract($opTimer);
-    //
-    // Ensure the operation is complete
-    //
-    if ($operation.isOperationComplete() == true) {
-        //
-        // It is complete, remove it from memory
-        //
-        retract($operation);
-        //
-        // Complete the operation
-        //
-        modify($manager) {finishOperation($operation)};
-    } else {
-        //
-        // Just doing this will kick off the LOCKED rule again
-        //
-        modify($operation) {};
+    try {
+        $manager.getOutcomes().remove();
+
+        if ($outcome.getEnd() != null && $outcome.isFor($step.getActorName(), $step.getOperationName())) {
+            // it's a completion for the step
+
+            // let the step record the response that's contained within the outcome
+            if ($outcome.getResult() == OperationResult.SUCCESS) {
+                $step.success($outcome);
+            }
+
+            // this step is complete - discard it
+            $manager.getSteps().remove();
+
+            $manager.setState(State.POLICY_LOADED);
+        }
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception processing operation outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle outcome");
     }
+
+    update($manager);
 end
 
 /*
 *
-* This is the timer that manages the overall control loop timeout.
+* Abort processing.  This can happen in any state (once the manager has been started).
 *
 */
-rule "EVENT.MANAGER.TIMEOUT"
+rule "EVENT.MANAGER.ABORT"
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $clTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "ClosedLoop", expired )
+        $manager : UsecasesEventManager(
+                        isActive(),
+                        getState() != State.DONE,
+                        $outcome : getOutcomes().peek(),
+                        $outcome != null,
+                        isAbort($outcome),
+                        $step : getSteps().peek() )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
+    logger.info("{}: {}.{}: {} manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $step, $manager);
 
-    logger.debug("{}: {}: event={}",
-              $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-              $event);
-    //
-    // Tell the Event Manager it has timed out
-    //
-    VirtualControlLoopNotification notification = $manager.setControlLoopTimedOut();
-    if (notification != null) {
-        notification.setFrom("policy");
-        notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-        notification.setPolicyScope($params.getPolicyScope());
-        notification.setPolicyVersion($params.getPolicyVersion());
-        //
-        // Let interested parties know
-        //
-        PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
+    try {
+        // determine the final message
+        String msg;
+        switch ($outcome.getActor()) {
+            case ActorConstants.CL_TIMEOUT_ACTOR:
+                msg = "Control Loop timed out";
+                break;
+            case ActorConstants.LOCK_ACTOR:
+                msg = "Target Lock was lost";
+                break;
+            default:
+                msg = "Processing aborted by " + $outcome.getActor();
+                break;
+        }
+
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE, msg);
+
+        if ($step != null && "SDNR".equals($step.getActorName())
+                && $outcome.isFor($step.getActorName(), $step.getOperationName())) {
+
+            // aborted while processing the SDNR step - generate a notification
+            ControlLoopResponse clResponse = $manager.makeControlLoopResponse($outcome);
+            $manager.deliver("DCAE_CL_RSP", clResponse, "SDNR notification", drools.getRule().getName());
+        }
+
+        if ($step != null) {
+            $outcome.setActor($step.getActorName());
+            $outcome.setOperation($step.getOperationName());
+
+            $manager.addToHistory($outcome);
+            $manager.storeInDataBase($manager.getPartialHistory().peekLast());
+        }
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception handling ABORT outcome",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
+        $manager.abort(State.DONE, OperationFinalResult.FINAL_FAILURE_EXCEPTION, "failed to handle ABORT");
     }
-    //
-    // Retract the event
-    //
-    retract($event);
+
+    update($manager);
 end
 
 /*
 *
-* This rule cleans up the manager and other objects after an event has
-* been retracted.
+* Done processing.  Arriving here implies that the event has been accepted.
 *
 */
-rule "EVENT.MANAGER.CLEANUP"
+rule "EVENT.MANAGER.FINAL"
     when
-        $manager : ControlLoopEventManager( $clName : getClosedLoopControlName(), $requestId : getRequestId() )
-        $operations : LinkedList()
-                        from collect( ControlLoopOperationManager( onset.closedLoopControlName == $clName,
-                            onset.getRequestId() == $requestId ) )
-        $timers : LinkedList()
-                        from collect( ControlLoopTimer( closedLoopControlName == $clName,
-                            requestId == $requestId.toString() ) )
-        not( VirtualControlLoopEvent( closedLoopControlName == $clName, requestId == $requestId ) )
+        $manager : UsecasesEventManager(
+                        !isActive() || getState() == State.DONE )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, drools.getRule().getName());
+    logger.info("{}: {}.{}: manager={}",
+            $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+            $manager);
 
-    logger.debug("{}: {}: manager={} timers={} operations={}",
-              $clName, drools.getRule().getName(),
-              $manager, $timers.size(), $operations.size());
+    retract($manager);
 
-    //
-    // Retract lock by invoking unlock()
-    //
-    Lock lock = $manager.unlockCurrentOperation();
-    if(lock != null) {
-        retract(lock);
-    }
+    try {
+        VirtualControlLoopNotification notification = $manager.makeNotification();
+        notification.setPolicyScope(drools.getRule().getName());
+        notification.setPolicyName($manager.getPolicyName());
+        notification.setPolicyVersion($manager.getPolicyVersion());
+        notification.setHistory($manager.getFullHistory().stream().map(OperationOutcome2::getClOperation)
+                                    .collect(Collectors.toList()));
+
+        OperationFinalResult finalResult = $manager.getFinalResult();
+        if (finalResult == null) {
+            finalResult = ($manager.isActive() ? OperationFinalResult.FINAL_SUCCESS : OperationFinalResult.FINAL_FAILURE);
+        }
 
-    //
-    // Retract EVERYTHING
-    //
-    retract($manager);
+        switch (finalResult) {
+            case FINAL_FAILURE_EXCEPTION:
+                notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
+                notification.setMessage("Exception in processing closed loop");
+                break;
+            case FINAL_SUCCESS:
+                notification.setNotification(ControlLoopNotificationType.FINAL_SUCCESS);
+                break;
+            case FINAL_OPENLOOP:
+                notification.setNotification(ControlLoopNotificationType.FINAL_OPENLOOP);
+                break;
+            case FINAL_FAILURE:
+            default:
+                notification.setNotification(ControlLoopNotificationType.FINAL_FAILURE);
+                break;
+        }
 
-    for(Object manager: $operations) {
-        retract((ControlLoopOperationManager) manager);
-    }
-    for(Object timer: $timers) {
-        retract((ControlLoopTimer) timer);
+        if ($manager.getFinalMessage() != null) {
+            notification.setMessage($manager.getFinalMessage());
+        }
+
+        $manager.deliver("POLICY-CL-MGT", notification, "notification", drools.getRule().getName());
+
+    } catch(RuntimeException e) {
+        logger.warn("{}: {}.{}: manager={} exception generating final notification",
+                $manager.getClosedLoopControlName(), $manager.getPolicyName(), drools.getRule().getName(),
+                $manager, e);
     }
+
+    $manager.destroy();
 end
 
 /*
 *
-* This rule will clean up any rogue onsets where there is no
+* This rule will clean up any rogue events where there is no
 * ControlLoopParams object corresponding to the onset event.
 *
 */
 rule "EVENT.CLEANUP"
+    salience -100
     when
         $event : VirtualControlLoopEvent( $clName: closedLoopControlName )
-        not ( ControlLoopParams( getClosedLoopControlName() == $clName) )
     then
 
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
     logger.info("{}: {}", $clName, drools.getRule().getName());
-    logger.debug("{}: {}: orphan onset event={}",
+    logger.debug("{}: {}: orphan event={}",
                 $clName, drools.getRule().getName(), $event);
-
+    //
+    // Retract the event
+    //
     retract($event);
 end
 
 /*
 *
-* This rule responds to SDNR Response Events.
+* At this point, it appears that if we prevent the rules from getting messages from
+* topics, then that will also prevent the actors from getting them.  So the following
+* rules are here just to discard those messages.
+*
+* These have a higher salience so the objects are removed before the "FINAL" message
+* is processed, so that the junit test can assume things are done once they see the
+* "FINAL" message.  Otherwise, tests might fail sporadically.
 *
 */
-rule "SDNR.RESPONSE"
+rule "APPC.Response.CLEANUP"
+    salience 1
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $event : VirtualControlLoopEvent( closedLoopControlName == $clName,
-            closedLoopEventStatus == ControlLoopEventStatus.ONSET )
-        $manager : ControlLoopEventManager( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId() )
-        $operation : ControlLoopOperationManager( onset.closedLoopControlName == $event.getClosedLoopControlName(),
-            onset.getRequestId() == $event.getRequestId() )
-        $opTimer : ControlLoopTimer( closedLoopControlName == $event.getClosedLoopControlName(),
-            requestId == $event.getRequestId().toString(), timerType == "Operation", !expired )
-        $response : PciResponseWrapper( getBody().getCommonHeader().getRequestId() == $event.getRequestId() )
+        $msg : org.onap.policy.appc.Response( )
     then
+        retract($msg);
+end
 
-    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: event={} manager={} operation={} opTimer={} response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                $event, $manager, $operation, $operation, $opTimer, $response);
-
-    //
-    // Get the result of the operation
-    //
-    PolicyResult policyResult = $operation.onResponse($response);
-    if (policyResult != null) {
-      logger.debug("{}: {}: operation finished - result={}",
-                  $clName, $params.getPolicyName() + "." + drools.getRule().getName(),
-                  policyResult);
-
-      //
-      // This Operation has completed, construct a notification showing our results. (DB write - end operation)
-      //
-      VirtualControlLoopNotification notification = new VirtualControlLoopNotification($event);
-      notification.setFrom("policy");
-      notification.setPolicyName($params.getPolicyName() + "." + drools.getRule().getName());
-      notification.setPolicyScope($params.getPolicyScope());
-      notification.setPolicyVersion($params.getPolicyVersion());
-      notification.setMessage($operation.getOperationHistory());
-      notification.setHistory($operation.getHistory());
-      if (policyResult.equals(PolicyResult.SUCCESS)) {
-          notification.setNotification(ControlLoopNotificationType.OPERATION_SUCCESS);
-      } else {
-          notification.setNotification(ControlLoopNotificationType.OPERATION_FAILURE);
-      }
-      PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
-
-      ControlLoopResponse clResponse = $operation.getControlLoopResponse($response, $event);
-      PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse);
-      //
-      // Ensure the operation is complete
-      //
-      if ($operation.isOperationComplete() == true) {
-          //
-          // It is complete, remove it from memory
-          //
-          retract($operation);
-          //
-          // We must also retract the timer object
-          // NOTE: We could write a Rule to do this
-          //
-          retract($opTimer);
-          //
-          // Complete the operation
-          //
-          modify($manager) {finishOperation($operation)};
-      } else {
-          //
-          // Just doing this will kick off the LOCKED rule again
-          //
-          modify($operation) {};
-      }
-    } else {
-        //
-        // Its not finished yet (i.e. expecting more Response objects)
-        //
-        // Or possibly it is a leftover response that we timed the request out previously
-        //
-    }
-    //
-    // We are going to retract these objects from memory
-    //
-    retract($response);
+rule "APPC.Request.CLEANUP"
+    salience 1
+    when
+        $msg : org.onap.policy.appc.Request( )
+    then
+        retract($msg);
 end
 
-/*
-*
-* Clean Up any lingering SDNR reponses.
-*
-*/
-rule "SDNR.RESPONSE.CLEANUP"
+rule "APPC-LCM.Response.CLEANUP"
+    salience 1
     when
-        $params : ControlLoopParams( $clName : getClosedLoopControlName() )
-        $response : PciResponseWrapper($id : getBody().getCommonHeader().getRequestId )
-        not ( VirtualControlLoopEvent( requestId == $id, closedLoopEventStatus == ControlLoopEventStatus.ONSET ) )
+        $msg : org.onap.policy.appclcm.AppcLcmDmaapWrapper( )
     then
+        retract($msg);
+end
 
-    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $clName, $params.getPolicyName() + "." + drools.getRule().getName());
-    logger.debug("{}: {}: orphan sdnr response={}",
-                $clName, $params.getPolicyName() + "." + drools.getRule().getName(), $id);
-    //
-    // Retract it
-    //
-    retract($response);
+rule "SDNR.Response.CLEANUP"
+    salience 1
+    when
+        $msg : org.onap.policy.sdnr.PciResponseWrapper( )
+    then
+        retract($msg);
 end