[jboss-cvs] JBossAS SVN: r104329 - in projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha: framework/server/lock and 1 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Thu Apr 29 15:51:15 EDT 2010
Author: bstansberry at jboss.com
Date: 2010-04-29 15:51:15 -0400 (Thu, 29 Apr 2010)
New Revision: 104329
Added:
projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ClusteredLockManagerTestBase.java
projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ReadWriteClusteredLockManagerUnitTestCase.java
projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupportUnitTestCase.java
projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/util/
projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/util/MockClusterNode.java
Log:
[JBCLUSTER-223] Add tests
Copied: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ClusteredLockManagerTestBase.java (from rev 104321, trunk/testsuite/src/main/org/jboss/test/cluster/lock/ClusteredLockManagerTestBase.java)
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ClusteredLockManagerTestBase.java (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ClusteredLockManagerTestBase.java 2010-04-29 19:51:15 UTC (rev 104329)
@@ -0,0 +1,983 @@
+package org.jboss.test.ha.framework.server.lock;
+
+import static org.easymock.EasyMock.and;
+import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.makeThreadSafe;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.resetToNice;
+import static org.easymock.EasyMock.resetToStrict;
+import static org.easymock.EasyMock.same;
+import static org.easymock.EasyMock.verify;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.easymock.IArgumentMatcher;
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.ha.framework.interfaces.ResponseFilter;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport;
+import org.jboss.ha.framework.server.lock.LocalLockHandler;
+import org.jboss.ha.framework.server.lock.RemoteLockResponse;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport.RpcTarget;
+import org.jboss.test.ha.util.MockClusterNode;
+
+public abstract class ClusteredLockManagerTestBase<T extends AbstractClusterLockSupport> extends TestCase
+{
+ protected static final ResponseFilter NULL_FILTER = null;
+
+ protected ClusterNode node1;
+ protected ClusterNode node2;
+ protected ClusterNode node3;
+
+ public static Object[] eqLockParams(ClusterNode node, long timeout)
+ {
+ EasyMock.reportMatcher(new LockParamsMatcher(node, timeout));
+ return null;
+ }
+
+ protected static class LockParamsMatcher implements IArgumentMatcher
+ {
+ private final ClusterNode node;
+ private final long timeout;
+
+ LockParamsMatcher(ClusterNode node, long timeout)
+ {
+ this.node = node;
+ this.timeout = timeout;
+ }
+
+ public void appendTo(StringBuffer buffer)
+ {
+ buffer.append("eqRemoteLockParams({\"test\",");
+ buffer.append(node);
+ buffer.append(',');
+ buffer.append(timeout);
+ buffer.append("})");
+ }
+
+ public boolean matches(Object arg)
+ {
+ if (arg instanceof Object[])
+ {
+ Object[] args = (Object[]) arg;
+ if (args.length == 3)
+ {
+ if ("test".equals(args[0]) && node.equals(args[1])
+ && args[2] instanceof Long)
+ {
+ long l = ((Long) args[2]).longValue();
+ return l >= 0 && l <= timeout;
+ }
+ }
+ }
+ return false;
+ }
+
+ }
+
+ public ClusteredLockManagerTestBase()
+ {
+ super();
+ }
+
+ public ClusteredLockManagerTestBase(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ node1 = new MockClusterNode(1);
+ node2 = new MockClusterNode(2);
+ node3 = new MockClusterNode(3);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ }
+
+ public void testConstructor() throws Exception
+ {
+ HAPartition haPartition = createNiceMock(HAPartition.class);
+ LocalLockHandler handler = createNiceMock(LocalLockHandler.class);
+
+ try
+ {
+ createClusteredLockManager(null, haPartition, handler);
+ fail("Null serviceHAName should prevent construction");
+ }
+ catch (IllegalArgumentException good) {}
+
+ try
+ {
+ createClusteredLockManager("test", null, handler);
+ fail("Null HAPartition should prevent construction");
+ }
+ catch (IllegalArgumentException good) {}
+
+ try
+ {
+ createClusteredLockManager("test", haPartition, null);
+ fail("Null LocalLockHandler should prevent construction");
+ }
+ catch (IllegalArgumentException good) {}
+
+ expect(haPartition.getClusterNode()).andReturn(node1);
+ expect(haPartition.getPartitionName()).andReturn("TestPartition");
+
+ replay(haPartition);
+ replay(handler);
+
+ T testee = createClusteredLockManager("test", haPartition, handler);
+
+ assertEquals("test", testee.getServiceHAName());
+ assertEquals("TestPartition", testee.getPartitionName());
+ }
+
+ public void testStart() throws Exception
+ {
+ HAPartition haPartition = createNiceMock(HAPartition.class);
+ LocalLockHandler handler = createNiceMock(LocalLockHandler.class);
+ expect(haPartition.getClusterNode()).andReturn(node1);
+ expect(haPartition.getPartitionName()).andReturn("TestPartition");
+
+ replay(haPartition);
+ replay(handler);
+
+ T testee = createClusteredLockManager("test", haPartition, handler);
+
+ try
+ {
+ testee.lock("id", 1000);
+ fail("Call to lock() should fail if not started");
+ }
+ catch (IllegalStateException good) {}
+
+ try
+ {
+ testee.unlock("id");
+ fail("Call to unlock() should fail if not started");
+ }
+ catch (IllegalStateException good) {}
+
+ reset(haPartition);
+
+ assertEquals("Current view is empty when unstarted", 0, testee.getCurrentView().size());
+
+ haPartition.registerRPCHandler(eq("test"), isA(RpcTarget.class));
+ haPartition.registerMembershipListener(testee);
+ expect(haPartition.getClusterNodes()).andReturn(new ClusterNode[]{node1});
+ replay(haPartition);
+
+ testee.start();
+
+ verify(haPartition);
+
+ assertEquals("Current view is correct", 1, testee.getCurrentView().size());
+ assertTrue(testee.getCurrentView().contains(node1));
+
+ }
+
+ public void testStop() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 0, 1);
+ T testee = testeeSet.impl;
+ HAPartition haPartition = testee.getPartition();
+
+ reset(haPartition);
+
+ haPartition.unregisterMembershipListener(testee);
+ haPartition.unregisterRPCHandler(eq("test"), same(testeeSet.target));
+
+ replay(haPartition);
+
+ testee.stop();
+
+ verify(haPartition);
+
+ assertEquals("Current view is empty when stopped", 0, testee.getCurrentView().size());
+
+ try
+ {
+ testee.lock("id", 1000);
+ fail("Call to lock() should fail if stopped");
+ }
+ catch (IllegalStateException good) {}
+
+ try
+ {
+ testee.unlock("id");
+ fail("Call to unlock() should fail if stopped");
+ }
+ catch (IllegalStateException good) {}
+
+ }
+
+ public void testGetMembers() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 2);
+ T testee = testeeSet.impl;
+
+ List<ClusterNode> members = testee.getCurrentView();
+ assertEquals(2, members.size());
+ assertEquals(node1, members.get(1));
+
+ ClusterNode dead = members.get(0);
+ assertFalse(node1.equals(dead));
+
+ Vector<ClusterNode> newView = getView(node1, 0, 3);
+ newView.remove(dead);
+
+ Vector<ClusterNode> addedMembers = new Vector<ClusterNode>(newView);
+ addedMembers.removeAll(members);
+
+ Vector<ClusterNode> deadMembers = new Vector<ClusterNode>();
+ deadMembers.add(dead);
+
+ testee.membershipChanged(deadMembers, addedMembers, newView);
+
+ members = testee.getCurrentView();
+ assertEquals(2, members.size());
+ assertEquals(node1, members.get(0));
+ assertFalse(node1.equals(members.get(1)));
+ assertFalse(members.contains(dead));
+
+ }
+
+ /**
+ * Simple test of acquiring a cluster-wide lock in a two node cluster
+ * where local-only locks are supported.
+ *
+ * @throws Exception
+ */
+ public void testBasicClusterLock() throws Exception
+ {
+ basicClusterLockTest(2);
+ }
+
+ /**
+ * Simple test of acquiring a cluster-wide lock in a cluster where the
+ * caller is the only member and where local-only locks are supported.
+ *
+ * @throws Exception
+ */
+ public void testStandaloneClusterLock() throws Exception
+ {
+ basicClusterLockTest(1);
+ }
+
+ private void basicClusterLockTest(int viewSize) throws Exception
+ {
+ int viewPos = viewSize == 1 ? 0 : 1;
+ TesteeSet<T> testeeSet = getTesteeSet(node1, viewPos, viewSize);
+ AbstractClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+
+ resetToStrict(partition);
+ resetToStrict(handler);
+
+ List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ for (int i = 0; i < viewSize - 1; i++)
+ {
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ }
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+
+ replay(partition);
+ replay(handler);
+
+ assertTrue(testee.lock("test", 200000));
+
+ verify(partition);
+ verify(handler);
+
+ }
+
+ public void testRemoteRejectionFromSuperiorCaller() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 3);
+ AbstractClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+
+ resetToNice(partition);
+ resetToStrict(handler);
+
+ ClusterNode superior = testee.getCurrentView().get(0);
+
+ List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.REJECT, superior));
+
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList).atLeastOnce();
+
+ replay(partition);
+ replay(handler);
+
+ assertFalse(testee.lock("test", 50));
+
+ verify(partition);
+ verify(handler);
+
+ }
+
+ public void testRemoteRejectionFromInferiorCaller() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 3);
+ AbstractClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+
+ resetToStrict(partition);
+ resetToStrict(handler);
+
+ ClusterNode inferior = testee.getCurrentView().get(2);
+
+ List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.REJECT, inferior));
+
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+
+ expect((List<Object>) partition.callMethodOnCluster(eq("test"),
+ eq("releaseRemoteLock"),
+ aryEq(new Object[]{"test", node1}),
+ aryEq(AbstractClusterLockSupport.RELEASE_REMOTE_LOCK_TYPES),
+ eq(true))).andReturn(new ArrayList<Object>());
+
+ rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+ expectLastCall().atLeastOnce();
+
+ replay(partition);
+ replay(handler);
+
+ assertTrue(testee.lock("test", 50));
+
+ verify(partition);
+ verify(handler);
+
+ }
+
+ public void testLocalLockingStateRejectsSuperiorRemoteCaller() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 3);
+ T testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+ final RpcTarget target = testeeSet.target;
+
+ ClusterNode superiorCaller = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(superiorCaller));
+
+ resetToStrict(partition);
+ makeThreadSafe(partition, true);
+ resetToStrict(handler);
+ makeThreadSafe(handler, true);
+
+ List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+ // When caller 1 invokes, block before giving response
+ CountDownLatch answerAwaitLatch = new CountDownLatch(1);
+ CountDownLatch answerStartLatch = new CountDownLatch(1);
+ CountDownLatch answerDoneLatch = new CountDownLatch(1);
+ BlockingAnswer<Boolean> caller1Answer = new BlockingAnswer<Boolean>(Boolean.TRUE, answerAwaitLatch, answerStartLatch, null);
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+ expectLastCall().andAnswer(caller1Answer);
+
+ replay(partition);
+ replay(handler);
+
+ LocalLockCaller winner = new LocalLockCaller(testee, null, null, answerDoneLatch, 500);
+
+ Thread t1 = new Thread(winner);
+ t1.setDaemon(true);
+
+ try
+ {
+ t1.start();
+ assertTrue(answerStartLatch.await(500, TimeUnit.SECONDS));
+ // t1 should now be blocking in caller1Answer
+
+ RemoteLockResponse rsp = target.remoteLock("test", superiorCaller, 1);
+ assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
+ assertEquals(node1, rsp.holder);
+
+ // release t1
+ answerAwaitLatch.countDown();
+
+ // wait for t1 to complete
+ assertTrue(answerDoneLatch.await(5, TimeUnit.SECONDS));
+
+ verify(handler);
+
+ rethrow("winner had an exception", winner.getException());
+
+ Boolean locked = winner.getResult();
+ assertEquals(Boolean.TRUE, locked);
+ }
+ finally
+ {
+ if (t1.isAlive())
+ t1.interrupt();
+ }
+ }
+
+ public void testRemoteLockingStateAllowsSuperiorRemoteCaller() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 3);
+ T testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+ final RpcTarget target = testeeSet.target;
+
+ ClusterNode superiorCaller = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(superiorCaller));
+
+ resetToNice(partition); // nice as we may loop retrying and failing
+ makeThreadSafe(partition, true);
+ resetToStrict(handler);
+ makeThreadSafe(handler, true);
+
+ // When caller 1 invokes, block before giving response
+ CountDownLatch answerAwaitLatch = new CountDownLatch(1);
+ CountDownLatch answerStartLatch = new CountDownLatch(1);
+
+ ArrayList<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.REJECT, superiorCaller));
+
+ BlockingAnswer<List<RemoteLockResponse>> caller1Answer = new BlockingAnswer<List<RemoteLockResponse>>(rspList, answerAwaitLatch, answerStartLatch, null);
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andAnswer(caller1Answer).atLeastOnce();
+
+ handler.lockFromCluster(eq("test"), eq(superiorCaller), anyLong());
+
+ expect((List<Object>) partition.callMethodOnCluster(eq("test"),
+ eq("releaseRemoteLock"),
+ aryEq(new Object[]{"test", node1}),
+ aryEq(AbstractClusterLockSupport.RELEASE_REMOTE_LOCK_TYPES),
+ eq(true))).andReturn(new ArrayList<Object>()).atLeastOnce();
+
+ replay(partition);
+ replay(handler);
+
+ CountDownLatch finishedLatch = new CountDownLatch(1);
+ LocalLockCaller loser = new LocalLockCaller(testee, null, null, finishedLatch);
+
+ Thread t1 = new Thread(loser);
+ t1.setDaemon(true);
+
+ try
+ {
+ t1.start();
+ assertTrue(answerStartLatch.await(1, TimeUnit.SECONDS));
+ // t1 should now be blocking in caller1Answer
+
+ RemoteLockResponse rsp = target.remoteLock("test", superiorCaller, 1);
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+
+ // release t1
+ answerAwaitLatch.countDown();
+
+ // wait for t1 to complete
+ assertTrue(finishedLatch.await(5, TimeUnit.SECONDS));
+
+ verify(handler);
+
+ rethrow("winner had an exception", loser.getException());
+
+ Boolean locked = loser.getResult();
+ assertEquals(Boolean.FALSE, locked);
+ }
+ finally
+ {
+ if (t1.isAlive())
+ t1.interrupt();
+ }
+ }
+
+ public void testRemoteLockingStateRejectsInferiorRemoteCaller() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 3);
+ T testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+ final RpcTarget target = testeeSet.target;
+
+ ClusterNode inferiorNode = testee.getCurrentView().get(2);
+ assertFalse(node1.equals(inferiorNode));
+
+ ClusterNode superiorNode = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(superiorNode));
+
+ resetToStrict(partition);
+ makeThreadSafe(partition, true);
+ resetToStrict(handler);
+ makeThreadSafe(handler, true);
+
+ // When caller 1 invokes, block before giving response
+ CountDownLatch answerAwaitLatch = new CountDownLatch(1);
+ CountDownLatch answerStartLatch = new CountDownLatch(1);
+
+ ArrayList<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(superiorNode, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(inferiorNode, RemoteLockResponse.Flag.REJECT, inferiorNode));
+
+ BlockingAnswer<List<RemoteLockResponse>> caller1Answer =
+ new BlockingAnswer<List<RemoteLockResponse>>(rspList, answerAwaitLatch, answerStartLatch, null);
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andAnswer(caller1Answer);
+
+ expect((List<Object>) partition.callMethodOnCluster(eq("test"),
+ eq("releaseRemoteLock"),
+ aryEq(new Object[]{"test", node1}),
+ aryEq(AbstractClusterLockSupport.RELEASE_REMOTE_LOCK_TYPES),
+ eq(true))).andReturn(new ArrayList<Object>());
+
+ rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(superiorNode, RemoteLockResponse.Flag.OK));
+ rspList.add(new RemoteLockResponse(inferiorNode, RemoteLockResponse.Flag.OK));
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+
+ replay(partition);
+ replay(handler);
+
+ CountDownLatch finishedLatch = new CountDownLatch(1);
+ LocalLockCaller winner = new LocalLockCaller(testee, null, null, finishedLatch);
+
+ Thread t1 = new Thread(winner);
+ t1.setDaemon(true);
+
+ try
+ {
+ t1.start();
+ assertTrue(answerStartLatch.await(1, TimeUnit.SECONDS));
+ // t1 should now be blocking in caller1Answer
+
+ RemoteLockResponse rsp = target.remoteLock("test", inferiorNode, 1);
+ assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
+ assertEquals(node1, rsp.holder);
+
+ // release t1
+ answerAwaitLatch.countDown();
+
+ // wait for t1 to complete
+ assertTrue(finishedLatch.await(5, TimeUnit.SECONDS));
+
+ verify(handler);
+
+ rethrow("winner had an exception", winner.getException());
+
+ Boolean locked = winner.getResult();
+ assertEquals(Boolean.TRUE, locked);
+ }
+ finally
+ {
+ if (t1.isAlive())
+ t1.interrupt();
+ }
+ }
+
+ /**
+ * Local node acquires a lock; remote node tries to releasem which is ignored.
+ *
+ * @throws Exception
+ */
+ public void testSpuriousRemoteLockReleaseIgnored() throws Exception
+ {
+ TesteeSet<T> testeeSet = getTesteeSet(node1, 1, 2);
+ AbstractClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+
+ ClusterNode other = testee.getCurrentView().get(0);
+ resetToStrict(partition);
+ resetToStrict(handler);
+
+ ArrayList<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 200000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList);
+
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+
+ expect(handler.getLockHolder("test")).andReturn(node1);
+
+ replay(partition);
+ replay(handler);
+
+ assertTrue(testee.lock("test", 200000));
+ testeeSet.target.releaseRemoteLock("test", other);
+
+ verify(partition);
+ verify(handler);
+ }
+
+ protected TesteeSet<T> getTesteeSet(ClusterNode node, int viewPos, int viewSize) throws Exception
+ {
+ HAPartition haPartition = createNiceMock(HAPartition.class);
+ LocalLockHandler handler = createNiceMock(LocalLockHandler.class);
+ expect(haPartition.getClusterNode()).andReturn(node);
+ expect(haPartition.getPartitionName()).andReturn("TestPartition");
+
+ Capture<RpcTarget> c = new Capture<RpcTarget>();
+ haPartition.registerRPCHandler(eq("test"), and(isA(RpcTarget.class), capture(c)));
+ Vector<ClusterNode> view = getView(node, viewPos, viewSize);
+ expect(haPartition.getClusterNodes()).andReturn(view.toArray(new ClusterNode[view.size()]));
+
+ replay(haPartition);
+ replay(handler);
+
+ T testee = createClusteredLockManager("test", haPartition, handler);
+
+ testee.start();
+
+ reset(haPartition);
+ reset(handler);
+
+ return new TesteeSet<T>(testee, c.getValue());
+ }
+
+ protected abstract T createClusteredLockManager(String serviceHAName, HAPartition partition, LocalLockHandler handler);
+
+ private Vector<ClusterNode> getView(ClusterNode member, int viewPos, int numMembers)
+ {
+ Vector<ClusterNode> all = new Vector<ClusterNode>(Arrays.asList(new ClusterNode[]{node1, node2, node3}));
+ all.remove(member);
+ while (all.size() > numMembers - 1) // -1 'cause we'll add one in a sec
+ {
+ all.remove(all.size() - 1);
+ }
+ all.add(viewPos, member);
+
+ return all;
+ }
+
+ protected static void rethrow(String msg, Throwable t) throws Exception
+ {
+ if (t != null)
+ {
+ if (t instanceof AssertionError)
+ {
+ AssertionError rethrow = new AssertionError(msg);
+ rethrow.initCause(t);
+ throw rethrow;
+ }
+
+ throw new RuntimeException(msg, t);
+ }
+ }
+
+ protected class TesteeSet<C extends AbstractClusterLockSupport>
+ {
+ public final C impl;
+ public final RpcTarget target;
+
+ TesteeSet(C impl, RpcTarget target)
+ {
+ this.impl = impl;
+ this.target = target;
+ }
+ }
+
+ /**
+ * Allows EasyMock to block before returning.
+ *
+ * @author Brian Stansberry
+ *
+ * @param <T>
+ */
+ protected class BlockingAnswer<C> implements IAnswer<C>
+ {
+ private final C answer;
+ private final Exception toThrow;
+ private final CountDownLatch startLatch;
+ private final CountDownLatch awaitlatch;
+ private final CountDownLatch endLatch;
+ private final long timeout;
+
+ public BlockingAnswer(C answer, CountDownLatch awaitLatch, CountDownLatch startLatch, CountDownLatch endLatch)
+ {
+ this(answer, awaitLatch, 0, startLatch, endLatch);
+ }
+
+ public BlockingAnswer(C answer, CountDownLatch awaitLatch, long timeout, CountDownLatch startLatch, CountDownLatch endLatch)
+ {
+ this.awaitlatch = awaitLatch;
+ this.startLatch = startLatch;
+ this.endLatch = endLatch;
+ this.timeout = timeout;
+ this.answer = answer;
+ this.toThrow = null;
+ }
+
+ public BlockingAnswer(Exception toThrow, CountDownLatch awaitLatch, long timeout, CountDownLatch startLatch, CountDownLatch endLatch)
+ {
+ this.awaitlatch = awaitLatch;
+ this.startLatch = startLatch;
+ this.endLatch = endLatch;
+ this.timeout = timeout;
+ this.answer = null;
+ this.toThrow = toThrow;
+ }
+
+ public C answer() throws Throwable
+ {
+ if (startLatch != null)
+ {
+ startLatch.countDown();
+ }
+
+ try
+ {
+ if (timeout > 0)
+ {
+ awaitlatch.await(timeout, TimeUnit.MILLISECONDS);
+ }
+ else
+ {
+ awaitlatch.await();
+ }
+
+ if (toThrow != null)
+ {
+ throw toThrow;
+ }
+
+ return answer;
+ }
+ finally
+ {
+ if (endLatch != null)
+ {
+ endLatch.countDown();
+ }
+ }
+ }
+ }
+
+ protected abstract class AbstractCaller<C> implements Runnable
+ {
+ private final CountDownLatch startLatch;
+ private final CountDownLatch proceedLatch;
+ private final CountDownLatch finishLatch;
+ private C result;
+ private Throwable exception;
+
+ AbstractCaller(CountDownLatch startLatch,
+ CountDownLatch proceedLatch, CountDownLatch finishLatch)
+ {
+ this.startLatch = startLatch;
+ this.proceedLatch = proceedLatch;
+ this.finishLatch = finishLatch;
+ }
+
+ public void run()
+ {
+ try
+ {
+ if (startLatch != null)
+ {
+ startLatch.countDown();
+ }
+
+ if (proceedLatch != null)
+ {
+ proceedLatch.await();
+ }
+
+ result = execute();
+ }
+ catch (Throwable t)
+ {
+ exception = t;
+ }
+ finally
+ {
+ if (finishLatch != null)
+ {
+ finishLatch.countDown();
+ }
+
+ }
+ }
+
+ protected abstract C execute();
+
+ public C getResult()
+ {
+ return result;
+ }
+
+ public Throwable getException()
+ {
+ return exception;
+ }
+
+ }
+
+ protected class RemoteLockCaller extends AbstractCaller<RemoteLockResponse>
+ {
+ private final RpcTarget target;
+ private final ClusterNode caller;
+
+ public RemoteLockCaller(RpcTarget target, ClusterNode caller, CountDownLatch startLatch,
+ CountDownLatch proceedLatch, CountDownLatch finishLatch)
+ {
+ super(startLatch, proceedLatch, finishLatch);
+ this.target = target;
+ this.caller = caller;
+ }
+
+ protected RemoteLockResponse execute()
+ {
+ return target.remoteLock("test", caller, 1000);
+ }
+ }
+
+ protected class LocalLockCaller extends AbstractCaller<Boolean>
+ {
+ private final AbstractClusterLockSupport target;
+ private final long timeout;
+
+ public LocalLockCaller(AbstractClusterLockSupport target, CountDownLatch startLatch,
+ CountDownLatch proceedLatch, CountDownLatch finishLatch)
+ {
+ this(target, startLatch, proceedLatch, finishLatch, 3000);
+ }
+
+ public LocalLockCaller(AbstractClusterLockSupport target, CountDownLatch startLatch,
+ CountDownLatch proceedLatch, CountDownLatch finishLatch, long timeout)
+ {
+ super(startLatch, proceedLatch, finishLatch);
+ this.target = target;
+ this.timeout = timeout;
+ }
+
+ protected Boolean execute()
+ {
+ return Boolean.valueOf(target.lock("test", timeout));
+ }
+ }
+
+}
\ No newline at end of file
Copied: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ReadWriteClusteredLockManagerUnitTestCase.java (from rev 104321, trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/test/ReadWriteClusteredLockManagerUnitTestCase.java)
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ReadWriteClusteredLockManagerUnitTestCase.java (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/ReadWriteClusteredLockManagerUnitTestCase.java 2010-04-29 19:51:15 UTC (rev 104329)
@@ -0,0 +1,439 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.ha.framework.server.lock;
+
+import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.aryEq;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.makeThreadSafe;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.resetToNice;
+import static org.easymock.EasyMock.resetToStrict;
+import static org.easymock.EasyMock.verify;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport;
+import org.jboss.ha.framework.server.lock.LocalLockHandler;
+import org.jboss.ha.framework.server.lock.NonGloballyExclusiveClusterLockSupport;
+import org.jboss.ha.framework.server.lock.RemoteLockResponse;
+import org.jboss.ha.framework.server.lock.TimeoutException;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport.RpcTarget;
+
+/**
+ * Unit test of ClusteredLockManagerImpl
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class ReadWriteClusteredLockManagerUnitTestCase extends ClusteredLockManagerTestBase<NonGloballyExclusiveClusterLockSupport>
+{
+ /**
+ * Create a new ClusteredLockManagerImplUnitTestCase.
+ *
+ * @param name
+ */
+ public ReadWriteClusteredLockManagerUnitTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected NonGloballyExclusiveClusterLockSupport createClusteredLockManager(String serviceHAName,
+ HAPartition partition, LocalLockHandler handler)
+ {
+ return new NonGloballyExclusiveClusterLockSupport(serviceHAName, partition, handler);
+ }
+
+ public void testBasicRemoteLock() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 2);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller));
+
+ resetToStrict(handler);
+ handler.lockFromCluster("test", caller, 1000);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // Do it again; should fail as another thread from caller already
+ // acquired the lock
+ resetToStrict(handler); // fail if we call the local handler
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
+ assertEquals(caller, rsp.holder);
+
+ verify(handler);
+ }
+
+ public void testContestedRemoteLock() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller1 = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller1));
+
+ ClusterNode caller2 = testee.getCurrentView().get(2);
+ assertFalse(node1.equals(caller2));
+
+ resetToStrict(handler);
+ handler.lockFromCluster("test", caller1, 1000);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // A call from a different caller should be rejected without need
+ // to go to the LocalLockHandler
+ resetToStrict(handler);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller2, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
+ assertEquals(caller1, rsp.holder);
+
+ verify(handler);
+ }
+
+ /**
+ * Test for handling concurrent calls to remoteLock when the lock
+ * is in UNLOCKED state. Calls should get passed to the local lock handler,
+ * which allows one to succeed and the other to throw a TimeoutException;
+ * testee should react correctly.
+ *
+ * FIXME We are using a MockObject for the LocalLockHandler impl, and with
+ * that approach we can't really get concurrent calls to it. Effect is
+ * sometimes the thread that acquires the lock has already done so before
+ * the other thread even invokes remoteLock, defeating the purpose of this
+ * test and turning it into a variant of testContestedRemoteLock. Need to redo
+ * this test with a true multithreaded local lock handler, updating the latches
+ * such that both threads are in BlockingAnswer.answer at the same time.
+ *
+ * @throws Exception
+ */
+ public void testConcurrentRemoteLock() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ final RpcTarget target = testeeSet.target;
+
+ ClusterNode caller1 = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller1));
+
+ ClusterNode caller2 = testee.getCurrentView().get(2);
+ assertFalse(node1.equals(caller2));
+
+
+ resetToStrict(handler);
+ makeThreadSafe(handler, true);
+ // When caller 1 invokes, block before giving response
+ CountDownLatch answerStartLatch = new CountDownLatch(1);
+ CountDownLatch answerDoneLatch = new CountDownLatch(1);
+ BlockingAnswer<Boolean> caller1Answer = new BlockingAnswer<Boolean>(Boolean.TRUE, answerStartLatch, null, answerDoneLatch);
+ BlockingAnswer<Boolean> caller2Answer = new BlockingAnswer<Boolean>(new TimeoutException(caller1), answerDoneLatch, 0, null, null);
+ handler.lockFromCluster("test", caller1, 1000);
+ expectLastCall().andAnswer(caller1Answer);
+ handler.lockFromCluster("test", caller2, 1000);
+
+
+ // There is a race where t1 may have already marked the lock as LOCKED in
+ // which case t2 will not call handler.lockFromCluster("test", caller2, 1000);
+ // See FIXME in method javadoc. So, we use times(0, 1) to specify no
+ // calls are OK
+ expectLastCall().andAnswer(caller2Answer).times(0, 1);
+ replay(handler);
+
+ CountDownLatch startLatch1 = new CountDownLatch(1);
+ CountDownLatch startLatch2 = new CountDownLatch(1);
+ CountDownLatch finishedLatch = new CountDownLatch(2);
+
+ RemoteLockCaller winner = new RemoteLockCaller(target, caller1, startLatch1, null, finishedLatch);
+ RemoteLockCaller loser = new RemoteLockCaller(target, caller2, startLatch2, null, finishedLatch);
+
+ Thread t1 = new Thread(winner);
+ t1.setDaemon(true);
+ Thread t2 = new Thread(loser);
+ t2.setDaemon(true);
+
+ try
+ {
+ t1.start();
+ assertTrue(startLatch1.await(1, TimeUnit.SECONDS));
+ // t1 should now be blocking in caller1Answer
+
+ t2.start();
+ assertTrue(startLatch2.await(1, TimeUnit.SECONDS));
+ // t2 should now be blocking due to t1
+
+ // release t1
+ answerStartLatch.countDown();
+
+ // wait for both to complete
+ assertTrue(finishedLatch.await(1, TimeUnit.SECONDS));
+
+ verify(handler);
+
+ rethrow("winner had an exception", winner.getException());
+ rethrow("loser had an exception", loser.getException());
+
+ RemoteLockResponse rsp = winner.getResult();
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ rsp = loser.getResult();
+ if (rsp.flag != RemoteLockResponse.Flag.REJECT)
+ {
+ assertEquals(RemoteLockResponse.Flag.FAIL, rsp.flag);
+ }
+ assertEquals(caller1, rsp.holder);
+ }
+ finally
+ {
+ if (t1.isAlive())
+ t1.interrupt();
+ if (t2.isAlive())
+ t2.interrupt();
+ }
+ }
+
+ public void testRemoteLockFailsAgainstLocalLock() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 2);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller1 = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller1));
+
+ resetToStrict(handler);
+ // We throw TimeoutException to indicate "node1" holds the lock
+ handler.lockFromCluster("test", caller1, 1000);
+ expectLastCall().andThrow(new TimeoutException(node1));
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.FAIL, rsp.flag);
+ assertEquals(node1, rsp.holder);
+
+ verify(handler);
+
+ // A second attempt should succeed if the local lock is released
+
+ resetToStrict(handler);
+ // We return normally to indicate success
+ handler.lockFromCluster("test", caller1, 1000);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+ }
+
+ public void testBasicClusterLockFailsAgainstLocalLock() throws Exception
+ {
+ basicClusterLockFailsAgainstLocalLockTest(2);
+ }
+
+ public void testStandaloneClusterLockFailsAgainstLocalLock() throws Exception
+ {
+ basicClusterLockFailsAgainstLocalLockTest(2);
+ }
+
+ private void basicClusterLockFailsAgainstLocalLockTest(int viewSize) throws Exception
+ {
+ int viewPos = viewSize == 1 ? 0 : 1;
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, viewPos, viewSize);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+
+ resetToNice(partition);
+ resetToStrict(handler);
+
+ List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+ for (int i = 0; i < viewSize - 1; i++)
+ {
+ rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+ }
+
+ expect(partition.getMethodCallTimeout()).andReturn(60000l);
+ expect(partition.callMethodOnCluster(eq("test"),
+ eq("remoteLock"),
+ eqLockParams(node1, 2000000),
+ aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES),
+ eq(RemoteLockResponse.class),
+ eq(true),
+ eq(NULL_FILTER),
+ anyInt(),
+ eq(false))).andReturn(rspList).atLeastOnce();
+
+ handler.lockFromCluster(eq("test"), eq(node1), anyLong());
+ expectLastCall().andThrow(new TimeoutException(node1)).atLeastOnce();
+
+
+ expect((List<Object>) partition.callMethodOnCluster(eq("test"),
+ eq("releaseRemoteLock"),
+ aryEq(new Object[]{"test", node1}),
+ aryEq(AbstractClusterLockSupport.RELEASE_REMOTE_LOCK_TYPES),
+ eq(true))).andReturn(new ArrayList<Object>()).atLeastOnce();
+ replay(partition);
+ replay(handler);
+
+ assertFalse(testee.lock("test", 10));
+
+ verify(partition);
+ verify(handler);
+ }
+
+ /**
+ * Test that if a member holds a lock but is then removed from the
+ * view, another remote member can obtain the lock.
+ *
+ * @throws Exception
+ */
+ public void testDeadMemberCleanupAllowsRemoteLock() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ List<ClusterNode> members = testee.getCurrentView();
+ ClusterNode caller1 = members.get(0);
+ assertFalse(node1.equals(caller1));
+
+ ClusterNode caller2 = members.get(2);
+ assertFalse(node1.equals(caller2));
+
+ resetToStrict(handler);
+ handler.lockFromCluster("test", caller1, 1000);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // Change the view
+ Vector<ClusterNode> dead = new Vector<ClusterNode>();
+ dead.add(caller1);
+
+ Vector<ClusterNode> all = new Vector<ClusterNode>(members);
+ all.remove(caller1);
+
+ resetToStrict(handler);
+ expect(handler.getLockHolder("test")).andReturn(caller1);
+ handler.unlockFromCluster("test", caller1);
+ replay(handler);
+
+ testee.membershipChanged(dead, new Vector<ClusterNode>(), all);
+
+ verify(handler);
+
+ // A call from a different caller should work
+ resetToStrict(handler);
+ handler.lockFromCluster("test", caller2, 1000);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller2, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+ }
+
+ /**
+ * Remote node acquires a lock; different remote node tries to release which is ignored.
+ *
+ * @throws Exception
+ */
+ public void testSpuriousLockReleaseIgnored2() throws Exception
+ {
+ TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ HAPartition partition = testee.getPartition();
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller1 = testee.getCurrentView().get(0);
+ ClusterNode caller2 = testee.getCurrentView().get(2);
+
+ resetToStrict(partition);
+ resetToStrict(handler);
+
+ handler.lockFromCluster(eq("test"), eq(caller1), anyLong());
+
+ expect(handler.getLockHolder("test")).andReturn(caller1);
+
+ replay(partition);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1);
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+
+ target.releaseRemoteLock("test", caller2);
+
+ verify(partition);
+ verify(handler);
+ }
+
+}
Copied: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupportUnitTestCase.java (from rev 104321, trunk/testsuite/src/main/org/jboss/test/cluster/defaultcfg/test/YieldingGloballyExclusiveClusterLockSupportUnitTestCase.java)
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupportUnitTestCase.java (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupportUnitTestCase.java 2010-04-29 19:51:15 UTC (rev 104329)
@@ -0,0 +1,189 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.ha.framework.server.lock;
+
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.resetToStrict;
+import static org.easymock.EasyMock.verify;
+
+import java.util.List;
+import java.util.Vector;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.ha.framework.server.lock.YieldingGloballyExclusiveClusterLockSupport;
+import org.jboss.ha.framework.server.lock.LocalLockHandler;
+import org.jboss.ha.framework.server.lock.RemoteLockResponse;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport.RpcTarget;
+
+/**
+ * Unit test of ExclusiveClusterLockManager
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class YieldingGloballyExclusiveClusterLockSupportUnitTestCase extends ClusteredLockManagerTestBase<YieldingGloballyExclusiveClusterLockSupport>
+{
+ /**
+ * Create a new ClusteredLockManagerImplUnitTestCase.
+ *
+ * @param name
+ */
+ public YieldingGloballyExclusiveClusterLockSupportUnitTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected YieldingGloballyExclusiveClusterLockSupport createClusteredLockManager(String serviceHAName,
+ HAPartition partition, LocalLockHandler handler)
+ {
+ return new YieldingGloballyExclusiveClusterLockSupport(serviceHAName, partition, handler);
+ }
+
+ public void testBasicRemoteLock() throws Exception
+ {
+ TesteeSet<YieldingGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 2);
+ YieldingGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller));
+
+ resetToStrict(handler);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // Do it again; should still work
+ resetToStrict(handler);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+ }
+
+ public void testContestedRemoteLock() throws Exception
+ {
+ TesteeSet<YieldingGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ YieldingGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ ClusterNode caller1 = testee.getCurrentView().get(0);
+ assertFalse(node1.equals(caller1));
+
+ ClusterNode caller2 = testee.getCurrentView().get(2);
+ assertFalse(node1.equals(caller2));
+
+ resetToStrict(handler);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // A call from a different caller should still work as
+ // w/ supportLockOnly==false we only reject if WE hold the lock
+ resetToStrict(handler);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller2, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ }
+
+ /**
+ * Test that if a member holds a lock but is then removed from the
+ * view, another remote member can obtain the lock.
+ *
+ * @throws Exception
+ */
+ public void testDeadMemberCleanupAllowsRemoteLock() throws Exception
+ {
+ TesteeSet<YieldingGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
+ YieldingGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
+ LocalLockHandler handler = testee.getLocalHandler();
+ RpcTarget target = testeeSet.target;
+
+ List<ClusterNode> members = testee.getCurrentView();
+ ClusterNode caller1 = members.get(0);
+ assertFalse(node1.equals(caller1));
+
+ ClusterNode caller2 = members.get(2);
+ assertFalse(node1.equals(caller2));
+
+ resetToStrict(handler);
+ replay(handler);
+
+ RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+
+ // Change the view
+ Vector<ClusterNode> dead = new Vector<ClusterNode>();
+ dead.add(caller1);
+
+ Vector<ClusterNode> all = new Vector<ClusterNode>(members);
+ all.remove(caller1);
+
+ resetToStrict(handler);
+ replay(handler);
+
+ testee.membershipChanged(dead, new Vector<ClusterNode>(), all);
+
+ verify(handler);
+
+ // A call from a different caller should work
+ resetToStrict(handler);
+ replay(handler);
+
+ rsp = target.remoteLock("test", caller2, 1000);
+
+ assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
+ assertNull(rsp.holder);
+
+ verify(handler);
+ }
+
+}
Added: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/util/MockClusterNode.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/util/MockClusterNode.java (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/util/MockClusterNode.java 2010-04-29 19:51:15 UTC (rev 104329)
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.test.ha.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A MockClusterNode.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class MockClusterNode implements ClusterNode
+{
+
+ private static final long serialVersionUID = 1L;
+
+ private final InetAddress address;
+ private final int port;
+ private final String name;
+
+ public MockClusterNode(int port) throws UnknownHostException
+ {
+ this(InetAddress.getLocalHost(), port);
+ }
+
+ public MockClusterNode(InetAddress addr, int port)
+ {
+ this.address = addr;
+ this.port = port;
+ this.name = addr.getHostAddress() + ":" + port;
+ }
+
+ @Override
+ public InetAddress getIpAddress()
+ {
+ return address;
+ }
+
+ @Override
+ public String getName()
+ {
+ return name;
+ }
+
+ @Override
+ public int getPort()
+ {
+ return port;
+ }
+
+ @Override
+ public int compareTo(ClusterNode o)
+ {
+ return o == null ? - 1 : name.compareTo(o.getName());
+ }
+
+}
More information about the jboss-cvs-commits
mailing list