001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.dbcp2.managed; 018 019import java.sql.Connection; 020import java.sql.SQLException; 021import java.util.Objects; 022 023import javax.sql.ConnectionEvent; 024import javax.sql.ConnectionEventListener; 025import javax.sql.PooledConnection; 026import javax.sql.XAConnection; 027import javax.sql.XADataSource; 028import javax.transaction.TransactionManager; 029import javax.transaction.TransactionSynchronizationRegistry; 030import javax.transaction.xa.XAResource; 031 032import org.apache.commons.dbcp2.Utils; 033 034/** 035 * An implementation of XAConnectionFactory which uses a real XADataSource to obtain connections and XAResources. 036 * 037 * @since 2.0 038 */ 039public class DataSourceXAConnectionFactory implements XAConnectionFactory { 040 041 private static final class XAConnectionEventListener implements ConnectionEventListener { 042 @Override 043 public void connectionClosed(final ConnectionEvent event) { 044 final PooledConnection pc = (PooledConnection) event.getSource(); 045 pc.removeConnectionEventListener(this); 046 try { 047 pc.close(); 048 } catch (final SQLException e) { 049 System.err.println("Failed to close XAConnection"); 050 e.printStackTrace(); 051 } 052 } 053 054 @Override 055 public void connectionErrorOccurred(final ConnectionEvent event) { 056 connectionClosed(event); 057 } 058 } 059 060 private final TransactionRegistry transactionRegistry; 061 private final XADataSource xaDataSource; 062 private String userName; 063 private char[] userPassword; 064 065 /** 066 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 067 * The connections are enlisted into transactions using the specified transaction manager. 068 * 069 * @param transactionManager 070 * the transaction manager in which connections will be enlisted 071 * @param xaDataSource 072 * the data source from which connections will be retrieved 073 * @since 2.6.0 074 */ 075 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) { 076 this(transactionManager, xaDataSource, null, (char[]) null, null); 077 } 078 079 /** 080 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 081 * The connections are enlisted into transactions using the specified transaction manager. 082 * 083 * @param transactionManager 084 * the transaction manager in which connections will be enlisted 085 * @param xaDataSource 086 * the data source from which connections will be retrieved 087 * @param userName 088 * the user name used for authenticating new connections or null for unauthenticated 089 * @param userPassword 090 * the password used for authenticating new connections 091 */ 092 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 093 final String userName, final char[] userPassword) { 094 this(transactionManager, xaDataSource, userName, userPassword, null); 095 } 096 097 /** 098 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 099 * The connections are enlisted into transactions using the specified transaction manager. 100 * 101 * @param transactionManager 102 * the transaction manager in which connections will be enlisted 103 * @param xaDataSource 104 * the data source from which connections will be retrieved 105 * @param userName 106 * the user name used for authenticating new connections or null for unauthenticated 107 * @param userPassword 108 * the password used for authenticating new connections 109 * @param transactionSynchronizationRegistry 110 * register with this TransactionSynchronizationRegistry 111 * @since 2.6.0 112 */ 113 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 114 final String userName, final char[] userPassword, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 115 Objects.requireNonNull(transactionManager, "transactionManager"); 116 Objects.requireNonNull(xaDataSource, "xaDataSource"); 117 // We do allow the transactionSynchronizationRegistry to be null for non-app server environments 118 this.transactionRegistry = new TransactionRegistry(transactionManager, transactionSynchronizationRegistry); 119 this.xaDataSource = xaDataSource; 120 this.userName = userName; 121 this.userPassword = Utils.clone(userPassword); 122 } 123 124 /** 125 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 126 * The connections are enlisted into transactions using the specified transaction manager. 127 * 128 * @param transactionManager 129 * the transaction manager in which connections will be enlisted 130 * @param xaDataSource 131 * the data source from which connections will be retrieved 132 * @param userName 133 * the user name used for authenticating new connections or null for unauthenticated 134 * @param userPassword 135 * the password used for authenticating new connections 136 */ 137 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 138 final String userName, final String userPassword) { 139 this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword), null); 140 } 141 142 /** 143 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 144 * The connections are enlisted into transactions using the specified transaction manager. 145 * 146 * @param transactionManager 147 * the transaction manager in which connections will be enlisted 148 * @param xaDataSource 149 * the data source from which connections will be retrieved 150 * @param transactionSynchronizationRegistry 151 * register with this TransactionSynchronizationRegistry 152 */ 153 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, final TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 154 this(transactionManager, xaDataSource, null, (char[]) null, transactionSynchronizationRegistry); 155 } 156 157 @Override 158 public Connection createConnection() throws SQLException { 159 // create a new XAConnection 160 final XAConnection xaConnection; 161 if (userName == null) { 162 xaConnection = xaDataSource.getXAConnection(); 163 } else { 164 xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); 165 } 166 // get the real connection and XAResource from the connection 167 final Connection connection = xaConnection.getConnection(); 168 final XAResource xaResource = xaConnection.getXAResource(); 169 // register the XA resource for the connection 170 transactionRegistry.registerConnection(connection, xaResource); 171 // The Connection we're returning is a handle on the XAConnection. 172 // When the pool calling us closes the Connection, we need to 173 // also close the XAConnection that holds the physical connection. 174 xaConnection.addConnectionEventListener(new XAConnectionEventListener()); 175 176 return connection; 177 } 178 179 @Override 180 public TransactionRegistry getTransactionRegistry() { 181 return transactionRegistry; 182 } 183 184 /** 185 * Gets the user name used to authenticate new connections. 186 * 187 * @return the user name or null if unauthenticated connections are used 188 * @deprecated Use {@link #getUserName()}. 189 */ 190 @Deprecated 191 public String getUsername() { 192 return userName; 193 } 194 195 /** 196 * Gets the user name used to authenticate new connections. 197 * 198 * @return the user name or null if unauthenticated connections are used 199 * @since 2.6.0 200 */ 201 public String getUserName() { 202 return userName; 203 } 204 205 /** 206 * Gets the user password. 207 * 208 * @return the user password. 209 */ 210 public char[] getUserPassword() { 211 return Utils.clone(userPassword); 212 } 213 214 /** 215 * Gets the XA data source. 216 * 217 * @return the XA data source. 218 */ 219 public XADataSource getXaDataSource() { 220 return xaDataSource; 221 } 222 223 /** 224 * Sets the password used to authenticate new connections. 225 * 226 * @param userPassword 227 * the password used for authenticating the connection or null for unauthenticated. 228 * @since 2.4.0 229 */ 230 public void setPassword(final char[] userPassword) { 231 this.userPassword = Utils.clone(userPassword); 232 } 233 234 /** 235 * Sets the password used to authenticate new connections. 236 * 237 * @param userPassword 238 * the password used for authenticating the connection or null for unauthenticated 239 */ 240 public void setPassword(final String userPassword) { 241 this.userPassword = Utils.toCharArray(userPassword); 242 } 243 244 /** 245 * Sets the user name used to authenticate new connections. 246 * 247 * @param userName 248 * the user name used for authenticating the connection or null for unauthenticated 249 */ 250 public void setUsername(final String userName) { 251 this.userName = userName; 252 } 253}