Coverage Report - org.apache.commons.dbcp.managed.TransactionContext
 
Classes in this File Line Coverage Branch Coverage Complexity
TransactionContext
68%
24/35
42%
6/14
3.5
TransactionContext$1
100%
4/4
50%
1/2
3.5
 
 1  
 /**
 2  
  *
 3  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 4  
  * contributor license agreements.  See the NOTICE file distributed with
 5  
  * this work for additional information regarding copyright ownership.
 6  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 7  
  * (the "License"); you may not use this file except in compliance with
 8  
  * the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  *  Unless required by applicable law or agreed to in writing, software
 13  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 14  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  *  See the License for the specific language governing permissions and
 16  
  *  limitations under the License.
 17  
  */
 18  
 package org.apache.commons.dbcp.managed;
 19  
 
 20  
 import javax.transaction.RollbackException;
 21  
 import javax.transaction.Status;
 22  
 import javax.transaction.Synchronization;
 23  
 import javax.transaction.SystemException;
 24  
 import javax.transaction.Transaction;
 25  
 import javax.transaction.xa.XAResource;
 26  
 import java.sql.Connection;
 27  
 import java.sql.SQLException;
 28  
 import java.lang.ref.WeakReference;
 29  
 
 30  
 /**
 31  
  * TransactionContext represents the association between a single XAConnectionFactory and a Transaction.
 32  
  * This context contains a single shared connection which should be used by all ManagedConnections for
 33  
  * the XAConnectionFactory, the ability to listen for the transaction completion event, and a method
 34  
  * to check the status of the transaction.
 35  
  *
 36  
  * @author Dain Sundstrom
 37  
  * @version $Revision$
 38  
  */
 39  
 public class TransactionContext {
 40  
     private final TransactionRegistry transactionRegistry;
 41  
     private final WeakReference transactionRef;
 42  
     private Connection sharedConnection;
 43  
 
 44  
     /**
 45  
      * Creates a TransactionContext for the specified Transaction and TransactionRegistry.  The
 46  
      * TransactionRegistry is used to obtain the XAResource for the shared connection when it is
 47  
      * enlisted in the transaction.
 48  
      *
 49  
      * @param transactionRegistry the TransactionRegistry used to obtain the XAResource for the
 50  
      * shared connection
 51  
      * @param transaction the transaction
 52  
      */
 53  88
     public TransactionContext(TransactionRegistry transactionRegistry, Transaction transaction) {
 54  88
         if (transactionRegistry == null) throw new NullPointerException("transactionRegistry is null");
 55  88
         if (transaction == null) throw new NullPointerException("transaction is null");
 56  88
         this.transactionRegistry = transactionRegistry;
 57  88
         this.transactionRef = new WeakReference(transaction);
 58  88
     }
 59  
 
 60  
     /**
 61  
      * Gets the connection shared by all ManagedConnections in the transaction.  Specifically,
 62  
      * connection using the same XAConnectionFactory from which the TransactionRegistry was
 63  
      * obtained.
 64  
      * @return the shared connection for this transaction
 65  
      */
 66  
     public Connection getSharedConnection() {
 67  908
         return sharedConnection;
 68  
     }
 69  
 
 70  
     /**
 71  
      * Sets the shared connection for this transaction.  The shared connection is enlisted
 72  
      * in the transaction.
 73  
      *
 74  
      * @param sharedConnection the shared connection
 75  
      * @throws SQLException if a shared connection is already set, if XAResource for the connection
 76  
      * could not be found in the transaction registry, or if there was a problem enlisting the
 77  
      * connection in the transaction
 78  
      */
 79  
     public void setSharedConnection(Connection sharedConnection) throws SQLException {
 80  86
         if (this.sharedConnection != null) {
 81  0
             throw new IllegalStateException("A shared connection is alredy set");
 82  
         }
 83  
 
 84  
         // This is the first use of the connection in this transaction, so we must
 85  
         // enlist it in the transaction
 86  86
         Transaction transaction = getTransaction();
 87  
         try {
 88  86
             XAResource xaResource = transactionRegistry.getXAResource(sharedConnection);
 89  86
             transaction.enlistResource(xaResource);
 90  0
         } catch (RollbackException e) {
 91  
             // transaction was rolled back... proceed as if there never was a transaction
 92  0
         } catch (SystemException e) {
 93  0
             throw (SQLException) new SQLException("Unable to enlist connection the transaction").initCause(e);
 94  86
         }
 95  
 
 96  86
         this.sharedConnection = sharedConnection;
 97  86
     }
 98  
 
 99  
     /**
 100  
      * Adds a listener for transaction completion events.
 101  
      *
 102  
      * @param listener the listener to add
 103  
      * @throws SQLException if a problem occurs adding the listener to the transaction
 104  
      */
 105  
     public void addTransactionContextListener(final TransactionContextListener listener) throws SQLException {
 106  
         try {
 107  496
             getTransaction().registerSynchronization(new Synchronization() {
 108  
                 public void beforeCompletion() {
 109  496
                 }
 110  
 
 111  
                 public void afterCompletion(int status) {
 112  496
                     listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
 113  496
                 }
 114  
             });
 115  0
         } catch (RollbackException e) {
 116  
             // JTA spec doesn't let us register with a transaction marked rollback only
 117  
             // just ignore this and the tx state will be cleared another way.
 118  0
         } catch (Exception e) {
 119  0
             throw (SQLException) new SQLException("Unable to register transaction context listener").initCause(e);
 120  496
         }
 121  496
     }
 122  
 
 123  
     /**
 124  
      * True if the transaction is active or marked for rollback only.
 125  
      * @return true if the transaction is active or marked for rollback only; false otherwise
 126  
      * @throws SQLException if a problem occurs obtaining the transaction status
 127  
      */
 128  
     public boolean isActive() throws SQLException {
 129  
         try {
 130  294
             Transaction transaction = (Transaction) this.transactionRef.get();
 131  294
             if (transaction == null) {
 132  0
                 return false;
 133  
             }
 134  294
             int status = transaction.getStatus();
 135  294
             return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
 136  0
         } catch (SystemException e) {
 137  0
             throw (SQLException) new SQLException("Unable to get transaction status").initCause(e);
 138  
         }
 139  
     }
 140  
 
 141  
     private Transaction getTransaction() throws SQLException {
 142  582
         Transaction transaction = (Transaction) this.transactionRef.get();
 143  582
         if (transaction == null) {
 144  0
             throw new SQLException("Unable to enlist connection because the transaction has been garbage collected");
 145  
         }
 146  582
         return transaction;
 147  
     }
 148  
 }