View Javadoc

1   /*
2    * $Id: AbstractSingleResourceTransaction.java 21939 2011-05-18 13:32:09Z aperepel $
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.TransactionException;
15  import org.mule.config.i18n.CoreMessages;
16  
17  import java.lang.reflect.Field;
18  import java.util.Collections;
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.concurrent.atomic.AtomicBoolean;
22  
23  import javax.transaction.Status;
24  
25  /**
26   * This abstract class can be used as a base class for transactions that can enlist
27   * only one resource (such as a JMS session or JDBC connection).
28   */
29  public abstract class AbstractSingleResourceTransaction extends AbstractTransaction
30  {
31  
32      /**
33       * TX status code to human-readable string mappings.
34       *
35       * @see javax.transaction.Status
36       */
37      protected static Map<Integer, String> txStatusMappings = new HashMap<Integer, String>(10); // populated later
38  
39      protected volatile Object key;
40      protected volatile Object resource;
41  
42      protected final AtomicBoolean started = new AtomicBoolean(false);
43      protected final AtomicBoolean committed = new AtomicBoolean(false);
44      protected final AtomicBoolean rolledBack = new AtomicBoolean(false);
45      protected final AtomicBoolean rollbackOnly = new AtomicBoolean(false);
46  
47      static
48      {
49          Field[] fields = Status.class.getFields();
50          for (Field field : fields)
51          {
52              try
53              {
54                  txStatusMappings.put(field.getInt(Status.class), field.getName());
55              }
56              catch (IllegalAccessException e)
57              {
58                  // ignore
59              }
60          }
61  
62          txStatusMappings = Collections.unmodifiableMap(txStatusMappings);
63      }
64  
65      protected AbstractSingleResourceTransaction(MuleContext muleContext)
66      {
67          super(muleContext);
68      }
69  
70      public void begin() throws TransactionException
71      {
72          super.begin();
73          started.compareAndSet(false, true);
74      }
75  
76      public void commit() throws TransactionException
77      {
78          super.commit();
79          committed.compareAndSet(false, true);
80      }
81  
82      public void rollback() throws TransactionException
83      {
84          super.rollback();
85          rolledBack.compareAndSet(false, true);
86      }
87  
88      public int getStatus() throws TransactionStatusException
89      {
90          if (rolledBack.get())
91          {
92              return STATUS_ROLLEDBACK;
93          }
94          if (committed.get())
95          {
96              return STATUS_COMMITTED;
97          }
98          if (rollbackOnly.get())
99          {
100             return STATUS_MARKED_ROLLBACK;
101         }
102         if (started.get())
103         {
104             return STATUS_ACTIVE;
105         }
106         return STATUS_NO_TRANSACTION;
107     }
108 
109     public Object getResource(Object key)
110     {
111         return key != null && this.key == key ? this.resource : null;
112     }
113 
114     public boolean hasResource(Object key)
115     {
116         return key != null && this.key == key;
117     }
118 
119     public void bindResource(Object key, Object resource) throws TransactionException
120     {
121         if (key == null)
122         {
123             throw new IllegalTransactionStateException(CoreMessages.transactionCannotBindToNullKey());
124         }
125         if (resource == null)
126         {
127             throw new IllegalTransactionStateException(CoreMessages.transactionCannotBindNullResource());
128         }
129         if (this.key != null)
130         {
131             throw new IllegalTransactionStateException(CoreMessages.transactionSingleResourceOnly());
132         }
133         
134         if (logger.isDebugEnabled())
135         {
136             logger.debug("Binding " + resource + " to " + key);
137         }
138         
139         this.key = key;
140         this.resource = resource;
141     }
142 
143     public void setRollbackOnly()
144     {
145         rollbackOnly.set(true);
146     }
147 
148     @Override
149     public String toString()
150     {
151         int status;
152         try
153         {
154             status = getStatus();
155         }
156         catch (TransactionException e)
157         {
158             status = -1;
159         }
160 
161         // map status to a human-readable string
162 
163         String statusName = txStatusMappings.get(status);
164         if (statusName == null)
165         {
166             statusName = "*undefined*";
167         }
168 
169         return new StringBuilder().append(getClass().getName())
170                 .append('@').append(id)
171                 .append("[status=").append(statusName)
172                 .append(", key=").append(key)
173                 .append(", resource=").append(resource)
174                 .append("]").toString();
175     }
176 }