aboutsummaryrefslogtreecommitdiffstats
path: root/policy-core
diff options
context:
space:
mode:
authorJim Hahn <jrh3@att.com>2019-09-24 10:51:21 -0400
committerJim Hahn <jrh3@att.com>2019-10-17 15:40:32 -0400
commit6e0b450abe7e62fa47ffe14e95a67d035174dbdb (patch)
treee91c7bfb7365f9a06ad5674bc83e041b7237e378 /policy-core
parent1528214803af722cd660b7c4a3129f3de5b4ea7f (diff)
Reimplement Lock API using Lock objects
Modified PolicyResourceLockManager to just return a feature, deferring the lock() call/method to the feature, itself. The manager was also modified so that, if it can't find an enabled provider, it will return a default provider, whose lock() methods always fail. Once a feature has been identified, the manager will cache it for use thereafter. Modified the feature API to return lock objects and simplified the interface to remove the beforeXxx and afterXxx methods. Moved the unlock and refresh methods from the feature API into the lock class, renaming them to free and extend, respectively. Added a separate, feature-simple-locking project, which implements a simple version of the locking feature, over a single JVM. Extensively revised the distributed locking feature to fit in with the new API. Added support for persistence so that the various LockImpl classes can be serialized and still function correctly when they are deserialized back into new feature instances Added default implementations of free & extend to LockImpl. Modified API to take the ownerKey string, instead of the owner object. Removed Extractor as unneeded - may add via another review, if still useful. Updates per review comments: - Updated licenses in feature-simple-locking - Added beforeCreateLock & afterCreateLock to feature API - Moved SimpleLockingFeature into policy-management so that it's always available - Moved the executor service, "exsvc", into PolicyEngine - Moved Extrator into policy-utils - Changed Extractor logging level for exceptions - Fixed feature sequence numbers - Fixed mixing of seconds and milliseconds - Renamed exsvc - Modified to use property method with default value - Configured scheduled executor - Added suffix to Extractor.register() - Eliminated Feature Api and tied lock manager into engine - Added non-null checks to LockImpl parameters - Added non-null checks to createLock() parameters - Checked that lockManager is initialized Change-Id: Iddba38157ddc5f7277656979c0e679e5489eb7b1 Issue-ID: POLICY-2113 Signed-off-by: Jim Hahn <jrh3@att.com>
Diffstat (limited to 'policy-core')
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/AlwaysFailLock.java71
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java164
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockCallback.java (renamed from policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiConstants.java)31
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockImpl.java158
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockState.java43
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApi.java168
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java227
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/SimpleLockManager.java384
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/AlwaysFailLockTest.java106
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/LockImplTest.java261
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/LockTest.java134
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiTest.java92
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockManagerTest.java595
-rw-r--r--policy-core/src/test/java/org/onap/policy/drools/core/lock/SimpleLockManagerTest.java527
14 files changed, 725 insertions, 2236 deletions
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/AlwaysFailLock.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/AlwaysFailLock.java
new file mode 100644
index 00000000..0a4d327b
--- /dev/null
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/AlwaysFailLock.java
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.core.lock;
+
+
+/**
+ * Lock implementation whose operations always fail.
+ */
+public class AlwaysFailLock extends LockImpl {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs the object.
+ */
+ public AlwaysFailLock() {
+ super();
+ }
+
+ /**
+ * Constructs the object.
+ *
+ * @param resourceId identifier of the resource to be locked
+ * @param ownerKey information identifying the owner requesting the lock
+ * @param holdSec amount of time, in seconds, for which the lock should be held once
+ * it has been granted, after which it will automatically be released
+ * @param callback callback to be invoked once the lock is granted, or subsequently
+ * lost; must not be {@code null}
+ */
+ public AlwaysFailLock(String resourceId, String ownerKey, int holdSec, LockCallback callback) {
+ super(LockState.UNAVAILABLE, resourceId, ownerKey, holdSec, callback);
+ }
+
+ /**
+ * Always returns false.
+ */
+ @Override
+ public boolean free() {
+ return false;
+ }
+
+ /**
+ * Always fails and invokes {@link LockCallback#lockUnavailable(Lock)}.
+ */
+ @Override
+ public void extend(int holdSec, LockCallback callback) {
+ synchronized (this) {
+ setHoldSec(holdSec);
+ setCallback(callback);
+ }
+
+ notifyUnavailable();
+ }
+}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java
index de62b24a..b2ed9c7f 100644
--- a/policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java
@@ -2,14 +2,14 @@
* ============LICENSE_START=======================================================
* ONAP
* ================================================================================
- * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,145 +20,67 @@
package org.onap.policy.drools.core.lock;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map.Entry;
-import org.onap.policy.drools.utils.Pair;
-
/**
- * Lock that is held for a resource. This not only identifies the current owner of the
- * lock, but it also includes a queue of requesters. An item is associated with each
- * requester that is waiting in the queue. Note: this class is <b>not</b> thread-safe.
- *
- * @param <T> type of item to be associated with a request
+ * Lock held on a resource.
*/
-public class Lock<T> {
+public interface Lock {
/**
- * Result returned by <i>removeRequester()</i>.
+ * Frees/release the lock.
+ *
+ * <p/>
+ * Note: client code may choose to invoke this method <i>before</i> the lock has been
+ * granted.
+ *
+ * @return {@code true} if the request was accepted, {@code false} if the lock is
+ * unavailable
*/
- public enum RemoveResult {
- /**
- * The requester was the owner of the lock, and the lock is no longer needed,
- * because there were no other requesters waiting to get the lock.
- */
- UNLOCKED,
-
- /**
- * The requester was the owner of the lock, and has been replaced with the next
- * requester waiting in the queue.
- */
- RELOCKED,
-
- /**
- * The requester had been waiting in the queue, and has now been removed.
- */
- REMOVED,
-
- /**
- * The requester was not the owner, nor was it waiting in the queue.
- */
- NOT_FOUND
- }
+ boolean free();
/**
- * The last owner to grab the lock, never {@code null}.
+ * Determines if the lock is active.
+ *
+ * @return {@code true} if the lock is <b>ACTIVE</b>, {@code false} otherwise
*/
- private String owner;
+ boolean isActive();
/**
- * Requesters waiting to get the lock. Maps the requester (i.e., owner for which the
- * request is being made) to its associated item. Uses a Linked map so that the order
- * of the requesters is maintained. We don't expect many requesters for any given
- * lock, thus we'll start with a small hash size.
+ * Determines if the lock is unavailable. Once a lock object becomes unavailable, it
+ * will never become active again.
+ *
+ * @return {@code true} if the lock is <b>UNAVAILABLE</b>, {@code false} otherwise
*/
- private LinkedHashMap<String, T> requester2item = new LinkedHashMap<>(5);
+ boolean isUnavailable();
/**
- * Constructor.
- *
- * @param owner the current owner of this lock
+ * Determines if this object is waiting for a lock to be granted or denied. This
+ * applies when the lock is first created, or after {@link #extend(int, LockCallback)}
+ * has been invoked.
+ *
+ * @return {@code true} if the lock is <b>WAITING</b>, {@code false} otherwise
*/
- public Lock(String owner) {
- this.owner = owner;
- }
+ boolean isWaiting();
/**
- * Get owner.
- *
- * @return the current owner of the lock, or the last owner of the lock, if the lock
- * is not currently owned. (This will never be {@code null}.)
+ * Gets the ID of the resource to which the lock applies.
+ *
+ * @return the ID of the resource to which the lock applies
*/
- public String getOwner() {
- return owner;
- }
+ String getResourceId();
/**
- * Adds a new requester to the queue of requesters.
- *
- * @param requester the requester
- * @param item to be associated with the requester, must not be {@code null}
- * @return {@code true} if the requester was added, {@code false} if it already owns
- * the lock or is already in the queue
- * @throws IllegalArgumentException if the item is null
+ * Gets the lock's owner key.
+ *
+ * @return the lock's owner key
*/
- public boolean add(String requester, T item) {
- if (item == null) {
- throw SimpleLockManager.makeNullArgException("lock requester item is null");
- }
-
- if (requester.equals(owner)) {
- // requester already owns the lock
- return false;
- }
-
- T prev = requester2item.putIfAbsent(requester, item);
-
- // if there's a previous value, then that means this requester is already
- // waiting for a lock on this resource. In that case, we return false
- return (prev == null);
- }
+ String getOwnerKey();
/**
- * Removes a requester from the lock. The requester may currently own the lock, or it
- * may be in the queue waiting for the lock. Note: as this is agnostic to the type of
- * item associated with the requester, it is unable to notify the new owner that it's
- * the new owner; that is left up to the code that invokes this method.
- *
- * @param requester the requester
- * @param newOwner the new owner info is placed here, if the result is <i>RELOCKED</i>
- * @return the result
+ * Extends a lock an additional amount of time from now. The callback will always be
+ * invoked, and may be invoked <i>before</i> this method returns.
+ *
+ * @param holdSec the additional amount of time to hold the lock, in seconds
+ * @param callback callback to be invoked when the extension completes
*/
- public RemoveResult removeRequester(String requester, Pair<String, T> newOwner) {
-
- if (!requester.equals(owner)) {
- // requester does not currently own the lock - remove it from the
- // queue
- T ent = requester2item.remove(requester);
-
- // if there was an entry in the queue, then return true to indicate
- // that it was removed. Otherwise, return false
- return (ent != null ? RemoveResult.REMOVED : RemoveResult.NOT_FOUND);
- }
-
- /*
- * requester was the owner - find something to take over
- */
- Iterator<Entry<String, T>> it = requester2item.entrySet().iterator();
- if (!it.hasNext()) {
- // no one to take over the lock - it's now unlocked
- return RemoveResult.UNLOCKED;
- }
-
- // there's another requester to take over
- Entry<String, T> ent = it.next();
- it.remove();
-
- owner = ent.getKey();
-
- newOwner.first(owner);
- newOwner.second(ent.getValue());
-
- return RemoveResult.RELOCKED;
- }
+ void extend(int holdSec, LockCallback callback);
}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiConstants.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockCallback.java
index 8510e3d9..fae1cb43 100644
--- a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiConstants.java
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockCallback.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * api-resource-locks
+ * ONAP
* ================================================================================
* Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
* ================================================================================
@@ -20,20 +20,25 @@
package org.onap.policy.drools.core.lock;
-import lombok.Getter;
-import org.onap.policy.common.utils.services.OrderedServiceImpl;
-
-public class PolicyResourceLockFeatureApiConstants {
+/**
+ * Callback invoked when a lock is granted or lost.
+ *
+ * <p/>
+ * Note: these methods may or may not be invoked by the thread that requested the lock.
+ */
+public interface LockCallback {
/**
- * 'FeatureAPI.impl.getList()' returns an ordered list of objects implementing the
- * 'FeatureAPI' interface.
+ * Called to indicate that a lock has been granted.
+ *
+ * @param lock lock that has been granted
*/
- @Getter
- private static final OrderedServiceImpl<PolicyResourceLockFeatureApi> impl =
- new OrderedServiceImpl<>(PolicyResourceLockFeatureApi.class);
+ void lockAvailable(Lock lock);
- private PolicyResourceLockFeatureApiConstants() {
- // do nothing
- }
+ /**
+ * Called to indicate that a lock is permanently unavailable (e.g., lost, expired).
+ *
+ * @param lock lock that has been lost
+ */
+ void lockUnavailable(Lock lock);
}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockImpl.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockImpl.java
new file mode 100644
index 00000000..9596dbe8
--- /dev/null
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockImpl.java
@@ -0,0 +1,158 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.core.lock;
+
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+import lombok.ToString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lock implementation.
+ */
+@Getter
+@Setter
+@ToString(exclude = {"callback"})
+public class LockImpl implements Lock, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger logger = LoggerFactory.getLogger(LockImpl.class);
+
+ private LockState state;
+ private final String resourceId;
+ private final String ownerKey;
+ private transient LockCallback callback;
+ private int holdSec;
+
+ /**
+ * Constructs the object.
+ */
+ public LockImpl() {
+ this.state = LockState.UNAVAILABLE;
+ this.resourceId = null;
+ this.ownerKey = null;
+ this.callback = null;
+ this.holdSec = 0;
+ }
+
+ /**
+ * Constructs the object.
+ *
+ * @param state the initial lock state
+ * @param resourceId identifier of the resource to be locked
+ * @param ownerKey information identifying the owner requesting the lock
+ * @param holdSec amount of time, in seconds, for which the lock should be held once
+ * it has been granted, after which it will automatically be released
+ * @param callback callback to be invoked once the lock is granted, or subsequently
+ * lost; must not be {@code null}
+ */
+ public LockImpl(@NonNull LockState state, @NonNull String resourceId, @NonNull String ownerKey, int holdSec,
+ @NonNull LockCallback callback) {
+
+ if (holdSec < 0) {
+ throw new IllegalArgumentException("holdSec is negative");
+ }
+
+ this.state = state;
+ this.resourceId = resourceId;
+ this.ownerKey = ownerKey;
+ this.callback = callback;
+ this.holdSec = holdSec;
+ }
+
+ @Override
+ public boolean isActive() {
+ return (getState() == LockState.ACTIVE);
+ }
+
+ @Override
+ public boolean isUnavailable() {
+ return (getState() == LockState.UNAVAILABLE);
+ }
+
+ @Override
+ public boolean isWaiting() {
+ return (getState() == LockState.WAITING);
+ }
+
+ /**
+ * This method always succeeds, unless the lock is already unavailable.
+ */
+ @Override
+ public synchronized boolean free() {
+ if (isUnavailable()) {
+ return false;
+ }
+
+ logger.info("releasing lock: {}", this);
+ setState(LockState.UNAVAILABLE);
+
+ return true;
+ }
+
+ /**
+ * This method always succeeds, unless the lock is already unavailable.
+ */
+ @Override
+ public void extend(int holdSec, LockCallback callback) {
+ synchronized (this) {
+ if (isUnavailable()) {
+ return;
+ }
+
+ logger.info("lock granted: {}", this);
+ setState(LockState.ACTIVE);
+ setHoldSec(holdSec);
+ setCallback(callback);
+ }
+
+ notifyAvailable();
+ }
+
+ /**
+ * Invokes the {@link LockCallback#lockAvailable(Lock)}, <i>from the current
+ * thread</i>. Note: subclasses may choose to invoke the callback from other threads.
+ */
+ public void notifyAvailable() {
+ try {
+ callback.lockAvailable(this);
+
+ } catch (RuntimeException e) {
+ logger.warn("lock callback threw an exception", e);
+ }
+ }
+
+ /**
+ * Invokes the {@link LockCallback#lockUnavailable(Lock)}, <i>from the current
+ * thread</i>. Note: subclasses may choose to invoke the callback from other threads.
+ */
+ public void notifyUnavailable() {
+ try {
+ callback.lockUnavailable(this);
+
+ } catch (RuntimeException e) {
+ logger.warn("lock callback threw an exception", e);
+ }
+ }
+}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockState.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockState.java
new file mode 100644
index 00000000..41699ce6
--- /dev/null
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/LockState.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.core.lock;
+
+/**
+ * States of a Lock.
+ */
+
+public enum LockState {
+
+ /**
+ * Waiting for the lock request to complete.
+ */
+ WAITING,
+
+ /**
+ * This lock currently holds the resource.
+ */
+ ACTIVE,
+
+ /**
+ * The resource is no longer available to the lock.
+ */
+ UNAVAILABLE
+}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApi.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApi.java
deleted file mode 100644
index b7968486..00000000
--- a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApi.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * api-resource-locks
- * ================================================================================
- * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import org.onap.policy.common.utils.services.OrderedService;
-
-/**
- * Resource locks. Each lock has an "owner", which is intended to be unique across a
- * single instance of a running PolicyEngine.
- *
- * <p>This interface provides a way to invoke optional features at various points in the
- * code. At appropriate points in the application, the code iterates through this list,
- * invoking these optional methods.
- *
- * <p>Implementers may choose to implement a level of locking appropriate to the application.
- * For instance, they may choose to implement an engine-wide locking scheme, or they may
- * choose to implement a global locking scheme (e.g., through a shared DB).
- */
-public interface PolicyResourceLockFeatureApi extends OrderedService {
-
- /**
- * Result of a requested operation.
- */
- public enum OperResult {
-
- /**
- * The implementer accepted the request; no additional locking logic should be
- * performed.
- */
- OPER_ACCEPTED,
-
- /**
- * The implementer denied the request; no additional locking logic should be
- * performed.
- */
- OPER_DENIED,
-
-
- /**
- * The implementer did not handle the request; additional locking logic <i>should
- * be</i> performed.
- */
- OPER_UNHANDLED
- }
-
- /**
- * This method is called before a lock is acquired on a resource.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param holdSec the amount of time, in seconds, that the lock should be held
- * @return the result, where <b>OPER_DENIED</b> indicates that the lock is currently
- * held by another owner
- */
- public default OperResult beforeLock(String resourceId, String owner, int holdSec) {
- return OperResult.OPER_UNHANDLED;
- }
-
- /**
- * This method is called after a lock for a resource has been acquired or denied.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param locked {@code true} if the lock was acquired, {@code false} if it was denied
- * @return {@code true} if the implementer handled the request, {@code false}
- * otherwise
- */
- public default boolean afterLock(String resourceId, String owner, boolean locked) {
- return false;
- }
-
- /**
- * This method is called before a lock is refreshed on a resource. It may be invoked
- * repeatedly to extend the time that a lock is held.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param holdSec the amount of time, in seconds, that the lock should be held
- * @return the result, where <b>OPER_DENIED</b> indicates that the resource is not
- * currently locked by the given owner
- */
- public default OperResult beforeRefresh(String resourceId, String owner, int holdSec) {
- return OperResult.OPER_UNHANDLED;
- }
-
- /**
- * This method is called after a lock for a resource has been refreshed (or after the
- * refresh has been denied).
- *
- * @param resourceId resource id
- * @param owner owner
- * @param locked {@code true} if the lock was acquired, {@code false} if it was denied
- * @return {@code true} if the implementer handled the request, {@code false}
- * otherwise
- */
- public default boolean afterRefresh(String resourceId, String owner, boolean locked) {
- return false;
- }
-
- /**
- * This method is called before a lock on a resource is released.
- *
- * @param resourceId resource id
- * @param owner owner
- * @return the result, where <b>OPER_DENIED</b> indicates that the lock is not
- * currently held by the given owner
- */
- public default OperResult beforeUnlock(String resourceId, String owner) {
- return OperResult.OPER_UNHANDLED;
- }
-
- /**
- * This method is called after a lock on a resource is released.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param unlocked {@code true} if the lock was released, {@code false} if the owner
- * did not have a lock on the resource
- * @return {@code true} if the implementer handled the request, {@code false}
- * otherwise
- */
- public default boolean afterUnlock(String resourceId, String owner, boolean unlocked) {
- return false;
- }
-
- /**
- * This method is called before a check is made to determine if a resource is locked.
- *
- * @param resourceId resource id
- * @return the result, where <b>OPER_ACCEPTED</b> indicates that the resource is
- * locked, while <b>OPER_DENIED</b> indicates that it is not
- */
- public default OperResult beforeIsLocked(String resourceId) {
- return OperResult.OPER_UNHANDLED;
- }
-
- /**
- * This method is called before a check is made to determine if a particular owner
- * holds the lock on a resource.
- *
- * @param resourceId resource id
- * @param owner owner
- * @return the result, where <b>OPER_ACCEPTED</b> indicates that the resource is
- * locked by the given owner, while <b>OPER_DENIED</b> indicates that it is
- * not
- */
- public default OperResult beforeIsLockedBy(String resourceId, String owner) {
- return OperResult.OPER_UNHANDLED;
- }
-}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java
index 0e73eac1..bbf7d229 100644
--- a/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java
+++ b/policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java
@@ -20,213 +20,36 @@
package org.onap.policy.drools.core.lock;
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import org.onap.policy.drools.core.lock.PolicyResourceLockFeatureApi.OperResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.onap.policy.common.capabilities.Lockable;
+import org.onap.policy.common.capabilities.Startable;
/**
- * Manager of resource locks. Checks for API implementers.
+ * Manager of resource locks.
*/
-public class PolicyResourceLockManager extends SimpleLockManager {
-
- private static Logger logger = LoggerFactory.getLogger(PolicyResourceLockManager.class);
-
- /**
- * Used by junit tests.
- */
- protected PolicyResourceLockManager() {
- super();
- }
-
- /**
- * Get instance.
- *
- * @return the manager singleton
- */
- public static PolicyResourceLockManager getInstance() {
- return Singleton.instance;
- }
-
- @Override
- public boolean lock(String resourceId, String owner, int holdSec) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
-
- return doBoolIntercept(impl -> impl.beforeLock(resourceId, owner, holdSec), () -> {
-
- // implementer didn't do the work - defer to the superclass
- boolean locked = super.lock(resourceId, owner, holdSec);
-
- doIntercept(false, impl -> impl.afterLock(resourceId, owner, locked));
-
- return locked;
- });
- }
-
- @Override
- public boolean refresh(String resourceId, String owner, int holdSec) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
-
- return doBoolIntercept(impl -> impl.beforeRefresh(resourceId, owner, holdSec), () -> {
-
- // implementer didn't do the work - defer to the superclass
- boolean refreshed = super.refresh(resourceId, owner, holdSec);
-
- doIntercept(false, impl -> impl.afterRefresh(resourceId, owner, refreshed));
-
- return refreshed;
- });
- }
-
- @Override
- public boolean unlock(String resourceId, String owner) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
-
- return doBoolIntercept(impl -> impl.beforeUnlock(resourceId, owner), () -> {
-
- // implementer didn't do the work - defer to the superclass
- boolean unlocked = super.unlock(resourceId, owner);
-
- doIntercept(false, impl -> impl.afterUnlock(resourceId, owner, unlocked));
-
- return unlocked;
- });
- }
-
- /**
- * Is locked.
- *
- * @throws IllegalArgumentException if the resourceId is {@code null}
- */
- @Override
- public boolean isLocked(String resourceId) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
-
- return doBoolIntercept(impl -> impl.beforeIsLocked(resourceId), () ->
-
- // implementer didn't do the work - defer to the superclass
- super.isLocked(resourceId)
- );
- }
+public interface PolicyResourceLockManager extends Startable, Lockable {
/**
- * Is locked by.
+ * Requests a lock on a resource. Typically, the lock is not immediately granted,
+ * though a "lock" object is always returned. Once the lock has been granted (or
+ * denied), the callback will be invoked to indicate the result.
*
- * @throws IllegalArgumentException if the resourceId or owner is {@code null}
- */
- @Override
- public boolean isLockedBy(String resourceId, String owner) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
- return doBoolIntercept(impl -> impl.beforeIsLockedBy(resourceId, owner), () ->
-
- // implementer didn't do the work - defer to the superclass
- super.isLockedBy(resourceId, owner)
- );
- }
-
- /**
- * Applies a function to each implementer of the lock feature. Returns as soon as one
- * of them returns a result other than <b>OPER_UNHANDLED</b>. If they all return
- * <b>OPER_UNHANDLED</b>, then it returns the result of applying the default function.
+ * <p/>
+ * Notes:
+ * <dl>
+ * <li>The callback may be invoked <i>before</i> this method returns</li>
+ * <li>The implementation need not honor waitForLock={@code true}</li>
+ * </dl>
*
- * @param interceptFunc intercept function
- * @param defaultFunc default function
- * @return {@code true} if success, {@code false} otherwise
- */
- private boolean doBoolIntercept(Function<PolicyResourceLockFeatureApi, OperResult> interceptFunc,
- Supplier<Boolean> defaultFunc) {
-
- OperResult result = doIntercept(OperResult.OPER_UNHANDLED, interceptFunc);
- if (result != OperResult.OPER_UNHANDLED) {
- return (result == OperResult.OPER_ACCEPTED);
- }
-
- return defaultFunc.get();
- }
-
- /**
- * Applies a function to each implementer of the lock feature. Returns as soon as one
- * of them returns a non-null value.
- *
- * @param continueValue if the implementer returns this value, then it continues to
- * check addition implementers
- * @param func function to be applied to the implementers
- * @return first non-null value returned by an implementer, <i>continueValue</i> if
- * they all returned <i>continueValue</i>
- */
- private <T> T doIntercept(T continueValue, Function<PolicyResourceLockFeatureApi, T> func) {
-
- for (PolicyResourceLockFeatureApi impl : getImplementers()) {
- try {
- T result = func.apply(impl);
- if (result != continueValue) {
- return result;
- }
-
- } catch (RuntimeException e) {
- logger.warn("lock feature {} threw an exception", impl, e);
- }
- }
-
- return continueValue;
- }
-
- // these may be overridden by junit tests
-
- /**
- * Get implementers.
- *
- * @return the list of feature implementers
- */
- protected List<PolicyResourceLockFeatureApi> getImplementers() {
- return PolicyResourceLockFeatureApiConstants.getImpl().getList();
- }
-
- /**
- * Initialization-on-demand holder idiom.
- */
- private static class Singleton {
-
- private static final PolicyResourceLockManager instance = new PolicyResourceLockManager();
-
- /**
- * Not invoked.
- */
- private Singleton() {
- super();
- }
- }
+ * @param resourceId identifier of the resource to be locked
+ * @param ownerKey information identifying the owner requesting the lock
+ * @param holdSec amount of time, in seconds, for which the lock should be held once
+ * it has been granted, after which it will automatically be released
+ * @param callback callback to be invoked once the lock is granted, or subsequently
+ * lost; must not be {@code null}
+ * @param waitForLock {@code true} to wait for the lock, if it is currently locked,
+ * {@code false} otherwise
+ * @return a new lock
+ */
+ public Lock createLock(String resourceId, String ownerKey, int holdSec, LockCallback callback,
+ boolean waitForLock);
}
diff --git a/policy-core/src/main/java/org/onap/policy/drools/core/lock/SimpleLockManager.java b/policy-core/src/main/java/org/onap/policy/drools/core/lock/SimpleLockManager.java
deleted file mode 100644
index 427fbbc6..00000000
--- a/policy-core/src/main/java/org/onap/policy/drools/core/lock/SimpleLockManager.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP
- * ================================================================================
- * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-import org.onap.policy.common.utils.time.CurrentTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Simple lock manager. Callbacks are ignored. Does not redirect to lock feature
- * implementers.
- */
-public class SimpleLockManager {
-
- protected static Logger logger = LoggerFactory.getLogger(SimpleLockManager.class);
-
- // messages used in exceptions
- public static final String MSG_NULL_RESOURCE_ID = "null resourceId";
- public static final String MSG_NULL_OWNER = "null owner";
-
- /**
- * Used to access the current time. May be overridden by junit tests.
- */
- private static CurrentTime currentTime = new CurrentTime();
-
- /**
- * Used to synchronize updates to {@link #resource2data} and {@link #locks}.
- */
- private final Object locker = new Object();
-
- /**
- * Maps a resource to its lock data. Lock data is stored in both this and in
- * {@link #locks}.
- */
- private final Map<String, Data> resource2data = new HashMap<>();
-
- /**
- * Lock data, sorted by expiration time. Lock data is stored in both this and in
- * {@link #resource2data}. Whenever a lock operation is performed, this structure is
- * examined and any expired locks are removed; thus no timer threads are needed to
- * remove expired locks.
- */
- private final SortedSet<Data> locks = new TreeSet<>();
-
- /**
- * Constructor.
- */
- public SimpleLockManager() {
- super();
- }
-
- /**
- * Attempts to lock a resource, rejecting the lock if it is already owned, even if
- * it's the same owner; the same owner can use {@link #refresh(String, String, int)
- * refresh()}, instead, to extend a lock on a resource.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param holdSec the amount of time, in seconds, that the lock should be held
- * @return {@code true} if locked, {@code false} if the resource is already locked by
- * a different owner
- * @throws IllegalArgumentException if the resourceId or owner is {@code null}
- */
- public boolean lock(String resourceId, String owner, int holdSec) {
-
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
- boolean locked = false;
-
- synchronized (locker) {
- cleanUpLocks();
-
- if (!resource2data.containsKey(resourceId)) {
- Data data = new Data(owner, resourceId, currentTime.getMillis() + TimeUnit.SECONDS.toMillis(holdSec));
- resource2data.put(resourceId, data);
- locks.add(data);
- locked = true;
- }
- }
-
- logger.info("lock {} for resource {} owner {}", locked, resourceId, owner);
-
- return locked;
- }
-
- /**
- * Attempts to refresh a lock on a resource.
- *
- * @param resourceId resource id
- * @param owner owner
- * @param holdSec the amount of time, in seconds, that the lock should be held
- * @return {@code true} if locked, {@code false} if the resource is not currently
- * locked by the given owner
- * @throws IllegalArgumentException if the resourceId or owner is {@code null}
- */
- public boolean refresh(String resourceId, String owner, int holdSec) {
-
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
- boolean refreshed = false;
-
- synchronized (locker) {
- cleanUpLocks();
-
- Data existingLock = resource2data.get(resourceId);
- if (existingLock != null && existingLock.getOwner().equals(owner)) {
- // MUST remove the existing lock from the set
- locks.remove(existingLock);
-
- refreshed = true;
-
- Data data = new Data(owner, resourceId, currentTime.getMillis() + TimeUnit.SECONDS.toMillis(holdSec));
- resource2data.put(resourceId, data);
- locks.add(data);
- }
- }
-
- logger.info("refresh lock {} for resource {} owner {}", refreshed, resourceId, owner);
-
- return refreshed;
- }
-
- /**
- * Unlocks a resource.
- *
- * @param resourceId resource id
- * @param owner owner
- * @return {@code true} if unlocked, {@code false} if the given owner does not
- * currently hold a lock on the resource
- * @throws IllegalArgumentException if the resourceId or owner is {@code null}
- */
- public boolean unlock(String resourceId, String owner) {
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
- Data data;
-
- synchronized (locker) {
- cleanUpLocks();
-
- if ((data = resource2data.get(resourceId)) != null) {
- if (owner.equals(data.getOwner())) {
- resource2data.remove(resourceId);
- locks.remove(data);
-
- } else {
- data = null;
- }
- }
- }
-
- boolean unlocked = (data != null);
- logger.info("unlock resource {} owner {} = {}", resourceId, owner, unlocked);
-
- return unlocked;
- }
-
- /**
- * Determines if a resource is locked by anyone.
- *
- * @param resourceId resource id
- * @return {@code true} if the resource is locked, {@code false} otherwise
- * @throws IllegalArgumentException if the resourceId is {@code null}
- */
- public boolean isLocked(String resourceId) {
-
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- boolean locked;
-
- synchronized (locker) {
- cleanUpLocks();
-
- locked = resource2data.containsKey(resourceId);
- }
-
- logger.debug("resource {} isLocked = {}", resourceId, locked);
-
- return locked;
- }
-
- /**
- * Determines if a resource is locked by a particular owner.
- *
- * @param resourceId resource id
- * @param owner owner
- * @return {@code true} if the resource is locked, {@code false} otherwise
- * @throws IllegalArgumentException if the resourceId or owner is {@code null}
- */
- public boolean isLockedBy(String resourceId, String owner) {
-
- if (resourceId == null) {
- throw makeNullArgException(MSG_NULL_RESOURCE_ID);
- }
-
- if (owner == null) {
- throw makeNullArgException(MSG_NULL_OWNER);
- }
-
- Data data;
-
- synchronized (locker) {
- cleanUpLocks();
-
- data = resource2data.get(resourceId);
- }
-
- boolean locked = (data != null && owner.equals(data.getOwner()));
- logger.debug("resource {} isLockedBy {} = {}", resourceId, owner, locked);
-
- return locked;
- }
-
- /**
- * Releases expired locks.
- */
- private void cleanUpLocks() {
- long tcur = currentTime.getMillis();
-
- synchronized (locker) {
- Iterator<Data> it = locks.iterator();
- while (it.hasNext()) {
- Data data = it.next();
- if (data.getExpirationMs() <= tcur) {
- it.remove();
- resource2data.remove(data.getResource());
- } else {
- break;
- }
- }
- }
- }
-
- /**
- * Makes an exception for when an argument is {@code null}.
- *
- * @param msg exception message
- * @return a new Exception
- */
- public static IllegalArgumentException makeNullArgException(String msg) {
- return new IllegalArgumentException(msg);
- }
-
- /**
- * Data for a single Lock. Sorts by expiration time, then resource, and
- * then owner.
- */
- protected static class Data implements Comparable<Data> {
-
- /**
- * Owner of the lock.
- */
- private final String owner;
-
- /**
- * Resource that is locked.
- */
- private final String resource;
-
- /**
- * Time when the lock will expire, in milliseconds.
- */
- private final long texpireMs;
-
- /**
- * Constructor.
- *
- * @param resource resource
- * @param owner owner
- * @param texpireMs time expire in milliseconds
- */
- public Data(String owner, String resource, long texpireMs) {
- this.owner = owner;
- this.resource = resource;
- this.texpireMs = texpireMs;
- }
-
- public String getOwner() {
- return owner;
- }
-
- public String getResource() {
- return resource;
- }
-
- public long getExpirationMs() {
- return texpireMs;
- }
-
- @Override
- public int compareTo(Data data) {
- int diff = Long.compare(texpireMs, data.texpireMs);
- if (diff == 0) {
- diff = resource.compareTo(data.resource);
- }
- if (diff == 0) {
- diff = owner.compareTo(data.owner);
- }
- return diff;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((owner == null) ? 0 : owner.hashCode());
- result = prime * result + ((resource == null) ? 0 : resource.hashCode());
- result = prime * result + (int) (texpireMs ^ (texpireMs >>> 32));
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- Data other = (Data) obj;
- if (owner == null) {
- if (other.owner != null) {
- return false;
- }
- } else if (!owner.equals(other.owner)) {
- return false;
- }
- if (resource == null) {
- if (other.resource != null) {
- return false;
- }
- } else if (!resource.equals(other.resource)) {
- return false;
- }
- return (texpireMs == other.texpireMs);
- }
- }
-}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/AlwaysFailLockTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/AlwaysFailLockTest.java
new file mode 100644
index 00000000..ce4ca5fd
--- /dev/null
+++ b/policy-core/src/test/java/org/onap/policy/drools/core/lock/AlwaysFailLockTest.java
@@ -0,0 +1,106 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.core.lock;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AlwaysFailLockTest {
+ private static final String RESOURCE = "hello";
+ private static final String OWNER_KEY = "world";
+ private static final int HOLD_SEC = 10;
+ private static final int HOLD_SEC2 = 10;
+
+ private LockCallback callback;
+ private AlwaysFailLock lock;
+
+ /**
+ * Populates {@link #lock}.
+ */
+ @Before
+ public void setUp() {
+ callback = mock(LockCallback.class);
+
+ lock = new AlwaysFailLock(RESOURCE, OWNER_KEY, HOLD_SEC, callback);
+ }
+
+ @Test
+ public void testSerializable() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(lock);
+ }
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ try (ObjectInputStream ois = new ObjectInputStream(bais)) {
+ lock = (AlwaysFailLock) ois.readObject();
+ }
+
+ assertEquals(LockState.UNAVAILABLE, lock.getState());
+ assertEquals(RESOURCE, lock.getResourceId());
+ assertEquals(OWNER_KEY, lock.getOwnerKey());
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+
+ // these fields are transient
+ assertNull(lock.getCallback());
+ }
+
+ @Test
+ public void testAlwaysFailLockNoArgs() {
+ // verify that no-arg constructor doesn't throw an exception
+ new AlwaysFailLock();
+ }
+
+ @Test
+ public void testAlwaysFailLock() {
+ assertTrue(lock.isUnavailable());
+ assertEquals(RESOURCE, lock.getResourceId());
+ assertEquals(OWNER_KEY, lock.getOwnerKey());
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+ assertSame(callback, lock.getCallback());
+ }
+
+ @Test
+ public void testFree() {
+ assertFalse(lock.free());
+ assertTrue(lock.isUnavailable());
+ }
+
+ @Test
+ public void testExtend() {
+ LockCallback callback2 = mock(LockCallback.class);
+ lock.extend(HOLD_SEC2, callback2);
+
+ assertEquals(HOLD_SEC2, lock.getHoldSec());
+ assertSame(callback2, lock.getCallback());
+ }
+}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockImplTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockImplTest.java
new file mode 100644
index 00000000..aab04dc4
--- /dev/null
+++ b/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockImplTest.java
@@ -0,0 +1,261 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.core.lock;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LockImplTest {
+ private static final LockState STATE = LockState.WAITING;
+ private static final String RESOURCE = "hello";
+ private static final String OWNER_KEY = "world";
+ private static final int HOLD_SEC = 10;
+ private static final int HOLD_SEC2 = 20;
+ private static final String EXPECTED_EXCEPTION = "expected exception";
+
+ private LockCallback callback;
+ private LockImpl lock;
+
+ /**
+ * Populates {@link #lock}.
+ */
+ @Before
+ public void setUp() {
+ callback = mock(LockCallback.class);
+
+ lock = new LockImpl(STATE, RESOURCE, OWNER_KEY, HOLD_SEC, callback);
+ }
+
+ @Test
+ public void testSerializable() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(lock);
+ }
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ try (ObjectInputStream ois = new ObjectInputStream(bais)) {
+ lock = (LockImpl) ois.readObject();
+ }
+
+ assertEquals(STATE, lock.getState());
+ assertEquals(RESOURCE, lock.getResourceId());
+ assertEquals(OWNER_KEY, lock.getOwnerKey());
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+
+ // these fields are transient
+ assertNull(lock.getCallback());
+ }
+
+ @Test
+ public void testLockImplNoArgs() {
+ // use no-arg constructor
+ lock = new LockImpl();
+ assertEquals(LockState.UNAVAILABLE, lock.getState());
+ assertNull(lock.getResourceId());
+ assertNull(lock.getOwnerKey());
+ assertNull(lock.getCallback());
+ assertEquals(0, lock.getHoldSec());
+ }
+
+ @Test
+ public void testLockImpl_testGetters() {
+ assertEquals(STATE, lock.getState());
+ assertEquals(RESOURCE, lock.getResourceId());
+ assertEquals(OWNER_KEY, lock.getOwnerKey());
+ assertSame(callback, lock.getCallback());
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+
+ // test illegal args
+ assertThatThrownBy(() -> new LockImpl(null, RESOURCE, OWNER_KEY, HOLD_SEC, callback))
+ .hasMessageContaining("state");
+ assertThatThrownBy(() -> new LockImpl(STATE, null, OWNER_KEY, HOLD_SEC, callback))
+ .hasMessageContaining("resourceId");
+ assertThatThrownBy(() -> new LockImpl(STATE, RESOURCE, null, HOLD_SEC, callback))
+ .hasMessageContaining("ownerKey");
+ assertThatIllegalArgumentException().isThrownBy(() -> new LockImpl(STATE, RESOURCE, OWNER_KEY, -1, callback))
+ .withMessageContaining("holdSec");
+ assertThatThrownBy(() -> new LockImpl(STATE, RESOURCE, OWNER_KEY, HOLD_SEC, null))
+ .hasMessageContaining("callback");
+ }
+
+ @Test
+ public void testFree() {
+ assertTrue(lock.free());
+ assertTrue(lock.isUnavailable());
+
+ // should fail this time
+ assertFalse(lock.free());
+ assertTrue(lock.isUnavailable());
+
+ // no call-backs should have been invoked
+ verify(callback, never()).lockAvailable(any());
+ verify(callback, never()).lockUnavailable(any());
+ }
+
+ @Test
+ public void testExtend() {
+ lock.setState(LockState.WAITING);
+
+ LockCallback callback2 = mock(LockCallback.class);
+ lock.extend(HOLD_SEC2, callback2);
+ assertTrue(lock.isActive());
+ assertEquals(HOLD_SEC2, lock.getHoldSec());
+ assertSame(callback2, lock.getCallback());
+ verify(callback2).lockAvailable(lock);
+ verify(callback2, never()).lockUnavailable(any());
+
+ // first call-back should never have been invoked
+ verify(callback, never()).lockAvailable(any());
+ verify(callback, never()).lockUnavailable(any());
+
+ // extend again
+ LockCallback callback3 = mock(LockCallback.class);
+ lock.extend(HOLD_SEC, callback3);
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+ assertSame(callback3, lock.getCallback());
+ assertTrue(lock.isActive());
+ verify(callback3).lockAvailable(lock);
+ verify(callback3, never()).lockUnavailable(any());
+
+ // other call-backs should not have been invoked again
+ verify(callback, never()).lockAvailable(any());
+ verify(callback, never()).lockUnavailable(any());
+
+ verify(callback2).lockAvailable(any());
+ verify(callback2, never()).lockUnavailable(any());
+
+ assertTrue(lock.free());
+
+ // extend after free - should fail
+ lock.extend(HOLD_SEC2, callback);
+ assertTrue(lock.isUnavailable());
+
+ // call-backs should not have been invoked again
+ verify(callback, never()).lockAvailable(any());
+ verify(callback, never()).lockUnavailable(any());
+
+ verify(callback2).lockAvailable(any());
+ verify(callback2, never()).lockUnavailable(any());
+
+ verify(callback3).lockAvailable(lock);
+ verify(callback3, never()).lockUnavailable(any());
+ }
+
+ @Test
+ public void testNotifyAvailable() {
+ lock.notifyAvailable();
+
+ verify(callback).lockAvailable(any());
+ verify(callback, never()).lockUnavailable(any());
+ }
+
+ @Test
+ public void testNotifyAvailable_Ex() {
+ doThrow(new IllegalArgumentException(EXPECTED_EXCEPTION)).when(callback).lockAvailable(any());
+ doThrow(new IllegalArgumentException(EXPECTED_EXCEPTION)).when(callback).lockUnavailable(any());
+
+ // should not throw an exception
+ lock.notifyAvailable();
+ }
+
+ @Test
+ public void testNotifyUnavailable() {
+ lock.notifyUnavailable();
+
+ verify(callback, never()).lockAvailable(any());
+ verify(callback).lockUnavailable(any());
+ }
+
+ @Test
+ public void testNotifyUnavailable_Ex() {
+ doThrow(new IllegalArgumentException(EXPECTED_EXCEPTION)).when(callback).lockAvailable(any());
+ doThrow(new IllegalArgumentException(EXPECTED_EXCEPTION)).when(callback).lockUnavailable(any());
+
+ // should not throw an exception
+ lock.notifyUnavailable();
+ }
+
+ @Test
+ public void testSetState_testIsActive_testIsWaiting_testIsUnavailable() {
+ lock.setState(LockState.WAITING);
+ assertEquals(LockState.WAITING, lock.getState());
+ assertFalse(lock.isActive());
+ assertFalse(lock.isUnavailable());
+ assertTrue(lock.isWaiting());
+
+ lock.setState(LockState.ACTIVE);
+ assertEquals(LockState.ACTIVE, lock.getState());
+ assertTrue(lock.isActive());
+ assertFalse(lock.isUnavailable());
+ assertFalse(lock.isWaiting());
+
+ lock.setState(LockState.UNAVAILABLE);
+ assertEquals(LockState.UNAVAILABLE, lock.getState());
+ assertFalse(lock.isActive());
+ assertTrue(lock.isUnavailable());
+ assertFalse(lock.isWaiting());
+ }
+
+ @Test
+ public void testSetHoldSec() {
+ assertEquals(HOLD_SEC, lock.getHoldSec());
+
+ lock.setHoldSec(HOLD_SEC2);
+ assertEquals(HOLD_SEC2, lock.getHoldSec());
+ }
+
+ @Test
+ public void testSetCallback() {
+ assertSame(callback, lock.getCallback());
+
+ LockCallback callback2 = mock(LockCallback.class);
+ lock.setCallback(callback2);
+ assertSame(callback2, lock.getCallback());
+ }
+
+ @Test
+ public void testToString() {
+ String text = lock.toString();
+
+ assertNotNull(text);
+ assertThat(text).doesNotContain("ownerInfo").doesNotContain("callback").doesNotContain("succeed");
+ }
+}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockTest.java
deleted file mode 100644
index 5a88b02f..00000000
--- a/policy-core/src/test/java/org/onap/policy/drools/core/lock/LockTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP
- * ================================================================================
- * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.onap.policy.drools.core.lock.Lock.RemoveResult;
-import org.onap.policy.drools.utils.Pair;
-
-public class LockTest {
-
- private static final String OWNER = "my.owner";
- private static final String OWNER2 = "another.owner";
- private static final String OWNER3 = "third.owner";
-
- private static final Integer ITEM2 = 10;
- private static final Integer ITEM3 = 20;
-
- private Lock<Integer> lock;
- private Pair<String, Integer> newOwner;
-
- @Before
- public void setUp() {
- lock = new Lock<>(OWNER);
- newOwner = new Pair<>(null, null);
- }
-
-
- @Test
- public void testLock() {
- assertEquals(OWNER, lock.getOwner());
- }
-
- @Test
- public void testGetOwner() {
- assertEquals(OWNER, lock.getOwner());
- }
-
- @Test
- public void testAdd() {
- assertTrue(lock.add(OWNER2, ITEM2));
- assertTrue(lock.add(OWNER3, ITEM3));
-
- // attempt to re-add owner2 with the same item - should fail
- assertFalse(lock.add(OWNER2, ITEM2));
-
- // attempt to re-add owner2 with a different item - should fail
- assertFalse(lock.add(OWNER2, ITEM3));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testAdd_ArgEx() {
- lock.add(OWNER2, null);
- }
-
- @Test
- public void testAdd_AlreadyOwner() {
- assertFalse(lock.add(OWNER, ITEM2));
- }
-
- @Test
- public void testAdd_AlreadyInQueue() {
- lock.add(OWNER2, ITEM2);
-
- assertFalse(lock.add(OWNER2, ITEM2));
- }
-
- @Test
- public void testRemoveRequester_Owner_QueueEmpty() {
- assertEquals(RemoveResult.UNLOCKED, lock.removeRequester(OWNER, newOwner));
- }
-
- @Test
- public void testRemoveRequester_Owner_QueueHasOneItem() {
- lock.add(OWNER2, ITEM2);
-
- assertEquals(RemoveResult.RELOCKED, lock.removeRequester(OWNER, newOwner));
- assertEquals(OWNER2, newOwner.first());
- assertEquals(ITEM2, newOwner.second());
-
- assertEquals(RemoveResult.UNLOCKED, lock.removeRequester(OWNER2, newOwner));
- }
-
- @Test
- public void testRemoveRequester_Owner_QueueHasMultipleItems() {
- lock.add(OWNER2, ITEM2);
- lock.add(OWNER3, ITEM3);
-
- assertEquals(RemoveResult.RELOCKED, lock.removeRequester(OWNER, newOwner));
- assertEquals(OWNER2, newOwner.first());
- assertEquals(ITEM2, newOwner.second());
-
- assertEquals(RemoveResult.RELOCKED, lock.removeRequester(OWNER2, newOwner));
- assertEquals(OWNER3, newOwner.first());
- assertEquals(ITEM3, newOwner.second());
-
- assertEquals(RemoveResult.UNLOCKED, lock.removeRequester(OWNER3, newOwner));
- }
-
- @Test
- public void testRemoveRequester_InQueue() {
- lock.add(OWNER2, ITEM2);
-
- assertEquals(RemoveResult.REMOVED, lock.removeRequester(OWNER2, newOwner));
- }
-
- @Test
- public void testRemoveRequester_NeitherOwnerNorInQueue() {
- assertEquals(RemoveResult.NOT_FOUND, lock.removeRequester(OWNER2, newOwner));
- }
-
-}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiTest.java
deleted file mode 100644
index 999ae50f..00000000
--- a/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * api-resource-locks
- * ================================================================================
- * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.onap.policy.drools.core.lock.PolicyResourceLockFeatureApi.OperResult;
-
-public class PolicyResourceLockFeatureApiTest {
-
- private static final String RESOURCE_ID = "the resource";
- private static final String OWNER = "the owner";
-
- private PolicyResourceLockFeatureApi api;
-
- /**
- * set up.
- */
- @Before
- public void setUp() {
- api = new PolicyResourceLockFeatureApi() {
- @Override
- public int getSequenceNumber() {
- return 0;
- }
- };
- }
-
- @Test
- public void testBeforeLock() {
- assertEquals(OperResult.OPER_UNHANDLED, api.beforeLock(RESOURCE_ID, OWNER, 0));
- }
-
- @Test
- public void testAfterLock() {
- assertFalse(api.afterLock(RESOURCE_ID, OWNER, true));
- assertFalse(api.afterLock(RESOURCE_ID, OWNER, false));
- }
-
- @Test
- public void testBeforeRefresh() {
- assertEquals(OperResult.OPER_UNHANDLED, api.beforeRefresh(RESOURCE_ID, OWNER, 0));
- }
-
- @Test
- public void testAfterRefresh() {
- assertFalse(api.afterRefresh(RESOURCE_ID, OWNER, true));
- assertFalse(api.afterRefresh(RESOURCE_ID, OWNER, false));
- }
-
- @Test
- public void testBeforeUnlock() {
- assertEquals(OperResult.OPER_UNHANDLED, api.beforeUnlock(RESOURCE_ID, OWNER));
- }
-
- @Test
- public void testAfterUnlock() {
- assertFalse(api.afterUnlock(RESOURCE_ID, OWNER, true));
- assertFalse(api.afterUnlock(RESOURCE_ID, OWNER, false));
- }
-
- @Test
- public void testBeforeIsLocked() {
- assertEquals(OperResult.OPER_UNHANDLED, api.beforeIsLocked(RESOURCE_ID));
- }
-
- @Test
- public void testBeforeIsLockedBy() {
- assertEquals(OperResult.OPER_UNHANDLED, api.beforeIsLockedBy(RESOURCE_ID, OWNER));
- }
-}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockManagerTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockManagerTest.java
deleted file mode 100644
index f575ce49..00000000
--- a/policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockManagerTest.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP
- * ================================================================================
- * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.onap.policy.drools.core.lock.PolicyResourceLockFeatureApi.OperResult;
-
-public class PolicyResourceLockManagerTest {
-
- private static final int MAX_AGE_SEC = 4 * 60;
-
- private static final String NULL_RESOURCE_ID = "null resourceId";
- private static final String NULL_OWNER = "null owner";
-
- private static final String RESOURCE_A = "resource.a";
- private static final String RESOURCE_B = "resource.b";
- private static final String RESOURCE_C = "resource.c";
-
- private static final String OWNER1 = "owner.one";
- private static final String OWNER2 = "owner.two";
- private static final String OWNER3 = "owner.three";
-
- private PolicyResourceLockFeatureApi impl1;
- private PolicyResourceLockFeatureApi impl2;
- private List<PolicyResourceLockFeatureApi> implList;
-
- private PolicyResourceLockManager mgr;
-
- /**
- * Set up.
- */
- @Before
- public void setUp() {
- impl1 = mock(PolicyResourceLockFeatureApi.class);
- impl2 = mock(PolicyResourceLockFeatureApi.class);
-
- initImplementer(impl1);
- initImplementer(impl2);
-
- // list of feature API implementers
- implList = new LinkedList<>(Arrays.asList(impl1, impl2));
-
- mgr = new PolicyResourceLockManager() {
- @Override
- protected List<PolicyResourceLockFeatureApi> getImplementers() {
- return implList;
- }
- };
- }
-
- /**
- * Initializes an implementer so it always returns {@code null}.
- *
- * @param impl implementer
- */
- private void initImplementer(PolicyResourceLockFeatureApi impl) {
- when(impl.beforeLock(anyString(), anyString(), anyInt())).thenReturn(OperResult.OPER_UNHANDLED);
- when(impl.beforeRefresh(anyString(), anyString(), anyInt())).thenReturn(OperResult.OPER_UNHANDLED);
- when(impl.beforeUnlock(anyString(), anyString())).thenReturn(OperResult.OPER_UNHANDLED);
- when(impl.beforeIsLocked(anyString())).thenReturn(OperResult.OPER_UNHANDLED);
- when(impl.beforeIsLockedBy(anyString(), anyString())).thenReturn(OperResult.OPER_UNHANDLED);
- }
-
- @Test
- public void testLock() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2).beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl1).afterLock(RESOURCE_A, OWNER1, true);
- verify(impl2).afterLock(RESOURCE_A, OWNER1, true);
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
-
- // null callback - not locked yet
- assertTrue(mgr.lock(RESOURCE_C, OWNER3, MAX_AGE_SEC));
-
- // null callback - already locked
- assertFalse(mgr.lock(RESOURCE_A, OWNER3, MAX_AGE_SEC));
- }
-
- @Test
- public void testLock_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.lock(null, OWNER1, MAX_AGE_SEC))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.lock(RESOURCE_A, null, MAX_AGE_SEC))
- .withMessage(NULL_OWNER);
-
- // this should not throw an exception
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- }
-
- @Test
- public void testLock_Acquired_BeforeIntercepted() {
- // have impl1 intercept
- when(impl1.beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC)).thenReturn(OperResult.OPER_ACCEPTED);
-
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2, never()).beforeLock(anyString(), anyString(), anyInt());
-
- verify(impl1, never()).afterLock(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterLock(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testLock_Denied_BeforeIntercepted() {
- // have impl1 intercept
- when(impl1.beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC)).thenReturn(OperResult.OPER_DENIED);
-
- assertFalse(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeLock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2, never()).beforeLock(anyString(), anyString(), anyInt());
-
- verify(impl1, never()).afterLock(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterLock(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testLock_Acquired_AfterIntercepted() throws Exception {
-
- // impl1 intercepts during afterLock()
- when(impl1.afterLock(RESOURCE_A, OWNER1, true)).thenReturn(true);
-
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // impl1 sees it, but impl2 does not
- verify(impl1).afterLock(RESOURCE_A, OWNER1, true);
- verify(impl2, never()).afterLock(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testLock_Acquired() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).afterLock(RESOURCE_A, OWNER1, true);
- verify(impl2).afterLock(RESOURCE_A, OWNER1, true);
- }
-
- @Test
- public void testLock_Denied_AfterIntercepted() throws Exception {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // impl1 intercepts during afterLock()
- when(impl1.afterLock(RESOURCE_A, OWNER2, false)).thenReturn(true);
-
- // owner2 tries to lock
- assertFalse(mgr.lock(RESOURCE_A, OWNER2, MAX_AGE_SEC));
-
- // impl1 sees it, but impl2 does not
- verify(impl1).afterLock(RESOURCE_A, OWNER2, false);
- verify(impl2, never()).afterLock(RESOURCE_A, OWNER2, false);
- }
-
- @Test
- public void testLock_Denied() {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // owner2 tries to lock
- mgr.lock(RESOURCE_A, OWNER2, MAX_AGE_SEC);
-
- verify(impl1).afterLock(RESOURCE_A, OWNER2, false);
- verify(impl2).afterLock(RESOURCE_A, OWNER2, false);
- }
-
- @Test
- public void testRefresh() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2).beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl1).afterRefresh(RESOURCE_A, OWNER1, true);
- verify(impl2).afterRefresh(RESOURCE_A, OWNER1, true);
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
-
- // different owner and resource
- assertFalse(mgr.refresh(RESOURCE_C, OWNER3, MAX_AGE_SEC));
-
- // different owner
- assertFalse(mgr.refresh(RESOURCE_A, OWNER3, MAX_AGE_SEC));
- }
-
- @Test
- public void testRefresh_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.refresh(null, OWNER1, MAX_AGE_SEC))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.refresh(RESOURCE_A, null, MAX_AGE_SEC))
- .withMessage(NULL_OWNER);
-
- // this should not throw an exception
- mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- }
-
- @Test
- public void testRefresh_Acquired_BeforeIntercepted() {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // have impl1 intercept
- when(impl1.beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC)).thenReturn(OperResult.OPER_ACCEPTED);
-
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2, never()).beforeRefresh(anyString(), anyString(), anyInt());
-
- verify(impl1, never()).afterRefresh(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterRefresh(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testRefresh_Denied_BeforeIntercepted() {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // have impl1 intercept
- when(impl1.beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC)).thenReturn(OperResult.OPER_DENIED);
-
- assertFalse(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).beforeRefresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- verify(impl2, never()).beforeRefresh(anyString(), anyString(), anyInt());
-
- verify(impl1, never()).afterRefresh(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterRefresh(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testRefresh_Acquired_AfterIntercepted() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // impl1 intercepts during afterRefresh()
- when(impl1.afterRefresh(RESOURCE_A, OWNER1, true)).thenReturn(true);
-
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // impl1 sees it, but impl2 does not
- verify(impl1).afterRefresh(RESOURCE_A, OWNER1, true);
- verify(impl2, never()).afterRefresh(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testRefresh_Acquired() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- verify(impl1).afterRefresh(RESOURCE_A, OWNER1, true);
- verify(impl2).afterRefresh(RESOURCE_A, OWNER1, true);
- }
-
- @Test
- public void testRefresh_Denied_AfterIntercepted() throws Exception {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // impl1 intercepts during afterRefresh()
- when(impl1.afterRefresh(RESOURCE_A, OWNER2, false)).thenReturn(true);
-
- // owner2 tries to lock
- assertFalse(mgr.refresh(RESOURCE_A, OWNER2, MAX_AGE_SEC));
-
- // impl1 sees it, but impl2 does not
- verify(impl1).afterRefresh(RESOURCE_A, OWNER2, false);
- verify(impl2, never()).afterRefresh(RESOURCE_A, OWNER2, false);
- }
-
- @Test
- public void testRefresh_Denied() {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // owner2 tries to lock
- mgr.refresh(RESOURCE_A, OWNER2, MAX_AGE_SEC);
-
- verify(impl1).afterRefresh(RESOURCE_A, OWNER2, false);
- verify(impl2).afterRefresh(RESOURCE_A, OWNER2, false);
- }
-
- @Test
- public void testUnlock() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- mgr.lock(RESOURCE_B, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2).beforeUnlock(RESOURCE_A, OWNER1);
-
- verify(impl1).afterUnlock(RESOURCE_A, OWNER1, true);
- verify(impl2).afterUnlock(RESOURCE_A, OWNER1, true);
- }
-
- @Test
- public void testUnlock_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.unlock(null, OWNER1)).withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.unlock(RESOURCE_A, null)).withMessage(NULL_OWNER);
- }
-
- @Test
- public void testUnlock_BeforeInterceptedTrue() {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // have impl1 intercept
- when(impl1.beforeUnlock(RESOURCE_A, OWNER1)).thenReturn(OperResult.OPER_ACCEPTED);
-
- assertTrue(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2, never()).beforeUnlock(anyString(), anyString());
-
- verify(impl1, never()).afterUnlock(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterUnlock(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testUnlock_BeforeInterceptedFalse() {
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // have impl1 intercept
- when(impl1.beforeUnlock(RESOURCE_A, OWNER1)).thenReturn(OperResult.OPER_DENIED);
-
- assertFalse(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2, never()).beforeUnlock(anyString(), anyString());
-
- verify(impl1, never()).afterUnlock(anyString(), anyString(), anyBoolean());
- verify(impl2, never()).afterUnlock(anyString(), anyString(), anyBoolean());
- }
-
- @Test
- public void testUnlock_Unlocked() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2).beforeUnlock(RESOURCE_A, OWNER1);
-
- verify(impl1).afterUnlock(RESOURCE_A, OWNER1, true);
- verify(impl2).afterUnlock(RESOURCE_A, OWNER1, true);
- }
-
- @Test
- public void testUnlock_Unlocked_AfterIntercepted() {
- // have impl1 intercept
- when(impl1.afterUnlock(RESOURCE_A, OWNER1, true)).thenReturn(true);
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2).beforeUnlock(RESOURCE_A, OWNER1);
-
- verify(impl1).afterUnlock(RESOURCE_A, OWNER1, true);
- verify(impl2, never()).afterUnlock(RESOURCE_A, OWNER1, true);
- }
-
- @Test
- public void testUnlock_NotUnlocked() {
- assertFalse(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2).beforeUnlock(RESOURCE_A, OWNER1);
-
- verify(impl1).afterUnlock(RESOURCE_A, OWNER1, false);
- verify(impl2).afterUnlock(RESOURCE_A, OWNER1, false);
- }
-
- @Test
- public void testUnlock_NotUnlocked_AfterIntercepted() {
- // have impl1 intercept
- when(impl1.afterUnlock(RESOURCE_A, OWNER1, false)).thenReturn(true);
-
- assertFalse(mgr.unlock(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeUnlock(RESOURCE_A, OWNER1);
- verify(impl2).beforeUnlock(RESOURCE_A, OWNER1);
-
- verify(impl1).afterUnlock(RESOURCE_A, OWNER1, false);
- verify(impl2, never()).afterUnlock(RESOURCE_A, OWNER1, false);
- }
-
- @Test
- public void testIsLocked_True() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2).beforeIsLocked(RESOURCE_A);
- }
-
- @Test
- public void testIsLocked_False() {
- assertFalse(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2).beforeIsLocked(RESOURCE_A);
- }
-
- @Test
- public void testIsLocked_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLocked(null)).withMessage(NULL_RESOURCE_ID);
- }
-
- @Test
- public void testIsLocked_BeforeIntercepted_True() {
-
- // have impl1 intercept
- when(impl1.beforeIsLocked(RESOURCE_A)).thenReturn(OperResult.OPER_ACCEPTED);;
-
- assertTrue(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2, never()).beforeIsLocked(RESOURCE_A);
- }
-
- @Test
- public void testIsLocked_BeforeIntercepted_False() {
-
- // lock it so we can verify that impl1 overrides the superclass isLocker()
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // have impl1 intercept
- when(impl1.beforeIsLocked(RESOURCE_A)).thenReturn(OperResult.OPER_DENIED);
-
- assertFalse(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2, never()).beforeIsLocked(RESOURCE_A);
- }
-
- @Test
- public void testIsLockedBy_True() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeIsLockedBy(RESOURCE_A, OWNER1);
- verify(impl2).beforeIsLockedBy(RESOURCE_A, OWNER1);
- }
-
- @Test
- public void testIsLockedBy_False() {
- // different owner
- mgr.lock(RESOURCE_A, OWNER2, MAX_AGE_SEC);
-
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeIsLockedBy(RESOURCE_A, OWNER1);
- verify(impl2).beforeIsLockedBy(RESOURCE_A, OWNER1);
- }
-
- @Test
- public void testIsLockedBy_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLockedBy(null, OWNER1))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLockedBy(RESOURCE_A, null)).withMessage(NULL_OWNER);
- }
-
- @Test
- public void testIsLockedBy_BeforeIntercepted_True() {
-
- // have impl1 intercept
- when(impl1.beforeIsLockedBy(RESOURCE_A, OWNER1)).thenReturn(OperResult.OPER_ACCEPTED);;
-
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeIsLockedBy(RESOURCE_A, OWNER1);
- verify(impl2, never()).beforeIsLockedBy(RESOURCE_A, OWNER1);
- }
-
- @Test
- public void testIsLockedBy_BeforeIntercepted_False() {
-
- // lock it so we can verify that impl1 overrides the superclass isLocker()
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // have impl1 intercept
- when(impl1.beforeIsLockedBy(RESOURCE_A, OWNER1)).thenReturn(OperResult.OPER_DENIED);
-
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- verify(impl1).beforeIsLockedBy(RESOURCE_A, OWNER1);
- verify(impl2, never()).beforeIsLockedBy(RESOURCE_A, OWNER1);
- }
-
- @Test
- public void testGetInstance() {
- PolicyResourceLockManager inst = PolicyResourceLockManager.getInstance();
- assertNotNull(inst);
-
- // should return the same instance each time
- assertEquals(inst, PolicyResourceLockManager.getInstance());
- assertEquals(inst, PolicyResourceLockManager.getInstance());
- }
-
- @Test
- public void testDoIntercept_Empty() {
- // clear the implementer list
- implList.clear();
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertFalse(mgr.isLocked(RESOURCE_B));
-
- verify(impl1, never()).beforeIsLocked(anyString());
- }
-
- @Test
- public void testDoIntercept_Impl1() {
- when(impl1.beforeIsLocked(RESOURCE_A)).thenReturn(OperResult.OPER_ACCEPTED);;
-
- assertTrue(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2, never()).beforeIsLocked(anyString());
- }
-
- @Test
- public void testDoIntercept_Impl2() {
- when(impl2.beforeIsLocked(RESOURCE_A)).thenReturn(OperResult.OPER_ACCEPTED);;
-
- assertTrue(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2).beforeIsLocked(RESOURCE_A);
- }
-
- @Test
- public void testDoIntercept_Ex() {
- doThrow(new RuntimeException("expected exception")).when(impl1).beforeIsLocked(RESOURCE_A);
-
- assertFalse(mgr.isLocked(RESOURCE_A));
-
- verify(impl1).beforeIsLocked(RESOURCE_A);
- verify(impl2).beforeIsLocked(RESOURCE_A);
- }
-}
diff --git a/policy-core/src/test/java/org/onap/policy/drools/core/lock/SimpleLockManagerTest.java b/policy-core/src/test/java/org/onap/policy/drools/core/lock/SimpleLockManagerTest.java
deleted file mode 100644
index 51cf68fc..00000000
--- a/policy-core/src/test/java/org/onap/policy/drools/core/lock/SimpleLockManagerTest.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * ONAP
- * ================================================================================
- * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.policy.drools.core.lock;
-
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.util.LinkedList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.onap.policy.common.utils.time.CurrentTime;
-import org.onap.policy.common.utils.time.TestTime;
-import org.onap.policy.drools.core.lock.SimpleLockManager.Data;
-import org.powermock.reflect.Whitebox;
-
-public class SimpleLockManagerTest {
-
- // Note: this must be a multiple of four
- private static final int MAX_AGE_SEC = 4 * 60;
- private static final int MAX_AGE_MS = MAX_AGE_SEC * 1000;
-
- private static final String EXPECTED_EXCEPTION = "expected exception";
-
- private static final String NULL_RESOURCE_ID = "null resourceId";
- private static final String NULL_OWNER = "null owner";
-
- private static final String RESOURCE_A = "resource.a";
- private static final String RESOURCE_B = "resource.b";
- private static final String RESOURCE_C = "resource.c";
- private static final String RESOURCE_D = "resource.d";
-
- private static final String OWNER1 = "owner.one";
- private static final String OWNER2 = "owner.two";
- private static final String OWNER3 = "owner.three";
-
- /**
- * Name of the "current time" field within the {@link SimpleLockManager} class.
- */
- private static final String TIME_FIELD = "currentTime";
-
- private static CurrentTime savedTime;
-
- private TestTime testTime;
- private SimpleLockManager mgr;
-
- @BeforeClass
- public static void setUpBeforeClass() {
- savedTime = Whitebox.getInternalState(SimpleLockManager.class, TIME_FIELD);
- }
-
- @AfterClass
- public static void tearDownAfterClass() {
- Whitebox.setInternalState(SimpleLockManager.class, TIME_FIELD, savedTime);
- }
-
- /**
- * Set up.
- */
- @Before
- public void setUp() {
- testTime = new TestTime();
- Whitebox.setInternalState(SimpleLockManager.class, TIME_FIELD, testTime);
-
- mgr = new SimpleLockManager();
- }
-
- @Test
- public void testCurrentTime() {
- assertNotNull(savedTime);
- }
-
- @Test
- public void testLock() throws Exception {
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
-
- // different owner and resource - should succeed
- assertTrue(mgr.lock(RESOURCE_C, OWNER3, MAX_AGE_SEC));
-
- // different owner - already locked
- assertFalse(mgr.lock(RESOURCE_A, OWNER3, MAX_AGE_SEC));
- }
-
- @Test
- public void testLock_ExtendLock() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // sleep half of the cycle
- testTime.sleep(MAX_AGE_MS / 2);
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- // extend the lock
- mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // verify still locked after sleeping the other half of the cycle
- testTime.sleep(MAX_AGE_MS / 2 + 1);
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- // and should release after another half cycle
- testTime.sleep(MAX_AGE_MS / 2);
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
- }
-
- @Test
- public void testLock_AlreadyLocked() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // same owner
- assertFalse(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // different owner
- assertFalse(mgr.lock(RESOURCE_A, OWNER2, MAX_AGE_SEC));
- }
-
- @Test
- public void testLock_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.lock(null, OWNER1, MAX_AGE_SEC))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.lock(RESOURCE_A, null, MAX_AGE_SEC))
- .withMessage(NULL_OWNER);
-
- // this should not throw an exception
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- }
-
- @Test
- public void testRefresh() throws Exception {
- // don't own the lock yet - cannot refresh
- assertFalse(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- assertTrue(mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // now the lock is owned
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // refresh again
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC + 1));
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
-
- // different owner
- assertFalse(mgr.refresh(RESOURCE_A, OWNER3, MAX_AGE_SEC));
-
- // different resource
- assertFalse(mgr.refresh(RESOURCE_C, OWNER1, MAX_AGE_SEC));
- }
-
- @Test
- public void testRefresh_ExtendLock() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // sleep half of the cycle
- testTime.sleep(MAX_AGE_MS / 2);
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- // extend the lock
- mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // verify still locked after sleeping the other half of the cycle
- testTime.sleep(MAX_AGE_MS / 2 + 1);
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- // and should release after another half cycle
- testTime.sleep(MAX_AGE_MS / 2);
-
- // cannot refresh expired lock
- assertFalse(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
- }
-
- @Test
- public void testRefresh_AlreadyLocked() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // same owner
- assertTrue(mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC));
-
- // different owner
- assertFalse(mgr.refresh(RESOURCE_A, OWNER2, MAX_AGE_SEC));
- assertFalse(mgr.lock(RESOURCE_A, OWNER2, MAX_AGE_SEC));
- }
-
- @Test
- public void testRefresh_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.refresh(null, OWNER1, MAX_AGE_SEC))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.refresh(RESOURCE_A, null, MAX_AGE_SEC))
- .withMessage(NULL_OWNER);
-
- // this should not throw an exception
- mgr.refresh(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- }
-
- @Test
- public void testUnlock() throws Exception {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // unlock it
- assertTrue(mgr.unlock(RESOURCE_A, OWNER1));
- }
-
- @Test
- public void testUnlock_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.unlock(null, OWNER1)).withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.unlock(RESOURCE_A, null)).withMessage(NULL_OWNER);
- }
-
- @Test
- public void testUnlock_NotLocked() {
- assertFalse(mgr.unlock(RESOURCE_A, OWNER1));
- }
-
- @Test
- public void testUnlock_DiffOwner() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- assertFalse(mgr.unlock(RESOURCE_A, OWNER2));
- }
-
- @Test
- public void testIsLocked() {
- assertFalse(mgr.isLocked(RESOURCE_A));
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- mgr.lock(RESOURCE_B, OWNER1, MAX_AGE_SEC);
-
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLocked(RESOURCE_C));
-
- // unlock from first resource
- mgr.unlock(RESOURCE_A, OWNER1);
- assertFalse(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLocked(RESOURCE_C));
-
- // unlock from second resource
- mgr.unlock(RESOURCE_B, OWNER1);
- assertFalse(mgr.isLocked(RESOURCE_A));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertFalse(mgr.isLocked(RESOURCE_C));
- }
-
- @Test
- public void testIsLocked_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLocked(null)).withMessage(NULL_RESOURCE_ID);
- }
-
- @Test
- public void testIsLockedBy() {
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
-
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- assertFalse(mgr.isLockedBy(RESOURCE_B, OWNER1));
-
- assertTrue(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
-
- // unlock from the resource
- mgr.unlock(RESOURCE_A, OWNER1);
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER1));
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
- assertFalse(mgr.isLockedBy(RESOURCE_B, OWNER1));
- }
-
- @Test
- public void testIsLockedBy_ArgEx() {
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLockedBy(null, OWNER1))
- .withMessage(NULL_RESOURCE_ID);
-
- assertThatIllegalArgumentException().isThrownBy(() -> mgr.isLockedBy(RESOURCE_A, null)).withMessage(NULL_OWNER);
- }
-
- @Test
- public void testIsLockedBy_NotLocked() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // different resource, thus no lock
- assertFalse(mgr.isLockedBy(RESOURCE_B, OWNER1));
- }
-
- @Test
- public void testIsLockedBy_LockedButNotOwner() {
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
-
- // different owner
- assertFalse(mgr.isLockedBy(RESOURCE_A, OWNER2));
- }
-
- @Test
- public void testCleanUpLocks() throws Exception {
- // note: this assumes that MAX_AGE_MS is divisible by 4
- mgr.lock(RESOURCE_A, OWNER1, MAX_AGE_SEC);
- assertTrue(mgr.isLocked(RESOURCE_A));
-
- testTime.sleep(10);
- mgr.lock(RESOURCE_B, OWNER1, MAX_AGE_SEC);
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLocked(RESOURCE_B));
-
- testTime.sleep(MAX_AGE_MS / 4);
- mgr.lock(RESOURCE_C, OWNER1, MAX_AGE_SEC);
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLocked(RESOURCE_B));
- assertTrue(mgr.isLocked(RESOURCE_C));
-
- testTime.sleep(MAX_AGE_MS / 4);
- mgr.lock(RESOURCE_D, OWNER1, MAX_AGE_SEC);
- assertTrue(mgr.isLocked(RESOURCE_A));
- assertTrue(mgr.isLocked(RESOURCE_B));
- assertTrue(mgr.isLocked(RESOURCE_C));
- assertTrue(mgr.isLocked(RESOURCE_D));
-
- // sleep remainder of max age - first two should expire
- testTime.sleep(MAX_AGE_MS / 2);
- assertFalse(mgr.isLocked(RESOURCE_A));
- assertFalse(mgr.isLocked(RESOURCE_B));
- assertTrue(mgr.isLocked(RESOURCE_C));
- assertTrue(mgr.isLocked(RESOURCE_D));
-
- // another quarter - next one should expire
- testTime.sleep(MAX_AGE_MS / 4);
- assertFalse(mgr.isLocked(RESOURCE_C));
- assertTrue(mgr.isLocked(RESOURCE_D));
-
- // another quarter - last one should expire
- testTime.sleep(MAX_AGE_MS / 4);
- assertFalse(mgr.isLocked(RESOURCE_D));
- }
-
- @Test
- public void testMakeNullArgException() {
- IllegalArgumentException ex = SimpleLockManager.makeNullArgException(EXPECTED_EXCEPTION);
- assertEquals(EXPECTED_EXCEPTION, ex.getMessage());
- }
-
- @Test
- public void testDataGetXxx() {
- long ttime = System.currentTimeMillis() + 5;
- Data data = new Data(OWNER1, RESOURCE_A, ttime);
-
- assertEquals(OWNER1, data.getOwner());
- assertEquals(RESOURCE_A, data.getResource());
- assertEquals(ttime, data.getExpirationMs());
- }
-
- @Test
- public void testDataCompareTo() {
- long ttime = System.currentTimeMillis() + 50;
- Data data = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataSame = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataDiffExpire = new Data(OWNER1, RESOURCE_A, ttime + 1);
-
- assertEquals(0, data.compareTo(data));
- assertEquals(0, data.compareTo(dataSame));
-
- assertTrue(data.compareTo(dataDiffExpire) < 0);
- assertTrue(dataDiffExpire.compareTo(data) > 0);
-
- Data dataDiffOwner = new Data(OWNER2, RESOURCE_A, ttime);
- Data dataDiffResource = new Data(OWNER1, RESOURCE_B, ttime);
-
- assertTrue(data.compareTo(dataDiffOwner) < 0);
- assertTrue(dataDiffOwner.compareTo(data) > 0);
-
- assertTrue(data.compareTo(dataDiffResource) < 0);
- assertTrue(dataDiffResource.compareTo(data) > 0);
- }
-
- @Test
- public void testDataHashCode() {
- long ttime = System.currentTimeMillis() + 1;
- Data data = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataSame = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataDiffExpire = new Data(OWNER1, RESOURCE_A, ttime + 1);
- Data dataDiffOwner = new Data(OWNER2, RESOURCE_A, ttime);
-
- int hc1 = data.hashCode();
- assertEquals(hc1, dataSame.hashCode());
-
- assertTrue(hc1 != dataDiffExpire.hashCode());
- assertTrue(hc1 != dataDiffOwner.hashCode());
-
- Data dataDiffResource = new Data(OWNER1, RESOURCE_B, ttime);
- Data dataNullOwner = new Data(null, RESOURCE_A, ttime);
- Data dataNullResource = new Data(OWNER1, null, ttime);
-
- assertTrue(hc1 != dataDiffResource.hashCode());
- assertTrue(hc1 != dataNullOwner.hashCode());
- assertTrue(hc1 != dataNullResource.hashCode());
- }
-
- @Test
- public void testDataEquals() {
- long ttime = System.currentTimeMillis() + 50;
- Data data = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataSame = new Data(OWNER1, RESOURCE_A, ttime);
- Data dataDiffExpire = new Data(OWNER1, RESOURCE_A, ttime + 1);
-
- assertTrue(data.equals(data));
- assertTrue(data.equals(dataSame));
-
- Data dataDiffOwner = new Data(OWNER2, RESOURCE_A, ttime);
- Data dataDiffResource = new Data(OWNER1, RESOURCE_B, ttime);
-
- assertFalse(data.equals(dataDiffExpire));
- assertFalse(data.equals(dataDiffOwner));
- assertFalse(data.equals(dataDiffResource));
-
- assertFalse(data.equals(null));
- assertFalse(data.equals("string"));
-
- Data dataNullOwner = new Data(null, RESOURCE_A, ttime);
- Data dataNullResource = new Data(OWNER1, null, ttime);
-
- assertFalse(dataNullOwner.equals(data));
- assertFalse(dataNullResource.equals(data));
-
- assertTrue(dataNullOwner.equals(new Data(null, RESOURCE_A, ttime)));
- assertTrue(dataNullResource.equals(new Data(OWNER1, null, ttime)));
- }
-
- @Test
- public void testMultiThreaded() throws InterruptedException {
- int nthreads = 10;
- int nlocks = 100;
-
- LinkedList<Thread> threads = new LinkedList<>();
-
- String[] resources = {RESOURCE_A, RESOURCE_B};
-
- final AtomicInteger nfail = new AtomicInteger(0);
-
- CountDownLatch stopper = new CountDownLatch(1);
- CountDownLatch completed = new CountDownLatch(nthreads);
-
- for (int x = 0; x < nthreads; ++x) {
- String owner = "owner." + x;
-
- Thread thread = new Thread(() -> {
-
- for (int y = 0; y < nlocks; ++y) {
- String res = resources[y % resources.length];
-
- try {
- // some locks will be acquired, some denied
- mgr.lock(res, owner, MAX_AGE_SEC);
-
- // do some "work"
- stopper.await(1L, TimeUnit.MILLISECONDS);
-
- mgr.unlock(res, owner);
-
- } catch (InterruptedException expected) {
- Thread.currentThread().interrupt();
- break;
- }
- }
-
- completed.countDown();
- });
-
- thread.setDaemon(true);
- threads.add(thread);
- }
-
- // start the threads
- for (Thread t : threads) {
- t.start();
- }
-
- // wait for them to complete
- completed.await(5000L, TimeUnit.SECONDS);
-
- // stop the threads from sleeping
- stopper.countDown();
-
- completed.await(1L, TimeUnit.SECONDS);
-
- // interrupt those that are still alive
- for (Thread t : threads) {
- if (t.isAlive()) {
- t.interrupt();
- }
- }
-
- assertEquals(0, nfail.get());
- }
-
-}