View Javadoc

1   /*
2    * $Id: TransactionTemplate.java 10524 2008-01-24 19:20:11Z akuzmin $
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.transaction;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.umo.TransactionException;
15  import org.mule.umo.UMOTransaction;
16  import org.mule.umo.UMOTransactionConfig;
17  
18  import java.beans.ExceptionListener;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  public class TransactionTemplate
24  {
25      private static final Log logger = LogFactory.getLog(TransactionTemplate.class);
26  
27      private final UMOTransactionConfig config;
28      private final ExceptionListener exceptionListener;
29  
30      public TransactionTemplate(UMOTransactionConfig config, ExceptionListener listener)
31      {
32          this.config = config;
33          exceptionListener = listener;
34      }
35  
36      public Object execute(TransactionCallback callback) throws Exception
37      {
38          if (config == null || (config!=null && !config.isConfigured()))
39          {
40              return callback.doInTransaction();
41          }
42          else
43          {
44              byte action = config.getAction();
45              UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
46              UMOTransaction suspendedXATx = null;
47  
48              if (action == UMOTransactionConfig.ACTION_NONE && tx != null)
49              {
50                  //TODO RM*: I'm not sure there is any value in throwing an exection here, since
51                  //there may be a transaction in progress but has nothing to to with this invocation
52                  //so maybe we just process outside the tx. Not sure yet
53                  //return callback.doInTransaction();
54  
55                  /*
56                      Reply from AP: There is value at the moment, at least in having fewer surprises
57                      with a more explicit config. Current behavior is that of 'Never' TX attribute
58                      in Java EE parlance.
59  
60                      What you refer to, however, is the 'Not Supported' TX behavior. A SUSPEND is performed
61                      in this case with (optional) RESUME later.
62  
63                   */
64  
65                  throw new IllegalTransactionStateException(
66                      CoreMessages.transactionAvailableButActionIs("None"));
67              }
68              else if (action == UMOTransactionConfig.ACTION_ALWAYS_BEGIN && tx != null)
69              {
70                  if (logger.isDebugEnabled())
71                  {
72                      logger.debug("Transaction action is ACTION_ALWAYS_BEGIN, " +
73                                   "current TX: " + tx);
74                  }
75                  if (tx.isXA())
76                  {
77                      // suspend current transaction
78                      suspendedXATx = tx;
79                      suspendXATransaction(suspendedXATx);
80                  }
81                  else
82                  {
83                      // commit/rollback
84                      resolveTransaction(tx);
85                  }
86                  //transaction will be started below
87                  tx = null;
88              }
89              else if (action == UMOTransactionConfig.ACTION_ALWAYS_JOIN && tx == null)
90              {
91                  throw new IllegalTransactionStateException(
92                      CoreMessages.transactionNotAvailableButActionIs("Always Join"));
93              }
94  
95              if (action == UMOTransactionConfig.ACTION_ALWAYS_BEGIN
96                              || (action == UMOTransactionConfig.ACTION_BEGIN_OR_JOIN && tx == null))
97              {
98                  logger.debug("Beginning transaction");
99                  tx = config.getFactory().beginTransaction();
100                 logger.debug("Transaction successfully started: " + tx);
101             }
102             else
103             {
104                 tx = null;
105             }
106             try
107             {
108                 Object result = callback.doInTransaction();
109                 if (tx != null)
110                 {
111                     resolveTransaction(tx);
112                     if (suspendedXATx != null)
113                     {
114                         resumeXATransaction(suspendedXATx);
115                         tx = suspendedXATx;
116                     }
117                 }
118                 return result;
119             }
120             catch (Exception e)
121             {
122                 if (exceptionListener != null)
123                 {
124                     logger.info("Exception Caught in Transaction template.  Handing off to exception handler: "
125                                         + exceptionListener);
126                     exceptionListener.exceptionThrown(e);
127                 }
128                 else
129                 {
130                     logger
131                         .info("Exception Caught in Transaction template without any exception listeners defined, exception is rethrown.");
132                     if (tx != null)
133                     {
134                         tx.setRollbackOnly();
135                     }
136                 }
137                 if (tx != null)
138                 {
139                     // The exception strategy can choose to route exception
140                     // messages
141                     // as part of the current transaction. So only rollback the
142                     // tx
143                     // if it has been marked for rollback (which is the default
144                     // case in the
145                     // AbstractExceptionListener)
146                     if (tx.isRollbackOnly())
147                     {
148                         logger.debug("Exception caught: rollback transaction", e);
149                     }
150                     resolveTransaction(tx);
151                     if (suspendedXATx != null)
152                     {
153                         resumeXATransaction(suspendedXATx);
154                     }
155                 }
156                 // we've handled this exception above. just return null now
157                 if (exceptionListener != null)
158                 {
159                     return null;
160                 }
161                 else
162                 {
163                     throw e;
164                 }
165             }
166             catch (Error e)
167             {
168                 if (tx != null)
169                 {
170                     logger.info("Error caught, rolling back TX " + tx, e);
171                     tx.rollback();
172                 }
173                 throw e;
174             }
175         }
176     }
177 
178     protected void resolveTransaction(UMOTransaction tx) throws TransactionException
179     {
180         if (tx.isRollbackOnly())
181         {
182             logger.debug("Transaction has been marked rollbackOnly, rolling it back: " + tx);
183             tx.rollback();
184         }
185         else
186         {
187             logger.debug("Committing transaction " + tx);
188             tx.commit();
189         }
190     }
191 
192     protected void suspendXATransaction(UMOTransaction tx) throws TransactionException
193     {
194         if (logger.isDebugEnabled())
195         {
196             logger.debug("Suspending " + tx);
197         }
198 
199         tx.suspend();
200 
201         if (logger.isDebugEnabled())
202         {
203             logger.debug("Successfully suspended " + tx);
204             logger.debug("Unbinding the following TX from the current context: " + tx);
205         }
206         
207         TransactionCoordination.getInstance().unbindTransaction(tx);
208     }
209 
210     protected void resumeXATransaction(UMOTransaction tx) throws TransactionException
211     {
212         if (logger.isDebugEnabled())
213         {
214             logger.debug("Re-binding and Resuming " + tx);
215         }
216 
217         TransactionCoordination.getInstance().bindTransaction(tx);
218         tx.resume();
219     }
220 
221 }