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