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 javax.sql.ConnectionEvent; 021import javax.sql.ConnectionEventListener; 022import javax.sql.PooledConnection; 023import javax.sql.XAConnection; 024import javax.sql.XADataSource; 025import javax.transaction.TransactionManager; 026import javax.transaction.xa.XAResource; 027 028import org.apache.commons.dbcp2.Utils; 029 030import java.sql.Connection; 031import java.sql.SQLException; 032import java.util.Objects; 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 private final TransactionRegistry transactionRegistry; 041 private final XADataSource xaDataSource; 042 private String userName; 043 private char[] userPassword; 044 045 /** 046 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 047 * The connections are enlisted into transactions using the specified transaction manager. 048 * 049 * @param transactionManager 050 * the transaction manager in which connections will be enlisted 051 * @param xaDataSource 052 * the data source from which connections will be retrieved 053 */ 054 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource) { 055 this(transactionManager, xaDataSource, null, (char[]) null); 056 } 057 058 /** 059 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 060 * The connections are enlisted into transactions using the specified transaction manager. 061 * 062 * @param transactionManager 063 * the transaction manager in which connections will be enlisted 064 * @param xaDataSource 065 * the data source from which connections will be retrieved 066 * @param userName 067 * the user name used for authenticating new connections or null for unauthenticated 068 * @param userPassword 069 * the password used for authenticating new connections 070 */ 071 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 072 final String userName, final char[] userPassword) { 073 Objects.requireNonNull(transactionManager, "transactionManager is null"); 074 Objects.requireNonNull(xaDataSource, "xaDataSource is null"); 075 this.transactionRegistry = new TransactionRegistry(transactionManager); 076 this.xaDataSource = xaDataSource; 077 this.userName = userName; 078 this.userPassword = userPassword; 079 } 080 081 /** 082 * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database connections. 083 * The connections are enlisted into transactions using the specified transaction manager. 084 * 085 * @param transactionManager 086 * the transaction manager in which connections will be enlisted 087 * @param xaDataSource 088 * the data source from which connections will be retrieved 089 * @param userName 090 * the user name used for authenticating new connections or null for unauthenticated 091 * @param userPassword 092 * the password used for authenticating new connections 093 */ 094 public DataSourceXAConnectionFactory(final TransactionManager transactionManager, final XADataSource xaDataSource, 095 final String userName, final String userPassword) { 096 this(transactionManager, xaDataSource, userName, Utils.toCharArray(userPassword)); 097 } 098 099 /** 100 * Gets the user name used to authenticate new connections. 101 * 102 * @return the user name or null if unauthenticated connections are used 103 */ 104 public String getUsername() { 105 return userName; 106 } 107 108 /** 109 * Sets the user name used to authenticate new connections. 110 * 111 * @param userName 112 * the user name used for authenticating the connection or null for unauthenticated 113 */ 114 public void setUsername(final String userName) { 115 this.userName = userName; 116 } 117 118 /** 119 * Sets the password used to authenticate new connections. 120 * 121 * @param userPassword 122 * the password used for authenticating the connection or null for unauthenticated. 123 * @since 2.4.0 124 */ 125 public void setPassword(final char[] userPassword) { 126 this.userPassword = userPassword; 127 } 128 129 /** 130 * Sets the password used to authenticate new connections. 131 * 132 * @param userPassword 133 * the password used for authenticating the connection or null for unauthenticated 134 */ 135 public void setPassword(final String userPassword) { 136 this.userPassword = Utils.toCharArray(userPassword); 137 } 138 139 @Override 140 public TransactionRegistry getTransactionRegistry() { 141 return transactionRegistry; 142 } 143 144 @Override 145 public Connection createConnection() throws SQLException { 146 // create a new XAConnection 147 XAConnection xaConnection; 148 if (userName == null) { 149 xaConnection = xaDataSource.getXAConnection(); 150 } else { 151 xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); 152 } 153 154 // get the real connection and XAResource from the connection 155 final Connection connection = xaConnection.getConnection(); 156 final XAResource xaResource = xaConnection.getXAResource(); 157 158 // register the xa resource for the connection 159 transactionRegistry.registerConnection(connection, xaResource); 160 161 // The Connection we're returning is a handle on the XAConnection. 162 // When the pool calling us closes the Connection, we need to 163 // also close the XAConnection that holds the physical connection. 164 xaConnection.addConnectionEventListener(new ConnectionEventListener() { 165 166 @Override 167 public void connectionClosed(final ConnectionEvent event) { 168 final PooledConnection pc = (PooledConnection) event.getSource(); 169 pc.removeConnectionEventListener(this); 170 try { 171 pc.close(); 172 } catch (final SQLException e) { 173 System.err.println("Failed to close XAConnection"); 174 e.printStackTrace(); 175 } 176 } 177 178 @Override 179 public void connectionErrorOccurred(final ConnectionEvent event) { 180 connectionClosed(event); 181 } 182 }); 183 184 return connection; 185 } 186}