/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.interceptors;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.config.Option;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.transaction.GlobalTransaction;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
/**
* Always place this interceptor at the start of the interceptor chain to ensure invocation contexts and set up and cleaned up correctly.
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
*/
public class InvocationContextInterceptor extends BaseTransactionalContextInterceptor implements InvocationContextInterceptorMBean
{
public InvocationContextInterceptor()
{
initLogger();
}
@Override
public Object invoke(InvocationContext ctx) throws Throwable
{
MethodCall call = ctx.getMethodCall();
Option optionOverride = ctx.getOptionOverrides();
boolean suppressExceptions = false;
Transaction suspendedTransaction = null;
boolean resumeSuspended = false;
if (trace)
log.trace("Invoked with InvocationContext [" + ctx + "]");
if (MethodDeclarations.isBlockUnblockMethod(call.getMethodId())) return nextInterceptor(ctx);
try
{
Transaction tx = getTransaction();
setTransactionalContext(tx, getGlobalTransaction(tx, call), ctx);
if (optionOverride != null)
{
if (optionOverride.isFailSilently())
{
log.debug("FAIL_SILENTLY Option is present - suspending any ongoing transaction.");
suppressExceptions = true;
if (ctx.getTransaction() != null)
{
suspendedTransaction = txManager.suspend();
setTransactionalContext(null, null, ctx);
if (trace) log.trace("Suspending transaction " + suspendedTransaction);
resumeSuspended = true;
}
else
{
if (trace) log.trace("No ongoing transaction to suspend");
}
}
}
Object retval;
try
{
retval = nextInterceptor(ctx);
}
catch (Throwable th)
{
retval = th;
}
// assume we're the first interceptor in the chain. Handle the exception-throwing.
if (retval instanceof Throwable)
{
// if fail silently return a null
if (suppressExceptions) return null;
Throwable t = (Throwable) retval;
if (t instanceof RuntimeException && t.getCause() != null)
throw t.getCause();
else
throw t;
}
return retval;
}
finally
{
// clean up any invocation-scope options set up
if (trace) log.trace("Resetting invocation-scope options");
ctx.getOptionOverrides().reset();
if (resumeSuspended)
{
txManager.resume(suspendedTransaction);
}
else
{
if (ctx.getTransaction() != null && (isValid(ctx.getTransaction())))// || isRollingBack(ctx.getTransaction())))
{
copyInvocationScopeOptionsToTxScope(ctx);
}
}
// JBCACHE-811 - backed out for now
// ctx.wipePeekedNodes();
}
}
private GlobalTransaction getGlobalTransaction(Transaction tx, MethodCall call)
{
GlobalTransaction gtx = findGlobalTransaction(call.getArgs());
if (gtx == null) gtx = cache.getCurrentTransaction(tx, false);
if (gtx != null) gtx.setRemote(isRemoteGlobalTx(gtx));
return gtx;
}
private Transaction getTransaction() throws SystemException
{
// this creates a context if one did not exist.
if (txManager == null)
{
if (trace) log.trace("no transaction manager configured, setting tx as null.");
return null;
}
else
{
return txManager.getTransaction();
}
}
protected GlobalTransaction findGlobalTransaction(Object[] params)
{
int clue = 0;
if (params[clue] instanceof GlobalTransaction)
return (GlobalTransaction) params[clue];
else
for (Object param : params) if (param instanceof GlobalTransaction) return (GlobalTransaction) param;
return null;
}
/**
* Tests if a global transaction originated from a different cache in the cluster
*
* @param gtx
* @return true if the gtx is remote, false if it originated locally.
*/
private boolean isRemoteGlobalTx(GlobalTransaction gtx)
{
return gtx != null && (gtx.getAddress() != null) && (!gtx.getAddress().equals(cache.getLocalAddress()));
}
}