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 public TransactionContext(TransactionRegistry transactionRegistry, Transaction transaction) {
54 if (transactionRegistry == null) throw new NullPointerException("transactionRegistry is null");
55 if (transaction == null) throw new NullPointerException("transaction is null");
56 this.transactionRegistry = transactionRegistry;
57 this.transactionRef = new WeakReference(transaction);
58 }
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 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 if (this.sharedConnection != null) {
81 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 Transaction transaction = getTransaction();
87 try {
88 XAResource xaResource = transactionRegistry.getXAResource(sharedConnection);
89 transaction.enlistResource(xaResource);
90 } catch (RollbackException e) {
91 // transaction was rolled back... proceed as if there never was a transaction
92 } catch (SystemException e) {
93 throw (SQLException) new SQLException("Unable to enlist connection the transaction").initCause(e);
94 }
95
96 this.sharedConnection = sharedConnection;
97 }
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 getTransaction().registerSynchronization(new Synchronization() {
108 public void beforeCompletion() {
109 }
110
111 public void afterCompletion(int status) {
112 listener.afterCompletion(TransactionContext.this, status == Status.STATUS_COMMITTED);
113 }
114 });
115 } 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 } catch (Exception e) {
119 throw (SQLException) new SQLException("Unable to register transaction context listener").initCause(e);
120 }
121 }
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 Transaction transaction = (Transaction) this.transactionRef.get();
131 if (transaction == null) {
132 return false;
133 }
134 int status = transaction.getStatus();
135 return status == Status.STATUS_ACTIVE || status == Status.STATUS_MARKED_ROLLBACK;
136 } catch (SystemException e) {
137 throw (SQLException) new SQLException("Unable to get transaction status").initCause(e);
138 }
139 }
140
141 private Transaction getTransaction() throws SQLException {
142 Transaction transaction = (Transaction) this.transactionRef.get();
143 if (transaction == null) {
144 throw new SQLException("Unable to enlist connection because the transaction has been garbage collected");
145 }
146 return transaction;
147 }
148 }