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