diff options
Diffstat (limited to 'policy-core/src')
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()); - } - -} |