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