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