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 */ 018package org.apache.commons.dbcp2.managed; 019 020import java.sql.Connection; 021import java.sql.SQLException; 022import java.util.Map; 023import java.util.WeakHashMap; 024 025import javax.transaction.Status; 026import javax.transaction.SystemException; 027import javax.transaction.Transaction; 028import javax.transaction.TransactionManager; 029import javax.transaction.xa.XAResource; 030 031import org.apache.commons.dbcp2.DelegatingConnection; 032 033 034/** 035 * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory. 036 * <p> 037 * The TransactionRegistry hides the details of transaction processing from the existing DBCP pooling code, and gives 038 * the ManagedConnection a way to enlist connections in a transaction, allowing for the maximal rescue of DBCP. 039 * </p> 040 * @author Dain Sundstrom 041 * @version $Id: TransactionRegistry.java 1660791 2015-02-19 04:13:10Z psteitz $ 042 * @since 2.0 043 */ 044public class TransactionRegistry { 045 private final TransactionManager transactionManager; 046 private final Map<Transaction, TransactionContext> caches = 047 new WeakHashMap<>(); 048 private final Map<Connection, XAResource> xaResources = new WeakHashMap<>(); 049 050 /** 051 * Creates a TransactionRegistry for the specified transaction manager. 052 * @param transactionManager the transaction manager used to enlist connections 053 */ 054 public TransactionRegistry(TransactionManager transactionManager) { 055 this.transactionManager = transactionManager; 056 } 057 058 /** 059 * Registers the association between a Connection and a XAResource. When a connection 060 * is enlisted in a transaction, it is actually the XAResource that is given to the transaction 061 * manager. 062 * 063 * @param connection the JDBC connection 064 * @param xaResource the XAResource which managed the connection within a transaction 065 */ 066 public synchronized void registerConnection(Connection connection, XAResource xaResource) { 067 if (connection == null) { 068 throw new NullPointerException("connection is null"); 069 } 070 if (xaResource == null) { 071 throw new NullPointerException("xaResource is null"); 072 } 073 xaResources.put(connection, xaResource); 074 } 075 076 /** 077 * Gets the XAResource registered for the connection. 078 * @param connection the connection 079 * @return the XAResource registered for the connection; never null 080 * @throws SQLException if the connection does not have a registered XAResource 081 */ 082 public synchronized XAResource getXAResource(Connection connection) throws SQLException { 083 if (connection == null) { 084 throw new NullPointerException("connection is null"); 085 } 086 Connection key = getConnectionKey(connection); 087 XAResource xaResource = xaResources.get(key); 088 if (xaResource == null) { 089 throw new SQLException("Connection does not have a registered XAResource " + connection); 090 } 091 return xaResource; 092 } 093 094 /** 095 * Gets the active TransactionContext or null if not Transaction is active. 096 * @return the active TransactionContext or null if no Transaction is active 097 * @throws SQLException if an error occurs while fetching the transaction 098 */ 099 public TransactionContext getActiveTransactionContext() throws SQLException { 100 Transaction transaction = null; 101 try { 102 transaction = transactionManager.getTransaction(); 103 104 // was there a transaction? 105 if (transaction == null) { 106 return null; 107 } 108 109 // is it active 110 int status = transaction.getStatus(); 111 if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) { 112 return null; 113 } 114 } catch (SystemException e) { 115 throw new SQLException("Unable to determine current transaction ", e); 116 } 117 118 // register the the context (or create a new one) 119 synchronized (this) { 120 TransactionContext cache = caches.get(transaction); 121 if (cache == null) { 122 cache = new TransactionContext(this, transaction); 123 caches.put(transaction, cache); 124 } 125 return cache; 126 } 127 } 128 129 /** 130 * Unregisters a destroyed connection from {@link TransactionRegistry} 131 * @param connection 132 */ 133 public synchronized void unregisterConnection(Connection connection) { 134 Connection key = getConnectionKey(connection); 135 xaResources.remove(key); 136 } 137 138 139 private Connection getConnectionKey(Connection connection) { 140 Connection result; 141 if (connection instanceof DelegatingConnection) { 142 result = ((DelegatingConnection<?>) connection).getInnermostDelegateInternal(); 143 } else { 144 result = connection; 145 } 146 return result; 147 } 148} 149