User: sparre Date: 01/06/19 22:56:12 Modified: src/main/org/jboss/ejb/plugins TxInterceptorCMT.java Log: Cleaned up source, and corrected exception handling. Revision Changes Path 1.12 +226 -434 jboss/src/main/org/jboss/ejb/plugins/TxInterceptorCMT.java Index: TxInterceptorCMT.java =================================================================== RCS file: /cvsroot/jboss/jboss/src/main/org/jboss/ejb/plugins/TxInterceptorCMT.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- TxInterceptorCMT.java 2001/06/18 20:01:23 1.11 +++ TxInterceptorCMT.java 2001/06/20 05:56:11 1.12 @@ -9,10 +9,9 @@ import java.lang.reflect.Method; import java.rmi.RemoteException; import java.rmi.ServerException; -import java.util.*; +import java.rmi.NoSuchObjectException; +import java.util.HashMap; -import javax.swing.tree.*; - import javax.transaction.Status; import javax.transaction.Transaction; import javax.transaction.TransactionManager; @@ -22,6 +21,7 @@ import javax.transaction.SystemException; import javax.ejb.EJBException; +import javax.ejb.NoSuchEntityException; import org.jboss.ejb.Container; import org.jboss.ejb.EnterpriseContext; @@ -33,23 +33,28 @@ import org.jboss.metadata.MethodMetaData; /** -* <description> -* -* @see <related> -* @author <a href="mailto:[EMAIL PROTECTED]">Rickard �berg</a> -* @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a> -* @author <a href="mailto:[EMAIL PROTECTED]">Sebastien Alborini</a> -* @author <a href="mailto:[EMAIL PROTECTED]">Anatoly Akkerman</a> -* @version $Revision: 1.11 $ -*/ + * This interceptor handles transactions for CMT beans. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Rickard �berg</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Marc Fleury</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Sebastien Alborini</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Anatoly Akkerman</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Ole Husgaard</a> + * @version $Revision: 1.12 $ + */ public class TxInterceptorCMT extends AbstractInterceptor { // Attributes ---------------------------------------------------- + + /** Local reference to the container's TransactionManager. */ private TransactionManager tm; + + /** A cache mapping methods to transaction attributes. */ private HashMap methodTx = new HashMap(); + /** The container that we manage transactions for. */ protected Container container; // Static -------------------------------------------------------- @@ -57,19 +62,26 @@ // Constructors -------------------------------------------------- // Public -------------------------------------------------------- + + /** + * Set the container that we manage transactions for. + */ public void setContainer(Container container) { this.container = container; } - public Container getContainer() + /** + * Set the container that we manage transactions for. + */ + public Container getContainer() { return container; } // Interceptor implementation -------------------------------------- public void init() - throws Exception + throws Exception { // Store TM reference locally tm = (TransactionManager) getContainer().getTransactionManager(); @@ -81,21 +93,17 @@ } public Object invokeHome(MethodInvocation mi) - throws Exception + throws Exception { return runWithTransactions(false, 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 { + * This method does invocation interpositioning of tx management + */ + public Object invoke(MethodInvocation mi) + throws Exception + { return runWithTransactions(true, mi); } @@ -123,471 +131,255 @@ default: name = "TX_UNKNOWN"; } -//DEBUG Logger.debug(name+" for "+m.getName()); + Logger.debug(name+" for "+m.getName()); } - private Object invokeNext(boolean remoteInvocation, MethodInvocation mi) throws Exception { - try - { - if (remoteInvocation) { - return getNext().invoke(mi); - } else { - return getNext().invokeHome(mi); - } - } catch (RuntimeException e) - { - // EJB 2.0 17.3, table 15 - if (mi.getTransaction() != null) - { - try { - mi.getTransaction().setRollbackOnly(); - } catch (IllegalStateException ex) { - } - RemoteException tre = new TransactionRolledbackException(e.getMessage()); - tre.detail = e; - throw tre; - } else - { - // This exception will be transformed into a RemoteException by the LogInterceptor - throw e; - } - } catch (RemoteException e) - { - // EJB 2.0 17.3, table 15 - if (mi.getTransaction() != null) - { - try { - mi.getTransaction().setRollbackOnly(); - } catch (IllegalStateException ex) { - } - RemoteException tre = new TransactionRolledbackException(e.getMessage()); - tre.detail = e; - throw tre; - } else - { - // This exception will be transformed into a RemoteException by the LogInterceptor - throw e; - } - } catch (Error e) - { - // EJB 2.0 17.3, table 15 - if (mi.getTransaction() != null) - { - try { - mi.getTransaction().setRollbackOnly(); - } catch (IllegalStateException ex) { - } - RemoteException tre = new TransactionRolledbackException(e.getMessage()); - tre.detail = e; - throw tre; - } else - { - // This exception will be transformed into a RemoteException by the LogInterceptor - throw e; - } - } + /* + * This method calls the next interceptor in the chain. + * + * Throwables <code>Error</code>, <code>RemoteException</code> and + * <code>RuntimeException</code> are caught and result in the transaction + * being marked for rollback only. If such a non-application exception + * happens with a transaction that was not started locally, it is wrapped + * in a <code>TransactionRolledbackException</code>. + * + * @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>MethodInvocation</code> of this call. + * @param newTx If <code>true</code> the transaction has just been + * started in this interceptor. + */ + private Object invokeNext(boolean remoteInvocation, MethodInvocation mi, boolean newTx) + throws Exception + { + try { + if (remoteInvocation) + return getNext().invoke(mi); + else + return getNext().invokeHome(mi); + } catch (RuntimeException e) { + try { + mi.getTransaction().setRollbackOnly(); + } catch (SystemException ex) { + Logger.exception(ex); + } catch (IllegalStateException ex) { + Logger.exception(ex); + } + RemoteException ex; + if (newTx) { + if (e instanceof NoSuchEntityException) { + // Convert NoSuchEntityException to a NoSuchObjectException + // with the same detail. + ex = new NoSuchObjectException(e.getMessage()); + ex.detail = ((NoSuchEntityException)e).getCausedByException(); + throw ex; + } + // OSH: Should this be wrapped? + ex = new ServerException(e.getMessage()); + } else + // We inherited tx: Tell caller that we marked for rollback only. + ex = new TransactionRolledbackException(e.getMessage()); + ex.detail = e; + throw ex; + } catch (RemoteException e) { + try { + mi.getTransaction().setRollbackOnly(); + } catch (SystemException ex) { + Logger.exception(ex); + } catch (IllegalStateException ex) { + Logger.exception(ex); + } + RemoteException ex; + if (newTx) { + if (e instanceof NoSuchObjectException) + throw e; // Do not wrap this. + // OSH: Should this be wrapped? + ex = new ServerException(e.getMessage()); + } else + ex = new TransactionRolledbackException(e.getMessage()); + ex.detail = e; + throw ex; + } catch (Error e) { + if (mi.getTransaction() != null) { + try { + mi.getTransaction().setRollbackOnly(); + } catch (IllegalStateException ex) { + } + RemoteException tre = new TransactionRolledbackException(e.getMessage()); + tre.detail = e; + throw tre; + } else { + // This exception will be transformed into a RemoteException + // by the LogInterceptor + throw e; + } + } } /* - * runWithTransactions - * - * This is where the meat is. We define what to do with the Tx based on the declaration. - * The MethodInvocation is always the final authority on what the Tx looks like leaving this - * interceptor. In other words, interceptors down the chain should not rely on the thread - * association with Tx but on the Tx present in the MethodInvocation - */ - - private Object runWithTransactions(boolean remoteInvocation, MethodInvocation mi) throws Exception { - - // Thread transaction is the transaction that the - // current thread came in with - Transaction threadTransaction = tm.getTransaction(); + * This method does invocation interpositioning of tx management. + * + * This is where the meat is. We define what to do with the Tx based + * on the declaration. + * The MethodInvocation is always the final authority on what the Tx + * looks like leaving this interceptor. In other words, interceptors + * down the chain should not rely on the thread association with Tx but + * on the Tx present in the MethodInvocation. + * + * @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>MethodInvocation</code> of this call. + */ + private Object runWithTransactions(boolean remoteInvocation, + MethodInvocation mi) + throws Exception + { // Old transaction is the transaction that comes with the MI Transaction oldTransaction = mi.getTransaction(); // New transaction is the new transaction this might start Transaction newTransaction = null; - - // Indicates if the call was made already - // in the context of the right transaction - boolean optimized = ( (threadTransaction != null) && - (oldTransaction != null) && - (threadTransaction.equals(oldTransaction)) ); - - //DEBUG Logger.debug("Current transaction in MI is "+mi.getTransaction()); - //DEBUG Logger.debug("Current thread transaction is "+threadTransaction); - //DEBUG Logger.debug("Current method "+mi.getMethod()); - byte transType = getTransactionMethod(mi.getMethod(), remoteInvocation); - + + //DEBUG Logger.debug("Current transaction in MI is "+mi.getTransaction()); + //DEBUG Logger.debug("Current method "+mi.getMethod()); + byte transType = getTransactionMethod(mi.getMethod(), remoteInvocation); printMethod(mi.getMethod(), transType); + + // Thread arriving must be clean (jboss doesn't set the thread + // previously). However optimized calls come with associated + // thread for example. We suspend the thread association here, and + // resume in the finally block of the following try. + Transaction threadTx = tm.suspend(); + try { // OSH FIXME: Indentation switch (transType) { - case MetaData.TX_NOT_SUPPORTED: - { - //DEBUG Logger.debug("TX_NOT_SUPPORTED begin"); - // Thread arriving must be clean (jboss doesn't set the thread previously) - // However optimized calls come with associated thread for example - if (threadTransaction != null) { - //DEBUG Logger.debug("Suspending current thread transaction"); - tm.suspend(); - } - - try { - - // Do not set a transaction on the thread even if in MI, just run - return invokeNext(remoteInvocation,mi ); - } - finally { - - // IN case we had a Tx associated with the thread reassociate - if (threadTransaction != null) { - //DEBUG Logger.debug("Resuming original thread transaction"); - tm.resume(threadTransaction); - } - //DEBUG Logger.debug("TX_NOT_SUPPORTED end"); - } - } - - + // Do not set a transaction on the thread even if in MI, just run + return invokeNext(remoteInvocation, mi, false); + case MetaData.TX_REQUIRED: - { - //DEBUG Logger.debug("TX_REQUIRED begin"); - - if (oldTransaction == null) { // No tx running - - if (threadTransaction != null) { - //DEBUG Logger.debug("Suspending current thread transaction"); - tm.suspend(); - } - - //DEBUG Logger.debug("Starting new transaction"); - // Create tx - tm.begin(); - - // get the tx - newTransaction = tm.getTransaction(); - - // Let the method invocation know - mi.setTransaction(newTransaction); - } - - else { // We have a tx propagated - - // are we in optimized tx context? - if ( ! optimized ) { - // the incoming thread is not optimized, so - // suspend its transaction - // DEBUG Logger.debug("Suspending current thread transaction"); - tm.suspend(); - // Associate the propagated tx with the thread - // DEBUG Logger.debug("Resuming propagated transaction"); - tm.resume(oldTransaction); - } - /* - else { - //DEBUG - Logger.debug("Optimized call -- current transaction same as propagated"); - } - */ - } - - // Continue invocation - try { - //DEBUG Logger.debug("Current transaction is "+tm.getTransaction()); - - return invokeNext(remoteInvocation,mi ); - } - catch (RemoteException e) { - - if (newTransaction != null) { - - //We started it, it will be rolled back in the finally - newTransaction.setRollbackOnly(); - } - - throw e; - } - catch (RuntimeException e) { - - if (newTransaction != null) { - - // We started it, it will be rolled back in the finally - newTransaction.setRollbackOnly(); - } - - throw new ServerException("Exception occurred", e); - } - catch (Error e) { - - if (newTransaction != null) { - - // we started it, it will be rolled back in the finally - newTransaction.setRollbackOnly(); - } - throw new ServerException("Exception occurred:"+e.getMessage()); - } - - finally { - - //DEBUG Logger.debug("TxInterceptorCMT: in finally"); - - // Do something wuth the transaction we've started - if (newTransaction != null) { - - //DEBUG Logger.debug("TxInterceptorCMT: newTransaction is not null"); - - // Marked rollback - if (newTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) { - - // actually roll it back - newTransaction.rollback(); - //DEBUG Logger.debug("TxInterceptorCMT: rolling back newTransaction"); - } - - //Still running - else if(newTransaction.getStatus() == Status.STATUS_ACTIVE) { - - // Commit tx - // This will happen if - // a) everything goes well - // b) app. exception was thrown -//DEBUG Logger.debug("TxInterceptorCMT:before commit"); - newTransaction.commit(); -//DEBUG Logger.debug("TxInterceptorCMT:after commit"); - - } - - // reassociate the oldTransaction with the methodInvocation (even null) - mi.setTransaction(oldTransaction); - } - - // Either the newTransaction was committed/rolled back - // or it was null. Just disassociate ourselves from - // whatever the current transaction is (the only optimization - // here could be to check if the current transaction is the - // same as the threadTransaction but this, AFAIK, is not - // very likely) - // In any case, if the original invocation was without a - // transactional context, we need to make sure we come out - // without one, especially not in the context of already - // committed transaction, for this may cause big trouble later on - - Transaction currentTx = tm.getTransaction(); - - if (threadTransaction == null) { - - if (currentTx != null) - tm.suspend(); - } - else if (! threadTransaction.equals(currentTx)) { - - tm.suspend(); - //DEBUG Logger.debug("TxInterceptorCMT: resuming threadTransaction"); - tm.resume(threadTransaction); + if (oldTransaction == null) { // No tx running + // Create tx + tm.begin(); + + // get the tx + newTransaction = tm.getTransaction(); + + // Let the method invocation know + mi.setTransaction(newTransaction); + } else { // We have a tx propagated + // Associate it with the thread + tm.resume(oldTransaction); + } + + // Continue invocation + try { + return invokeNext(remoteInvocation, mi, newTransaction != null); + } finally { +//DEBUG Logger.debug("TxInterceptorCMT: In finally"); + + // Only do something if we started the transaction + if (newTransaction != null) { + // Marked rollback + if (newTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) { + newTransaction.rollback(); + } else { + // Commit tx + // This will happen if + // a) everything goes well + // b) app. exception was thrown +//DEBUG Logger.debug("TxInterceptorCMT:before commit"); + newTransaction.commit(); +//DEBUG Logger.debug("TxInterceptorCMT:after commit"); } - /* else we are still in the right context, don't do anything */ - + + // reassociate the oldTransaction with the methodInvocation (even null) + mi.setTransaction(oldTransaction); + } else { + // Drop thread association + tm.suspend(); } } case MetaData.TX_SUPPORTS: { - - // DEBUG Logger.debug("TX_SUPPORTS begin"); - - if (oldTransaction != null) { // We have a tx propagated - // are we in the optimized tx context? - if ( ! optimized ) { - // the incoming thread is not optimized, so - // suspend its transaction - // DEBUG Logger.debug("Suspending current thread transaction"); - tm.suspend(); - // Associate the propagated tx with the thread - // DEBUG Logger.debug("Resuming propagated transaction"); - tm.resume(oldTransaction); - } - /* - else { - //DEBUG - Logger.debug("Optimized call -- current transaction same as propagated"); - } - */ - } - - // If we don't have a tx propagated we don't do anything - - // Continue invocation - try { - - return invokeNext(remoteInvocation,mi ); - } - catch (RuntimeException e) { - - throw new ServerException("Exception occurred", e); - } - catch (Error e) { - - throw new ServerException("Exception occurred:"+e.getMessage()); - } - finally { - - Transaction currentTx = tm.getTransaction(); - - if (threadTransaction == null) { - - if (currentTx != null) - tm.suspend(); - } - else if ( ! threadTransaction.equals(currentTx) ){ + // Associate old transaction (may be null) with the thread + tm.resume(oldTransaction); + + try { + return invokeNext(remoteInvocation, mi, false); + } finally { tm.suspend(); - // resume the original transaction - //DEBUG Logger.debug("Resuming original thread transaction"); - tm.resume(threadTransaction); - } - - //DEBUG Logger.debug("TX_SUPPORTS end"); } + + // Even on error we don't do anything with the tx, we didn't start it } - + case MetaData.TX_REQUIRES_NEW: { - - // Thread arriving must be clean (jboss doesn't set the thread previously) - // However optimized calls come with associated thread for example - tm.suspend(); - - // Always begin a transaction tm.begin(); - + // get it newTransaction = tm.getTransaction(); - + // Set it on the method invocation mi.setTransaction(newTransaction); - // Continue invocation try { - - return invokeNext(remoteInvocation,mi ); - } - catch (RemoteException e) { - - // We started it for sure - // will be rolled back in the finally - newTransaction.setRollbackOnly(); - - throw e; - } - catch (RuntimeException e) { - - // We started it for sure - // will be rolled back in the finally - newTransaction.setRollbackOnly(); - - throw new ServerException("Exception occurred", e); - } - catch (Error e) { - - // We started it for sure - // will be rolled back in the finally - newTransaction.setRollbackOnly(); - - throw new ServerException("Exception occurred:"+e.getMessage()); - } - finally { - + return invokeNext(remoteInvocation, mi, true); + } finally { // We started the transaction for sure so we commit or roll back - + if (newTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) { - newTransaction.rollback(); - } - else { - + } else { // Commit tx // This will happen if // a) everything goes well // b) app. exception was thrown newTransaction.commit(); } - - // set the old transaction back on the method invocation - mi.setTransaction(oldTransaction); - - // make sure if we came w/o a transactional - // context into this call, we leave w/o one - tm.suspend(); - - // or in case we had a Tx associated - // with this thread reassociate - if (threadTransaction != null) { - tm.resume(threadTransaction); - } + + // set the old transaction back on the method invocation mi.setTransaction(oldTransaction); } } - case MetaData.TX_MANDATORY: - { - - if (oldTransaction == null) { // no transaction = bad! - - throw new TransactionRequiredException("Transaction Required, read the spec!"); - } - else { - // are we in the optimized tx context? - if ( ! optimized ) { - // clean up this thread - tm.suspend(); - - // Associate it with the thread - tm.resume(oldTransaction); - } - - try { - return invokeNext(remoteInvocation,mi ); - } - finally { - - Transaction currentTx = tm.getTransaction(); - - if (threadTransaction == null) { - - if (currentTx != null) - tm.suspend(); - } - else if ( ! threadTransaction.equals(currentTx) ) { - tm.suspend(); - tm.resume(threadTransaction); - } - // else we are still in the right context - } - } + if (oldTransaction == null) // no transaction = bad! + throw new TransactionRequiredException("Transaction Required, read the spec!"); + // Associate it with the thread + tm.resume(oldTransaction); + try { + return invokeNext(remoteInvocation, mi, false); + } finally { + tm.suspend(); } - + case MetaData.TX_NEVER: - { - - if (oldTransaction != null) { // Transaction = bad! - - throw new RemoteException("Transaction not allowed"); - } - else { - - return invokeNext(remoteInvocation,mi ); - } - } + if (oldTransaction != null) // Transaction = bad! + throw new RemoteException("Transaction not allowed"); + return invokeNext(remoteInvocation, mi, false); } - + + } finally { // OSH FIXME: Indentation + // IN case we had a Tx associated with the thread reassociate + if (threadTx != null) + tm.resume(threadTx); + } + return null; } // Protected ---------------------------------------------------- // This should be cached, since this method is called very often - protected byte getTransactionMethod(Method m, boolean remoteInvocation) { + protected byte getTransactionMethod(Method m, boolean remoteInvocation) + { Byte b = (Byte)methodTx.get(m); - if(b != null) return b.byteValue(); + if (b != null) return b.byteValue(); BeanMetaData bmd = container.getBeanMetaData(); _______________________________________________ Jboss-development mailing list [EMAIL PROTECTED] http://lists.sourceforge.net/lists/listinfo/jboss-development
