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