View Javadoc

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