001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.commons.dbcp.managed;
019
020 import java.sql.Connection;
021 import java.sql.SQLException;
022 import java.util.Map;
023 import java.util.WeakHashMap;
024
025 import javax.transaction.Status;
026 import javax.transaction.SystemException;
027 import javax.transaction.Transaction;
028 import javax.transaction.TransactionManager;
029 import javax.transaction.xa.XAResource;
030
031
032 /**
033 * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
034 * </p>
035 * The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and gives
036 * the ManagedConnection a way to enlist connections in a transaction, allowing for the maximal rescue of DBCP.
037 *
038 * @author Dain Sundstrom
039 * @version $Revision: 892307 $
040 */
041 public class TransactionRegistry {
042 private final TransactionManager transactionManager;
043 private final Map caches = new WeakHashMap();
044 private final Map xaResources = new WeakHashMap();
045
046 /**
047 * Creates a TransactionRegistry for the specified transaction manager.
048 * @param transactionManager the transaction manager used to enlist connections
049 */
050 public TransactionRegistry(TransactionManager transactionManager) {
051 this.transactionManager = transactionManager;
052 }
053
054 /**
055 * Registers the association between a Connection and a XAResource. When a connection
056 * is enlisted in a transaction, it is actually the XAResource that is given to the transaction
057 * manager.
058 *
059 * @param connection the JDBC connection
060 * @param xaResource the XAResource which managed the connection within a transaction
061 */
062 public synchronized void registerConnection(Connection connection, XAResource xaResource) {
063 if (connection == null) throw new NullPointerException("connection is null");
064 if (xaResource == null) throw new NullPointerException("xaResource is null");
065 xaResources.put(connection, xaResource);
066 }
067
068 /**
069 * Gets the XAResource registered for the connection.
070 * @param connection the connection
071 * @return the XAResource registered for the connection; never null
072 * @throws SQLException if the connection does not have a registered XAResource
073 */
074 public synchronized XAResource getXAResource(Connection connection) throws SQLException {
075 if (connection == null) throw new NullPointerException("connection is null");
076 XAResource xaResource = (XAResource) xaResources.get(connection);
077 if (xaResource == null) {
078 throw new SQLException("Connection does not have a registered XAResource " + connection);
079 }
080 return xaResource;
081 }
082
083 /**
084 * Gets the active TransactionContext or null if not Transaction is active.
085 * @return the active TransactionContext or null if not Transaction is active
086 * @throws SQLException if an error occurs while fetching the transaction
087 */
088 public TransactionContext getActiveTransactionContext() throws SQLException {
089 Transaction transaction = null;
090 try {
091 transaction = transactionManager.getTransaction();
092
093 // was there a transaction?
094 if (transaction == null) {
095 return null;
096 }
097
098 // is it active
099 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