Make drools-pdp work with drools and java versions compatible
[policy/drools-applications.git] / controlloop / common / eventmanager / src / main / java / org / onap / policy / controlloop / eventmanager / ControlLoopEventManager.java
index 2fc43a0..72d498a 100644 (file)
@@ -1,15 +1,16 @@
 /*-
  * ============LICENSE_START=======================================================
- * controlloop event manager
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 package org.onap.policy.controlloop.eventmanager;
 
 import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
+import java.time.Instant;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.UUID;
-
-import org.onap.policy.aai.AAIGETVnfResponse;
-import org.onap.policy.aai.AAIGETVserverResponse;
-import org.onap.policy.aai.AAIManager;
-import org.onap.policy.controlloop.ControlLoopEventStatus;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.ToString;
 import org.onap.policy.controlloop.ControlLoopException;
-import org.onap.policy.controlloop.ControlLoopNotificationType;
 import org.onap.policy.controlloop.ControlLoopOperation;
-import org.onap.policy.controlloop.VirtualControlLoopEvent;
-import org.onap.policy.controlloop.VirtualControlLoopNotification;
-import org.onap.policy.controlloop.policy.FinalResult;
-import org.onap.policy.controlloop.policy.Policy;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.OperationResult;
+import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
+import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
+import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerStub;
 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
-import org.onap.policy.guard.GuardResult;
-import org.onap.policy.guard.LockCallback;
-import org.onap.policy.guard.PolicyGuard;
-import org.onap.policy.guard.PolicyGuard.LockResult;
-import org.onap.policy.guard.TargetLock;
-import org.onap.policy.drools.system.PolicyEngine; 
+import org.onap.policy.drools.core.lock.LockCallback;
+import org.onap.policy.drools.system.PolicyEngineConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ControlLoopEventManager implements LockCallback, Serializable {
-       
-       /**
-        * 
-        */
-       private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
-       
-       private static final long serialVersionUID = -1216568161322872641L;
-       public final String closedLoopControlName;
-       public final UUID requestID;
-       
-       private String controlLoopResult;
-       private transient ControlLoopProcessor processor = null;
-       private VirtualControlLoopEvent onset;
-       private Integer numOnsets = 0;
-       private Integer numAbatements = 0;
-       private VirtualControlLoopEvent abatement;
-       private FinalResult controlLoopTimedOut = null;
-
-       private boolean isActivated = false;
-       private LinkedList<ControlLoopOperation> controlLoopHistory = new LinkedList<>();
-       private ControlLoopOperationManager currentOperation = null;
-       private transient TargetLock targetLock = null;
-       private AAIGETVnfResponse vnfResponse = null;
-       private AAIGETVserverResponse vserverResponse = null;
-       private static String aaiHostURL; 
-       private static String aaiUser; 
-       private static String aaiPassword;
-       
-       private static Collection<String> requiredAAIKeys = new ArrayList<>();
-       static {
-               requiredAAIKeys.add("AICVServerSelfLink");
-               requiredAAIKeys.add("AICIdentity");
-               requiredAAIKeys.add("is_closed_loop_disabled");
-               requiredAAIKeys.add("VM_NAME");
-       }
-
-       public ControlLoopEventManager(String closedLoopControlName, UUID requestID) {
-               this.closedLoopControlName = closedLoopControlName;
-               this.requestID = requestID;
-       }
-       
-       public String getControlLoopResult() {
-               return controlLoopResult;
-       }
-       
-       public void setControlLoopResult(String controlLoopResult) {
-               this.controlLoopResult = controlLoopResult;
-       }
-       
-       public Integer getNumOnsets() {
-               return numOnsets;
-       }
-
-       public void setNumOnsets(Integer numOnsets) {
-               this.numOnsets = numOnsets;
-       }
-
-       public Integer getNumAbatements() {
-               return numAbatements;
-       }
-
-       public void setNumAbatements(Integer numAbatements) {
-               this.numAbatements = numAbatements;
-       }
-
-       public boolean isActivated() {
-               return isActivated;
-       }
-
-       public void setActivated(boolean isActivated) {
-               this.isActivated = isActivated;
-       }
-       
-       public VirtualControlLoopEvent  getOnsetEvent() {
-               return this.onset;
-       }
-       
-       public VirtualControlLoopEvent getAbatementEvent() {
-               return this.abatement;
-       }
-       
-       public ControlLoopProcessor getProcessor() {
-               return this.processor;
-       }
-
-       public VirtualControlLoopNotification   activate(VirtualControlLoopEvent event) {
-               VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
-               try {
-                       //
-                       // This method should ONLY be called ONCE
-                       //
-                       if (this.isActivated) {
-                               throw new ControlLoopException("ControlLoopEventManager has already been activated.");
-                       }
-                       //
-                       // Syntax check the event
-                       //
-                       checkEventSyntax(event);
-                       //
-                       // At this point we are good to go with this event
-                       //
-                       this.onset = event;
-                       this.numOnsets = 1;
-                       //
-                       notification.notification = ControlLoopNotificationType.ACTIVE;
-                       //
-                       // Set ourselves as active
-                       //
-                       this.isActivated = true;
-               } catch (ControlLoopException e) {
-                       logger.error("{}: activate threw: ",this, e);
-                       notification.notification = ControlLoopNotificationType.REJECTED;
-                       notification.message = e.getMessage();
-               }
-               return notification;
-       }
-       
-       
-       
-       public VirtualControlLoopNotification   activate(String yamlSpecification, VirtualControlLoopEvent event) {
-               VirtualControlLoopNotification notification = new VirtualControlLoopNotification(event);
-               try {
-                       //
-                       // This method should ONLY be called ONCE
-                       //
-                       if (this.isActivated) {
-                               throw new ControlLoopException("ControlLoopEventManager has already been activated.");
-                       }
-                       //
-                       // Syntax check the event
-                       //
-                       checkEventSyntax(event);
-       
-                       //
-                       // Check the YAML
-                       //
-                       if (yamlSpecification == null || yamlSpecification.length() < 1) {
-                               throw new ControlLoopException("yaml specification is null or 0 length");
-                       }
-                       String decodedYaml = null;
-                       try {
-                               decodedYaml = URLDecoder.decode(yamlSpecification, "UTF-8");
-                               if (decodedYaml != null && decodedYaml.length() > 0) {
-                                       yamlSpecification = decodedYaml;
-                               }
-                       } catch (UnsupportedEncodingException e) {
-                               logger.error("{}: activate threw: ",this, e);
-                       }
-                       //
-                       // Parse the YAML specification
-                       //
-                       this.processor = new ControlLoopProcessor(yamlSpecification);
-                       
-                       //
-                       // At this point we are good to go with this event
-                       //
-                       this.onset = event;
-                       this.numOnsets = 1;
-                       //
-                       //
-                       //
-                       notification.notification = ControlLoopNotificationType.ACTIVE;
-                       //
-                       // Set ourselves as active
-                       //
-                       this.isActivated = true;
-               } catch (ControlLoopException e) {
-                       logger.error("{}: activate threw: ",this, e);
-                       notification.notification = ControlLoopNotificationType.REJECTED;
-                       notification.message = e.getMessage();
-               }
-               return notification;
-       }
-       
-       public VirtualControlLoopNotification   isControlLoopFinal() throws ControlLoopException {
-               //
-               // Check if they activated us
-               //
-               if (this.isActivated == false) {
-                       throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
-               }
-               //
-               // Make sure we are expecting this call.
-               //
-               if (this.onset == null) {
-                       throw new ControlLoopException("No onset event for ControlLoopEventManager.");
-               }
-               //
-               // Ok, start creating the notification
-               //
-               VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
-               //
-               // Check if the overall control loop has timed out
-               //
-               if (this.isControlLoopTimedOut()) {
-                       //
-                       // Yes we have timed out
-                       //
-                       notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
-                       notification.message = "Control Loop timed out";
-                       notification.history.addAll(this.controlLoopHistory);
-                       return notification;                    
-               }
-               //
-               // Check if the current policy is Final
-               //
-               FinalResult result = this.processor.checkIsCurrentPolicyFinal();
-               if (result == null) {
-                       //
-                       // we are not at a final result
-                       //
-                       return null;
-               }
-       
-               switch (result) {
-               case FINAL_FAILURE_EXCEPTION:
-                       notification.message = "Exception in processing closed loop";
-               case FINAL_FAILURE:
-               case FINAL_FAILURE_RETRIES:
-               case FINAL_FAILURE_TIMEOUT:
-               case FINAL_FAILURE_GUARD:
-                       notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
-                       break;
-               case FINAL_OPENLOOP:
-                       notification.notification = ControlLoopNotificationType.FINAL_OPENLOOP;
-                       break;
-               case FINAL_SUCCESS:
-                       notification.notification = ControlLoopNotificationType.FINAL_SUCCESS;
-                       break;
-               default:
-                       return null;
-               }
-               //
-               // Be sure to add all the history
-               //
-               notification.history.addAll(this.controlLoopHistory);
-               return notification;
-       }
-               
-       public ControlLoopOperationManager      processControlLoop() throws ControlLoopException {
-               //
-               // Check if they activated us
-               //
-               if (this.isActivated == false) {
-                       throw new ControlLoopException("ControlLoopEventManager MUST be activated first.");
-               }
-               //
-               // Make sure we are expecting this call.
-               //
-               if (this.onset == null) {
-                       throw new ControlLoopException("No onset event for ControlLoopEventManager.");
-               }
-               //
-               // Is there a current operation?
-               //
-               if (this.currentOperation != null) {
-                       //
-                       // Throw an exception, or simply return the current operation?
-                       //
-                       throw new ControlLoopException("Already working an Operation, do not call this method.");
-               }
-               //
-               // Ensure we are not FINAL
-               //
-               VirtualControlLoopNotification notification = this.isControlLoopFinal();
-               if (notification != null) {
-                       //
-                       // This is weird, we require them to call the isControlLoopFinal() method first
-                       //
-                       // We should really abstract this and avoid throwing an exception, because it really
-                       // isn't an exception.
-                       //
-                       throw new ControlLoopException("Control Loop is in FINAL state, do not call this method.");
-               }
-               //
-               // Not final so get the policy that needs to be worked on.
-               //
-               Policy policy = this.processor.getCurrentPolicy();
-               if (policy == null) {
-                       throw new ControlLoopException("ControlLoopEventManager: processor came upon null Policy.");
-               }
-               //
-               // And setup an operation
-               //
-               this.currentOperation = new ControlLoopOperationManager(this.onset, policy, this);
-               //
-               // Return it
-               //
-               return this.currentOperation;
-       }
-       
-       public void finishOperation(ControlLoopOperationManager operation) throws ControlLoopException {
-               //
-               // Verify we have a current operation
-               //
-               if (this.currentOperation != null) {
-                       //
-                       // Validate they are finishing the current operation
-                       // PLD - this is simply comparing the policy. Do we want to equals the whole object?
-                       //
-                       if (this.currentOperation.policy.equals(operation.policy)) {
-                               logger.debug("Finishing {} result is {}", this.currentOperation.policy.getRecipe(), this.currentOperation.getOperationResult());
-                               //
-                               // Save history
-                               //
-                               this.controlLoopHistory.addAll(this.currentOperation.getHistory());
-                               //
-                               // Move to the next Policy
-                               //
-                               this.processor.nextPolicyForResult(this.currentOperation.getOperationResult());
-                               //
-                               // Just null this out
-                               //
-                               this.currentOperation = null;
-                               //
-                               // TODO: Release our lock
-                               //
-                               return;
-                       }
-                       logger.debug("Cannot finish current operation {} does not match given operation {}", this.currentOperation.policy, operation.policy);
-                       return;
-               }
-               throw new ControlLoopException("No operation to finish.");
-       }
-       
-       public synchronized LockResult<GuardResult, TargetLock> lockCurrentOperation() throws ControlLoopException {
-               //
-               // Sanity check
-               //
-               if (this.currentOperation == null) {
-                       throw new ControlLoopException("Do not have a current operation.");
-               }
-               //
-               // Have we acquired it already?
-               //
-               if (this.targetLock != null) {
-                       //
-                       // TODO: Make sure the current lock is for the same target.
-                       // Currently, it should be. But in the future it may not.
-                       //
-                       return new LockResult<GuardResult, TargetLock>(GuardResult.LOCK_ACQUIRED, this.targetLock);
-               } else {
-                       //
-                       // Ask the Guard
-                       //
-                       LockResult<GuardResult, TargetLock> lockResult = PolicyGuard.lockTarget(
-                                                                                                                                               this.currentOperation.policy.getTarget().getType(), 
-                                                                                                                                               this.getTargetInstance(this.currentOperation.policy),
-                                                                                                                                               this.onset.requestID,
-                                                                                                                                               this);
-                       //
-                       // Was it acquired?
-                       //
-                       if (lockResult.getA().equals(GuardResult.LOCK_ACQUIRED)) {
-                               //
-                               // Yes, let's save it
-                               //
-                               this.targetLock = lockResult.getB();
-                       }
-                       return lockResult;
-               }
-       }
-       
-       public synchronized TargetLock unlockCurrentOperation() {
-               if (this.targetLock == null) {
-                       return null;
-               }
-               if (PolicyGuard.unlockTarget(this.targetLock) == true) {
-                       TargetLock returnLock = this.targetLock;
-                       this.targetLock = null;
-                       return returnLock;
-               }
-               return null;
-       }
-       
-       public enum NEW_EVENT_STATUS {
-               FIRST_ONSET,
-               SUBSEQUENT_ONSET,
-               FIRST_ABATEMENT,
-               SUBSEQUENT_ABATEMENT,
-               SYNTAX_ERROR
-               ;
-       }
-               
-       public NEW_EVENT_STATUS onNewEvent(VirtualControlLoopEvent event) {
-               try {
-                       this.checkEventSyntax(event);
-                       if (event.closedLoopEventStatus == ControlLoopEventStatus.ONSET) {
-                               //
-                               // Check if this is our original ONSET
-                               //
-                               if (event.equals(this.onset)) {
-                                       //
-                                       // DO NOT retract it
-                                       //
-                                       return NEW_EVENT_STATUS.FIRST_ONSET;
-                               }
-                               //
-                               // Log that we got an onset
-                               //
-                               this.numOnsets++;
-                               return NEW_EVENT_STATUS.SUBSEQUENT_ONSET;
-                       } else if (event.closedLoopEventStatus == ControlLoopEventStatus.ABATED) {
-                               //
-                               // Have we already got an abatement?
-                               //
-                               if (this.abatement == null) {
-                                       //
-                                       // Save this
-                                       //
-                                       this.abatement = event;
-                                       //
-                                       // Keep track that we received another
-                                       //
-                                       this.numAbatements++;
-                                       //
-                                       //
-                                       //
-                                       return NEW_EVENT_STATUS.FIRST_ABATEMENT;
-                               } else {
-                                       //
-                                       // Keep track that we received another
-                                       //
-                                       this.numAbatements++;
-                                       //
-                                       //
-                                       //
-                                       return NEW_EVENT_STATUS.SUBSEQUENT_ABATEMENT;
-                               }
-                       } else {
-                               return NEW_EVENT_STATUS.SYNTAX_ERROR;
-                       }
-               } catch (ControlLoopException e) {
-                       logger.error("{}: onNewEvent threw: ",this, e);
-                       return NEW_EVENT_STATUS.SYNTAX_ERROR;
-               }
-       }
-       
-       public VirtualControlLoopNotification setControlLoopTimedOut() {
-               this.controlLoopTimedOut = FinalResult.FINAL_FAILURE_TIMEOUT;
-               VirtualControlLoopNotification notification = new VirtualControlLoopNotification(this.onset);
-               notification.notification = ControlLoopNotificationType.FINAL_FAILURE;
-               notification.message = "Control Loop timed out";
-               notification.history.addAll(this.controlLoopHistory);
-               return notification;                    
-       }
-       
-       public boolean isControlLoopTimedOut() {
-               return (this.controlLoopTimedOut == FinalResult.FINAL_FAILURE_TIMEOUT);
-       }
-       
-       public int      getControlLoopTimeout(Integer defaultTimeout) {
-               if (this.processor != null && this.processor.getControlLoop() != null) {
-                       return this.processor.getControlLoop().getTimeout();
-               }
-               if (defaultTimeout != null) {
-                       return defaultTimeout;
-               }
-               return 0;
-       }
-       
-       public AAIGETVnfResponse getVnfResponse() {
-               return vnfResponse; 
-       }
-
-       public AAIGETVserverResponse getVserverResponse() {
-               return vserverResponse; 
-       }
-       
-       public void checkEventSyntax(VirtualControlLoopEvent event) throws ControlLoopException {
-               if (event.closedLoopEventStatus == null || 
-                               (event.closedLoopEventStatus != ControlLoopEventStatus.ONSET &&
-                               event.closedLoopEventStatus != ControlLoopEventStatus.ABATED)) {
-                       throw new ControlLoopException("Invalid value in closedLoopEventStatus");
-               }
-               if (event.closedLoopControlName == null || event.closedLoopControlName.length() < 1) {
-                       throw new ControlLoopException("No control loop name");
-               }
-               if (event.requestID == null) {
-                       throw new ControlLoopException("No request ID");
-               }
-               if (event.AAI == null) {
-                       throw new ControlLoopException("AAI is null");
-               }
-               if (event.AAI.get("generic-vnf.vnf-id") == null && event.AAI.get("vserver.vserver-name") == null &&
-                       event.AAI.get("generic-vnf.vnf-name") == null) {
-                       throw new ControlLoopException("generic-vnf.vnf-id or generic-vnf.vnf-name or vserver.vserver-name information missing");
-               }
-               if (event.AAI.get("vserver.is-closed-loop-disabled") == null) {
-                       try {
-                               if (event.AAI.get("generic-vnf.vnf-id") != null) {
-                              vnfResponse = getAAIVnfInfo(event); 
-                              if (vnfResponse == null) {
-                                  throw new ControlLoopException("AAI Response is null (query by vnf-id)");
-                              }
-                              if (isClosedLoopDisabled(vnfResponse) == true) {
-                                          throw new ControlLoopException("is-closed-loop-disabled is set to true");    
-                              }
-                               } else if (event.AAI.get("generic-vnf.vnf-name") != null) {
-                                   vnfResponse = getAAIVnfInfo(event); 
-                                   if (vnfResponse == null) {
-                                       throw new ControlLoopException("AAI Response is null (query by vnf-name)");
-                                   }
-                                   if (isClosedLoopDisabled(vnfResponse) == true) {
-                                               throw new ControlLoopException("is-closed-loop-disabled is set to true");       
-                                   }
-                               } else if (event.AAI.get("vserver.vserver-name") != null) {
-                                   vserverResponse = getAAIVserverInfo(event); 
-                                   if (vserverResponse == null) {
-                                      throw new ControlLoopException("AAI Response is null (query by vserver-name)");
-                                   }
-                                   if (isClosedLoopDisabled(vserverResponse) == true) {
-                                               throw new ControlLoopException("is-closed-loop-disabled is set to true");       
-                                   }
-                               }
-                       } catch (Exception e) {
-                               logger.error("Exception from getAAIInfo: ", e);
-                               throw new ControlLoopException("Exception from getAAIInfo: " + e.toString());
-                       }
-               } else if (isClosedLoopDisabled(event)) {
-                       throw new ControlLoopException("is-closed-loop-disabled is set to true");
-               }
-               if (event.target == null || event.target.length() < 1) {
-                       throw new ControlLoopException("No target field");
-               } else {
-                       if (! event.target.equalsIgnoreCase("VM_NAME") &&
-                               ! event.target.equalsIgnoreCase("VNF_NAME") &&
-                               ! event.target.equalsIgnoreCase("vserver.vserver-name") &&
-                               ! event.target.equalsIgnoreCase("generic-vnf.vnf-id") &&
-                               ! event.target.equalsIgnoreCase("generic-vnf.vnf-name") ) {
-                               throw new ControlLoopException("target field invalid - expecting VM_NAME or VNF_NAME");
-                       }
-               }
-       }
-       
-       public static boolean isClosedLoopDisabled(AAIGETVnfResponse aaiResponse) {
-               if (aaiResponse != null && aaiResponse.isClosedLoopDisabled != null) {
-                       String value = aaiResponse.isClosedLoopDisabled; 
-                       if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
-                               "yes".equalsIgnoreCase(value)  || "Y".equalsIgnoreCase(value)) {
-                               return true; 
-                       } 
-               }
-  
-               return false; 
-       }
-       
-       public static boolean isClosedLoopDisabled(AAIGETVserverResponse aaiResponse) {
-               if (aaiResponse != null && aaiResponse.isClosedLoopDisabled != null) {
-                       String value = aaiResponse.isClosedLoopDisabled; 
-                       if ("true".equalsIgnoreCase(value) || "T".equalsIgnoreCase(value) ||
-                       "yes".equalsIgnoreCase(value)  || "Y".equalsIgnoreCase(value)) {
-                               return true; 
-                       } 
-               }
-  
-               return false; 
-       }
-       
-       public static boolean isClosedLoopDisabled(VirtualControlLoopEvent event) {
-               if ("true".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) || 
-                   "T".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) || 
-                   "yes".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled")) || 
-                   "Y".equalsIgnoreCase(event.AAI.get("vserver.is-closed-loop-disabled"))) { 
-                       return true; 
-               }               
-               return false;
-       }
-       
-       public static AAIGETVserverResponse getAAIVserverInfo(VirtualControlLoopEvent event) throws ControlLoopException {
-               UUID requestID = event.requestID;  
-               AAIGETVserverResponse response = null; 
-               String vserverName = event.AAI.get("vserver.vserver-name"); 
-
-               try {
-               if (vserverName != null) {
-                  aaiHostURL  = PolicyEngine.manager.getEnvironmentProperty("aai.url"); 
-                  aaiUser     = PolicyEngine.manager.getEnvironmentProperty("aai.username"); 
-                  aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
-                  String aaiGetQueryByVserver = "/aai/v11/nodes/vservers?vserver-name="; 
-                          String url = aaiHostURL + aaiGetQueryByVserver; 
-                          logger.info("url: " + url);
-                          response = AAIManager.getQueryByVserverName(url, aaiUser, aaiPassword, requestID, vserverName);
-               } 
-           } catch (Exception e) {
-               logger.error("getAAIVserverInfo exception: ", e);
-               throw new ControlLoopException("Exception in getAAIVserverInfo: ", e);
+/**
+ * Manager for a single event. Once this has been created, the event can be retracted from
+ * working memory. Invoke {@link #isActive()} to determine if the manager is active (i.e.,
+ * hasn't been replicated from another server). When the manager is no longer needed,
+ * {@link #destroy()} should be invoked.
+ */
+@ToString(onlyExplicitlyIncluded = true)
+public class ControlLoopEventManager implements StepContext, Serializable {
+
+    private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
+    private static final long serialVersionUID = -1216568161322872641L;
+
+    /**
+     * Data manager used when the policy engine's guard.disabled property is "true".
+     */
+    private static final OperationHistoryDataManager STUB_DATA_MANAGER = new OperationHistoryDataManagerStub();
+
+    public static final String GUARD_DISABLED_PROPERTY = "guard.disabled";
+
+    /**
+     * Counts the number of these objects that have been created. This is used by junit
+     * tests.
+     */
+    private static final AtomicLong createCount = new AtomicLong(0);
+
+    /**
+     * {@code True} if this object was created by this JVM instance, {@code false}
+     * otherwise. This will be {@code false} if this object is reconstituted from a
+     * persistent store or by transfer from another server.
+     */
+    private final transient boolean createdByThisJvmInstance;
+
+    private final transient EventManagerServices services;
+
+    @Getter
+    @ToString.Include
+    public final String closedLoopControlName;
+    @Getter
+    @ToString.Include
+    private final UUID requestId;
+
+    /**
+     * Time, in milliseconds, when the control loop will time out.
+     */
+    @Getter
+    private final long endTimeMs;
+
+    // fields extracted from the ControlLoopParams
+    @Getter
+    private final String policyName;
+    @Getter
+    private final String policyVersion;
+
+    /**
+     * Maps a target entity to its lock.
+     */
+    private final transient Map<String, LockData> target2lock = new HashMap<>();
+
+    @Getter(AccessLevel.PROTECTED)
+    private final ControlLoopProcessor processor;
+
+    /**
+     * Set of properties used while processing the event.
+     */
+    private final Map<String, Serializable> properties = new ConcurrentHashMap<>();
+
+    /**
+     * Unprocessed outcomes from the operations. Outcomes are added to this each time the
+     * "start" or "complete" callback is invoked, typically by an operation running in a
+     * background thread, thus it must be thread safe.
+     */
+    @Getter
+    private final transient Deque<OperationOutcome> outcomes = new ConcurrentLinkedDeque<>();
+
+
+    /**
+     * Constructs the object.
+     *
+     * @param services services the manager should use when processing the event
+     * @param params control loop parameters
+     * @param requestId event request ID
+     * @throws ControlLoopException if the event is invalid or if a YAML processor cannot
+     *         be created
+     */
+    public ControlLoopEventManager(EventManagerServices services, ControlLoopParams params, UUID requestId)
+                    throws ControlLoopException {
+
+        createCount.incrementAndGet();
+
+        this.createdByThisJvmInstance = true;
+        this.services = services;
+        this.closedLoopControlName = params.getClosedLoopControlName();
+        this.requestId = requestId;
+        this.policyName = params.getPolicyName();
+        this.policyVersion = params.getPolicyVersion();
+        this.processor = new ControlLoopProcessor(params.getToscaPolicy());
+        this.endTimeMs = System.currentTimeMillis() + detmControlLoopTimeoutMs();
+    }
+
+    /**
+     * Gets the number of manager objects that have been created.
+     *
+     * @return the number of manager objects that have been created
+     */
+    public static long getCreateCount() {
+        return createCount.get();
+    }
+
+    /**
+     * Determines if the manager is still active.
+     *
+     * @return {@code true} if the manager is still active, {@code false} otherwise
+     */
+    public boolean isActive() {
+        return createdByThisJvmInstance;
+    }
+
+    /**
+     * Cancels the current operation and frees all locks.
+     */
+    public void destroy() {
+        if (isActive()) {
+            getBlockingExecutor().execute(this::freeAllLocks);
         }
-               
-               return response; 
-       }
-       
-       public static AAIGETVnfResponse getAAIVnfInfo(VirtualControlLoopEvent event) throws ControlLoopException {
-               UUID requestID = event.requestID;  
-               AAIGETVnfResponse response = null; 
-               String vnfName = event.AAI.get("generic-vnf.vnf-name"); 
-               String vnfID   = event.AAI.get("generic-vnf.vnf-id"); 
-               aaiHostURL  = PolicyEngine.manager.getEnvironmentProperty("aai.url"); 
-        aaiUser     = PolicyEngine.manager.getEnvironmentProperty("aai.username"); 
-        aaiPassword = PolicyEngine.manager.getEnvironmentProperty("aai.password");
-               
-               try {
-            if (vnfName != null) {
-                  String aaiGetQueryByVnfName = "/aai/v11/network/generic-vnfs/generic-vnf?vnf-name="; 
-                          String url = aaiHostURL + aaiGetQueryByVnfName; 
-                          logger.info("url: " + url);
-                          response = AAIManager.getQueryByVnfName(url, aaiUser, aaiPassword, requestID, vnfName);                      
-               } else if (vnfID != null) {
-                      String aaiGetQueryByVnfID = "/aai/v11/network/generic-vnfs/generic-vnf/"; 
-                          String url = aaiHostURL + aaiGetQueryByVnfID; 
-                          logger.info("url: " + url);
-                          response = AAIManager.getQueryByVnfID(url, aaiUser, aaiPassword, requestID, vnfID);                  
-               }
-           } catch (Exception e) {
-               logger.error("getAAIVnfInfo exception: ", e);
-               throw new ControlLoopException("Exception in getAAIVnfInfo: ", e);
+    }
+
+    /**
+     * Frees all locks.
+     */
+    private void freeAllLocks() {
+        target2lock.values().forEach(LockData::free);
+    }
+
+    /**
+     * Determines the overall control loop timeout.
+     *
+     * @return the policy timeout, in milliseconds, if specified, a default timeout
+     *         otherwise
+     */
+    private long detmControlLoopTimeoutMs() {
+        // validation checks preclude null or 0 timeout values in the policy
+        int timeout = processor.getPolicy().getProperties().getTimeout();
+        return TimeUnit.MILLISECONDS.convert(timeout, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public synchronized CompletableFuture<OperationOutcome> requestLock(String targetEntity) {
+
+        long remainingMs = endTimeMs - System.currentTimeMillis();
+        int remainingSec = 15 + Math.max(0, (int) TimeUnit.SECONDS.convert(remainingMs, TimeUnit.MILLISECONDS));
+
+        LockData data = target2lock.computeIfAbsent(targetEntity, key -> {
+            var data2 = new LockData(key, requestId);
+            makeLock(targetEntity, requestId.toString(), remainingSec, data2);
+
+            data2.addUnavailableCallback(this::onComplete);
+
+            return data2;
+        });
+
+        return data.getFuture();
+    }
+
+    @Override
+    public synchronized CompletableFuture<OperationOutcome> releaseLock(String targetEntity) {
+        LockData data = target2lock.remove(targetEntity);
+
+        if (data == null) {
+            // lock did not exist - immediately return a success
+            OperationOutcome outcome = makeUnlockOutcome(targetEntity);
+            outcome.setEnd(outcome.getStart());
+            onComplete(outcome);
+
+            return CompletableFuture.completedFuture(outcome);
         }
-               
-               return response; 
-       }
-       
-       @Override
-       public boolean isActive() {
-               // TODO
-               return true;
-       }
-
-       @Override
-       public boolean releaseLock() {
-               // TODO
-               return false;
-       }
-
-       public String getTargetInstance(Policy policy) {
-               if (policy.getTarget() != null) {
-                       if (policy.getTarget().getType() != null) {
-                               switch(policy.getTarget().getType()) {
-                               case PNF:
-                                       break;
-                               case VM:
-                               case VNF:
-                                       if (this.onset.target.equalsIgnoreCase("vserver.vserver-name")) {
-                                               return this.onset.AAI.get("vserver.vserver-name");
-                                       }
-                                       else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-id")) {
-                                           return this.onset.AAI.get("generic-vnf.vnf-id");
-                                       }
-                                       else if (this.onset.target.equalsIgnoreCase("generic-vnf.vnf-name")) {
-                                           return this.onset.AAI.get("generic-vnf.vnf-name");
-                                       }
-                                       break;
-                               default:
-                                       break;
-                               }
-                       }
-               }
-               return null;
-       }
-
-       @Override
-       public String toString() {
-               return "ControlLoopEventManager [closedLoopControlName=" + closedLoopControlName + ", requestID=" + requestID
-                               + ", processor=" + processor + ", onset=" + (onset != null ? onset.requestID : "null") + ", numOnsets=" + numOnsets + ", numAbatements="
-                               + numAbatements + ", isActivated="
-                               + isActivated + ", currentOperation=" + currentOperation + ", targetLock=" + targetLock + "]";
-       }
-       
+
+        /*
+         * previous lock operation may not have completed yet, thus we tack the unlock
+         * operation onto it.
+         *
+         * Note: we must invoke free(), asynchronously (i.e., using whenCompleteAsync()),
+         * as it may block
+         */
+
+        return data.getFuture().whenCompleteAsync((lockOutcome, thrown) -> {
+
+            OperationOutcome outcome = makeUnlockOutcome(targetEntity);
+
+            try {
+                data.free();
+
+            } catch (RuntimeException e) {
+                logger.warn("failed to unlock {}", targetEntity, e);
+                outcome.setResult(OperationResult.FAILURE_EXCEPTION);
+                outcome.setMessage(ControlLoopOperation.FAILED_MSG + ": " + e.getMessage());
+            }
+
+            outcome.setEnd(Instant.now());
+            onComplete(outcome);
+
+        }, getBlockingExecutor());
+    }
+
+    private OperationOutcome makeUnlockOutcome(String targetEntity) {
+        var outcome = new OperationOutcome();
+        outcome.setActor(ActorConstants.LOCK_ACTOR);
+        outcome.setOperation(ActorConstants.UNLOCK_OPERATION);
+        outcome.setTarget(targetEntity);
+        outcome.setResult(OperationResult.SUCCESS);
+        outcome.setMessage(ControlLoopOperation.SUCCESS_MSG);
+        outcome.setFinalOutcome(true);
+        outcome.setStart(Instant.now());
+        return outcome;
+    }
+
+    public void onStart(OperationOutcome outcome) {
+        outcomes.add(outcome);
+    }
+
+    public void onComplete(OperationOutcome outcome) {
+        outcomes.add(outcome);
+    }
+
+    /**
+     * Determines if the context contains a property.
+     *
+     * @param name name of the property of interest
+     * @return {@code true} if the context contains the property, {@code false} otherwise
+     */
+    @Override
+    public boolean contains(String name) {
+        return properties.containsKey(name);
+    }
+
+    /**
+     * Gets a property, casting it to the desired type.
+     *
+     * @param <T> desired type
+     * @param name name of the property whose value is to be retrieved
+     * @return the property's value, or {@code null} if it does not yet have a value
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getProperty(String name) {
+        return (T) properties.get(name);
+    }
+
+    /**
+     * Sets a property's value.
+     *
+     * @param name property name
+     * @param value new property value
+     */
+    @Override
+    public void setProperty(String name, Serializable value) {
+        logger.info("set property {}={} manager={}", name, value, this);
+        properties.put(name, value);
+    }
+
+    /**
+     * Removes a property.
+     *
+     * @param name property name
+     */
+    @Override
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
+    // the following methods may be overridden by junit tests
+
+    public Executor getExecutor() {
+        return ForkJoinPool.commonPool();
+    }
+
+    protected ExecutorService getBlockingExecutor() {
+        return PolicyEngineConstants.getManager().getExecutorService();
+    }
+
+    protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
+        PolicyEngineConstants.getManager().createLock(targetEntity, requestId, holdSec, callback, false);
+    }
+
+    public ActorService getActorService() {
+        return services.getActorService();
+    }
+
+    public OperationHistoryDataManager getDataManager() {
+        boolean guardDisabled = "true".equalsIgnoreCase(getEnvironmentProperty(GUARD_DISABLED_PROPERTY));
+        return (guardDisabled ? STUB_DATA_MANAGER : services.getDataManager());
+    }
+
+    protected String getEnvironmentProperty(String propName) {
+        return PolicyEngineConstants.getManager().getEnvironmentProperty(propName);
+    }
 }