From 6e0b450abe7e62fa47ffe14e95a67d035174dbdb Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Tue, 24 Sep 2019 10:51:21 -0400 Subject: Reimplement Lock API using Lock objects Modified PolicyResourceLockManager to just return a feature, deferring the lock() call/method to the feature, itself. The manager was also modified so that, if it can't find an enabled provider, it will return a default provider, whose lock() methods always fail. Once a feature has been identified, the manager will cache it for use thereafter. Modified the feature API to return lock objects and simplified the interface to remove the beforeXxx and afterXxx methods. Moved the unlock and refresh methods from the feature API into the lock class, renaming them to free and extend, respectively. Added a separate, feature-simple-locking project, which implements a simple version of the locking feature, over a single JVM. Extensively revised the distributed locking feature to fit in with the new API. Added support for persistence so that the various LockImpl classes can be serialized and still function correctly when they are deserialized back into new feature instances Added default implementations of free & extend to LockImpl. Modified API to take the ownerKey string, instead of the owner object. Removed Extractor as unneeded - may add via another review, if still useful. Updates per review comments: - Updated licenses in feature-simple-locking - Added beforeCreateLock & afterCreateLock to feature API - Moved SimpleLockingFeature into policy-management so that it's always available - Moved the executor service, "exsvc", into PolicyEngine - Moved Extrator into policy-utils - Changed Extractor logging level for exceptions - Fixed feature sequence numbers - Fixed mixing of seconds and milliseconds - Renamed exsvc - Modified to use property method with default value - Configured scheduled executor - Added suffix to Extractor.register() - Eliminated Feature Api and tied lock manager into engine - Added non-null checks to LockImpl parameters - Added non-null checks to createLock() parameters - Checked that lockManager is initialized Change-Id: Iddba38157ddc5f7277656979c0e679e5489eb7b1 Issue-ID: POLICY-2113 Signed-off-by: Jim Hahn --- .../drools/core/lock/AlwaysFailLockTest.java | 106 ++++ .../onap/policy/drools/core/lock/LockImplTest.java | 261 +++++++++ .../org/onap/policy/drools/core/lock/LockTest.java | 134 ----- .../lock/PolicyResourceLockFeatureApiTest.java | 92 ---- .../core/lock/PolicyResourceLockManagerTest.java | 595 --------------------- .../drools/core/lock/SimpleLockManagerTest.java | 527 ------------------ 6 files changed, 367 insertions(+), 1348 deletions(-) create mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/AlwaysFailLockTest.java create mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/LockImplTest.java delete mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/LockTest.java delete mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockFeatureApiTest.java delete mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/PolicyResourceLockManagerTest.java delete mode 100644 policy-core/src/test/java/org/onap/policy/drools/core/lock/SimpleLockManagerTest.java (limited to 'policy-core/src/test') 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 lock; - private Pair 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 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 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 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()); - } - -} -- cgit 1.2.3-korg