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 * https://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 XAConnection xaConnection = null; 161 Connection connection = null; 162 final XAResource xaResource; 163 try { 164 if (userName == null) { 165 xaConnection = xaDataSource.getXAConnection(); 166 } else { 167 xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); 168 } 169 // get the real connection and XAResource from the connection 170 connection = xaConnection.getConnection(); 171 xaResource = xaConnection.getXAResource(); 172 } catch (final SQLException sqle) { 173 if (connection != null) { 174 try { 175 connection.close(); 176 } catch (final SQLException ignored) { 177 // Ignore 178 } 179 } 180 if (xaConnection != null) { 181 try { 182 xaConnection.close(); 183 } catch (final SQLException ignored) { 184 // Ignore 185 } 186 } 187 throw sqle; 188 } 189 // register the XA resource for the connection 190 transactionRegistry.registerConnection(connection, xaResource); 191 // The Connection we're returning is a handle on the XAConnection. 192 // When the pool calling us closes the Connection, we need to 193 // also close the XAConnection that holds the physical connection. 194 xaConnection.addConnectionEventListener(new XAConnectionEventListener()); 195 196 return connection; 197 } 198 199 @Override 200 public TransactionRegistry getTransactionRegistry() { 201 return transactionRegistry; 202 } 203 204 /** 205 * Gets the user name used to authenticate new connections. 206 * 207 * @return the user name or null if unauthenticated connections are used 208 * @deprecated Use {@link #getUserName()}. 209 */ 210 @Deprecated 211 public String getUsername() { 212 return userName; 213 } 214 215 /** 216 * Gets the user name used to authenticate new connections. 217 * 218 * @return the user name or null if unauthenticated connections are used 219 * @since 2.6.0 220 */ 221 public String getUserName() { 222 return userName; 223 } 224 225 /** 226 * Gets the user password. 227 * 228 * @return the user password. 229 */ 230 public char[] getUserPassword() { 231 return Utils.clone(userPassword); 232 } 233 234 /** 235 * Gets the XA data source. 236 * 237 * @return the XA data source. 238 */ 239 public XADataSource getXaDataSource() { 240 return xaDataSource; 241 } 242 243 /** 244 * Sets the password used to authenticate new connections. 245 * 246 * @param userPassword 247 * the password used for authenticating the connection or null for unauthenticated. 248 * @since 2.4.0 249 */ 250 public void setPassword(final char[] userPassword) { 251 this.userPassword = Utils.clone(userPassword); 252 } 253 254 /** 255 * Sets the password used to authenticate new connections. 256 * 257 * @param userPassword 258 * the password used for authenticating the connection or null for unauthenticated 259 */ 260 public void setPassword(final String userPassword) { 261 this.userPassword = Utils.toCharArray(userPassword); 262 } 263 264 /** 265 * Sets the user name used to authenticate new connections. 266 * 267 * @param userName 268 * the user name used for authenticating the connection or null for unauthenticated 269 */ 270 public void setUsername(final String userName) { 271 this.userName = userName; 272 } 273}