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.util.xa;
8   
9   import javax.transaction.Status;
10  import javax.transaction.xa.XAException;
11  import javax.transaction.xa.XAResource;
12  import javax.transaction.xa.Xid;
13  
14  import org.apache.commons.logging.Log;
15  import org.apache.commons.logging.LogFactory;
16  
17  /**
18   * TODO document
19   */
20  public class DefaultXASession implements XAResource
21  {
22  
23      /**
24       * logger used by this class
25       */
26      protected transient Log logger = LogFactory.getLog(getClass());
27  
28      protected AbstractTransactionContext localContext;
29      protected Xid localXid;
30      protected AbstractXAResourceManager resourceManager;
31  
32      public DefaultXASession(AbstractXAResourceManager resourceManager)
33      {
34          this.localContext = null;
35          this.localXid = null;
36          this.resourceManager = resourceManager;
37      }
38  
39      public XAResource getXAResource()
40      {
41          return this;
42      }
43  
44      public Object getResourceManager()
45      {
46          return resourceManager;
47      }
48  
49      //
50      // Local transaction implementation
51      //
52      public void begin() throws ResourceManagerException
53      {
54          if (localXid != null)
55          {
56              throw new IllegalStateException(
57                  "Cannot start local transaction. An XA transaction is already in progress.");
58          }
59          if (localContext != null)
60          {
61              throw new IllegalStateException(
62                  "Cannot start local transaction. A local transaction already in progress.");
63          }
64          localContext = resourceManager.createTransactionContext(this);
65          resourceManager.beginTransaction(localContext);
66      }
67  
68      public void commit() throws ResourceManagerException
69      {
70          if (localXid != null)
71          {
72              throw new IllegalStateException(
73                  "Cannot commit local transaction as an XA transaction is in progress.");
74          }
75          if (localContext == null)
76          {
77              throw new IllegalStateException("Cannot commit local transaction as no transaction was begun");
78          }
79          resourceManager.commitTransaction(localContext);
80          localContext = null;
81      }
82  
83      public void rollback() throws ResourceManagerException
84      {
85          if (localXid != null)
86          {
87              throw new IllegalStateException(
88                  "Cannot rollback local transaction as an XA transaction is in progress.");
89          }
90          if (localContext == null)
91          {
92              throw new IllegalStateException("Cannot commit local transaction as no transaction was begun");
93          }
94          resourceManager.rollbackTransaction(localContext);
95          localContext = null;
96      }
97  
98      //
99      // XAResource implementation
100     //
101 
102     public boolean isSameRM(XAResource xares) throws XAException
103     {
104         return xares instanceof DefaultXASession
105                && ((DefaultXASession) xares).getResourceManager().equals(resourceManager);
106     }
107 
108     public Xid[] recover(int flag) throws XAException
109     {
110         return null;
111     }
112 
113     public void start(Xid xid, int flags) throws XAException
114     {
115         if (logger.isDebugEnabled())
116         {
117             logger.debug(new StringBuffer(128).append("Thread ").append(Thread.currentThread()).append(
118                 flags == TMNOFLAGS ? " starts" : flags == TMJOIN ? " joins" : " resumes").append(
119                 " work on behalf of transaction branch ").append(xid).toString());
120         }
121         // A local transaction is already begun
122         if (this.localContext != null)
123         {
124             throw new XAException(XAException.XAER_PROTO);
125         }
126         // This session has already been associated with an xid
127         if (this.localXid != null)
128         {
129             throw new XAException(XAException.XAER_PROTO);
130         }
131         switch (flags)
132         {
133             // a new transaction
134             case TMNOFLAGS :
135             case TMJOIN :
136             default :
137                 try
138                 {
139                     localContext = resourceManager.createTransactionContext(this);
140                     resourceManager.beginTransaction(localContext);
141                 }
142                 catch (Exception e)
143                 {
144                     // TODO MULE-863: Is logging necessary?
145                     logger.error("Could not create new transactional resource", e);
146                     throw (XAException) new XAException(e.getMessage()).initCause(e);
147                 }
148                 break;
149             case TMRESUME :
150                 localContext = resourceManager.getSuspendedTransactionalResource(xid);
151                 if (localContext == null)
152                 {
153                     throw new XAException(XAException.XAER_NOTA);
154                 }
155                 // TODO: resume context
156                 resourceManager.removeSuspendedTransactionalResource(xid);
157                 break;
158         }
159         localXid = xid;
160         resourceManager.addActiveTransactionalResource(localXid, localContext);
161     }
162 
163     public void end(Xid xid, int flags) throws XAException
164     {
165         if (logger.isDebugEnabled())
166         {
167             logger.debug(new StringBuffer(128).append("Thread ").append(Thread.currentThread()).append(
168                 flags == TMSUSPEND ? " suspends" : flags == TMFAIL ? " fails" : " ends").append(
169                 " work on behalf of transaction branch ").append(xid).toString());
170         }
171         // No transaction is already begun
172         if (localContext == null)
173         {
174             throw new XAException(XAException.XAER_NOTA);
175         }
176         // This session has already been associated with an xid
177         if (localXid == null || !localXid.equals(xid))
178         {
179             throw new XAException(XAException.XAER_PROTO);
180         }
181 
182         try
183         {
184             switch (flags)
185             {
186                 case TMSUSPEND :
187                     // TODO: suspend context
188                     resourceManager.addSuspendedTransactionalResource(localXid, localContext);
189                     resourceManager.removeActiveTransactionalResource(localXid);
190                     break;
191                 case TMFAIL :
192                     resourceManager.setTransactionRollbackOnly(localContext);
193                     break;
194                 case TMSUCCESS : // no-op
195                 default :        // no-op
196                     break;
197             }
198         }
199         catch (ResourceManagerException e)
200         {
201             throw (XAException) new XAException(XAException.XAER_RMERR).initCause(e);
202         }
203         localXid = null;
204         localContext = null;
205     }
206 
207     public void commit(Xid xid, boolean onePhase) throws XAException
208     {
209         if (xid == null)
210         {
211             throw new XAException(XAException.XAER_PROTO);
212         }
213         AbstractTransactionContext context = resourceManager.getActiveTransactionalResource(xid);
214         if (context == null)
215         {
216             throw new XAException(XAException.XAER_NOTA);
217         }
218         if (logger.isDebugEnabled())
219         {
220             logger.debug("Committing transaction branch " + xid);
221         }
222         if (context.status == Status.STATUS_MARKED_ROLLBACK)
223         {
224             throw new XAException(XAException.XA_RBROLLBACK);
225         }
226 
227         try
228         {
229             if (context.status != Status.STATUS_PREPARED)
230             {
231                 if (onePhase)
232                 {
233                     resourceManager.prepareTransaction(context);
234                 }
235                 else
236                 {
237                     throw new XAException(XAException.XAER_PROTO);
238                 }
239             }
240             resourceManager.commitTransaction(context);
241         }
242         catch (ResourceManagerException e)
243         {
244             throw (XAException) new XAException(XAException.XAER_RMERR).initCause(e);
245         }
246         resourceManager.removeActiveTransactionalResource(xid);
247         resourceManager.removeSuspendedTransactionalResource(xid);
248     }
249 
250     public void rollback(Xid xid) throws XAException
251     {
252         if (xid == null)
253         {
254             throw new XAException(XAException.XAER_PROTO);
255         }
256         AbstractTransactionContext context = resourceManager.getActiveTransactionalResource(xid);
257         if (context == null)
258         {
259             throw new XAException(XAException.XAER_NOTA);
260         }
261         if (logger.isDebugEnabled())
262         {
263             logger.debug("Rolling back transaction branch " + xid);
264         }
265         try
266         {
267             resourceManager.rollbackTransaction(context);
268         }
269         catch (ResourceManagerException e)
270         {
271             throw (XAException) new XAException(XAException.XAER_RMERR).initCause(e);
272         }
273         resourceManager.removeActiveTransactionalResource(xid);
274         resourceManager.removeSuspendedTransactionalResource(xid);
275     }
276 
277     public int prepare(Xid xid) throws XAException
278     {
279         if (xid == null)
280         {
281             throw new XAException(XAException.XAER_PROTO);
282         }
283 
284         AbstractTransactionContext context = resourceManager.getTransactionalResource(xid);
285         if (context == null)
286         {
287             throw new XAException(XAException.XAER_NOTA);
288         }
289 
290         if (logger.isDebugEnabled())
291         {
292             logger.debug("Preparing transaction branch " + xid);
293         }
294 
295         if (context.status == Status.STATUS_MARKED_ROLLBACK)
296         {
297             throw new XAException(XAException.XA_RBROLLBACK);
298         }
299 
300         try
301         {
302             return resourceManager.prepareTransaction(context);
303         }
304         catch (ResourceManagerException e)
305         {
306             throw (XAException) new XAException(XAException.XAER_RMERR).initCause(e);
307         }
308     }
309 
310     public void forget(Xid xid) throws XAException
311     {
312         if (logger.isDebugEnabled())
313         {
314             logger.debug("Forgetting transaction branch " + xid);
315         }
316         AbstractTransactionContext context = resourceManager.getTransactionalResource(xid);
317         if (context == null)
318         {
319             throw new XAException(XAException.XAER_NOTA);
320         }
321         resourceManager.removeActiveTransactionalResource(xid);
322         resourceManager.removeSuspendedTransactionalResource(xid);
323     }
324 
325     public int getTransactionTimeout() throws XAException
326     {
327         return (int)(resourceManager.getDefaultTransactionTimeout() / 1000);
328     }
329 
330     public boolean setTransactionTimeout(int timeout) throws XAException
331     {
332         resourceManager.setDefaultTransactionTimeout(timeout * 1000);
333         return false;
334     }
335 
336 }