summaryrefslogtreecommitdiffstats
path: root/policy-core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'policy-core/src/main')
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/AlwaysFailLock.java71
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/Lock.java164
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockCallback.java (renamed from policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiConstants.java)31
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockImpl.java158
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/LockState.java43
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApi.java168
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/PolicyResourceLockManager.java227
-rw-r--r--policy-core/src/main/java/org/onap/policy/drools/core/lock/SimpleLockManager.java384
8 files changed, 358 insertions, 888 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);
- }
- }
-}