[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