summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Hahn <jrh3@att.com>2021-05-03 09:11:30 -0400
committerJim Hahn <jrh3@att.com>2021-05-03 10:11:09 -0400
commitcbdf437729ca4d010147acfb208ecd90ef65777c (patch)
tree1505d656c5c455b6a0f69d533244a86e8a0879d7
parent9496f31d3b2a1674f57a0395d050be0d9c6b87f0 (diff)
Add releaseLock() method to event manager
Issue-ID: POLICY-3261 Change-Id: I28a5356ebfc4a6ea1792ef35bc603054208bf73b Signed-off-by: Jim Hahn <jrh3@att.com>
-rw-r--r--controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ActorConstants.java3
-rw-r--r--controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java64
-rw-r--r--controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/StepContext.java15
-rw-r--r--controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java92
4 files changed, 159 insertions, 15 deletions
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ActorConstants.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ActorConstants.java
index 2591a3fa0..26d5ab8bf 100644
--- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ActorConstants.java
+++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ActorConstants.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2020-2021 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.
@@ -24,6 +24,7 @@ public class ActorConstants {
public static final String CL_TIMEOUT_ACTOR = "-CL-TIMEOUT-";
public static final String LOCK_ACTOR = "LOCK";
public static final String LOCK_OPERATION = "Lock";
+ public static final String UNLOCK_OPERATION = "Unlock";
public static final String PAYLOAD_KEY_VF_COUNT = "vfCount";
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
index 248a41be6..2c7e133af 100644
--- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
+++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManager.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2021 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.
@@ -21,6 +21,7 @@
package org.onap.policy.controlloop.eventmanager;
import java.io.Serializable;
+import java.time.Instant;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
@@ -37,8 +38,10 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.ToString;
import org.onap.policy.controlloop.ControlLoopException;
+import org.onap.policy.controlloop.ControlLoopOperation;
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;
@@ -189,13 +192,6 @@ public class ControlLoopEventManager implements StepContext, Serializable {
return TimeUnit.MILLISECONDS.convert(timeout, TimeUnit.SECONDS);
}
- /**
- * Requests a lock. This requests the lock for the time that remains before the
- * timeout expires. This avoids having to extend the lock.
- *
- * @param targetEntity entity to be locked
- * @return a future that can be used to await the lock
- */
@Override
public synchronized CompletableFuture<OperationOutcome> requestLock(String targetEntity) {
@@ -214,6 +210,58 @@ public class ControlLoopEventManager implements StepContext, Serializable {
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);
+ }
+
+ /*
+ * 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) {
+ OperationOutcome 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);
}
diff --git a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/StepContext.java b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/StepContext.java
index 5251b7acc..319cc64a9 100644
--- a/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/StepContext.java
+++ b/controlloop/common/eventmanager/src/main/java/org/onap/policy/controlloop/eventmanager/StepContext.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2020-2021 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.
@@ -69,4 +69,17 @@ public interface StepContext {
* @return a future that can be used to await the lock
*/
public CompletableFuture<OperationOutcome> requestLock(String targetEntity);
+
+ /**
+ * Releases a lock.
+ * <p/>
+ * Note: once this has been invoked, whether or not the "release" operation succeeds,
+ * subsequent calls to {@link #requestLock(String)} for the same target entity may
+ * always fail, and subsequent calls to {@link #releaseLock(String)} may always
+ * succeed, depending on the implementation.
+ *
+ * @param targetEntity entity to be locked
+ * @return a future that can be used to await the release operation
+ */
+ public CompletableFuture<OperationOutcome> releaseLock(String targetEntity);
}
diff --git a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java
index 596400f94..b930f57f8 100644
--- a/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java
+++ b/controlloop/common/eventmanager/src/test/java/org/onap/policy/controlloop/eventmanager/ControlLoopEventManagerTest.java
@@ -60,6 +60,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
@RunWith(MockitoJUnitRunner.class)
public class ControlLoopEventManagerTest {
private static final UUID REQ_ID = UUID.randomUUID();
+ private static final String EXPECTED_EXCEPTION = "expected exception";
private static final String CL_NAME = "my-closed-loop-name";
private static final String POLICY_NAME = "my-policy-name";
private static final String POLICY_SCOPE = "my-scope";
@@ -184,15 +185,96 @@ public class ControlLoopEventManagerTest {
// indicate that the first lock failed
locks.get(0).notifyUnavailable();
- verifyLock(OperationResult.FAILURE);
+ verifyLock(OperationResult.FAILURE, ActorConstants.LOCK_OPERATION);
assertTrue(mgr.getOutcomes().isEmpty());
}
- private void verifyLock(OperationResult result) {
+ @Test
+ public void testReleaseLock() {
+ mgr.requestLock(LOCK1);
+ mgr.requestLock(LOCK2);
+
+ // release one lock
+ final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
+
+ // asynchronous, thus should not have executed yet
+ assertThat(future.isDone()).isFalse();
+
+ // asynchronous, thus everything should still be locked
+ for (LockImpl lock : locks) {
+ assertThat(lock.isUnavailable()).isFalse();
+ }
+
+ runExecutor();
+
+ verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
+ assertThat(mgr.getOutcomes()).isEmpty();
+
+ // first lock should have been released, thus no longer available to the manager
+ assertThat(locks.get(0).isUnavailable()).isTrue();
+
+ // second should still be locked
+ assertThat(locks.get(1).isUnavailable()).isFalse();
+ }
+
+ /**
+ * Tests releaseLock() when there is no lock.
+ */
+ @Test
+ public void testReleaseLockNotLocked() {
+ final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
+
+ // lock didn't exist, so the request should already be complete
+ assertThat(future.isDone()).isTrue();
+
+ verifyLock(OperationResult.SUCCESS, ActorConstants.UNLOCK_OPERATION);
+ assertThat(mgr.getOutcomes()).isEmpty();
+ }
+
+ /**
+ * Tests releaseLock() when lock.free() throws an exception.
+ */
+ @Test
+ public void testReleaseLockException() throws ControlLoopException {
+ mgr = new MyManager(params, REQ_ID) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
+
+ LockImpl lock = new LockImpl(LockState.ACTIVE, targetEntity, requestId, holdSec, callback) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean free() {
+ throw new RuntimeException(EXPECTED_EXCEPTION);
+ }
+ };
+
+ locks.add(lock);
+ callback.lockAvailable(lock);
+ }
+ };
+
+ mgr.requestLock(LOCK1);
+
+ // release the lock
+ final CompletableFuture<OperationOutcome> future = mgr.releaseLock(LOCK1);
+
+ // asynchronous, thus should not have executed yet
+ assertThat(future.isDone()).isFalse();
+
+ runExecutor();
+
+ verifyLock(OperationResult.FAILURE_EXCEPTION, ActorConstants.UNLOCK_OPERATION);
+ assertThat(mgr.getOutcomes()).isEmpty();
+ }
+
+ private void verifyLock(OperationResult result, String lockOperation) {
OperationOutcome outcome = mgr.getOutcomes().poll();
assertNotNull(outcome);
assertEquals(ActorConstants.LOCK_ACTOR, outcome.getActor());
- assertEquals(ActorConstants.LOCK_OPERATION, outcome.getOperation());
+ assertEquals(lockOperation, outcome.getOperation());
assertNotNull(outcome.getEnd());
assertTrue(outcome.isFinalOutcome());
assertEquals(result, outcome.getResult());
@@ -249,6 +331,7 @@ public class ControlLoopEventManagerTest {
public void testGetDataManagerDisabled() throws ControlLoopException {
mgr = new MyManager(params, REQ_ID) {
private static final long serialVersionUID = 1L;
+
@Override
protected String getEnvironmentProperty(String propName) {
return ("guard.disabled".equals(propName) ? "true" : null);
@@ -285,8 +368,7 @@ public class ControlLoopEventManagerTest {
private static ExecutorService executor;
private static List<LockImpl> locks;
- public MyManager(ControlLoopParams params, UUID requestId)
- throws ControlLoopException {
+ public MyManager(ControlLoopParams params, UUID requestId) throws ControlLoopException {
super(params, requestId);
}