User: fleury
Date: 00/09/26 10:30:19
Added: src/main/org/jboss/ejb/plugins TxInterceptorBMT.java
Log:
A new interceptor BMT for the session beans
Revision Changes Path
1.1 jboss/src/main/org/jboss/ejb/plugins/TxInterceptorBMT.java
Index: TxInterceptorBMT.java
===================================================================
/*
* jBoss, the OpenSource EJB server
*
* Distributable under GPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb.plugins;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.rmi.ServerException;
import java.util.*;
import javax.swing.tree.*;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.RollbackException;
import javax.transaction.TransactionRequiredException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
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 javax.ejb.EJBException;
import org.jboss.ejb.Container;
import org.jboss.ejb.EnterpriseContext;
import org.jboss.ejb.StatefulSessionEnterpriseContext;
import org.jboss.ejb.StatelessSessionEnterpriseContext;
import org.jboss.ejb.MethodInvocation;
import org.jboss.tm.TxManager;
import org.jboss.logging.Logger;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.BeanMetaData;
import org.jboss.metadata.SessionMetaData;
import org.jboss.metadata.MethodMetaData;
/**
* <description>
*
* @see <related>
* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Sebastien Alborini</a>
* @version $Revision: 1.1 $
*/
public class TxInterceptorBMT
extends AbstractInterceptor
{
// Attributes ----------------------------------------------------
private TxManager tm;
// lookup on java:comp/UserTransaction should be redirected to
// sessionContext.getUserTransaction()
// The ThreadLocal associates the thread to the UserTransaction
ThreadLocal userTransaction = new ThreadLocal();
protected Container container;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
// Public --------------------------------------------------------
public void setContainer(Container container)
{
this.container = container;
}
public Container getContainer()
{
return container;
}
// Interceptor implementation --------------------------------------
public void init()
throws Exception
{
// Store TM reference locally
tm = (TxManager) getContainer().getTransactionManager();
// 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 Object invokeHome(MethodInvocation mi)
throws Exception
{
// set the threadlocal to the userTransaction of the instance
// (mi has the sessioncontext from the previous interceptor)
if (((SessionMetaData)container.getBeanMetaData()).isStateful()) {
// retrieve the real userTransaction
userTransaction.set(((StatefulSessionEnterpriseContext)mi.getEnterpriseContext()).getSessionContext().getUserTransaction());
// t1 refers to the client transaction (spec ejb1.1, 11.6.1,
p174)
// this is necessary for optimized (inVM) calls: threads come
associated with the client transaction
Transaction t1 = tm.disassociateThread();
// t2 refers to the instance transaction (spec ejb1.1, 11.6.1,
p174)
Transaction t2 = mi.getEnterpriseContext().getTransaction();
try {
if (t2 != null) {
// associate the transaction to the thread
tm.associateThread(t2);
}
return getNext().invokeHome(mi);
} finally {
if (t1 != null) {
// reassociate the previous transaction before
returning
tm.associateThread(t1);
}
}
} else {
// stateless: no context, no transaction, no call to the
instance
return getNext().invokeHome(mi);
}
}
/**
* This method does invocation interpositioning of tx management
*
* @param id
* @param m
* @param args
* @return
* @exception Exception
*/
public Object invoke(MethodInvocation mi) throws Exception {
// set the threadlocal to the userTransaction of the instance
// (mi has the sessioncontext from the previous interceptor)
if (((SessionMetaData)container.getBeanMetaData()).isStateful()) {
// retrieve the real userTransaction
userTransaction.set(((StatefulSessionEnterpriseContext)mi.getEnterpriseContext()).getSessionContext().getUserTransaction());
} else {
// retrieve the real userTransaction
userTransaction.set(((StatelessSessionEnterpriseContext)mi.getEnterpriseContext()).getSessionContext().getUserTransaction());
}
// t1 refers to the client transaction (spec ejb1.1, 11.6.1, p174)
// this is necessary for optimized (inVM) calls: threads come
associated with the client transaction
Transaction t1 = tm.disassociateThread();
// DEBUG Logger.log("TxInterceptorBMT disassociate" + ((t1==null) ?
"null": Integer.toString(t1.hashCode())));
Logger.log("TxInterceptorBMT disassociate" + ((t1==null) ? "null":
Integer.toString(t1.hashCode())));
// t2 refers to the instance transaction (spec ejb1.1, 11.6.1, p174)
Transaction t2 = mi.getEnterpriseContext().getTransaction();
// DEBUG Logger.log("TxInterceptorBMT t2 in context" + ((t2==null) ?
"null": Integer.toString(t2.hashCode())));
Logger.log("TxInterceptorBMT t2 in context" + ((t2==null) ? "null":
Integer.toString(t2.hashCode())));
try {
if (t2 != null) {
// associate the transaction to the thread
tm.associateThread(t2);
}
return getNext().invoke(mi);
} finally {
if (t1 != null) {
// DEBUG Logger.log("TxInterceptorBMT reassociating
client tx " + t1.hashCode());
Logger.log("TxInterceptorBMT reassociating client tx "
+ t1.hashCode());
// reassociate the previous transaction before
returning
tm.associateThread(t1);
}
if
(((SessionMetaData)container.getBeanMetaData()).isStateless()) {
// t3 is the transaction associated with the context
at the end of the call
Transaction t3 =
mi.getEnterpriseContext().getTransaction();
Logger.log("in TxIntBMT " + t3);
// for a stateless sessionbean the transaction should
be completed at the end of the call
if (t3 != null) switch (t3.getStatus()) {
case Status.STATUS_ACTIVE:
case Status.STATUS_COMMITTING:
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_PREPARING:
case Status.STATUS_ROLLING_BACK:
t3.rollback();
case Status.STATUS_PREPARED:
// cf ejb1.1 11.6.1
Logger.log("Application error: CMT
stateless bean " + container.getBeanMetaData().getEjbName() + " should complete
transactions before returning (ejb1.1 spec, 11.6.1)");
// the instance interceptor will
discard the instance
throw new RemoteException("Application
error: CMT stateless bean " + container.getBeanMetaData().getEjbName() + " should
complete transactions before returning (ejb1.1 spec, 11.6.1)");
}
}
}
}
// Protected ----------------------------------------------------
// 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 whose content is the
threadlocal
ThreadLocal threadLocal =
(ThreadLocal)((Reference)ref).get(0).getContent();
// the threadlocal holds the UserTransaction
// we can now return the userTx, calls on it will indirect on
the right context
return threadLocal.get();
}
}
}