package org.jboss.cache.interceptors;
import org.jboss.cache.CacheImpl;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.config.Option;
import org.jboss.cache.factories.annotations.ComponentName;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.transaction.GlobalTransaction;
import javax.transaction.Transaction;
/**
* Always at the end of the chain, directly in front of the cache. Simply calls into the cache using reflection.
* If the call resulted in a modification, add the Modification to the end of the modification list
* keyed by the current transaction.
* <p/>
* Although always added to the end of an optimistically locked chain as well, calls should not make it down to
* this interceptor unless it is a call the OptimisticNodeInterceptor knows nothing about.
*
* @author Bela Ban
* @version $Id: CallInterceptor.java 4968 2008-01-03 19:17:09Z manik.surtani@jboss.com $
*/
public class CallInterceptor extends Interceptor
{
private CacheImpl cacheImpl;
private CacheSPI remoteDelegate;
public CallInterceptor()
{
initLogger();
}
@Inject
private void injectDependencies(CacheImpl cacheImpl, @ComponentName("remoteDelegate")CacheSPI remoteDelegate)
{
this.cacheImpl = cacheImpl;
this.remoteDelegate = remoteDelegate;
}
public Object invoke(InvocationContext ctx) throws Throwable
{
MethodCall m = ctx.getMethodCall();
Object retval = null;
if (!MethodDeclarations.isTransactionLifecycleMethod(m.getMethodId()))
{
if (trace) log.trace("Passing up method " + m + " so it gets invoked on cache.");
try
{
retval = m.invoke(m.getMethodId() == MethodDeclarations.dataGravitationCleanupMethod_id ? remoteDelegate : cacheImpl);
}
catch (Throwable t)
{
retval = t;
}
}
else
{
if (trace) log.trace("Suppressing invocation of method " + m + " on cache.");
}
Transaction tx = ctx.getTransaction();
if (tx != null && isValid(tx))
{
// test for exceptions.
if (retval instanceof Throwable)
{
tx.setRollbackOnly(); // no exception, regular return
}
else
{
// only add the modification to the modification list if we are using pessimistic locking.
// Optimistic locking calls *should* not make it this far down the interceptor chain, but just
// in case a method has been invoked that the OptimisticNodeInterceptor knows nothing about, it will
// filter down here.
if (!configuration.isNodeLockingOptimistic() && MethodDeclarations.isCrudMethod(m.getMethodId()))
{
// if method is a CRUD (Create/Remove/Update/Delete) method: add it to the modification
// list, otherwise skip (e.g. get() is not added)
// add the modification to the TX's modification list. this is used to later
// (on TX commit) send all modifications done in this TX to all members
GlobalTransaction gtx = ctx.getGlobalTransaction();
if (gtx == null)
{
if (log.isDebugEnabled())
{
log.debug("didn't find GlobalTransaction for " + tx + "; won't add modification to transaction list");
}
}
else
{
Option o = ctx.getOptionOverrides();
if (o != null && o.isCacheModeLocal())
{
log.debug("Not adding method to modification list since cache mode local is set.");
}
else
{
cache.getTransactionTable().addModification(gtx, m);
}
if (cache.getCacheLoaderManager() != null)
cache.getTransactionTable().addCacheLoaderModification(gtx, m);
}
}
}
}
if (retval instanceof Throwable)
{
throw (Throwable) retval;
}
return retval;
}
}