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.SQLException; 020 021import javax.sql.DataSource; 022import javax.sql.XADataSource; 023import javax.transaction.TransactionManager; 024import javax.transaction.TransactionSynchronizationRegistry; 025 026import org.apache.commons.dbcp2.BasicDataSource; 027import org.apache.commons.dbcp2.ConnectionFactory; 028import org.apache.commons.dbcp2.PoolableConnection; 029import org.apache.commons.dbcp2.PoolableConnectionFactory; 030import org.apache.commons.dbcp2.PoolingDataSource; 031import org.apache.commons.dbcp2.Utils; 032 033/** 034 * <p> 035 * BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can 036 * create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections 037 * are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only 038 * XA connections can be recovered in the case of a system crash. 039 * </p> 040 * <p> 041 * BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is 042 * required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the 043 * class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the 044 * driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard 045 * DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory. 046 * </p> 047 * 048 * @see BasicDataSource 049 * @see ManagedConnection 050 * @since 2.0 051 */ 052public class BasicManagedDataSource extends BasicDataSource { 053 054 /** Transaction Registry */ 055 private TransactionRegistry transactionRegistry; 056 057 /** Transaction Manager */ 058 private transient TransactionManager transactionManager; 059 060 /** XA data source class name */ 061 private String xaDataSource; 062 063 /** XA data source instance */ 064 private XADataSource xaDataSourceInstance; 065 066 /** Transaction Synchronization Registry */ 067 private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry; 068 069 /** 070 * Constructs a new instance. 071 */ 072 public BasicManagedDataSource() { 073 // empty 074 } 075 076 @Override 077 protected synchronized ConnectionFactory createConnectionFactory() throws SQLException { 078 if (transactionManager == null) { 079 throw new SQLException("Transaction manager must be set before a connection can be created"); 080 } 081 082 // If XA data source is not specified a DriverConnectionFactory is created and wrapped with a 083 // LocalXAConnectionFactory 084 if (xaDataSource == null) { 085 final ConnectionFactory connectionFactory = super.createConnectionFactory(); 086 final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), 087 getTransactionSynchronizationRegistry(), connectionFactory); 088 transactionRegistry = xaConnectionFactory.getTransactionRegistry(); 089 return xaConnectionFactory; 090 } 091 092 // Create the XADataSource instance using the configured class name if it has not been set 093 if (xaDataSourceInstance == null) { 094 Class<?> xaDataSourceClass = null; 095 try { 096 xaDataSourceClass = Class.forName(xaDataSource); 097 } catch (final Exception e) { 098 throw new SQLException("Cannot load XA data source class '" + xaDataSource + "'", e); 099 } 100 101 try { 102 xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance(); 103 } catch (final Exception e) { 104 throw new SQLException("Cannot create XA data source of class '" + xaDataSource + "'", e); 105 } 106 } 107 108 // finally, create the XAConnectionFactory using the XA data source 109 final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), 110 xaDataSourceInstance, getUserName(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry()); 111 transactionRegistry = xaConnectionFactory.getTransactionRegistry(); 112 return xaConnectionFactory; 113 } 114 115 @Override 116 protected DataSource createDataSourceInstance() throws SQLException { 117 final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(), 118 transactionRegistry); 119 pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 120 return pds; 121 } 122 123 /** 124 * Creates the PoolableConnectionFactory and attaches it to the connection pool. 125 * 126 * @param driverConnectionFactory 127 * JDBC connection factory created by {@link #createConnectionFactory()} 128 * @throws SQLException 129 * if an error occurs creating the PoolableConnectionFactory 130 */ 131 @Override 132 protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory) 133 throws SQLException { 134 PoolableConnectionFactory connectionFactory = null; 135 try { 136 connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory, getRegisteredJmxName()); 137 connectionFactory.setValidationQuery(getValidationQuery()); 138 connectionFactory.setValidationQueryTimeout(getValidationQueryTimeoutDuration()); 139 connectionFactory.setConnectionInitSql(getConnectionInitSqls()); 140 connectionFactory.setDefaultReadOnly(getDefaultReadOnly()); 141 connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit()); 142 connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation()); 143 connectionFactory.setDefaultCatalog(getDefaultCatalog()); 144 connectionFactory.setDefaultSchema(getDefaultSchema()); 145 connectionFactory.setCacheState(getCacheState()); 146 connectionFactory.setPoolStatements(isPoolPreparedStatements()); 147 connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn()); 148 connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements()); 149 connectionFactory.setMaxConn(getMaxConnDuration()); 150 connectionFactory.setRollbackOnReturn(getRollbackOnReturn()); 151 connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn()); 152 connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeoutDuration()); 153 connectionFactory.setFastFailValidation(getFastFailValidation()); 154 connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes()); 155 connectionFactory.setDisconnectionIgnoreSqlCodes(getDisconnectionIgnoreSqlCodes()); 156 validateConnectionFactory(connectionFactory); 157 } catch (final RuntimeException e) { 158 throw e; 159 } catch (final Exception e) { 160 throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e); 161 } 162 return connectionFactory; 163 } 164 165 /** 166 * Gets the required transaction manager property. 167 * 168 * @return the transaction manager used to enlist connections 169 */ 170 public TransactionManager getTransactionManager() { 171 return transactionManager; 172 } 173 174 /** 175 * Gets the transaction registry. 176 * 177 * @return the transaction registry associating XAResources with managed connections 178 */ 179 protected synchronized TransactionRegistry getTransactionRegistry() { 180 return transactionRegistry; 181 } 182 183 /** 184 * Gets the optional TransactionSynchronizationRegistry. 185 * 186 * @return the TSR that can be used to register synchronizations. 187 * @since 2.6.0 188 */ 189 public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { 190 return transactionSynchronizationRegistry; 191 } 192 193 /** 194 * Gets the optional XADataSource class name. 195 * 196 * @return the optional XADataSource class name 197 */ 198 public synchronized String getXADataSource() { 199 return xaDataSource; 200 } 201 202 /** 203 * Gets the XADataSource instance used by the XAConnectionFactory. 204 * 205 * @return the XADataSource 206 */ 207 public synchronized XADataSource getXaDataSourceInstance() { 208 return xaDataSourceInstance; 209 } 210 211 /** 212 * Sets the required transaction manager property. 213 * 214 * @param transactionManager 215 * the transaction manager used to enlist connections 216 */ 217 public void setTransactionManager(final TransactionManager transactionManager) { 218 this.transactionManager = transactionManager; 219 } 220 221 /** 222 * Sets the optional TransactionSynchronizationRegistry property. 223 * 224 * @param transactionSynchronizationRegistry 225 * the TSR used to register synchronizations 226 * @since 2.6.0 227 */ 228 public void setTransactionSynchronizationRegistry( 229 final TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 230 this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; 231 } 232 233 /** 234 * Sets the optional XADataSource class name. 235 * 236 * @param xaDataSource 237 * the optional XADataSource class name 238 */ 239 public synchronized void setXADataSource(final String xaDataSource) { 240 this.xaDataSource = xaDataSource; 241 } 242 243 /** 244 * Sets the XADataSource instance used by the XAConnectionFactory. 245 * <p> 246 * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first time one of the following methods is 247 * invoked: {@link #getConnection()}, {@link #setLogWriter(java.io.PrintWriter)}, {@link #setLoginTimeout(int)}, {@link #getLoginTimeout()}, 248 * {@link #getLogWriter()}. 249 * </p> 250 * 251 * @param xaDataSourceInstance XADataSource instance 252 */ 253 public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) { 254 this.xaDataSourceInstance = xaDataSourceInstance; 255 xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName(); 256 } 257}