View Javadoc

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