summaryrefslogtreecommitdiffstats
path: root/policy-core
diff options
context:
space:
mode:
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());
- }
-
-}