Author: dain Date: Fri Feb 25 15:10:24 2005 New Revision: 155376 URL: http://svn.apache.org/viewcvs?view=rev&rev=155376 Log: Fixed bugs in TransactionContext and handling of the contexts unturned by turning back on in-tx caching Added support for entrancy tracking and killing InstanceContexts
Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionCachingInterceptor.java geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionEnlistingInterceptor.java geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/TransactionCachingInterceptorTest.java geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/NontransactionalExecutorTask.java geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/ThreadPooledTimer.java geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/TransactionalExecutorTask.java geronimo/trunk/modules/timer/src/test/org/apache/geronimo/timer/AbstractThreadPooledTimerTest.java geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/DefaultInstanceContext.java geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/InstanceContext.java geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/InheritableTransactionContext.java geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/TransactionContext.java geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/UnspecifiedTransactionContext.java Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionCachingInterceptor.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionCachingInterceptor.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionCachingInterceptor.java (original) +++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionCachingInterceptor.java Fri Feb 25 15:10:24 2005 @@ -27,6 +27,7 @@ import org.apache.geronimo.transaction.ConnectionReleaser; import org.apache.geronimo.transaction.context.TransactionContext; import org.apache.geronimo.transaction.context.TransactionContextManager; +import org.apache.geronimo.transaction.context.InheritableTransactionContext; /** * TransactionCachingInterceptor.java @@ -59,16 +60,15 @@ } public void getConnection(ConnectionInfo connectionInfo) throws ResourceException { - TransactionContext transactionContext = transactionContextManager.getContext(); //There can be an inactive transaction context when a connection is requested in //Synchronization.afterCompletion(). - if (transactionContext == null || !transactionContext.isActive()) { - next.getConnection(connectionInfo); - } else { - ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) transactionContext.getManagedConnectionInfo(this); + TransactionContext transactionContext = transactionContextManager.getContext(); + if ((transactionContext instanceof InheritableTransactionContext) && ((InheritableTransactionContext) transactionContext).isActive()) { + InheritableTransactionContext inheritableTransactionContext = ((InheritableTransactionContext) transactionContext); + ManagedConnectionInfos managedConnectionInfos = (ManagedConnectionInfos) inheritableTransactionContext.getManagedConnectionInfo(this); if (managedConnectionInfos == null) { managedConnectionInfos = new ManagedConnectionInfos(); - transactionContext.setManagedConnectionInfo(this, managedConnectionInfos); + inheritableTransactionContext.setManagedConnectionInfo(this, managedConnectionInfos); } if (connectionInfo.isUnshareable()) { if (!managedConnectionInfos.containsUnshared(connectionInfo.getManagedConnectionInfo())) { @@ -85,6 +85,8 @@ managedConnectionInfos.setShared(connectionInfo.getManagedConnectionInfo()); } } + } else { + next.getConnection(connectionInfo); } } @@ -96,7 +98,7 @@ } TransactionContext transactionContext = transactionContextManager.getContext(); - if (transactionContext != null && transactionContext.isActive()) { + if ((transactionContext instanceof InheritableTransactionContext) && ((InheritableTransactionContext) transactionContext).isActive()) { return; } internalReturn(connectionInfo, connectionReturnAction); Modified: geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionEnlistingInterceptor.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionEnlistingInterceptor.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionEnlistingInterceptor.java (original) +++ geronimo/trunk/modules/connector/src/java/org/apache/geronimo/connector/outbound/TransactionEnlistingInterceptor.java Fri Feb 25 15:10:24 2005 @@ -24,6 +24,7 @@ import org.apache.geronimo.transaction.context.TransactionContext; import org.apache.geronimo.transaction.context.TransactionContextManager; +import org.apache.geronimo.transaction.context.InheritableTransactionContext; /** * TransactionEnlistingInterceptor.java @@ -48,9 +49,12 @@ try { ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); TransactionContext transactionContext = transactionContextManager.getContext(); - if (transactionContext != null && transactionContext.isActive()) { - XAResource xares = mci.getXAResource(); - transactionContext.getTransaction().enlistResource(xares); + if ((transactionContext instanceof InheritableTransactionContext)) { + InheritableTransactionContext inheritableTransactionContext = ((InheritableTransactionContext) transactionContext); + if (inheritableTransactionContext.isActive()) { + XAResource xares = mci.getXAResource(); + inheritableTransactionContext.getTransaction().enlistResource(xares); + } } } catch (SystemException e) { @@ -76,9 +80,12 @@ try { ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); TransactionContext transactionContext = transactionContextManager.getContext(); - if (transactionContext != null && transactionContext.isActive()) { - XAResource xares = mci.getXAResource(); - transactionContext.getTransaction().delistResource(xares, XAResource.TMSUSPEND); + if ((transactionContext instanceof InheritableTransactionContext)) { + InheritableTransactionContext inheritableTransactionContext = ((InheritableTransactionContext) transactionContext); + if (inheritableTransactionContext.isActive()) { + XAResource xares = mci.getXAResource(); + inheritableTransactionContext.getTransaction().delistResource(xares, XAResource.TMSUSPEND); + } } } catch (SystemException e) { Modified: geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/TransactionCachingInterceptorTest.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/TransactionCachingInterceptorTest.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/TransactionCachingInterceptorTest.java (original) +++ geronimo/trunk/modules/connector/src/test/org/apache/geronimo/connector/outbound/TransactionCachingInterceptorTest.java Fri Feb 25 15:10:24 2005 @@ -127,8 +127,6 @@ transactionCachingInterceptor.getConnection(connectionInfo1); assertTrue("Expected to get an initial connection", obtainedConnectionInfo != null); assertTrue("Expected nothing returned yet", returnedConnectionInfo == null); - assertTrue("Expected no ManagedConnectionInfo in the TransactionContext", - null == transactionContextManager.getContext().getManagedConnectionInfo(transactionCachingInterceptor)); obtainedConnectionInfo = null; ConnectionInfo connectionInfo2 = makeConnectionInfo(); transactionCachingInterceptor.getConnection(connectionInfo2); @@ -136,18 +134,12 @@ assertTrue("Expected nothing returned yet", returnedConnectionInfo == null); assertTrue("Expected different ManagedConnectionInfo in both ConnectionInfos", connectionInfo1.getManagedConnectionInfo() != connectionInfo2.getManagedConnectionInfo()); - assertTrue("Expected no ManagedConnectionInfo in the TransactionContext", - null == transactionContextManager.getContext().getManagedConnectionInfo(transactionCachingInterceptor)); //we didn't create any handles, so the "ManagedConnection" should be returned. - assertTrue("Expected TransactionContext to report inactive", !transactionContextManager.getContext().isActive()); transactionCachingInterceptor.returnConnection(connectionInfo1, ConnectionReturnAction.RETURN_HANDLE); assertTrue("Expected connection to be returned", returnedConnectionInfo != null); returnedConnectionInfo = null; transactionCachingInterceptor.returnConnection(connectionInfo2, ConnectionReturnAction.RETURN_HANDLE); assertTrue("Expected connection to be returned", returnedConnectionInfo != null); - - assertTrue("Expected TransactionContext to report inactive", !transactionContextManager.getContext().isActive()); - } public void testTransactionIndependence() throws Exception { Modified: geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/NontransactionalExecutorTask.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/NontransactionalExecutorTask.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/NontransactionalExecutorTask.java (original) +++ geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/NontransactionalExecutorTask.java Fri Feb 25 15:10:24 2005 @@ -19,14 +19,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.geronimo.timer.ExecutorTask; +import org.apache.geronimo.transaction.context.TransactionContext; +import org.apache.geronimo.transaction.context.UnspecifiedTransactionContext; /** - * - * * @version $Rev$ $Date$ - * - * */ + */ public class NontransactionalExecutorTask implements ExecutorTask { private static final Log log = LogFactory.getLog(NontransactionalExecutorTask.class); @@ -42,18 +40,26 @@ } public void run() { + UnspecifiedTransactionContext transactionContext = new UnspecifiedTransactionContext(); + TransactionContext oldTransactionContext = TransactionContext.getContext(); + TransactionContext.setContext(transactionContext); try { - userTask.run(); - } catch (Exception e) { - log.info(e); - } - try { - threadPooledTimer.workPerformed(workInfo); - } catch (PersistenceException e) { - log.info(e); - } - if (workInfo.isOneTime()) { - threadPooledTimer.removeWorkInfo(workInfo); + try { + userTask.run(); + } catch (Exception e) { + log.info(e); + } + try { + threadPooledTimer.workPerformed(workInfo); + } catch (PersistenceException e) { + log.info(e); + } + if (workInfo.isOneTime()) { + threadPooledTimer.removeWorkInfo(workInfo); + } + } finally { + transactionContext.commit(); + TransactionContext.setContext(oldTransactionContext); } } Modified: geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/ThreadPooledTimer.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/ThreadPooledTimer.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/ThreadPooledTimer.java (original) +++ geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/ThreadPooledTimer.java Fri Feb 25 15:10:24 2005 @@ -37,6 +37,7 @@ import org.apache.geronimo.gbean.GBeanLifecycle; import org.apache.geronimo.transaction.context.TransactionContext; import org.apache.geronimo.transaction.context.TransactionContextManager; +import org.apache.geronimo.transaction.context.InheritableTransactionContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -243,13 +244,21 @@ void registerSynchronization(Synchronization sync) throws RollbackException, SystemException { TransactionContext transactionContext = transactionContextManager.getContext(); + //TODO move the registerSynchronization to the TransactionContext - Transaction transaction = transactionContext == null? null: transactionContext.getTransaction(); + Transaction transaction; + if (transactionContext instanceof InheritableTransactionContext) { + InheritableTransactionContext inheritableTransactionContext = ((InheritableTransactionContext) transactionContext); + transaction = inheritableTransactionContext.getTransaction(); + assert transaction == null || inheritableTransactionContext.isActive(): "Trying to register a sync on an inactive transaction context"; + } else { + transaction = null; + } + if (transaction == null) { sync.beforeCompletion(); sync.afterCompletion(Status.STATUS_COMMITTED); } else { - assert transactionContext.isActive(): "Trying to register a sync on an inactive transaction context"; transaction.registerSynchronization(sync); } } Modified: geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/TransactionalExecutorTask.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/TransactionalExecutorTask.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/TransactionalExecutorTask.java (original) +++ geronimo/trunk/modules/timer/src/java/org/apache/geronimo/timer/TransactionalExecutorTask.java Fri Feb 25 15:10:24 2005 @@ -17,26 +17,14 @@ package org.apache.geronimo.timer; -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.NotSupportedException; -import javax.transaction.RollbackException; -import javax.transaction.SystemException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.geronimo.transaction.context.ContainerTransactionContext; import org.apache.geronimo.transaction.context.TransactionContextManager; -import org.apache.geronimo.timer.ExecutorTask; -import org.apache.geronimo.timer.PersistenceException; -import org.apache.geronimo.timer.ThreadPooledTimer; /** - * - * * @version $Rev$ $Date$ - * - * */ + */ public class TransactionalExecutorTask implements ExecutorTask { private static final Log log = LogFactory.getLog(TransactionalExecutorTask.class); @@ -54,49 +42,38 @@ this.transactionContextManager = transactionContextManager; this.repeatCount = repeatCount; } - + public void run() { ContainerTransactionContext transactionContext = null; for (int tries = 0; tries < repeatCount; tries++) { try { transactionContext = transactionContextManager.newContainerTransactionContext(); - } catch (NotSupportedException e) { - log.info(e); - break; - } catch (SystemException e) { - log.info(e); + } catch (Exception e) { + log.info("Exception occured while starting container transaction", e); break; } try { try { userTask.run(); } catch (Exception e) { - log.info(e); + log.info("Exception occured while running user task", e); } try { threadPooledTimer.workPerformed(workInfo); } catch (PersistenceException e) { - log.info(e); + log.info("Exception occured while updating timer persistent state", e); } } finally { try { - if (transactionContext.getRollbackOnly()) { - transactionContext.rollback(); - } else { - transactionContext.commit(); + if (transactionContext.commit()) { if (workInfo.isOneTime()) { threadPooledTimer.removeWorkInfo(workInfo); } + // todo this is a very weird code structure.... returning from a finally is very confusing return; } - } catch (SystemException e) { - log.info(e); - } catch (HeuristicMixedException e) { - log.info(e); - } catch (HeuristicRollbackException e) { - log.info(e); - } catch (RollbackException e) { - log.info(e); + } catch (Exception e) { + log.info("Exception occured while completing container transaction", e); } } } Modified: geronimo/trunk/modules/timer/src/test/org/apache/geronimo/timer/AbstractThreadPooledTimerTest.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/timer/src/test/org/apache/geronimo/timer/AbstractThreadPooledTimerTest.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/timer/src/test/org/apache/geronimo/timer/AbstractThreadPooledTimerTest.java (original) +++ geronimo/trunk/modules/timer/src/test/org/apache/geronimo/timer/AbstractThreadPooledTimerTest.java Fri Feb 25 15:10:24 2005 @@ -23,11 +23,6 @@ import org.apache.geronimo.transaction.context.TransactionContext; import org.apache.geronimo.transaction.context.TransactionContextManager; import org.apache.geronimo.timer.vm.VMWorkerPersistence; -import org.apache.geronimo.timer.ExecutorTaskFactory; -import org.apache.geronimo.timer.ThreadPooledTimer; -import org.apache.geronimo.timer.UserTaskFactory; -import org.apache.geronimo.timer.WorkerPersistence; -import org.apache.geronimo.timer.WorkInfo; /** * @@ -73,7 +68,7 @@ for (long i = 0; i < COUNT; i++) { timer.schedule(userTaskFactory, key, userId, userKey, i); } - Thread.currentThread().sleep(COUNT + SLOP); + Thread.sleep(COUNT + SLOP); assertEquals(COUNT, counter.get()); } @@ -85,7 +80,7 @@ for (int i = 0; i < workInfos.length; i++) { workInfos[i].getExecutorFeedingTimerTask().cancel(); } - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(0, counter.get()); } @@ -98,7 +93,7 @@ timer.doStart(); timer.playback(key, userTaskFactory); - Thread.currentThread().sleep(2 * SLOP + DELAY); + Thread.sleep(2 * SLOP + DELAY); assertEquals(COUNT, counter.get()); } @@ -134,48 +129,48 @@ for (long i = 0; i < COUNT; i++) { timer.schedule(userTaskFactory, key, userId, userKey, i); } - Thread.currentThread().sleep(COUNT + SLOP); + Thread.sleep(COUNT + SLOP); assertEquals(0, counter.get()); transactionContext.commit(); - Thread.currentThread().sleep(COUNT + SLOP); + Thread.sleep(COUNT + SLOP); assertEquals(COUNT, counter.get()); } public void testCancelInCommittedTransaction() throws Exception { - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); WorkInfo[] workInfos = new WorkInfo[COUNT]; for (long i = 0; i < COUNT; i++) { workInfos[(int) i] = timer.scheduleAtFixedRate(key, userTaskFactory, userId, userKey, DELAY, DELAY); } - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(COUNT, counter.get()); TransactionContext transactionContext = transactionContextManager.newContainerTransactionContext(); for (int i = 0; i < workInfos.length; i++) { workInfos[i].getExecutorFeedingTimerTask().cancel(); } - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(COUNT, counter.get()); transactionContext.commit(); - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(COUNT, counter.get()); } public void testCancelInRolledBackTransaction() throws Exception { - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); WorkInfo[] workInfos = new WorkInfo[COUNT]; for (long i = 0; i < COUNT; i++) { workInfos[(int) i] = timer.scheduleAtFixedRate(key, userTaskFactory, userId, userKey, DELAY, DELAY); } - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(COUNT, counter.get()); TransactionContext transactionContext = transactionContextManager.newContainerTransactionContext(); for (int i = 0; i < workInfos.length; i++) { workInfos[i].getExecutorFeedingTimerTask().cancel(); } - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); assertEquals(COUNT, counter.get()); transactionContext.rollback(); - Thread.currentThread().sleep(SLOP + DELAY); + Thread.sleep(SLOP + DELAY); // Catches up with two periods. assertEquals(3 * COUNT, counter.get()); } @@ -183,13 +178,13 @@ public void testRepeatCountFromPersisted() throws Exception { assert DELAY > 2 * SLOP; timer.scheduleAtFixedRate(key, userTaskFactory, userId, userKey, 0L, DELAY); - Thread.currentThread().sleep(4 * DELAY + SLOP); + Thread.sleep(4 * DELAY + SLOP); timer.doStop(); assertEquals(5, counter.get()); timer.doStart(); timer.playback(key, userTaskFactory); - Thread.currentThread().sleep(5 * DELAY + SLOP); + Thread.sleep(5 * DELAY + SLOP); assertEquals(2 * 5, counter.get()); } Modified: geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/DefaultInstanceContext.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/DefaultInstanceContext.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/DefaultInstanceContext.java (original) +++ geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/DefaultInstanceContext.java Fri Feb 25 15:10:24 2005 @@ -29,10 +29,11 @@ * * */ public class DefaultInstanceContext implements InstanceContext { - private final Map connectionManagerMap = new HashMap(); private final Set unshareableResources; private final Set applicationManagedSecurityResources; + private int callDepth; + private boolean dead = false; public DefaultInstanceContext(Set unshareableResources, Set applicationManagedSecurityResources) { this.unshareableResources = unshareableResources; @@ -43,9 +44,6 @@ return null; } - public void setId(Object id) { - } - public Object getContainerId() { return null; } @@ -62,6 +60,9 @@ public void afterCommit(boolean status) throws Exception { } + public void unassociate() throws Throwable { + } + public Map getConnectionManagerMap() { return connectionManagerMap; } @@ -74,4 +75,24 @@ return applicationManagedSecurityResources; } + public boolean isInCall() { + return callDepth > 0; + } + + public void enter() { + callDepth++; + } + + public void exit() { + assert isInCall(); + callDepth--; + } + + public boolean isDead() { + return dead; + } + + public void die() { + dead = true; + } } Modified: geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/InstanceContext.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/InstanceContext.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/InstanceContext.java (original) +++ geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/InstanceContext.java Fri Feb 25 15:10:24 2005 @@ -29,8 +29,6 @@ public interface InstanceContext { Object getId(); - void setId(Object id); - Object getContainerId(); void associate() throws Throwable; @@ -41,6 +39,8 @@ void afterCommit(boolean status) throws Throwable; + void unassociate() throws Throwable; + /** * IMPORTANT INVARIANT: this should always return a map, never null. * @return map of ConnectionManager to (list of ) managed connection info objects. @@ -51,4 +51,13 @@ Set getApplicationManagedSecurityResources(); + boolean isInCall(); + + void enter(); + + void exit(); + + boolean isDead(); + + void die(); } Modified: geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/InheritableTransactionContext.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/InheritableTransactionContext.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/InheritableTransactionContext.java (original) +++ geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/InheritableTransactionContext.java Fri Feb 25 15:10:24 2005 @@ -17,6 +17,10 @@ package org.apache.geronimo.transaction.context; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; import javax.transaction.SystemException; import javax.transaction.Status; import javax.transaction.Transaction; @@ -27,16 +31,17 @@ import javax.transaction.RollbackException; import org.apache.geronimo.transaction.ExtendedTransactionManager; +import org.apache.geronimo.transaction.ConnectionReleaser; +import org.apache.geronimo.transaction.InstanceContext; /** - * - * * @version $Rev$ $Date$ */ public abstract class InheritableTransactionContext extends TransactionContext { private final ExtendedTransactionManager txnManager; private Transaction transaction; private boolean threadAssociated = false; + private Map managedConnections; protected InheritableTransactionContext(ExtendedTransactionManager txnManager) { this.txnManager = txnManager; @@ -55,9 +60,26 @@ return transaction; } + public void setManagedConnectionInfo(ConnectionReleaser key, Object info) { + if (managedConnections == null) { + managedConnections = new HashMap(); + } + managedConnections.put(key, info); + } + + public Object getManagedConnectionInfo(ConnectionReleaser key) { + if (managedConnections == null) { + return null; + } + return managedConnections.get(key); + } + public boolean isActive() { + if (transaction == null) { + return false; + } try { - int status = txnManager.getStatus(); + int status = transaction.getStatus(); return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK; } catch (SystemException e) { return false; @@ -65,7 +87,6 @@ } public boolean getRollbackOnly() throws SystemException { - Transaction transaction = getTransaction(); if (transaction == null) { throw new IllegalStateException("There is no transaction in progress."); } @@ -77,7 +98,6 @@ } public void setRollbackOnly() throws IllegalStateException, SystemException { - Transaction transaction = getTransaction(); if (transaction == null) { throw new IllegalStateException("There is no transaction in progress."); } @@ -85,13 +105,16 @@ } public void begin(long transactionTimeoutMilliseconds) throws SystemException, NotSupportedException { + assert transaction == null: "Already associated with a transaction"; transaction = txnManager.begin(transactionTimeoutMilliseconds); threadAssociated = true; } public void suspend() throws SystemException { Transaction suspendedTransaction = txnManager.suspend(); - assert (transaction == suspendedTransaction) : "suspend did not return our transaction. ours: " + transaction + ", suspended returned: " + suspendedTransaction; + if (transaction != suspendedTransaction) { + throw new SystemException("Suspend did not return our transaction: expectedTx=" + transaction + ", suspendedTx=" + suspendedTransaction); + } threadAssociated = false; } @@ -101,6 +124,29 @@ } public boolean commit() throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException { + return complete(); + } + + public void rollback() throws SystemException { + setRollbackOnly(); + try { + complete(); + } catch (SystemException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw (SystemException) new SystemException("After commit of container transaction failed").initCause(e); + } + } + + private boolean complete() throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException { + if (transaction == null) { + throw new IllegalStateException("There is no transaction in progress."); + } + boolean wasCommitted = false; try { if (isRolledback()) { @@ -120,6 +166,14 @@ return false; } + // verify our tx is the current tx associated with the thread + // this is really only an error case and should never happen, but just to be sure double check + // immedately before committing using the transaction manager + Transaction currentTransaction = txnManager.getTransaction(); + if (currentTransaction != transaction) { + throw new SystemException("An unknown transaction is currently associated with the thread: expectedTx=" + transaction + ", currentTx=" + currentTransaction); + } + txnManager.commit(); wasCommitted = true; } catch (Throwable t) { @@ -130,23 +184,82 @@ } catch (Throwable e) { rollbackAndThrow("After commit of container transaction failed", e); } finally { + unassociateAll(); connectorAfterCommit(); transaction = null; + threadAssociated = false; } } return wasCommitted; } + private void beforeCommit() throws Throwable { + // @todo allow for enrollment during pre-commit + ArrayList toFlush = getAssociatedContexts(); + for (Iterator i = toFlush.iterator(); i.hasNext();) { + InstanceContext context = (InstanceContext) i.next(); + if (!context.isDead()) { + context.beforeCommit(); + } + } + } + + private void afterCommit(boolean status) throws Throwable { + Throwable firstThrowable = null; + ArrayList toFlush = getAssociatedContexts(); + for (Iterator i = toFlush.iterator(); i.hasNext();) { + InstanceContext context = (InstanceContext) i.next(); + if (!context.isDead()) { + try { + context.afterCommit(status); + } catch (Throwable e) { + if (firstThrowable == null) { + firstThrowable = e; + } + } + } + } + + if (firstThrowable instanceof Error) { + throw (Error) firstThrowable; + } else if (firstThrowable instanceof Exception) { + throw (Exception) firstThrowable; + } else if (firstThrowable != null) { + throw (SystemException) new SystemException().initCause(firstThrowable); + } + } + + private void connectorAfterCommit() { + if (managedConnections != null) { + for (Iterator entries = managedConnections.entrySet().iterator(); entries.hasNext();) { + Map.Entry entry = (Map.Entry) entries.next(); + ConnectionReleaser key = (ConnectionReleaser) entry.getKey(); + key.afterCompletion(entry.getValue()); + } + //If BeanTransactionContext never reuses the same instance for sequential BMT, this + //clearing is unnecessary. + managedConnections.clear(); + } + } + private boolean isRolledback() throws SystemException { int status; try { status = transaction.getStatus(); } catch (SystemException e) { - txnManager.rollback(); + transaction.rollback(); throw e; } if (status == Status.STATUS_MARKED_ROLLBACK) { + // verify our tx is the current tx associated with the thread + // this is really only an error case and should never happen, but just to be sure double check + // immedately before committing using the transaction manager + Transaction currentTransaction = txnManager.getTransaction(); + if (currentTransaction != transaction) { + throw new SystemException("An unknown transaction is currently associated with the thread: expectedTx=" + transaction + ", currentTx=" + currentTransaction); + } + // we need to rollback txnManager.rollback(); return true; @@ -160,7 +273,6 @@ private void rollbackAndThrow(String message, Throwable throwable) throws HeuristicMixedException, HeuristicRollbackException, SystemException, RollbackException { try { - // just incase there is a junk transaction on the thread if (txnManager.getStatus() != Status.STATUS_NO_TRANSACTION) { txnManager.rollback(); } @@ -168,6 +280,18 @@ log.error("Unable to roll back transaction", t); } + try { + // make doubly sure our transaction was rolled back + // this can happen when there was a junk transaction on the thread + int status = transaction.getStatus(); + if (status != Status.STATUS_ROLLEDBACK && + status != Status.STATUS_ROLLING_BACK) { + transaction.rollback(); + } + } catch (Throwable t) { + log.error("Unable to roll back transaction", t); + } + if (throwable instanceof HeuristicMixedException) { throw (HeuristicMixedException) throwable; } else if (throwable instanceof HeuristicRollbackException) { @@ -182,41 +306,6 @@ throw (RuntimeException) throwable; } else { throw (SystemException) new SystemException(message).initCause(throwable); - } - } - - public void rollback() throws SystemException { - try { - try { - if (txnManager.getStatus() != Status.STATUS_NO_TRANSACTION) { - txnManager.rollback(); - } - } finally { - try { - afterCommit(false); - } catch (Throwable e) { - try { - // just incase there is a junk transaction on the thread - if (txnManager.getStatus() != Status.STATUS_NO_TRANSACTION) { - txnManager.rollback(); - } - } catch (Throwable t1) { - log.error("Unable to roll back transaction", t1); - } - - if (e instanceof SystemException) { - throw (SystemException) e; - } else if (e instanceof Error) { - throw (Error) e; - } else if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw (SystemException) new SystemException("After commit of container transaction failed").initCause(e); - } - } - } finally { - connectorAfterCommit(); - transaction = null; } } } Modified: geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/TransactionContext.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/TransactionContext.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/TransactionContext.java (original) +++ geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/TransactionContext.java Fri Feb 25 15:10:24 2005 @@ -18,27 +18,21 @@ package org.apache.geronimo.transaction.context; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.RollbackException; import javax.transaction.SystemException; -import javax.transaction.Transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.geronimo.transaction.ConnectionReleaser; import org.apache.geronimo.transaction.DoubleKeyedHashMap; import org.apache.geronimo.transaction.InstanceContext; import org.tranql.cache.InTxCache; /** - * - * * @version $Rev$ $Date$ */ public abstract class TransactionContext { @@ -56,9 +50,12 @@ private InstanceContext currentContext; private final DoubleKeyedHashMap associatedContexts = new DoubleKeyedHashMap(); private final DoubleKeyedHashMap dirtyContexts = new DoubleKeyedHashMap(); - private Map managedConnections; private InTxCache inTxCache; + public abstract boolean getRollbackOnly() throws SystemException; + + public abstract void setRollbackOnly() throws SystemException; + public abstract void suspend() throws SystemException; public abstract void resume() throws SystemException, InvalidTransactionException; @@ -73,21 +70,51 @@ } } - public final void unassociate(Object containerId, Object id) throws Exception { - associatedContexts.remove(containerId, id); - dirtyContexts.remove(containerId, id); + public final void unassociate(InstanceContext context) throws Throwable { + associatedContexts.remove(context.getContainerId(), context.getId()); + context.unassociate(); + } + + public final void unassociate(Object containerId, Object id) throws Throwable { + InstanceContext context = (InstanceContext) associatedContexts.remove(containerId, id); + if (context != null) { + context.unassociate(); + } + } + + public final InstanceContext getContext(Object containerId, Object id) { + return (InstanceContext) associatedContexts.get(containerId, id); + } + + protected final ArrayList getAssociatedContexts() { + return new ArrayList(associatedContexts.values()); + } + + protected final void unassociateAll() { + ArrayList toFlush = getAssociatedContexts(); + for (Iterator i = toFlush.iterator(); i.hasNext();) { + InstanceContext context = (InstanceContext) i.next(); + try { + context.unassociate(); + } catch (Throwable throwable) { + log.warn("Error while unassociating instance from transaction context: " + context, throwable); + } + } } - public final InstanceContext beginInvocation(InstanceContext context) { + public final InstanceContext beginInvocation(InstanceContext context) throws Throwable { if (context.getId() != null) { + associate(context); dirtyContexts.put(context.getContainerId(), context.getId(), context); } + context.enter(); InstanceContext caller = currentContext; currentContext = context; return caller; } public final void endInvocation(InstanceContext caller) { + currentContext.exit(); currentContext = caller; } @@ -97,7 +124,9 @@ dirtyContexts.clear(); for (Iterator i = toFlush.iterator(); i.hasNext();) { InstanceContext context = (InstanceContext) i.next(); - context.flush(); + if (!context.isDead()) { + context.flush(); + } } } if (currentContext != null && currentContext.getId() != null) { @@ -108,42 +137,6 @@ } } - protected void beforeCommit() throws Throwable { - // @todo allow for enrollment during pre-commit - ArrayList toFlush = new ArrayList(associatedContexts.values()); - for (Iterator i = toFlush.iterator(); i.hasNext();) { - InstanceContext context = (InstanceContext) i.next(); - context.beforeCommit(); - } - } - - protected void afterCommit(boolean status) throws Throwable { - Throwable firstThrowable = null; - ArrayList toFlush = new ArrayList(associatedContexts.values()); - for (Iterator i = toFlush.iterator(); i.hasNext();) { - InstanceContext context = (InstanceContext) i.next(); - try { - context.afterCommit(status); - } catch (Throwable e) { - if (firstThrowable == null) { - firstThrowable = e; - } - } - } - - if (firstThrowable instanceof Error) { - throw (Error) firstThrowable; - } else if (firstThrowable instanceof Exception) { - throw (Exception) firstThrowable; - } else if (firstThrowable != null) { - throw (SystemException) new SystemException().initCause(firstThrowable); - } - } - - public final InstanceContext getContext(Object containerId, Object id) { - return (InstanceContext) associatedContexts.get(containerId, id); - } - public final void setInTxCache(InTxCache inTxCache) { this.inTxCache = inTxCache; } @@ -151,42 +144,4 @@ public final InTxCache getInTxCache() { return inTxCache; } - - //Geronimo connector framework support - public void setManagedConnectionInfo(ConnectionReleaser key, Object info) { - if (managedConnections == null) { - managedConnections = new HashMap(); - } - managedConnections.put(key, info); - } - - public Object getManagedConnectionInfo(ConnectionReleaser key) { - if (managedConnections == null) { - return null; - } - return managedConnections.get(key); - } - - /** - * determines if the transaction is in a pre-prepared state - * of STATUS_ACTIVE or STATUS_MARKED_ROLLBACK. - * @return true if more work can be done in the transaction (although it might be forced to roll back) - */ - public abstract boolean isActive(); - - public abstract Transaction getTransaction(); - - protected void connectorAfterCommit() { - if (managedConnections != null) { - for (Iterator entries = managedConnections.entrySet().iterator(); entries.hasNext();) { - Map.Entry entry = (Map.Entry) entries.next(); - ConnectionReleaser key = (ConnectionReleaser) entry.getKey(); - key.afterCompletion(entry.getValue()); - } - //If BeanTransactionContext never reuses the same instance for sequential BMT, this - //clearing is unnecessary. - managedConnections.clear(); - } - } - } Modified: geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/UnspecifiedTransactionContext.java URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/UnspecifiedTransactionContext.java?view=diff&r1=155375&r2=155376 ============================================================================== --- geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/UnspecifiedTransactionContext.java (original) +++ geronimo/trunk/modules/transaction/src/java/org/apache/geronimo/transaction/context/UnspecifiedTransactionContext.java Fri Feb 25 15:10:24 2005 @@ -17,16 +17,18 @@ package org.apache.geronimo.transaction.context; -import javax.transaction.Transaction; - -import org.apache.geronimo.transaction.ConnectionReleaser; - - /** * @version $Rev$ $Date$ */ public class UnspecifiedTransactionContext extends TransactionContext { - public void begin() { + private boolean failed = false; + + public boolean getRollbackOnly() { + return failed; + } + + public void setRollbackOnly() { + this.failed = true; } public void suspend() { @@ -36,33 +38,28 @@ } public boolean commit() { + complete(); + return true; + } + + public void rollback() { + setRollbackOnly(); + complete(); + } + + private void complete() { try { - flushState(); + if (!failed) { + flushState(); + } } catch (Error e) { throw e; } catch (RuntimeException re) { throw re; } catch (Throwable e) { log.error("Unable to flush state, continuing", e); + } finally { + unassociateAll(); } - return true; - } - - public void rollback() { - } - - public void setManagedConnectionInfo(ConnectionReleaser key, Object info) { - } - - public Object getManagedConnectionInfo(ConnectionReleaser key) { - return null; - } - - public boolean isActive() { - return false; - } - - public Transaction getTransaction() { - return null; } }