View Javadoc

1   /*
2    * $Id: AbstractTxThreadAssociationTestCase.java 10468 2008-01-22 21:18:22Z aperepel $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.tck;
12  
13  import org.mule.MuleManager;
14  import org.mule.impl.DefaultExceptionStrategy;
15  import org.mule.impl.MuleTransactionConfig;
16  import org.mule.transaction.TransactionCallback;
17  import org.mule.transaction.TransactionTemplate;
18  import org.mule.transaction.XaTransaction;
19  import org.mule.transaction.XaTransactionFactory;
20  import org.mule.umo.UMOTransactionConfig;
21  import org.mule.umo.manager.UMOTransactionManagerFactory;
22  
23  import javax.transaction.Status;
24  import javax.transaction.Transaction;
25  import javax.transaction.TransactionManager;
26  
27  /**
28   * Validate certain expectations when working with JTA API. It is called to catch discrepancies in TM implementations
29   * and alert early. Subclasses are supposed to plug in specific transaction managers for tests.
30   */
31  public abstract class AbstractTxThreadAssociationTestCase extends AbstractMuleTestCase
32  {
33      /* To allow access from the dead TX threads we spawn. */
34      private TransactionManager tm;
35      protected static final int TRANSACTION_TIMEOUT_SECONDS = 3;
36  
37      protected void doSetUp() throws Exception
38      {
39          super.doSetUp();
40          UMOTransactionManagerFactory factory = getTransactionManagerFactory();
41          tm = factory.create();
42          assertNotNull("Transaction Manager should be available.", tm);
43          assertNull("There sould be no current transaction associated.", tm.getTransaction());
44      }
45  
46      public void testTxHandleCommitKeepsThreadAssociation() throws Exception
47      {
48          // don't wait for ages, has to be set before TX is begun
49          tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
50          tm.begin();
51  
52          Transaction tx = tm.getTransaction();
53          assertNotNull("Transaction should have started.", tx);
54          assertEquals("TX should have been active", Status.STATUS_ACTIVE, tx.getStatus());
55  
56          tx.commit();
57  
58          tx = tm.getTransaction();
59          assertNotNull("Committing via TX handle should NOT disassociated TX from the current thread.",
60                        tx);
61          assertEquals("TX status should have been COMMITTED.", Status.STATUS_COMMITTED, tx.getStatus());
62  
63          // Remove the TX-thread association. The only public API to achieve it is suspend(),
64          // technically we never resume the same transaction (TX forget).
65          Transaction suspended = tm.suspend();
66          assertTrue("Wrong TX suspended?.", suspended.equals(tx));
67          assertNull("TX should've been disassociated from the thread.", tm.getTransaction());
68  
69          // should be no-op and never fail
70          tm.resume(null);
71  
72          // ensure we don't have any TX-Thread association lurking around a main thread
73          assertNull(tm.getTransaction());
74      }
75  
76      public void testTxManagerCommitDissassociatesThread() throws Exception
77      {
78          // don't wait for ages, has to be set before TX is begun
79          tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
80          tm.begin();
81  
82          Transaction tx = tm.getTransaction();
83          assertNotNull("Transaction should have started.", tx);
84          assertEquals("TX should have been active", Status.STATUS_ACTIVE, tx.getStatus());
85  
86          tm.commit();
87  
88          assertNull("Committing via TX Manager should have disassociated TX from the current thread.",
89                     tm.getTransaction());
90      }
91  
92      public void testTxManagerRollbackDissassociatesThread() throws Exception
93      {
94          // don't wait for ages, has to be set before TX is begun
95          tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
96          tm.begin();
97  
98          Transaction tx = tm.getTransaction();
99          assertNotNull("Transaction should have started.", tx);
100         assertEquals("TX should have been active", Status.STATUS_ACTIVE, tx.getStatus());
101 
102         tm.rollback();
103 
104         assertNull("Committing via TX Manager should have disassociated TX from the current thread.",
105                    tm.getTransaction());
106     }
107 
108     /**
109      * AlwaysBegin action suspends current transaction and begins a new one.
110      *
111      * @throws Exception if any error
112      */
113     public void testAlwaysBeginXaTransactionSuspendResume() throws Exception
114     {
115         MuleManager.getInstance().setTransactionManager(tm);
116         assertNull("There sould be no current transaction associated.", tm.getTransaction());
117 
118         // don't wait for ages, has to be set before TX is begun
119         tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
120 
121         // this is one component with a TX always begin
122         UMOTransactionConfig config = new MuleTransactionConfig();
123         config.setFactory(new XaTransactionFactory());
124         config.setAction(UMOTransactionConfig.ACTION_ALWAYS_BEGIN);
125         TransactionTemplate template = new TransactionTemplate(config, new DefaultExceptionStrategy());
126 
127         // and the callee component which should begin new transaction, current must be suspended
128         final UMOTransactionConfig nestedConfig = new MuleTransactionConfig();
129         nestedConfig.setFactory(new XaTransactionFactory());
130         nestedConfig.setAction(UMOTransactionConfig.ACTION_ALWAYS_BEGIN);
131 
132         // start the call chain
133         template.execute(new TransactionCallback()
134         {
135             public Object doInTransaction() throws Exception
136             {
137                 // the callee executes within its own TX template, but uses the same global XA transaction,
138                 // bound to the current thread of execution via a ThreadLocal
139                 TransactionTemplate nestedTemplate =
140                         new TransactionTemplate(nestedConfig, new DefaultExceptionStrategy());
141                 final Transaction firstTx = tm.getTransaction();
142                 assertNotNull(firstTx);
143                 assertEquals(firstTx.getStatus(), Status.STATUS_ACTIVE);
144                 return nestedTemplate.execute(new TransactionCallback()
145                 {
146                     public Object doInTransaction() throws Exception
147                     {
148                         Transaction secondTx = tm.getTransaction();
149                         assertNotNull(secondTx);
150                         assertEquals(firstTx.getStatus(), Status.STATUS_ACTIVE);
151                         assertEquals(secondTx.getStatus(), Status.STATUS_ACTIVE);
152                         try
153                         {
154                             tm.resume(firstTx);
155                             fail("Second transaction must be active");
156                         }
157                         catch (java.lang.IllegalStateException e)
158                         {
159                             // expected
160 
161                             //Thrown if the thread is already associated with another transaction.
162                             //Second tx is associated with the current thread
163                         }
164                         try
165                         {
166                             Transaction currentTx = tm.suspend();
167                             assertTrue(currentTx.equals(secondTx));
168                             tm.resume(firstTx);
169                             assertEquals(firstTx, tm.getTransaction());
170                             assertEquals(firstTx.getStatus(), Status.STATUS_ACTIVE);
171                             assertEquals(secondTx.getStatus(), Status.STATUS_ACTIVE);
172                             Transaction a = tm.suspend();
173                             assertTrue(a.equals(firstTx));
174                             tm.resume(secondTx);
175                         }
176                         catch (Exception e)
177                         {
178                             fail("Error: " + e);
179                         }
180 
181                         // do not care about the return really
182                         return null;
183                     }
184                 });
185             }
186         });
187         assertNull("Committing via TX Manager should have disassociated TX from the current thread.",
188                    tm.getTransaction());
189     }
190 
191     /**
192      * This is a former XaTransactionTestCase.
193      *
194      * @throws Exception in case of any error
195      */
196     public void testXaTransactionTermination() throws Exception
197     {
198         MuleManager.getInstance().setTransactionManager(tm);
199         assertNull("There sould be no current transaction associated.", tm.getTransaction());
200 
201         // don't wait for ages, has to be set before TX is begun
202         tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
203 
204         XaTransaction muleTx = new XaTransaction();
205         assertFalse(muleTx.isBegun());
206         assertEquals(Status.STATUS_NO_TRANSACTION, muleTx.getStatus());
207         muleTx.begin();
208 
209         assertTrue(muleTx.isBegun());
210 
211         muleTx.commit();
212 
213         Transaction jtaTx = tm.getTransaction();
214         assertNull("Committing via TX Manager should have disassociated TX from the current thread.", jtaTx);
215         assertEquals(Status.STATUS_NO_TRANSACTION, muleTx.getStatus());
216     }
217 
218     /**
219      * This is a former TransactionTemplateTestCase.
220      * http://mule.mulesource.org/jira/browse/MULE-1494
221      *
222      * @throws Exception in case of any error
223      */
224     public void testNoNestedTxStarted() throws Exception
225     {
226         MuleManager.getInstance().setTransactionManager(tm);
227         assertNull("There sould be no current transaction associated.", tm.getTransaction());
228 
229         // don't wait for ages, has to be set before TX is begun
230         tm.setTransactionTimeout(TRANSACTION_TIMEOUT_SECONDS);
231 
232         // this is one component with a TX always begin
233         UMOTransactionConfig config = new MuleTransactionConfig();
234         config.setFactory(new XaTransactionFactory());
235         config.setAction(UMOTransactionConfig.ACTION_ALWAYS_BEGIN);
236         TransactionTemplate template = new TransactionTemplate(config, new DefaultExceptionStrategy());
237 
238         // and the callee component which should join the current XA transaction, not begin a nested one
239         final UMOTransactionConfig nestedConfig = new MuleTransactionConfig();
240         nestedConfig.setFactory(new XaTransactionFactory());
241         nestedConfig.setAction(UMOTransactionConfig.ACTION_BEGIN_OR_JOIN);
242 
243         // start the call chain
244         template.execute(new TransactionCallback()
245         {
246             public Object doInTransaction() throws Exception
247             {
248                 // the callee executes within its own TX template, but uses the same global XA transaction,
249                 // bound to the current thread of execution via a ThreadLocal
250                 TransactionTemplate nestedTemplate =
251                         new TransactionTemplate(nestedConfig, new DefaultExceptionStrategy());
252                 return nestedTemplate.execute(new TransactionCallback()
253                 {
254                     public Object doInTransaction() throws Exception
255                     {
256                         // do not care about the return really
257                         return null;
258                     }
259                 });
260             }
261         });
262     }
263 
264 
265     protected TransactionManager getTransactionManager()
266     {
267         return tm;
268     }
269 
270     protected abstract UMOTransactionManagerFactory getTransactionManagerFactory();
271 
272 }