[jboss-cvs] JBossAS SVN: r104416 - in projects/cluster/ha-server-api/trunk/src: test/java/org/jboss/test/ha/framework/server/lock and 1 other directory.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Tue May 4 01:13:36 EDT 2010


Author: bstansberry at jboss.com
Date: 2010-05-04 01:13:35 -0400 (Tue, 04 May 2010)
New Revision: 104416

Added:
   projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java
   projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/SharedLocalYieldingClusterLockManagerUnitTestCase.java
Removed:
   projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingClusterLockManager.java
Modified:
   projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupport.java
Log:
[JBCLUSTER-268] Locking for session ownership case

Copied: projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java (from rev 104321, projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingClusterLockManager.java)
===================================================================
--- projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java	                        (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/SharedLocalYieldingClusterLockManager.java	2010-05-04 05:13:35 UTC (rev 104416)
@@ -0,0 +1,383 @@
+/*
+ * 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.ha.framework.server.lock;
+
+import java.io.Serializable;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.HAPartition;
+
+/**
+ * Distributed lock manager intended for use cases where multiple local threads
+ * can share the lock, but only one node in the cluster can have threads using
+ * the lock. Nodes holding the lock yield it to remote requestors if no local
+ * threads are using it; otherwise remote requestors block.
+ * <p>
+ * The expected use case for this class is controlling access to resources that
+ * are typically only accessed on a single node (e.g. web sessions or stateful
+ * session beans), with the distributed lock used primarily to guarantee that.
+ * </p>
+ * @author Brian Stansberry
+ * 
+ * @version $Revision:$
+ */
+public class SharedLocalYieldingClusterLockManager
+{
+   /** Result of a {@link SharedLocalYieldingClusterLockManager#lock(Serializable, long, boolean) lock call}*/
+   public static enum LockResult 
+   { 
+      /** Indicates the lock was acquired after requesting it from the cluster */
+      ACQUIRED_FROM_CLUSTER, 
+      /** Indicates this node already held the lock */
+      ALREADY_HELD, 
+      /** 
+       * Indicates the 'newLock' param passed to 
+       * {@link YieldingClusterLockManager#lock(Serializable, long, boolean)}
+       * was <code>true</code> and the local node in fact was unaware of the
+       * lock. If in fact the local node was already aware of the lock (which
+       * would generally indicate a flaw in the application using this class)
+       * NEW_LOCK will not be returned; rather one of the other enum values
+       * will be returned.
+       */
+      NEW_LOCK 
+   }
+   
+   private class LocalLock
+   {
+      private volatile ClusterNode holder;
+      private volatile boolean removable;
+      private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
+      private final AtomicInteger localLockCount = new AtomicInteger();
+
+      private void lock(ClusterNode caller, long timeout) throws TimeoutException
+      {
+         if (SharedLocalYieldingClusterLockManager.this.localNode.equals(caller))
+         {
+            this.localLockCount.incrementAndGet();
+            this.holder = SharedLocalYieldingClusterLockManager.this.localNode;
+         }
+         else
+         {
+            long deadline = System.currentTimeMillis() + timeout;
+            boolean wasInterrupted = false;
+            Thread current = Thread.currentThread();
+            waiters.add(current);
+            
+            try
+            {
+               // Block while not first in queue or cannot acquire lock
+               while (waiters.peek() != current || 
+                      localLockCount.get() > 0) 
+               { 
+                  LockSupport.parkUntil(deadline);
+                  if (Thread.interrupted()) // ignore interrupts while waiting
+                     wasInterrupted = true;
+                  if (System.currentTimeMillis() >= deadline)
+                  {
+                     if (waiters.peek() != current || 
+                           localLockCount.get() > 0)
+                     {
+                        throw new TimeoutException(this.holder);
+                     }
+                     break;
+                  }
+               }
+               
+               if (localLockCount.get() == 0)
+               {
+                  holder = caller;
+               }
+               else
+               {
+                  throw new TimeoutException(this.holder);
+               }
+            }
+            finally
+            {
+               waiters.remove();
+               if (wasInterrupted)          // reassert interrupt status on exit
+                  current.interrupt();
+            }
+         }
+      }
+      
+      private void unlock(ClusterNode caller)
+      {
+         if (caller.equals(holder))              
+         {
+            if (SharedLocalYieldingClusterLockManager.this.localNode.equals(caller))
+            {
+               if (this.localLockCount.decrementAndGet() == 0)
+               {
+                  holder = null;
+               }
+            }
+            else
+            {
+               holder = null;
+            }
+            
+            if (holder == null)
+            {
+               LockSupport.unpark(waiters.peek());
+            }
+         }
+       } 
+      
+   }
+   
+   /** Handles callbacks from the cluster lock support object */
+   private class ClusterHandler implements LocalLockHandler
+   {      
+      // ----------------------------------------------------- LocalLockHandler
+      
+      public ClusterNode getLocalNode(ClusterNode localNode)
+      {
+         return SharedLocalYieldingClusterLockManager.this.localNode;
+      }
+
+      public void setLocalNode(ClusterNode localNode)
+      {
+         SharedLocalYieldingClusterLockManager.this.localNode = localNode;
+      }
+
+      public void lockFromCluster(Serializable lockName, ClusterNode caller, long timeout) throws TimeoutException,
+            InterruptedException
+      {
+         LocalLock lock = getLocalLock(lockName, true);
+         lock.lock(caller, timeout);
+         if (!localNode.equals(caller))
+         {
+            // Any local thread who has a ref to lock will now need to request it
+            // remotely from caller, which won't grant it until this method returns.
+            // So, we can remove lock from the map. If that local thread is granted
+            // the lock by caller, when that thread calls lockFromCluster, we'll create
+            // a new lock to handle that.
+            localLocks.remove(lockName, lock);
+         }
+      }
+
+      public ClusterNode getLockHolder(Serializable lockName)
+      {
+         LocalLock lock = getLocalLock(lockName, false);
+         return lock == null ? null : lock.holder;
+      }
+
+      public void unlockFromCluster(Serializable lockName, ClusterNode caller)
+      {
+         LocalLock lock = getLocalLock(lockName, false);
+         if (lock != null)
+         {
+            lock.unlock(caller);
+         }
+      }
+      
+   }
+   
+   private ClusterNode localNode;
+   private ConcurrentMap<Serializable, LocalLock> localLocks = new ConcurrentHashMap<Serializable, LocalLock>();
+   private final YieldingGloballyExclusiveClusterLockSupport clusterSupport;
+   
+   public SharedLocalYieldingClusterLockManager(String serviceHAName, HAPartition partition)
+   {
+      ClusterHandler handler = new ClusterHandler();
+      clusterSupport = new YieldingGloballyExclusiveClusterLockSupport(serviceHAName, partition, handler);
+   }
+   
+   // ----------------------------------------------------------------- Public
+   
+   /**
+    * Acquire the given lock.
+    * 
+    * @param lockName the identifier of the lock that should be acquired
+    * @param timeout max time in ms to wait before throwing a TimeoutException
+    *                if the lock cannot be acquired
+    *                
+    * @return enum indicating how the lock was acquired
+    *                
+    * @throws TimeoutException if the lock cannot be acquired before the timeout
+    * @throws InterruptedException if the thread is interrupted while trying to
+    *                              acquire the lock
+    */
+   public LockResult lock (Serializable lockName, long timeout)
+      throws TimeoutException, InterruptedException
+   {
+      return lock(lockName, timeout, false);
+   }
+   
+   /**
+    * Acquire the given lock.
+    * 
+    * @param lockName the identifier of the lock that should be acquired
+    * @param timeout max time in ms to wait before throwing a TimeoutException
+    *                if the lock cannot be acquired
+    * @param newLock <code>true</code> if this object should assume this is the
+    *                first use cluster-wide of the lock identified by
+    *                <code>lockName</code>, and just acquire the lock locally 
+    *                without any cluster-wide call. See discussion of 
+    *                {@link LockResult#NEW_LOCK}.
+    *                
+    * @return enum indicating how the lock was acquired
+    *                
+    * @throws TimeoutException if the lock cannot be acquired before the timeout
+    * @throws InterruptedException if the thread is interrupted while trying to
+    *                              acquire the lock
+    */
+   public LockResult lock(Serializable lockName, long timeout, boolean newLock)
+      throws TimeoutException, InterruptedException
+   {   
+      if (this.localNode == null)
+      {
+         throw new IllegalStateException("Null localNode");
+      }
+      
+      LockResult result = null;
+      LocalLock localLock = getLocalLock(lockName, false);
+      if (localLock == null)
+      {
+         if (newLock)
+         {
+            // Here we assume the caller knows what they are doing and this 
+            // is really is a new lock, and that no other
+            // node is going to try to take it
+            localLock = getLocalLock(lockName, true);
+            localLock.lock(this.localNode, timeout);
+            result = (localLock.localLockCount.get() == 1 ? LockResult.NEW_LOCK : LockResult.ALREADY_HELD);
+         }
+         else
+         {
+            this.clusterSupport.lock(lockName, timeout);
+            result = LockResult.ACQUIRED_FROM_CLUSTER;
+         }
+      }
+      else
+      {
+         localLock.localLockCount.incrementAndGet(); // Now no other node can become localLock.holder         
+         try
+         {
+            if (this.localNode.equals(localLock.holder))
+            {
+               result = LockResult.ALREADY_HELD;
+               
+               // Check for race where we locked something that's been removed
+               if (localLock.removable && localLock != getLocalLock(lockName, false))
+               {
+                  return lock(lockName, timeout, newLock);
+               }
+            }
+            else
+            {
+               this.clusterSupport.lock(lockName, timeout);
+               result = LockResult.ACQUIRED_FROM_CLUSTER;
+            }
+         }
+         finally
+         {
+            // If we called clusterSupport.lock() above, its callback into
+            // ClusterHandler.lockFromCluster() will increment localLock.localLockCount.
+            // So, decrement so we don't double count
+            // (If we threw an exception above we should also decrement)
+            if (result != LockResult.ALREADY_HELD)
+            {
+               // Only decrement if the current lock object for this key is
+               // the same one we incremented above
+               LocalLock current = localLocks.get(lockName);
+               if (current == localLock)
+               {
+                  localLock.localLockCount.decrementAndGet();
+               }
+            }
+         }         
+      }
+      
+      return result;
+   }
+   
+   /**
+    * Releases a previously acquired lock.
+    * 
+    * @param lockName unique name identifying the lock to release
+    * @param remove <code>true</code> if this lock can be removed from
+    *               tracking once all local locks are unlocked.
+    */
+   public void unlock(Serializable lockName, boolean remove)
+   {
+      LocalLock lock = getLocalLock(lockName, false);
+      if (remove && lock != null)
+      {
+         lock.removable = true;
+      }
+      
+      this.clusterSupport.unlock(lockName);
+      
+      if (lock != null && lock.removable && lock.localLockCount.get() == 0)
+      {
+         localLocks.remove(lockName, lock);
+      }
+   }
+   
+   /**
+    * Brings this object to a state where it is ready for normal operation.
+    * 
+    * @throws Exception
+    */
+   public void start() throws Exception
+   {
+      this.clusterSupport.start();
+   }
+   
+   /**
+    * Removes this object from a state where it is ready for normal oepration
+    * and performs cleanup work.
+    * 
+    * @throws Exception
+    */
+   public void stop() throws Exception
+   {
+      this.clusterSupport.stop();
+   }
+   
+   // ----------------------------------------------------------------- Private
+   
+   private LocalLock getLocalLock(Serializable categoryName, boolean create)
+   {
+      LocalLock category = localLocks.get(categoryName);
+      if (category == null && create)
+      {
+         category = new LocalLock();
+         LocalLock existing = localLocks.putIfAbsent(categoryName, category);
+         if (existing != null)
+         {
+            category = existing;
+         }         
+      }
+      return category;
+   }
+
+}

Deleted: projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingClusterLockManager.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingClusterLockManager.java	2010-05-04 05:12:37 UTC (rev 104415)
+++ projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingClusterLockManager.java	2010-05-04 05:13:35 UTC (rev 104416)
@@ -1,330 +0,0 @@
-/*
- * 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.ha.framework.server.lock;
-
-import java.io.Serializable;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.LockSupport;
-
-import org.jboss.ha.framework.interfaces.ClusterNode;
-import org.jboss.ha.framework.interfaces.HAPartition;
-
-/**
- * @author Brian Stansberry
- * 
- * @version $Revision:$
- */
-public class YieldingClusterLockManager
-{
-   /** Result of a {@link YieldingClusterLockManager#lock(Serializable, long, boolean) lock call}*/
-   public static enum LockResult 
-   { 
-      /** Indicates the lock was acquired after requesting it from the cluster */
-      ACQUIRED_FROM_CLUSTER, 
-      /** Indicates this node already held the lock */
-      ALREADY_HELD, 
-      /** 
-       * Indicates the 'newLock' param passed to 
-       * {@link YieldingClusterLockManager#lock(Serializable, long, boolean)}
-       * was <code>true</code> and the local node in fact was unaware of the
-       * lock. If in fact the local node was already aware of the lock (which
-       * would generally indicate a flaw in the application using this class)
-       * NEW_LOCK will not be returned; rather one of the other enum values
-       * will be returned.
-       */
-      NEW_LOCK 
-   }
-   
-   private class LocalLock
-   {
-      private volatile ClusterNode holder;
-      private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
-      private final AtomicInteger localLockCount = new AtomicInteger();
-
-      private void lock(ClusterNode caller, long timeout) throws TimeoutException
-      {
-         if (YieldingClusterLockManager.this.localNode.equals(caller))
-         {
-            this.localLockCount.incrementAndGet();
-            this.holder = YieldingClusterLockManager.this.localNode;
-         }
-         else
-         {
-            long deadline = System.currentTimeMillis() + timeout;
-            boolean wasInterrupted = false;
-            Thread current = Thread.currentThread();
-            waiters.add(current);
-            
-            try
-            {
-               // Block while not first in queue or cannot acquire lock
-               while (waiters.peek() != current || 
-                      localLockCount.get() > 0) 
-               { 
-                  LockSupport.parkUntil(deadline);
-                  if (Thread.interrupted()) // ignore interrupts while waiting
-                     wasInterrupted = true;
-                  if (System.currentTimeMillis() >= deadline)
-                  {
-                     if (waiters.peek() != current || 
-                           localLockCount.get() > 0)
-                     {
-                        throw new TimeoutException(this.holder);
-                     }
-                     break;
-                  }
-               }
-               
-               if (localLockCount.get() == 0)
-               {
-                  holder = caller;
-               }
-               else
-               {
-                  throw new TimeoutException(this.holder);
-               }
-            }
-            finally
-            {
-               waiters.remove();
-               if (wasInterrupted)          // reassert interrupt status on exit
-                  current.interrupt();
-            }
-         }
-      }
-      
-      private void unlock(ClusterNode caller)
-      {
-         if (caller.equals(holder))              
-         {
-            if (YieldingClusterLockManager.this.localNode.equals(caller))
-            {
-               if (this.localLockCount.decrementAndGet() == 0)
-               {
-                  holder = null;
-               }
-            }
-            else
-            {
-               holder = null;
-            }
-            
-            if (holder == null)
-            {
-               LockSupport.unpark(waiters.peek());
-            }
-         }
-       } 
-      
-   }
-   
-   /** Handles callbacks from the cluster lock support object */
-   private class ClusterHandler implements LocalLockHandler
-   {      
-      // ----------------------------------------------------- LocalLockHandler
-      
-      public ClusterNode getLocalNode(ClusterNode localNode)
-      {
-         return YieldingClusterLockManager.this.localNode;
-      }
-
-      public void setLocalNode(ClusterNode localNode)
-      {
-         YieldingClusterLockManager.this.localNode = localNode;
-      }
-
-      public void lockFromCluster(Serializable lockName, ClusterNode caller, long timeout) throws TimeoutException,
-            InterruptedException
-      {
-         YieldingClusterLockManager.this.doLock(lockName, caller, timeout);
-      }
-
-      public ClusterNode getLockHolder(Serializable lockName)
-      {
-         LocalLock lock = YieldingClusterLockManager.this.getLocalLock(lockName, false);
-         return lock == null ? null : lock.holder;
-      }
-
-      public void unlockFromCluster(Serializable lockName, ClusterNode caller)
-      {
-         YieldingClusterLockManager.this.doUnlock(lockName, caller);
-      }
-      
-   }
-   
-   private ClusterNode localNode;
-   private ConcurrentMap<Serializable, LocalLock> localLocks = new ConcurrentHashMap<Serializable, LocalLock>();
-   private final YieldingGloballyExclusiveClusterLockSupport clusterSupport;
-   
-   public YieldingClusterLockManager(String serviceHAName, HAPartition partition)
-   {
-      ClusterHandler handler = new ClusterHandler();
-      clusterSupport = new YieldingGloballyExclusiveClusterLockSupport(serviceHAName, partition, handler);
-   }
-   
-   // ----------------------------------------------------------------- Public
-   
-   /**
-    * Acquire the given lock.
-    * 
-    * @param lockName the identifier of the lock that should be acquired
-    * @param timeout max time in ms to wait before throwing a TimeoutException
-    *                if the lock cannot be acquired
-    *                
-    * @return enum indicating how the lock was acquired
-    *                
-    * @throws TimeoutException if the lock cannot be acquired before the timeout
-    * @throws InterruptedException if the thread is interrupted while trying to
-    *                              acquire the lock
-    */
-   public LockResult lock (Serializable lockName, long timeout)
-      throws TimeoutException, InterruptedException
-   {
-      return lock(lockName, timeout, false);
-   }
-   
-   /**
-    * Acquire the given lock.
-    * 
-    * @param lockName the identifier of the lock that should be acquired
-    * @param timeout max time in ms to wait before throwing a TimeoutException
-    *                if the lock cannot be acquired
-    * @param newLock <code>true</code> if this object should assume this is the
-    *                first use cluster-wide of the lock identified by
-    *                <code>lockName</code>, and just acquire the lock locally 
-    *                without any cluster-wide call. See discussion of 
-    *                {@link LockResult#NEW_LOCK}.
-    *                
-    * @return enum indicating how the lock was acquired
-    *                
-    * @throws TimeoutException if the lock cannot be acquired before the timeout
-    * @throws InterruptedException if the thread is interrupted while trying to
-    *                              acquire the lock
-    */
-   public LockResult lock(Serializable lockName, long timeout, boolean newLock)
-      throws TimeoutException, InterruptedException
-   {   
-      if (this.localNode == null)
-      {
-         throw new IllegalStateException("Null localNode");
-      }
-      
-      LockResult result = null;
-      LocalLock localLock = getLocalLock(lockName, false);
-      if (localLock == null)
-      {
-         if (newLock)
-         {
-            doLock(lockName, this.localNode, timeout);
-            result = LockResult.NEW_LOCK;
-         }
-         else
-         {
-            this.clusterSupport.lock(lockName, timeout);
-            result = LockResult.ACQUIRED_FROM_CLUSTER;
-         }
-      }
-      else
-      {
-         localLock.localLockCount.incrementAndGet(); // Now no other node can become localLock.holder         
-         try
-         {
-            if (this.localNode.equals(localLock.holder))
-            {
-               result = LockResult.ALREADY_HELD;
-            }
-            else
-            {
-               this.clusterSupport.lock(lockName, timeout);
-               result = LockResult.ACQUIRED_FROM_CLUSTER;
-            }
-         }
-         finally
-         {
-            // If we called clusterSupport.lock() above, its callback into
-            // ClusterHandler.lockFromCluster() will increment localLock.localLockCount.
-            // So, decrement so we don't double count
-            // (If we threw an exception above we should also decrement)
-            if (result != LockResult.ALREADY_HELD)
-            {
-               localLock.localLockCount.decrementAndGet();
-            }
-         }         
-      }
-      
-      return result;
-   }
-   
-   public void unlock(Serializable lockName)
-   {
-      this.clusterSupport.unlock(lockName);
-   }
-   
-   public void start() throws Exception
-   {
-      this.clusterSupport.start();
-   }
-   
-   public void stop() throws Exception
-   {
-      this.clusterSupport.stop();
-   }
-   
-   // ----------------------------------------------------------------- Private
-   
-   private LocalLock getLocalLock(Serializable categoryName, boolean create)
-   {
-      LocalLock category = localLocks.get(categoryName);
-      if (category == null && create)
-      {
-         category = new LocalLock();
-         LocalLock existing = localLocks.putIfAbsent(categoryName, category);
-         if (existing != null)
-         {
-            category = existing;
-         }         
-      }
-      return category;
-   }
-   
-   private void doLock(Serializable lockName, ClusterNode caller, long timeout) throws TimeoutException,
-         InterruptedException
-   {
-      LocalLock lock = getLocalLock(lockName, true);
-      lock.lock(caller, timeout);
-   }
-   
-   private void doUnlock(Serializable lockName, ClusterNode caller)
-   {
-      LocalLock lock = getLocalLock(lockName, false);
-      if (lock != null)
-      {
-         lock.unlock(caller);
-      }
-   }
-
-}

Modified: projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupport.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupport.java	2010-05-04 05:12:37 UTC (rev 104415)
+++ projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/lock/YieldingGloballyExclusiveClusterLockSupport.java	2010-05-04 05:13:35 UTC (rev 104416)
@@ -63,8 +63,12 @@
       
       ClusterLockState category = getClusterLockState(lockId, false);
       
-      if (category != null && myself.equals(category.getHolder()))
+      if (category == null)
       {
+         getLocalHandler().unlockFromCluster(lockId, myself);
+      }
+      else if (myself.equals(category.getHolder()))
+      {
          category.invalidate();
          getLocalHandler().unlockFromCluster(lockId, myself);
          removeLockState(category);

Added: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/SharedLocalYieldingClusterLockManagerUnitTestCase.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/SharedLocalYieldingClusterLockManagerUnitTestCase.java	                        (rev 0)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/lock/SharedLocalYieldingClusterLockManagerUnitTestCase.java	2010-05-04 05:13:35 UTC (rev 104416)
@@ -0,0 +1,239 @@
+/*
+ * 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.framework.server.lock;
+
+import static org.easymock.EasyMock.and;
+import static org.easymock.EasyMock.anyInt;
+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.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.resetToStrict;
+import static org.jboss.test.ha.framework.server.lock.LockParamsMatcher.eqLockParams;
+
+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.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.RemoteLockResponse;
+import org.jboss.ha.framework.server.lock.SharedLocalYieldingClusterLockManager;
+import org.jboss.ha.framework.server.lock.AbstractClusterLockSupport.RpcTarget;
+import org.jboss.ha.framework.server.lock.SharedLocalYieldingClusterLockManager.LockResult;
+import org.jboss.test.ha.util.MockClusterNode;
+
+/**
+ * Unit tests of SharedLocalYieldingClusterLockManager.
+ *
+ *
+ * @author Brian Stansberry
+ * 
+ * @version $Revision$
+ */
+public class SharedLocalYieldingClusterLockManagerUnitTestCase extends TestCase
+{
+   private static final ResponseFilter NULL_FILTER = null;
+   
+   private ClusterNode node1;
+   private ClusterNode node2;
+   private ClusterNode node3;
+
+   public SharedLocalYieldingClusterLockManagerUnitTestCase(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 testBasicLock() throws Exception
+   {
+      TesteeSet ts = getTesteeSet(node1, 0, 3);
+      
+      resetToStrict(ts.partition);
+      
+      List<RemoteLockResponse> rspList = getOKResponses(2);
+      expect(ts.partition.getMethodCallTimeout()).andReturn(60000l);
+      expect(ts.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);
+      
+      replay(ts.partition);
+
+      assertEquals(LockResult.ACQUIRED_FROM_CLUSTER, ts.testee.lock("test", 1000, false));
+      
+      assertEquals(LockResult.ALREADY_HELD, ts.testee.lock("test", 1000, true));
+      ts.testee.unlock("test", false);
+      ts.testee.unlock("test", true);
+   }
+   
+   public void testNewLock() throws Exception
+   {
+      TesteeSet ts = getTesteeSet(node1, 0, 3);
+      assertEquals(LockResult.NEW_LOCK, ts.testee.lock("test", 1000, true));
+      assertEquals(LockResult.ALREADY_HELD, ts.testee.lock("test", 1000, true));
+      ts.testee.unlock("test", false);
+      ts.testee.unlock("test", false);
+      
+      resetToStrict(ts.partition);
+      
+      List<RemoteLockResponse> rspList = getOKResponses(2);
+      expect(ts.partition.getMethodCallTimeout()).andReturn(60000l);
+      expect(ts.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);
+      
+      replay(ts.partition);
+
+      assertEquals(LockResult.ACQUIRED_FROM_CLUSTER, ts.testee.lock("test", 1000, true));
+   }
+
+   protected TesteeSet getTesteeSet(ClusterNode node, int viewPos, int viewSize) throws Exception
+   {
+      HAPartition haPartition = createNiceMock(HAPartition.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); 
+      
+      SharedLocalYieldingClusterLockManager testee = new SharedLocalYieldingClusterLockManager("test", haPartition);
+      
+      testee.start();
+      
+      reset(haPartition);
+      
+      return new TesteeSet(testee, haPartition, c.getValue());     
+   }    
+   
+   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;
+   }
+
+   private static List<RemoteLockResponse> getOKResponses(int numResponses)
+   {
+      List<RemoteLockResponse> rspList = new ArrayList<RemoteLockResponse>();
+      for (int i = 0; i < numResponses + 1; i++)
+      {
+         rspList.add(new RemoteLockResponse(null, RemoteLockResponse.Flag.OK));
+      }
+      return rspList;
+   }
+   
+   private class TesteeSet
+   {
+      private final SharedLocalYieldingClusterLockManager testee;
+      private final HAPartition partition;
+      private final RpcTarget target;
+      
+      private TesteeSet(SharedLocalYieldingClusterLockManager testee, HAPartition partition, RpcTarget target)
+      {
+         this.testee = testee;
+         this.partition = partition;
+         this.target = target;
+      }
+   }
+   
+   private class Locker implements Runnable
+   {
+      private final TesteeSet ts;
+      private final boolean newLock;
+      private final CountDownLatch startLatch;
+      private final CountDownLatch endLatch;
+      private LockResult result;
+      private Exception exception;
+      
+      private Locker(TesteeSet ts, boolean newLock, CountDownLatch startLatch, CountDownLatch endLatch)
+      {
+         this.ts = ts;
+         this.newLock = newLock;
+         this.startLatch = startLatch;
+         this.endLatch = endLatch;
+      }
+      
+      public void run()
+      {
+         try
+         {
+            startLatch.await(10, TimeUnit.SECONDS);
+            result = ts.testee.lock("test", 1000, newLock);
+         }
+         catch (Exception e)
+         {
+            this.exception = e;
+         }
+         finally
+         {
+            endLatch.countDown();
+         }
+      }
+   }
+
+}




More information about the jboss-cvs-commits mailing list