Author: manik.surtani(a)jboss.com
Date: 2008-06-25 09:23:13 -0400 (Wed, 25 Jun 2008)
New Revision: 6028
Modified:
core/trunk/src/main/java/org/jboss/cache/InvocationContext.java
core/trunk/src/main/java/org/jboss/cache/transaction/TransactionEntry.java
Log:
Updated interface methods to simplify collection and recording of locks.
Modified: core/trunk/src/main/java/org/jboss/cache/InvocationContext.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/InvocationContext.java 2008-06-25 13:21:38
UTC (rev 6027)
+++ core/trunk/src/main/java/org/jboss/cache/InvocationContext.java 2008-06-25 13:23:13
UTC (rev 6028)
@@ -18,8 +18,11 @@
import javax.transaction.Transaction;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
/**
* This context holds information specific to a method invocation.
@@ -27,7 +30,7 @@
* @author <a href="mailto:manik@jboss.org">Manik Surtani
(manik(a)jboss.org)</a>
*/
@SuppressWarnings("deprecation")
-public class InvocationContext implements Cloneable
+public class InvocationContext
{
private static final Log log = LogFactory.getLog(InvocationContext.class);
private static final boolean trace = log.isTraceEnabled();
@@ -44,46 +47,39 @@
private MethodCall methodCall;
@Deprecated
private VisitableCommand command;
- List<NodeLock> invocationLocks;
- // used to store cache peeks within the scope of a single context. Performing a cache
peek can be a huge bottle neck.
- // See JBCACHE-811
- // backing out for now
- //private Map<Fqn, NodeSPI> peekedNodes = new HashMap<Fqn, NodeSPI>();
-
/**
- * Retrieves a node that may have previously been "peeked" within the scope
of the same invocation.
- *
- * @param f fqn of node to find
- * @return node, if previously peeked, or null if not.
- * @since 2.1.0
+ * LinkedHashSet of locks acquired by the invocation. We use a LinkedHashSet because
we need efficient Set semantics
+ * but also need guaranteed ordering for use by lock release code (see JBCCACHE-874).
+ * <p/>
+ * This needs to be unchecked since we support both MVCC (Fqns held here) or legacy
Opt/Pess locking (NodeLocks held here).
+ * once we drop support for opt/pess locks we can genericise this to contain Fqns. -
Manik Surtani, June 2008
*/
-// public NodeSPI getPeekedNode(Fqn f)
-// {
-// return peekedNodes.get(f);
-// }
+ private LinkedHashSet invocationLocks;
- /**
- * Adds a node to the previously peeked list.
- *
- * @param n node to add
- * @param f fqn of node
- * @since 2.1.0
- */
-// public void savePeekedNode(NodeSPI n, Fqn f)
-// {
-// peekedNodes.put(f, n);
-// }
+ private final Map<Fqn, NodeSPI> lookedUpNodes = new HashMap<Fqn,
NodeSPI>();
- /**
- * Wipe list of previously peeked nodes.
- *
- * @since 2.1.0
- */
-// public void wipePeekedNodes()
-// {
-// peekedNodes.clear();
-// }
+ public NodeSPI lookUpNode(Fqn fqn)
+ {
+ // TODO: should delegate to TransactionEntry if one is in scope
+ return lookedUpNodes.get(fqn);
+ }
+
+ public void putLookedUpNode(Fqn f, NodeSPI n)
+ {
+ lookedUpNodes.put(f, n);
+ }
+
+ public void clearLookedUpNodes()
+ {
+ lookedUpNodes.clear();
+ }
+
+ public Map<Fqn, NodeSPI> getLookedUpNodes()
+ {
+ return lookedUpNodes;
+ }
+
public void setLocalRollbackOnly(boolean localRollbackOnly)
{
this.localRollbackOnly = localRollbackOnly;
@@ -188,26 +184,139 @@
return originLocal;
}
- public List<NodeLock> getInvocationLocksAcquired()
+ /**
+ * Returns an immutable, defensive copy of the List of locks currently maintained for
the current scope.
+ * <p/>
+ * Note that if a transaction is in scope, these locks are retrieved from the {@link
org.jboss.cache.transaction.TransactionEntry} rather than
+ * this {@link org.jboss.cache.InvocationContext}. Retrieving locks from here will
ensure they are retrieved from the appropriate
+ * scope.
+ * <p/>
+ * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support
for both MVCC (which uses Fqns as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
return <tt>List<Fqn></tt>.
+ *
+ * @return locks held in current scope.
+ */
+ @SuppressWarnings("unchecked")
+ public List getLocks()
{
- return invocationLocks;
+ // first check transactional scope
+ if (transactionEntry != null) return transactionEntry.getLocks();
+ return Collections.unmodifiableList(new ArrayList(invocationLocks));
}
- public void addInvocationLocksAcquired(Collection<NodeLock> locks)
+ /**
+ * Adds a List of locks to the currently maintained collection of locks acquired.
+ * <p/>
+ * Note that if a transaction is in scope, these locks are recorded on the {@link
org.jboss.cache.transaction.TransactionEntry} rather than
+ * this {@link org.jboss.cache.InvocationContext}. Adding locks here will ensure they
are promoted to the appropriate
+ * scope.
+ * <p/>
+ * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support
for both MVCC (which uses Fqns as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept <tt>List<Fqn></tt>.
+ *
+ * @param locks locks to add
+ */
+ @SuppressWarnings("unchecked")
+ public void addAllLocks(List locks)
{
- // no need to worry about concurrency here - a context is only valid for a single
thread.
- if (invocationLocks == null) invocationLocks = new ArrayList<NodeLock>(5);
- invocationLocks.addAll(locks);
+ // first check transactional scope
+ if (transactionEntry != null)
+ {
+ transactionEntry.addAllLocks(locks);
+ }
+ else
+ {
+ // no need to worry about concurrency here - a context is only valid for a
single thread.
+ if (invocationLocks == null) invocationLocks = new LinkedHashSet(5);
+ invocationLocks.addAll(locks);
+ }
}
- public void addInvocationLockAcquired(NodeLock l)
+ /**
+ * Adds a lock to the currently maintained collection of locks acquired.
+ * <p/>
+ * Note that if a transaction is in scope, this lock is recorded on the {@link
org.jboss.cache.transaction.TransactionEntry} rather than
+ * this {@link org.jboss.cache.InvocationContext}. Adding a lock here will ensure it
is promoted to the appropriate
+ * scope.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
+ *
+ * @param lock lock to add
+ */
+ @SuppressWarnings("unchecked")
+ public void addLock(Object lock)
{
- // no need to worry about concurrency here - a context is only valid for a single
thread.
- if (invocationLocks == null) invocationLocks = new ArrayList<NodeLock>(5);
- invocationLocks.add(l);
+ // first check transactional scope
+ if (transactionEntry != null)
+ {
+ transactionEntry.addLock(lock);
+ }
+ else
+ {
+ // no need to worry about concurrency here - a context is only valid for a
single thread.
+ if (invocationLocks == null) invocationLocks = new LinkedHashSet(5);
+ invocationLocks.add(lock);
+ }
}
/**
+ * Removes a lock from the currently maintained collection of locks acquired.
+ * <p/>
+ * Note that if a transaction is in scope, this lock is removed from the {@link
org.jboss.cache.transaction.TransactionEntry} rather than
+ * this {@link org.jboss.cache.InvocationContext}. Removing a lock here will ensure
it is removed in the appropriate
+ * scope.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
+ *
+ * @param lock lock to remove
+ */
+ @SuppressWarnings("unchecked")
+ public void removeLock(Object lock)
+ {
+ // first check transactional scope
+ if (transactionEntry != null)
+ {
+ transactionEntry.removeLock(lock);
+ }
+ else
+ {
+ // no need to worry about concurrency here - a context is only valid for a
single thread.
+ if (invocationLocks != null) invocationLocks.remove(lock);
+ }
+ }
+
+ /**
+ * Clears all locks from the currently maintained collection of locks acquired.
+ * <p/>
+ * Note that if a transaction is in scope, locks are cleared from the {@link
org.jboss.cache.transaction.TransactionEntry} rather than
+ * this {@link org.jboss.cache.InvocationContext}. Clearing locks here will ensure
they are cleared in the appropriate
+ * scope.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
+ */
+ public void clearLocks()
+ {
+ // first check transactional scope
+ if (transactionEntry != null)
+ {
+ transactionEntry.clearLocks();
+ }
+ else
+ {
+ // no need to worry about concurrency here - a context is only valid for a
single thread.
+ if (invocationLocks != null) invocationLocks.clear();
+ }
+ }
+
+ /**
* @return true if options exist to suppress locking - false otherwise. Note that
this is only used by the {@link org.jboss.cache.interceptors.PessimisticLockInterceptor}.
*/
public boolean isLockingSuppressed()
@@ -268,12 +377,20 @@
command = null;
}
- @Override
- public InvocationContext clone() throws CloneNotSupportedException
+ /**
+ * This is a "copy-factory-method" that should be used whenever a clone of
this class is needed. The resulting instance
+ * is equal() to, but not ==, to the template passed in.
+ *
+ * @param template template to copy
+ * @return a new InvocationContext
+ */
+ public static InvocationContext copy(InvocationContext template)
{
- InvocationContext clone = (InvocationContext) super.clone();
- clone.setOptionOverrides(getOptionOverrides().clone());
- return clone;
+ // TODO: Remove all use of cloning and replace with copy factory methods.
+// InvocationContext clone = (InvocationContext) super.clone();
+// clone.setOptionOverrides(getOptionOverrides().clone());
+// return clone;
+ return null;
}
/**
Modified: core/trunk/src/main/java/org/jboss/cache/transaction/TransactionEntry.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/transaction/TransactionEntry.java 2008-06-25
13:21:38 UTC (rev 6027)
+++ core/trunk/src/main/java/org/jboss/cache/transaction/TransactionEntry.java 2008-06-25
13:23:13 UTC (rev 6028)
@@ -12,6 +12,7 @@
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.Modification;
+import org.jboss.cache.NodeSPI;
import org.jboss.cache.commands.ReversibleCommand;
import org.jboss.cache.config.Option;
import org.jboss.cache.interceptors.OrderedSynchronizationHandler;
@@ -22,12 +23,13 @@
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
/**
* Information associated with a {@link GlobalTransaction} about the transaction state.
@@ -72,13 +74,16 @@
private List<ReversibleCommand> localModifications;
/**
- * LinkedHashSet<IdentityLock> of locks acquired by the transaction. We use
- * a LinkedHashSet because we need efficient Set semantics (same lock can
- * be added multiple times) but also need guaranteed ordering for use
- * by lock release code (see JBCCACHE-874).
+ * LinkedHashSet of locks acquired by the transaction. We use a LinkedHashSet because
we need efficient Set semantics
+ * but also need guaranteed ordering for use by lock release code (see JBCCACHE-874).
+ * <p/>
+ * This needs to be unchecked since we support both MVCC (Fqns held here) or legacy
Opt/Pess locking (NodeLocks held here).
+ * once we drop support for opt/pess locks we can genericise this to contain Fqns. -
Manik Surtani, June 2008
*/
- private final LinkedHashSet<NodeLock> locks = new
LinkedHashSet<NodeLock>();
+ private LinkedHashSet transactionLocks;
+ private final Map<Fqn, NodeSPI> lookedUpNodes = new HashMap<Fqn,
NodeSPI>();
+
/**
* A list of dummy uninitialised nodes created by the cache loader interceptor to load
data for a
* given node in this tx.
@@ -170,49 +175,106 @@
}
/**
- * Adds a lock to the end of the lock list, if it isn't already present.
+ * Adds a lock to the currently maintained collection of locks acquired.
+ * <p/>
+ * Most code could not use this method directly, but use {@link
org.jboss.cache.InvocationContext#addLock(Object)} instead,
+ * which would delegate to this method if a transaction is in scope or otherwise use
invocation-specific locks.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
+ *
+ * @param lock lock to add
+ * @see org.jboss.cache.InvocationContext#addLock(Object)
*/
- public void addLock(NodeLock l)
+ @SuppressWarnings("unchecked")
+ public void addLock(Object lock)
{
- if (l != null)
- {
- synchronized (locks)
- {
- locks.add(l);
- }
- }
+ // no need to worry about concurrency here - a context is only valid for a single
thread.
+ if (transactionLocks == null) transactionLocks = new LinkedHashSet(5);
+ transactionLocks.add(lock);
}
/**
- * Add multiple locks to the lock list.
+ * Removes a lock from the currently maintained collection of locks acquired.
+ * <p/>
+ * Most code could not use this method directly, but use {@link
org.jboss.cache.InvocationContext#removeLock(Object)} instead,
+ * which would delegate to this method if a transaction is in scope or otherwise use
invocation-specific locks.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
*
- * @param newLocks Collection<NodeLock>
+ * @param lock lock to remove
+ * @see org.jboss.cache.InvocationContext#removeLock(Object)
*/
- public void addLocks(Collection<NodeLock> newLocks)
+ @SuppressWarnings("unchecked")
+ public void removeLock(Object lock)
{
- if (newLocks != null)
- {
- synchronized (locks)
- {
- locks.addAll(newLocks);
- }
- }
+ // no need to worry about concurrency here - a context is only valid for a single
thread.
+ if (transactionLocks != null) transactionLocks.remove(lock);
}
/**
- * Returns the locks in use.
+ * Clears all locks from the currently maintained collection of locks acquired.
+ * <p/>
+ * Most code could not use this method directly, but use {@link
org.jboss.cache.InvocationContext#clearLocks()} instead,
+ * which would delegate to this method if a transaction is in scope or otherwise use
invocation-specific locks.
+ * <p/>
+ * Note that currently (as of 3.0.0) this lock is weakly typed. This is to allow
support for both MVCC (which uses {@link Fqn}s as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept {@link Fqn}.
*
- * @return a defensive copy of the internal data structure.
+ * @see org.jboss.cache.InvocationContext#clearLocks()
*/
- public List<NodeLock> getLocks()
+ public void clearLocks()
{
- synchronized (locks)
- {
- return new ArrayList<NodeLock>(locks);
- }
+ if (transactionLocks != null) transactionLocks.clear();
}
+
/**
+ * Adds a List of locks to the currently maintained collection of locks acquired.
+ * <p/>
+ * Most code could not use this method directly, but use {@link
org.jboss.cache.InvocationContext#addAllLocks(java.util.List)} instead,
+ * which would delegate to this method if a transaction is in scope or otherwise use
invocation-specific locks.
+ * <p/>
+ * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support
for both MVCC (which uses Fqns as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
accept <tt>List<Fqn></tt>.
+ *
+ * @param newLocks locks to add
+ * @see org.jboss.cache.InvocationContext#addAllLocks(java.util.List)
+ */
+ @SuppressWarnings("unchecked")
+ public void addAllLocks(List newLocks)
+ {
+ // no need to worry about concurrency here - a context is only valid for a single
thread.
+ if (transactionLocks == null) transactionLocks = new LinkedHashSet(5);
+ transactionLocks.addAll(newLocks);
+ }
+
+ /**
+ * Returns an immutable, defensive copy of the List of locks currently maintained for
the transaction.
+ * <p/>
+ * Most code could not use this method directly, but use {@link
org.jboss.cache.InvocationContext#getLocks()} instead,
+ * which would delegate to this method if a transaction is in scope or otherwise use
invocation-specific locks.
+ * <p/>
+ * Note that currently (as of 3.0.0) this list is unchecked. This is to allow support
for both MVCC (which uses Fqns as locks)
+ * as well as legacy Optimistic and Pessimistic Locking schemes (which use {@link
NodeLock} as locks). Once support for
+ * legacy node locking schemes are dropped, this method will be more strongly typed to
return <tt>List<Fqn></tt>.
+ *
+ * @return locks held in current scope.
+ * @see org.jboss.cache.InvocationContext#getLocks()
+ */
+ @SuppressWarnings("unchecked")
+ public List getLocks()
+ {
+ return Collections.unmodifiableList(new ArrayList(transactionLocks));
+ }
+
+
+ /**
* Gets the value of the forceAsyncReplication flag. Used by ReplicationInterceptor
and OptimisticReplicationInterceptor
* when dealing with {@link
org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within
* a transactional context.
@@ -305,12 +367,8 @@
@Override
public String toString()
{
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
sb.append("TransactionEntry\nmodificationList:
").append(modificationList);
- synchronized (locks)
- {
- sb.append("\nlocks: ").append(locks);
- }
return sb.toString();
}
@@ -370,7 +428,27 @@
return localModifications != null && !localModifications.isEmpty();
}
+ public NodeSPI lookUpNode(Fqn fqn)
+ {
+ return lookedUpNodes.get(fqn);
+ }
+ public void putLookedUpNode(Fqn f, NodeSPI n)
+ {
+ lookedUpNodes.put(f, n);
+ }
+
+ public void clearLookedUpNodes()
+ {
+ lookedUpNodes.clear();
+ }
+
+ public Map<Fqn, NodeSPI> getLookedUpNodes()
+ {
+ return lookedUpNodes;
+ }
+
+
/**
* Cleans up internal state
*/
@@ -380,7 +458,7 @@
if (modificationList != null) modificationList = null;
if (localModifications != null) localModifications = null;
option = null;
- locks.clear();
+ transactionLocks.clear();
if (dummyNodesCreatedByCacheLoader != null)
dummyNodesCreatedByCacheLoader.clear();
removedNodes.clear();
}