View Javadoc

1   /*
2    * $Id: TransactionTemplate.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.transaction;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.transaction.ExternalTransactionAwareTransactionFactory;
15  import org.mule.api.transaction.Transaction;
16  import org.mule.api.transaction.TransactionCallback;
17  import org.mule.api.transaction.TransactionConfig;
18  import org.mule.api.transaction.TransactionException;
19  import org.mule.api.transaction.TransactionFactory;
20  import org.mule.config.i18n.CoreMessages;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  public class TransactionTemplate<T>
26  {
27      private static final Log logger = LogFactory.getLog(TransactionTemplate.class);
28  
29      private final TransactionConfig config;
30      private final MuleContext context;
31  
32      public TransactionTemplate(TransactionConfig config, MuleContext context)
33      {
34          this.config = config;
35          this.context = context;
36      }
37  
38      public T execute(TransactionCallback<T> callback) throws Exception
39      {
40          //if we want to skip TT
41          if (config == null)
42          {
43              return callback.doInTransaction();
44          }
45  
46          Transaction joinedExternal = null;
47          byte action = (config != null) ? config.getAction() : TransactionConfig.ACTION_DEFAULT;
48          Transaction tx = TransactionCoordination.getInstance().getTransaction();
49          if (tx == null && context != null && config != null && config.isInteractWithExternal())
50          {
51  
52              TransactionFactory tmFactory = config.getFactory();
53              if (tmFactory instanceof ExternalTransactionAwareTransactionFactory)
54              {
55                  ExternalTransactionAwareTransactionFactory extmFactory =
56                      (ExternalTransactionAwareTransactionFactory) tmFactory;
57                  joinedExternal = tx = extmFactory.joinExternalTransaction(context);
58              }
59          }
60  
61          Transaction suspendedXATx = null;
62          
63          if (action == TransactionConfig.ACTION_NEVER && tx != null)
64          {
65              throw new IllegalTransactionStateException(
66                  CoreMessages.transactionAvailableButActionIs("Never"));
67          }
68          else if ((action == TransactionConfig.ACTION_NONE || action == TransactionConfig.ACTION_ALWAYS_BEGIN)
69                     && tx != null)
70          {
71              if (logger.isDebugEnabled())
72              {
73                  logger.debug(action + ", " + "current TX: " + tx);
74              }
75  
76              if (tx.isXA())
77              {
78                  // suspend current transaction
79                  suspendedXATx = tx;
80                  suspendXATransaction(suspendedXATx);
81              }
82              else
83              {
84                  // commit/rollback
85                  resolveTransaction(tx);
86              }
87              //transaction will be started below
88              tx = null;
89          }
90          else if (action == TransactionConfig.ACTION_ALWAYS_JOIN && tx == null)
91          {
92              throw new IllegalTransactionStateException(
93                  CoreMessages.transactionNotAvailableButActionIs("Always Join"));
94          }
95  
96          if (action == TransactionConfig.ACTION_ALWAYS_BEGIN
97              || (action == TransactionConfig.ACTION_BEGIN_OR_JOIN && tx == null))
98          {
99              logger.debug("Beginning transaction");
100             tx = config.getFactory().beginTransaction(context);
101             logger.debug("Transaction successfully started: " + tx);
102         }
103         else
104         {
105             tx = null;
106         }
107 
108         try
109         {
110             T result = callback.doInTransaction();
111             if (tx != null)
112             {
113                 //verify that transaction is still active
114                 tx = TransactionCoordination.getInstance().getTransaction();
115             }
116             if (tx != null)
117             {
118                 resolveTransaction(tx);
119             }
120             if (suspendedXATx != null)
121             {
122                 resumeXATransaction(suspendedXATx);
123                 tx = suspendedXATx;
124             }
125             return result;
126         }
127         catch (Exception e)
128         {
129             tx = TransactionCoordination.getInstance().getTransaction();
130             if (tx != null)
131             {
132                 tx.setRollbackOnly();
133 
134                 // The exception strategy can choose to route exception
135                 // messages as part of the current transaction. So only rollback the
136                 // tx if it has been marked for rollback (which is the default
137                 // case in the AbstractExceptionListener)
138                 if (tx.isRollbackOnly())
139                 {
140                     logger.debug("Exception caught: rollback transaction", e);
141                 }
142                 resolveTransaction(tx);
143             }
144             if (suspendedXATx != null)
145             {
146                 resumeXATransaction(suspendedXATx);
147                 // we've handled this exception above. just return null now, this way we isolate
148                 // the context delimited by XA's ALWAYS_BEGIN
149                 return null;
150             }
151             throw e;
152         }
153         catch (Error e)
154         {
155             if (tx != null)
156             {
157                 logger.info("Error caught, rolling back TX " + tx, e);
158                 tx.rollback();
159             }
160             throw e;
161         }
162         finally
163         {
164             if (joinedExternal != null)
165                 TransactionCoordination.getInstance().unbindTransaction(joinedExternal);
166         }
167     }
168 
169     protected void resolveTransaction(Transaction tx) throws TransactionException
170     {
171         if (tx.isRollbackOnly())
172         {
173             if (logger.isDebugEnabled())
174             {
175                 logger.debug("Transaction has been marked rollbackOnly, rolling it back: " + tx);
176             }
177             tx.rollback();
178         }
179         else
180         {
181             if (logger.isDebugEnabled())
182             {
183                 logger.debug("Committing transaction " + tx);
184             }
185             tx.commit();
186         }
187     }
188 
189     protected void suspendXATransaction(Transaction tx) throws TransactionException
190     {
191         if (logger.isDebugEnabled())
192         {
193             logger.debug("Suspending " + tx);
194         }
195 
196         tx.suspend();
197 
198         if (logger.isDebugEnabled())
199         {
200             logger.debug("Successfully suspended " + tx);
201             logger.debug("Unbinding the following TX from the current context: " + tx);
202         }
203 
204         TransactionCoordination.getInstance().unbindTransaction(tx);
205     }
206 
207     protected void resumeXATransaction(Transaction tx) throws TransactionException
208     {
209         if (logger.isDebugEnabled())
210         {
211             logger.debug("Re-binding and Resuming " + tx);
212         }
213 
214         TransactionCoordination.getInstance().bindTransaction(tx);
215         tx.resume();
216     }
217 
218 }
219