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