View Javadoc

1   /*
2    * $Id: TransactionCoordination.java 19658 2010-09-15 19:17:41Z epere4 $
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.transaction.Transaction;
14  import org.mule.api.transaction.TransactionException;
15  import org.mule.config.i18n.CoreMessages;
16  
17  import org.apache.commons.logging.Log;
18  import org.apache.commons.logging.LogFactory;
19  
20  public final class TransactionCoordination
21  {
22      protected static final Log logger = LogFactory.getLog(TransactionCoordination.class);
23  
24      private static final TransactionCoordination instance = new TransactionCoordination();
25  
26      /**
27       * This field could be static because it is a {@link ThreadLocal} and this class
28       * is a singleton but, as it is used as an instance field by methods
29       * {@link #getTransaction()}, {@link #unbindTransaction(Transaction)} and
30       * {@link #bindTransaction(Transaction)}, it may be more consistent to have it as
31       * an instance variable.
32       */
33      private final ThreadLocal<Transaction> transactions = new ThreadLocal<Transaction>();
34  
35      /** Lock variable that is used to access {@link #txCounter}. */
36      private final Object txCounterLock = new Object();
37  
38      /** The access to this field is guarded by {@link #txCounterLock}. */
39      private int txCounter = 0;
40  
41      /** Do not instanciate. */
42      private TransactionCoordination()
43      {
44          super();
45      }
46  
47      public static TransactionCoordination getInstance()
48      {
49          return instance;
50      }
51  
52      public Transaction getTransaction()
53      {
54          return transactions.get();
55      }
56  
57      public void unbindTransaction(final Transaction transaction) throws TransactionException
58      {
59          Transaction oldTx = transactions.get();
60  
61          if (oldTx instanceof TransactionCollection)
62          {
63              // if there are more in-flight aggregated transactions, do nothing yet
64              if (!((TransactionCollection) oldTx).getTxCollection().isEmpty())
65              {
66                  return;
67              }
68          }
69  
70          try
71          {
72              if (oldTx != null && !oldTx.equals(transaction))
73              {
74                  throw new IllegalTransactionStateException(CoreMessages.transactionCannotUnbind());
75              }
76          }
77          finally
78          {
79              transactions.set(null);
80              logTransactionUnbound(transaction);
81          }
82      }
83  
84      private void logTransactionUnbound(final Transaction transaction)
85      {
86          // We store the txCounter in a local variable to minimize locking
87          int txCounter = 0;
88          synchronized (txCounterLock)
89          {
90              if (this.txCounter > 0)
91              {
92                  txCounter = --this.txCounter;
93              }
94          }
95  
96          if (logger.isDebugEnabled())
97          {
98              logger.debug("Unbinding transaction (" + txCounter + ") " + transaction);
99          }
100     }
101 
102     public void bindTransaction(final Transaction transaction) throws TransactionException
103     {
104         Transaction oldTx = transactions.get();
105         // special handling for transaction collection
106         if (oldTx != null && !(oldTx instanceof TransactionCollection))
107         {
108             throw new IllegalTransactionStateException(CoreMessages.transactionAlreadyBound());
109         }
110 
111         if (oldTx instanceof TransactionCollection)
112         {
113             TransactionCollection txCollection = (TransactionCollection) oldTx;
114             if (txCollection.getTxCollection().contains(transaction)) {
115                 // TODO improve the error message with more TX details
116                 throw new IllegalTransactionStateException(CoreMessages.transactionAlreadyBound());
117             }
118             else
119             {
120                 // will be aggregated next
121                 return;
122             }
123         }
124 
125         transactions.set(transaction);
126         logTransactionBound(transaction);
127     }
128 
129     private void logTransactionBound(final Transaction transaction)
130     {
131         // We store the txCounter in a local variable to minimize locking
132         int txCounter;
133         synchronized (txCounterLock)
134         {
135             txCounter = ++this.txCounter;
136         }
137 
138         if (logger.isDebugEnabled())
139         {
140             logger.debug("Binding new transaction (" + txCounter + ") " + transaction);
141         }
142     }
143 
144 }