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 java.sql.Connection;
21 import java.sql.SQLException;
22 import java.util.Map;
23 import java.util.WeakHashMap;
24
25 import javax.transaction.Status;
26 import javax.transaction.SystemException;
27 import javax.transaction.Transaction;
28 import javax.transaction.TransactionManager;
29 import javax.transaction.xa.XAResource;
30
31
32 /**
33 * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
34 * </p>
35 * The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and gives
36 * the ManagedConnection a way to enlist connections in a transaction, allowing for the maximal rescue of DBCP.
37 *
38 * @author Dain Sundstrom
39 * @version $Revision$
40 */
41 public class TransactionRegistry {
42 private final TransactionManager transactionManager;
43 private final Map caches = new WeakHashMap();
44 private final Map xaResources = new WeakHashMap();
45
46 /**
47 * Creates a TransactionRegistry for the specified transaction manager.
48 * @param transactionManager the transaction manager used to enlist connections
49 */
50 public TransactionRegistry(TransactionManager transactionManager) {
51 this.transactionManager = transactionManager;
52 }
53
54 /**
55 * Registers the association between a Connection and a XAResource. When a connection
56 * is enlisted in a transaction, it is actually the XAResource that is given to the transaction
57 * manager.
58 *
59 * @param connection the JDBC connection
60 * @param xaResource the XAResource which managed the connection within a transaction
61 */
62 public synchronized void registerConnection(Connection connection, XAResource xaResource) {
63 if (connection == null) throw new NullPointerException("connection is null");
64 if (xaResource == null) throw new NullPointerException("xaResource is null");
65 xaResources.put(connection, xaResource);
66 }
67
68 /**
69 * Gets the XAResource registered for the connection.
70 * @param connection the connection
71 * @return the XAResource registered for the connection; never null
72 * @throws SQLException if the connection does not have a registered XAResource
73 */
74 public synchronized XAResource getXAResource(Connection connection) throws SQLException {
75 if (connection == null) throw new NullPointerException("connection is null");
76 XAResource xaResource = (XAResource) xaResources.get(connection);
77 if (xaResource == null) {
78 throw new SQLException("Connection does not have a registered XAResource " + connection);
79 }
80 return xaResource;
81 }
82
83 /**
84 * Gets the active TransactionContext or null if not Transaction is active.
85 * @return the active TransactionContext or null if not Transaction is active
86 * @throws SQLException if an error occurs while fetching the transaction
87 */
88 public TransactionContext getActiveTransactionContext() throws SQLException {
89 Transaction transaction = null;
90 try {
91 transaction = transactionManager.getTransaction();
92
93 // was there a transaction?
94 if (transaction == null) {
95 return null;
96 }
97
98 // is it active
99 int status = transaction.getStatus();
100 if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) {
101 return null;
102 }
103 } catch (SystemException e) {
104 throw (SQLException) new SQLException("Unable to determine current transaction ").initCause(e);
105 }
106
107 // register the the context (or create a new one)
108 synchronized (this) {
109 TransactionContext cache = (TransactionContext) caches.get(transaction);
110 if (cache == null) {
111 cache = new TransactionContext(this, transaction);
112 caches.put(transaction, cache);
113 }
114 return cache;
115 }
116 }
117
118 /**
119 * Unregisters a destroyed connection from {@link TransactionRegistry}
120 * @param connection
121 */
122 public synchronized void unregisterConnection(Connection connection) {
123 xaResources.remove(connection);
124 }
125 }
126