}
public Object invoke(MethodCall call) throws Throwable
{
JBCMethodCall m = (JBCMethodCall) call;
Fqn fqn = null;
int lock_type = DataNode.LOCK_TYPE_NONE;
long lock_timeout = lock_acquisition_timeout;
Object[] args = m.getArgs();
InvocationContext ctx = getInvocationContext();
boolean storeLockedNode = false;
if (log.isTraceEnabled()) log.trace("PessimisticLockInterceptor invoked for method " + m);
if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isSuppressLocking())
{
log.trace("Suppressing locking");
switch (m.getMethodId())
{
case MethodDeclarations.putDataMethodLocal_id:
case MethodDeclarations.putDataEraseMethodLocal_id:
case MethodDeclarations.putKeyValMethodLocal_id:
case MethodDeclarations.putFailFastKeyValueMethodLocal_id:
log.trace("Creating nodes if necessary");
createNodes((Fqn) args[1], ctx.getGlobalTransaction());
break;
}
return super.invoke(m);
}
/** List<IdentityLock> locks. Locks acquired during the current method; will be released later by UnlockInterceptor.
* This list is only populated when there is no TX, otherwise the TransactionTable maintains the locks
* (keyed by TX) */
// List locks=null;
boolean recursive = false;
boolean createIfNotExists = false;
boolean zeroLockTimeout = false; // only used if the call is an evict() call. See JBCACHE-794
boolean isRemoveData = false;
// 1. Determine the type of lock (read, write, or none) depending on the method. If no lock is required, invoke
// the method, then return immediately
// Set the Fqn
switch (m.getMethodId())
{
case MethodDeclarations.putDataMethodLocal_id:
case MethodDeclarations.putDataEraseMethodLocal_id:
case MethodDeclarations.putKeyValMethodLocal_id:
case MethodDeclarations.putFailFastKeyValueMethodLocal_id:
createIfNotExists = true;
fqn = (Fqn) args[1];
lock_type = DataNode.LOCK_TYPE_WRITE;
if (m.getMethodId() == MethodDeclarations.putFailFastKeyValueMethodLocal_id)
lock_timeout = ((Long) args[5]).longValue();
break;
case MethodDeclarations.removeNodeMethodLocal_id:
fqn = (Fqn) args[1];
lock_type = DataNode.LOCK_TYPE_WRITE;
recursive = true; // remove node and *all* child nodes
// BES 2007/12/12 -- Revert JBCACHE-1165 fix as it causes endless loop
// in TransactionTest.testDoubleNodeRemoval, plus another failure
// in that test
// createIfNotExists = true;
// JBCACHE-871 We need to store the node
storeLockedNode = true;
break;
case MethodDeclarations.removeKeyMethodLocal_id:
case MethodDeclarations.removeDataMethodLocal_id:
isRemoveData = true;
case MethodDeclarations.addChildMethodLocal_id:
fqn = (Fqn) args[1];
lock_type = DataNode.LOCK_TYPE_WRITE;
break;
case MethodDeclarations.evictNodeMethodLocal_id:
zeroLockTimeout = true;
fqn = (Fqn) args[0];
lock_type = DataNode.LOCK_TYPE_WRITE;
break;
case MethodDeclarations.getKeyValueMethodLocal_id:
case MethodDeclarations.getNodeMethodLocal_id:
case MethodDeclarations.getKeysMethodLocal_id:
case MethodDeclarations.getChildrenNamesMethodLocal_id:
case MethodDeclarations.releaseAllLocksMethodLocal_id:
case MethodDeclarations.printMethodLocal_id:
fqn = (Fqn) args[0];
lock_type = DataNode.LOCK_TYPE_READ;
break;
case MethodDeclarations.lockMethodLocal_id:
fqn = (Fqn) args[0];
lock_type = ((Integer) args[1]).intValue();
recursive = ((Boolean) args[2]).booleanValue();
break;
case MethodDeclarations.commitMethod_id:
// commit propagated up from the tx interceptor
commit(ctx.getGlobalTransaction());
break;
case MethodDeclarations.rollbackMethod_id:
// rollback propagated up from the tx interceptor
rollback(ctx.getGlobalTransaction());
break;
default:
if (isOnePhaseCommitPrepareMehod(m))
{
// commit propagated up from the tx interceptor
commit(ctx.getGlobalTransaction());
}
break;
}
// Lock the node (must be either read or write if we get here)
// If no TX: add each acquired lock to the list of locks for this method (locks)
// If TX: [merge code from TransactionInterceptor]: register with TxManager, on commit/rollback,
// release the locks for the given TX
if (fqn != null)
{
if (createIfNotExists)
{
do
{
lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, zeroLockTimeout ? 0 : lock_timeout, createIfNotExists, storeLockedNode, isRemoveData);
}
while(!cache.exists(fqn)); // keep trying until we have the lock (fixes concurrent remove())
// terminates successfully, or with (Timeout)Exception
}
else
lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, zeroLockTimeout ? 0 : lock_timeout, createIfNotExists, storeLockedNode, isRemoveData);
}
else
{
if (log.isTraceEnabled())
log.trace("bypassed locking as method " + m.getName() + "() doesn't require locking");
}
if (m.getMethodId() == MethodDeclarations.lockMethodLocal_id)
return null;
Object o = super.invoke(m);
// FIXME this should be done in UnlockInterceptor, but I didn't want
// to add the removedNodes map to TreeCache
if (storeLockedNode && ctx.getGlobalTransaction() == null)
{
// do a REAL remove here.
// this is for NON TRANSACTIONAL calls
cache.realRemove(fqn, true);
}
else if (m.getMethodId() == MethodDeclarations.commitMethod_id || isOnePhaseCommitPrepareMehod(m) || m.getMethodId() == MethodDeclarations.rollbackMethod_id)
{
// and this is for transactional ones
cleanup(ctx.getGlobalTransaction());
}