| AbstractTxInterceptorBMT.java |
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb.plugins;
import java.util.Hashtable;
import java.rmi.RemoteException;
import javax.transaction.Transaction;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.RefAddr;
import javax.naming.spi.ObjectFactory;
import org.jboss.ejb.EnterpriseContext;
import org.jboss.ejb.AllowedOperationsAssociation;
import org.jboss.invocation.Invocation;
/**
* A common superclass for the BMT transaction interceptors.
*
* @author <a href="mailto:osh@sparre.dk">Ole Husgaard</a>
* @version $Revision: 1.13 $
*/
abstract class AbstractTxInterceptorBMT
extends AbstractTxInterceptor
{
// Attributes ----------------------------------------------------
/**
* This associates the thread to the UserTransaction.
*
* It is used to redirect lookups on java:comp/UserTransaction to
* the <code>getUserTransaction()</code> method of the context.
*/
private ThreadLocal userTransaction = new ThreadLocal();
/**
* If <code>false</code>, transactions may live across bean instance
* invocations, otherwise the bean instance should terminate any
* transaction before returning from the invocation.
* This attribute defaults to <code>true</code>.
*/
protected boolean stateless = true;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
// Interceptor implementation --------------------------------------
public void create()
throws Exception
{
// Do initialization in superclass.
super.create();
// bind java:comp/UserTransaction
RefAddr refAddr = new RefAddr("userTransaction") {
public Object getContent() {
return userTransaction;
}
};
Reference ref = new Reference("javax.transaction.UserTransaction",
refAddr,
new UserTxFactory().getClass().getName(),
null);
((Context)new InitialContext().lookup("java:comp/")).bind("UserTransaction", ref);
}
public void stop()
{
// bind java:comp/UserTransaction
try {
((Context)new InitialContext().lookup("java:comp/")).unbind("UserTransaction");
} catch (Exception e) {
//ignore
}
}
// Protected ----------------------------------------------------
/*
* This method calls the next interceptor in the chain.
*
* It handles the suspension of any client transaction, and the
* association of the calling thread with the instance transaction.
* And it takes care that any lookup of
* <code>java:comp/UserTransaction</code> will return the right
* UserTransaction for the bean instance.
*
* @param remoteInvocation If <code>true</code> this is an invocation
* of a method in the remote interface, otherwise
* it is an invocation of a method in the home
* interface.
* @param mi The <code>Invocation</code> of this call.
*/
protected Object invokeNext(Invocation mi)
throws Exception
{
// Save the transaction that comes with the MI
Transaction oldTransaction = mi.getTransaction();
// Get old threadlocal: It may be non-null if one BMT bean does a local
// call to another.
Object oldUserTx = userTransaction.get();
// Suspend any transaction associated with the thread: It may be
// non-null on optimized local calls.
Transaction threadTx = tm.suspend();
try {
EnterpriseContext ctx = ((EnterpriseContext) mi.getEnterpriseContext());
// Set the threadlocal to the userTransaction of the instance
try
{
AllowedOperationsAssociation.pushInMethodFlag(IN_INTERCEPTOR_METHOD);
userTransaction.set(ctx.getEJBContext().getUserTransaction());
}
finally
{
AllowedOperationsAssociation.popInMethodFlag();
}
// Get the bean instance transaction
Transaction beanTx = ctx.getTransaction();
// Resume the bean instance transaction
// only if it not null, some TMs can't resume(null), e.g. Tyrex
if (beanTx != null)
tm.resume(beanTx);
// Let the MI know about our new transaction
mi.setTransaction(beanTx);
try {
// Let the superclass call next interceptor and do the exception
// handling
return super.invokeNext(mi, false);
} finally {
try {
if (stateless)
checkStatelessDone();
} finally {
tm.suspend();
}
}
} finally {
// Reset threadlocal to its old value
userTransaction.set(oldUserTx);
// Restore old MI transaction
// OSH: Why ???
mi.setTransaction(oldTransaction);
// If we had a Tx associated with the thread reassociate
if (threadTx != null)
tm.resume(threadTx);
}
}
private void checkStatelessDone()
throws RemoteException
{
int status = Status.STATUS_NO_TRANSACTION;
try {
status = tm.getStatus();
} catch (SystemException ex) {
log.error("Failed to get status", ex);
}
switch (status) {
case Status.STATUS_ACTIVE:
case Status.STATUS_COMMITTING:
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_PREPARING:
case Status.STATUS_ROLLING_BACK:
try {
tm.rollback();
} catch (Exception ex) {
log.error("Failed to rollback", ex);
}
// fall through...
case Status.STATUS_PREPARED:
String msg = "Application error: BMT stateless bean " +
container.getBeanMetaData().getEjbName() +
" should complete transactions before" +
" returning (ejb1.1 spec, 11.6.1)";
log.error(msg);
// the instance interceptor will discard the instance
throw new RemoteException(msg);
}
}
// Inner classes -------------------------------------------------
public static class UserTxFactory
implements ObjectFactory
{
public Object getObjectInstance(Object ref, Name name,
Context nameCtx, Hashtable environment)
throws Exception
{
// The ref is a list with only one RefAddr ...
RefAddr refAddr = ((Reference)ref).get(0);
// ... whose content is the threadlocal
ThreadLocal threadLocal = (ThreadLocal)refAddr.getContent();
// The threadlocal holds the right UserTransaction
return threadLocal.get();
}
}
}
| AbstractTxInterceptorBMT.java |