[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