Coverage Report - org.mule.transaction.XaTransaction
 
Classes in this File Line Coverage Branch Coverage Complexity
XaTransaction
0%
0/127
0%
0/34
3.375
XaTransaction$MuleXaObject
N/A
N/A
3.375
 
 1  
 /*
 2  
  * $Id: XaTransaction.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  
 import org.mule.config.i18n.MessageFactory;
 17  
 
 18  
 import java.util.HashMap;
 19  
 import java.util.Iterator;
 20  
 import java.util.Map;
 21  
 
 22  
 import javax.transaction.HeuristicRollbackException;
 23  
 import javax.transaction.InvalidTransactionException;
 24  
 import javax.transaction.RollbackException;
 25  
 import javax.transaction.SystemException;
 26  
 import javax.transaction.Transaction;
 27  
 import javax.transaction.TransactionManager;
 28  
 import javax.transaction.xa.XAResource;
 29  
 
 30  
 /**
 31  
  * <code>XaTransaction</code> represents an XA transaction in Mule.
 32  
  */
 33  
 public class XaTransaction extends AbstractTransaction
 34  
 {
 35  
     /**
 36  
      * The inner JTA transaction
 37  
      */
 38  0
     protected Transaction transaction = null;
 39  
 
 40  
     /**
 41  
      * Map of enlisted resources
 42  
      */
 43  0
     private Map resources = new HashMap();
 44  
 
 45  
     protected TransactionManager txManager;
 46  
 
 47  
     public XaTransaction(MuleContext context)
 48  
     {
 49  0
         super(context);
 50  0
         this.txManager = context.getTransactionManager();
 51  0
     }
 52  
     
 53  
     protected void doBegin() throws TransactionException
 54  
     {
 55  0
         if (txManager == null)
 56  
         {
 57  0
             throw new IllegalStateException(
 58  
                     CoreMessages.objectNotRegistered("javax.transaction.TransactionManager", "Transaction Manager").getMessage());
 59  
         }
 60  
 
 61  
         try
 62  
         {
 63  0
             txManager.begin();
 64  0
             synchronized (this)
 65  
             {
 66  0
                 transaction = txManager.getTransaction();
 67  0
             }
 68  
         }
 69  0
         catch (Exception e)
 70  
         {
 71  0
             throw new TransactionException(CoreMessages.cannotStartTransaction("XA"), e);
 72  0
         }
 73  0
     }
 74  
 
 75  
     protected synchronized void doCommit() throws TransactionException
 76  
     {
 77  
         try
 78  
         {
 79  
             /*
 80  
            JTA spec quotes (parts highlighted by AP), the same applies to both TransactionManager and UserTransaction:
 81  
 
 82  
            3.2.2 Completing a Transaction
 83  
            The TransactionManager.commit method completes the transaction currently
 84  
            associated with the calling thread.
 85  
 
 86  
            ****
 87  
            After the commit method returns, the calling thread is not associated with a transaction.
 88  
            ****
 89  
 
 90  
            If the commit method is called when the thread is
 91  
            not associated with any transaction context, the TM throws an exception. In some
 92  
            implementations, the commit operation is restricted to the transaction originator only.
 93  
            If the calling thread is not allowed to commit the transaction, the TM throws an
 94  
            exception.
 95  
            The TransactionManager.rollback method rolls back the transaction associated
 96  
            with the current thread.
 97  
            ****
 98  
            After the rollback method completes, the thread is associated with no transaction.
 99  
            ****
 100  
 
 101  
            And the following block about Transaction (note there's no thread-tx disassociation clause)
 102  
 
 103  
            3.3.3 Transaction Completion
 104  
            The Transaction.commit and Transaction.rollback methods allow the target
 105  
            object to be comitted or rolled back. The calling thread is not required to have the same
 106  
            transaction associated with the thread.
 107  
            If the calling thread is not allowed to commit the transaction, the transaction manager
 108  
            throws an exception.
 109  
 
 110  
 
 111  
            So what it meant was that one can't use Transaction.commit()/rollback(), as it doesn't
 112  
            properly disassociate the thread of execution from the current transaction. There's no
 113  
            JTA API-way to do that after the call, so the thread's transaction is subject to manual
 114  
            recovery process. Instead TransactionManager or UserTransaction must be used.
 115  
             */
 116  0
             delistResources();
 117  0
             txManager.commit();
 118  
         }
 119  0
         catch (RollbackException e)
 120  
         {
 121  0
             throw new TransactionRollbackException(CoreMessages.transactionMarkedForRollback(), e);
 122  
         }
 123  0
         catch (HeuristicRollbackException e)
 124  
         {
 125  0
             throw new TransactionRollbackException(CoreMessages.transactionMarkedForRollback(), e);
 126  
         }
 127  0
         catch (Exception e)
 128  
         {
 129  0
             throw new IllegalTransactionStateException(CoreMessages.transactionCommitFailed(), e);
 130  
         }
 131  
         finally
 132  
         {
 133  
             /*
 134  
                 MUST nullify XA ref here, otherwise Transaction.getStatus() doesn't match
 135  
                 javax.transaction.Transaction.getStatus(). Must return STATUS_NO_TRANSACTION and not
 136  
                 STATUS_COMMITTED.
 137  
 
 138  
                 TransactionCoordination unbinds the association immediately on this method's exit.
 139  
             */
 140  0
             this.transaction = null;
 141  0
             closeResources();
 142  0
         }
 143  0
     }
 144  
 
 145  
     protected void doRollback() throws TransactionRollbackException
 146  
     {
 147  
         try
 148  
         {
 149  
             /*
 150  
            JTA spec quotes (parts highlighted by AP), the same applies to both TransactionManager and UserTransaction:
 151  
 
 152  
            3.2.2 Completing a Transaction
 153  
            The TransactionManager.commit method completes the transaction currently
 154  
            associated with the calling thread.
 155  
 
 156  
            ****
 157  
            After the commit method returns, the calling thread is not associated with a transaction.
 158  
            ****
 159  
 
 160  
            If the commit method is called when the thread is
 161  
            not associated with any transaction context, the TM throws an exception. In some
 162  
            implementations, the commit operation is restricted to the transaction originator only.
 163  
            If the calling thread is not allowed to commit the transaction, the TM throws an
 164  
            exception.
 165  
            The TransactionManager.rollback method rolls back the transaction associated
 166  
            with the current thread.
 167  
            ****
 168  
            After the rollback method completes, the thread is associated with no transaction.
 169  
            ****
 170  
 
 171  
            And the following block about Transaction (note there's no thread-tx disassociation clause)
 172  
 
 173  
            3.3.3 Transaction Completion
 174  
            The Transaction.commit and Transaction.rollback methods allow the target
 175  
            object to be comitted or rolled back. The calling thread is not required to have the same
 176  
            transaction associated with the thread.
 177  
            If the calling thread is not allowed to commit the transaction, the transaction manager
 178  
            throws an exception.
 179  
 
 180  
 
 181  
            So what it meant was that one can't use Transaction.commit()/rollback(), as it doesn't
 182  
            properly disassociate the thread of execution from the current transaction. There's no
 183  
            JTA API-way to do that after the call, so the thread's transaction is subject to manual
 184  
            recovery process. Instead TransactionManager or UserTransaction must be used.
 185  
             */
 186  
             //delistResources();
 187  0
             txManager.rollback();
 188  
         }
 189  0
         catch (SystemException e)
 190  
         {
 191  0
             throw new TransactionRollbackException(e);
 192  
         }
 193  0
         catch (Exception e)
 194  
         {
 195  0
             throw new TransactionRollbackException(e);
 196  
         }
 197  
         finally
 198  
         {
 199  
             /*
 200  
                 MUST nullify XA ref here, otherwise Transaction.getStatus() doesn't match
 201  
                 javax.transaction.Transaction.getStatus(). Must return STATUS_NO_TRANSACTION and not
 202  
                 STATUS_COMMITTED.
 203  
 
 204  
                 TransactionCoordination unbinds the association immediately on this method's exit.
 205  
             */
 206  0
             this.transaction = null;
 207  0
             closeResources();
 208  0
         }
 209  0
     }
 210  
 
 211  
     public synchronized int getStatus() throws TransactionStatusException
 212  
     {
 213  0
         if (transaction == null)
 214  
         {
 215  0
             return STATUS_NO_TRANSACTION;
 216  
         }
 217  
 
 218  
         try
 219  
         {
 220  0
             return transaction.getStatus();
 221  
         }
 222  0
         catch (SystemException e)
 223  
         {
 224  0
             throw new TransactionStatusException(e);
 225  
         }
 226  
     }
 227  
 
 228  
     public void setRollbackOnly()
 229  
     {
 230  0
         if (transaction == null)
 231  
         {
 232  0
             throw new IllegalStateException("Current thread is not associated with a transaction.");
 233  
         }
 234  
 
 235  
         try
 236  
         {
 237  0
             synchronized (this)
 238  
             {
 239  0
                 transaction.setRollbackOnly();
 240  0
             }
 241  
         }
 242  0
         catch (SystemException e)
 243  
         {
 244  0
             throw (IllegalStateException) new IllegalStateException(
 245  
                     "Failed to set transaction to rollback only: " + e.getMessage()
 246  
             ).initCause(e);
 247  0
         }
 248  0
     }
 249  
 
 250  
     public synchronized Object getResource(Object key)
 251  
     {
 252  0
         return resources.get(key);
 253  
     }
 254  
 
 255  
     public synchronized boolean hasResource(Object key)
 256  
     {
 257  0
         return resources.containsKey(key);
 258  
     }
 259  
 
 260  
     public synchronized void bindResource(Object key, Object resource) throws TransactionException
 261  
     {
 262  0
         if (resources.containsKey(key))
 263  
         {
 264  0
             throw new IllegalTransactionStateException(
 265  
                     CoreMessages.transactionResourceAlreadyListedForKey(key));
 266  
         }
 267  
 
 268  0
         resources.put(key, resource);
 269  
         
 270  0
         if (key == null)
 271  
         {
 272  0
             logger.error("Key for bound resource " + resource + " is null");
 273  
         }
 274  
         
 275  0
         if (resource instanceof MuleXaObject)
 276  
         {
 277  0
             MuleXaObject xaObject = (MuleXaObject) resource;
 278  0
             xaObject.enlist();
 279  0
         }
 280  0
         else if (resource instanceof XAResource)
 281  
         {
 282  0
             enlistResource((XAResource) resource);
 283  
         }
 284  
         else
 285  
         {
 286  0
             logger.error("Bound resource " + resource + " is neither a MuleXaObject nor XAResource");
 287  
         }
 288  0
     }
 289  
 
 290  
 
 291  
     // moved here from connection wrapper
 292  
     public boolean enlistResource(XAResource resource) throws TransactionException
 293  
     {
 294  0
         TransactionManager txManager = muleContext.getTransactionManager();
 295  
         try
 296  
         {
 297  0
             Transaction jtaTransaction = txManager.getTransaction();
 298  0
             if (jtaTransaction == null)
 299  
             {
 300  0
                 throw new TransactionException(MessageFactory.createStaticMessage("XATransaction is null"));
 301  
             }
 302  0
             return jtaTransaction.enlistResource(resource);
 303  
         }
 304  0
         catch (RollbackException e)
 305  
         {
 306  0
             throw new TransactionException(e);
 307  
         }
 308  0
         catch (SystemException e)
 309  
         {
 310  0
             throw new TransactionException(e);
 311  
         }
 312  
     }
 313  
 
 314  
     public boolean delistResource(XAResource resource, int tmflag) throws TransactionException
 315  
     {
 316  0
         TransactionManager txManager = muleContext.getTransactionManager();
 317  
         try
 318  
         {
 319  0
             Transaction jtaTransaction = txManager.getTransaction();
 320  0
             if (jtaTransaction == null)
 321  
             {
 322  0
                 throw new TransactionException(CoreMessages.noJtaTransactionAvailable(Thread.currentThread()));
 323  
             }
 324  0
             return jtaTransaction.delistResource(resource, tmflag);
 325  
         }
 326  0
         catch (SystemException e)
 327  
         {
 328  0
             throw new TransactionException(e);
 329  
         }
 330  
     }
 331  
 
 332  
 
 333  
     public String toString()
 334  
     {
 335  0
         return transaction == null ? " <n/a>" : transaction.toString();
 336  
     }
 337  
 
 338  
     public Transaction getTransaction()
 339  
     {
 340  0
         return transaction;
 341  
     }
 342  
 
 343  
     public boolean isXA()
 344  
     {
 345  0
         return true;
 346  
     }
 347  
 
 348  
     public void resume() throws TransactionException
 349  
     {
 350  0
         TransactionManager txManager = muleContext.getTransactionManager();
 351  
 
 352  0
         if (txManager == null)
 353  
         {
 354  0
             throw new IllegalStateException(
 355  
                     CoreMessages.objectNotRegistered("javax.transaction.TransactionManager", "Transaction Manager").getMessage());
 356  
         }
 357  
         try
 358  
         {
 359  0
             txManager.resume(transaction);
 360  
         }
 361  0
         catch (InvalidTransactionException e)
 362  
         {
 363  0
             throw new TransactionException(e);
 364  
         }
 365  0
         catch (SystemException e)
 366  
         {
 367  0
             throw new TransactionException(e);
 368  0
         }
 369  0
     }
 370  
 
 371  
     public Transaction suspend() throws TransactionException
 372  
     {
 373  0
         TransactionManager txManager = muleContext.getTransactionManager();
 374  
 
 375  0
         if (txManager == null)
 376  
         {
 377  0
             throw new IllegalStateException(
 378  
                     CoreMessages.objectNotRegistered("javax.transaction.TransactionManager", "Transaction Manager").getMessage());
 379  
         }
 380  
         try
 381  
         {
 382  0
             transaction = txManager.suspend();
 383  
         }
 384  0
         catch (SystemException e)
 385  
         {
 386  0
             throw new TransactionException(e);
 387  0
         }
 388  0
         return transaction;
 389  
     }
 390  
 
 391  
     protected void delistResources()
 392  
     {
 393  0
         Iterator i = resources.entrySet().iterator();
 394  0
         while (i.hasNext())
 395  
         {
 396  0
             Map.Entry entry = (Map.Entry) i.next();
 397  0
             final Object xaObject = entry.getValue();
 398  0
             if (xaObject instanceof MuleXaObject)
 399  
             {
 400  
                 //there is need for reuse object
 401  
                 try
 402  
                 {
 403  0
                     ((MuleXaObject) xaObject).delist();
 404  
                 }
 405  0
                 catch (Exception e)
 406  
                 {
 407  0
                     logger.error("Failed to delist resource " + xaObject, e);
 408  0
                 }
 409  
             }
 410  0
         }
 411  0
     }
 412  
 
 413  
     protected void closeResources()
 414  
     {
 415  0
         Iterator i = resources.entrySet().iterator();
 416  0
         while (i.hasNext())
 417  
         {
 418  0
             Map.Entry entry = (Map.Entry) i.next();
 419  0
             final Object value = entry.getValue();
 420  0
             if (value instanceof MuleXaObject)
 421  
             {
 422  0
                 MuleXaObject xaObject = (MuleXaObject) value;
 423  0
                 if (!xaObject.isReuseObject())
 424  
                 {
 425  
                     try
 426  
                     {
 427  0
                         xaObject.close();
 428  0
                         i.remove();
 429  
                     }
 430  0
                     catch (Exception e)
 431  
                     {
 432  0
                         logger.error("Failed to close resource " + xaObject, e);
 433  0
                     }
 434  
                 }
 435  
             }
 436  0
         }
 437  0
     }
 438  
 
 439  
     public static interface MuleXaObject
 440  
     {
 441  
 
 442  
         void close() throws Exception;
 443  
 
 444  
         void setReuseObject(boolean reuseObject);
 445  
 
 446  
         boolean isReuseObject();
 447  
 
 448  
         boolean enlist() throws TransactionException;
 449  
         
 450  
         boolean delist() throws Exception;
 451  
 
 452  
         /**
 453  
          * Get XAConnection or XASession from wrapper / proxy
 454  
          *
 455  
          * @return return javax.sql.XAConnection for jdbc or javax.jms.XASession for jms
 456  
          */
 457  
         Object getTargetObject();
 458  
 
 459  
         String SET_REUSE_OBJECT_METHOD_NAME = "setReuseObject";
 460  
         String IS_REUSE_OBJECT_METHOD_NAME = "isReuseObject";
 461  
         String DELIST_METHOD_NAME = "delist";
 462  
         String ENLIST_METHOD_NAME = "enlist";
 463  
         String GET_TARGET_OBJECT_METHOD_NAME = "getTargetObject";
 464  
         String CLOSE_METHOD_NAME = "close";
 465  
     }
 466  
 
 467  
 }