/jboss-as-7.1.1.Final/clustering/api/src/test/java/org/jboss/as/clustering/lock/ReadWriteClusteredLockManagerUnitTestCase.java

# · Java · 351 lines · 194 code · 78 blank · 79 comment · 6 complexity · 869ae2762eb89da391e37c04f7dd1742 MD5 · raw file

  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2009, Red Hat Middleware LLC, and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.as.clustering.lock;
  23. import static org.mockito.Mockito.*;
  24. import static org.mockito.AdditionalMatchers.*;
  25. import static org.junit.Assert.*;
  26. import static org.jboss.as.clustering.lock.LockParamsMatcher.*;
  27. import java.util.ArrayList;
  28. import java.util.List;
  29. import java.util.Vector;
  30. import java.util.concurrent.CountDownLatch;
  31. import java.util.concurrent.TimeUnit;
  32. import org.jboss.as.clustering.ClusterNode;
  33. import org.jboss.as.clustering.GroupMembershipNotifier;
  34. import org.jboss.as.clustering.GroupRpcDispatcher;
  35. import org.jboss.as.clustering.lock.AbstractClusterLockSupport.RpcTarget;
  36. import org.junit.Test;
  37. /**
  38. * Unit test of ClusteredLockManagerImpl
  39. *
  40. * @author Brian Stansberry
  41. *
  42. */
  43. public class ReadWriteClusteredLockManagerUnitTestCase extends ClusteredLockManagerTestBase<NonGloballyExclusiveClusterLockSupport> {
  44. @Override
  45. protected NonGloballyExclusiveClusterLockSupport createClusteredLockManager(String serviceHAName, GroupRpcDispatcher rpcDispatcher, GroupMembershipNotifier notifier, LocalLockHandler handler) {
  46. return new NonGloballyExclusiveClusterLockSupport(serviceHAName, rpcDispatcher, notifier, handler);
  47. }
  48. @Test
  49. public void testBasicRemoteLock() throws Exception {
  50. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 2);
  51. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  52. LocalLockHandler handler = testee.getLocalHandler();
  53. RpcTarget target = testeeSet.target;
  54. ClusterNode caller = testee.getCurrentView().get(0);
  55. assertFalse(node1.equals(caller));
  56. // resetToStrict(handler);
  57. RemoteLockResponse rsp = target.remoteLock("test", caller, 1000);
  58. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  59. assertNull(rsp.holder);
  60. verify(handler).lockFromCluster("test", caller, 1000);
  61. // Do it again; should fail as another thread from caller already
  62. // acquired the lock
  63. // resetToStrict(handler); // fail if we call the local handler
  64. // replay(handler);
  65. rsp = target.remoteLock("test", caller, 1000);
  66. assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
  67. assertEquals(caller, rsp.holder);
  68. verifyZeroInteractions(handler);
  69. }
  70. @Test
  71. public void testContestedRemoteLock() throws Exception {
  72. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
  73. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  74. LocalLockHandler handler = testee.getLocalHandler();
  75. RpcTarget target = testeeSet.target;
  76. ClusterNode caller1 = testee.getCurrentView().get(0);
  77. assertFalse(node1.equals(caller1));
  78. ClusterNode caller2 = testee.getCurrentView().get(2);
  79. assertFalse(node1.equals(caller2));
  80. RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
  81. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  82. assertNull(rsp.holder);
  83. verify(handler).lockFromCluster("test", caller1, 1000);
  84. // A call from a different caller should be rejected without need
  85. // to go to the LocalLockHandler
  86. // resetToStrict(handler);
  87. // replay(handler);
  88. rsp = target.remoteLock("test", caller2, 1000);
  89. assertEquals(RemoteLockResponse.Flag.REJECT, rsp.flag);
  90. assertEquals(caller1, rsp.holder);
  91. verifyNoMoreInteractions(handler);
  92. }
  93. /**
  94. * Test for handling concurrent calls to remoteLock when the lock is in UNLOCKED state. Calls should get passed to the local
  95. * lock handler, which allows one to succeed and the other to throw a TimeoutException; testee should react correctly.
  96. *
  97. * FIXME We are using a MockObject for the LocalLockHandler impl, and with that approach we can't really get concurrent
  98. * calls to it. Effect is sometimes the thread that acquires the lock has already done so before the other thread even
  99. * invokes remoteLock, defeating the purpose of this test and turning it into a variant of testContestedRemoteLock. Need to
  100. * redo this test with a true multithreaded local lock handler, updating the latches such that both threads are in
  101. * BlockingAnswer.answer at the same time.
  102. *
  103. * @throws Exception
  104. */
  105. @Test
  106. public void testConcurrentRemoteLock() throws Exception {
  107. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
  108. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  109. LocalLockHandler handler = testee.getLocalHandler();
  110. final RpcTarget target = testeeSet.target;
  111. ClusterNode caller1 = testee.getCurrentView().get(0);
  112. assertFalse(node1.equals(caller1));
  113. ClusterNode caller2 = testee.getCurrentView().get(2);
  114. assertFalse(node1.equals(caller2));
  115. // resetToStrict(handler);
  116. // makeThreadSafe(handler, true);
  117. // When caller 1 invokes, block before giving response
  118. CountDownLatch answerStartLatch = new CountDownLatch(1);
  119. CountDownLatch answerDoneLatch = new CountDownLatch(1);
  120. BlockingAnswer<Boolean> caller1Answer = new BlockingAnswer<Boolean>(Boolean.TRUE, answerStartLatch, null,
  121. answerDoneLatch);
  122. BlockingAnswer<Boolean> caller2Answer = new BlockingAnswer<Boolean>(new TimeoutException(caller1), answerDoneLatch, 0,
  123. null, null);
  124. doAnswer(caller1Answer).when(handler).lockFromCluster("test", caller1, 1000);
  125. doAnswer(caller2Answer).when(handler).lockFromCluster("test", caller2, 1000);
  126. // There is a race where t1 may have already marked the lock as LOCKED in
  127. // which case t2 will not call handler.lockFromCluster("test", caller2, 1000);
  128. // See FIXME in method javadoc. So, we use times(0, 1) to specify no
  129. // calls are OK
  130. /*
  131. expectLastCall().andAnswer(caller2Answer).times(0, 1);
  132. replay(handler);
  133. */
  134. CountDownLatch startLatch1 = new CountDownLatch(1);
  135. CountDownLatch startLatch2 = new CountDownLatch(1);
  136. CountDownLatch finishedLatch = new CountDownLatch(2);
  137. RemoteLockCaller winner = new RemoteLockCaller(target, caller1, startLatch1, null, finishedLatch);
  138. RemoteLockCaller loser = new RemoteLockCaller(target, caller2, startLatch2, null, finishedLatch);
  139. Thread t1 = new Thread(winner);
  140. t1.setDaemon(true);
  141. Thread t2 = new Thread(loser);
  142. t2.setDaemon(true);
  143. try {
  144. t1.start();
  145. assertTrue(startLatch1.await(1, TimeUnit.SECONDS));
  146. // t1 should now be blocking in caller1Answer
  147. t2.start();
  148. assertTrue(startLatch2.await(1, TimeUnit.SECONDS));
  149. // t2 should now be blocking due to t1
  150. // release t1
  151. answerStartLatch.countDown();
  152. // wait for both to complete
  153. assertTrue(finishedLatch.await(1, TimeUnit.SECONDS));
  154. // verifyNoMoreInteractions(handler);
  155. rethrow("winner had an exception", winner.getException());
  156. rethrow("loser had an exception", loser.getException());
  157. RemoteLockResponse rsp = winner.getResult();
  158. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  159. assertNull(rsp.holder);
  160. rsp = loser.getResult();
  161. if (rsp.flag != RemoteLockResponse.Flag.REJECT) {
  162. assertEquals(RemoteLockResponse.Flag.FAIL, rsp.flag);
  163. }
  164. assertEquals(caller1, rsp.holder);
  165. } finally {
  166. if (t1.isAlive()) {
  167. t1.interrupt();
  168. }
  169. if (t2.isAlive()) {
  170. t2.interrupt();
  171. }
  172. }
  173. }
  174. @Test
  175. public void testRemoteLockFailsAgainstLocalLock() throws Exception {
  176. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 2);
  177. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  178. LocalLockHandler handler = testee.getLocalHandler();
  179. RpcTarget target = testeeSet.target;
  180. ClusterNode caller1 = testee.getCurrentView().get(0);
  181. assertFalse(node1.equals(caller1));
  182. // We throw TimeoutException to indicate "node1" holds the lock
  183. // A second attempt should succeed if the local lock is released
  184. // We return normally to indicate success
  185. doThrow(new TimeoutException(node1)).doNothing().when(handler).lockFromCluster("test", caller1, 1000);
  186. RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
  187. assertEquals(RemoteLockResponse.Flag.FAIL, rsp.flag);
  188. assertEquals(node1, rsp.holder);
  189. rsp = target.remoteLock("test", caller1, 1000);
  190. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  191. assertNull(rsp.holder);
  192. }
  193. @Test
  194. public void testBasicClusterLockFailsAgainstLocalLock() throws Exception {
  195. basicClusterLockFailsAgainstLocalLockTest(2);
  196. }
  197. @Test
  198. public void testStandaloneClusterLockFailsAgainstLocalLock() throws Exception {
  199. basicClusterLockFailsAgainstLocalLockTest(2);
  200. }
  201. private void basicClusterLockFailsAgainstLocalLockTest(int viewSize) throws Exception {
  202. int viewPos = viewSize == 1 ? 0 : 1;
  203. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, viewPos, viewSize);
  204. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  205. GroupRpcDispatcher rpcDispatcher = testee.getGroupRpcDispatcher();
  206. LocalLockHandler handler = testee.getLocalHandler();
  207. List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
  208. for (int i = 0; i < viewSize - 1; i++) {
  209. rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
  210. }
  211. when(rpcDispatcher.getMethodCallTimeout()).thenReturn(60000l);
  212. when(rpcDispatcher.<RemoteLockResponse>callMethodOnCluster(eq("test"), eq("remoteLock"), eqLockParams(node1, 2000000),
  213. aryEq(AbstractClusterLockSupport.REMOTE_LOCK_TYPES), eq(true), eq(NULL_FILTER), anyInt(), eq(false))).thenReturn(rspList);
  214. doThrow(new TimeoutException(node1)).when(handler).lockFromCluster(eq("test"), eq(node1), anyLong());
  215. when((List<Object>) rpcDispatcher.callMethodOnCluster(eq("test"), eq("releaseRemoteLock"), aryEq(new Object[] { "test", node1 }),
  216. aryEq(AbstractClusterLockSupport.RELEASE_REMOTE_LOCK_TYPES), eq(true))).thenReturn(new ArrayList<Object>());
  217. assertFalse(testee.lock("test", 10));
  218. }
  219. /**
  220. * Test that if a member holds a lock but is then removed from the view, another remote member can obtain the lock.
  221. *
  222. * @throws Exception
  223. */
  224. @Test
  225. public void testDeadMemberCleanupAllowsRemoteLock() throws Exception {
  226. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
  227. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  228. LocalLockHandler handler = testee.getLocalHandler();
  229. RpcTarget target = testeeSet.target;
  230. List<ClusterNode> members = testee.getCurrentView();
  231. ClusterNode caller1 = members.get(0);
  232. assertFalse(node1.equals(caller1));
  233. ClusterNode caller2 = members.get(2);
  234. assertFalse(node1.equals(caller2));
  235. RemoteLockResponse rsp = target.remoteLock("test", caller1, 1000);
  236. verify(handler).lockFromCluster("test", caller1, 1000);
  237. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  238. assertNull(rsp.holder);
  239. // Change the view
  240. Vector<ClusterNode> dead = new Vector<ClusterNode>();
  241. dead.add(caller1);
  242. Vector<ClusterNode> all = new Vector<ClusterNode>(members);
  243. all.remove(caller1);
  244. when(handler.getLockHolder("test")).thenReturn(caller1);
  245. testee.membershipChanged(dead, new Vector<ClusterNode>(), all);
  246. verify(handler).unlockFromCluster("test", caller1);
  247. // A call from a different caller should work
  248. rsp = target.remoteLock("test", caller2, 1000);
  249. verify(handler).lockFromCluster("test", caller2, 1000);
  250. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  251. assertNull(rsp.holder);
  252. }
  253. /**
  254. * Remote node acquires a lock; different remote node tries to release which is ignored.
  255. *
  256. * @throws Exception
  257. */
  258. @Test
  259. public void testSpuriousLockReleaseIgnored2() throws Exception {
  260. TesteeSet<NonGloballyExclusiveClusterLockSupport> testeeSet = getTesteeSet(node1, 1, 3);
  261. NonGloballyExclusiveClusterLockSupport testee = testeeSet.impl;
  262. LocalLockHandler handler = testee.getLocalHandler();
  263. RpcTarget target = testeeSet.target;
  264. ClusterNode caller1 = testee.getCurrentView().get(0);
  265. ClusterNode caller2 = testee.getCurrentView().get(2);
  266. when(handler.getLockHolder("test")).thenReturn(caller1);
  267. RemoteLockResponse rsp = target.remoteLock("test", caller1, 1);
  268. verify(handler).lockFromCluster(eq("test"), eq(caller1), anyLong());
  269. assertEquals(RemoteLockResponse.Flag.OK, rsp.flag);
  270. target.releaseRemoteLock("test", caller2);
  271. verify(handler, never()).unlockFromCluster("test", caller2);
  272. }
  273. }