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.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 @Override 070 protected ConnectionFactory createConnectionFactory() throws SQLException { 071 if (transactionManager == null) { 072 throw new SQLException("Transaction manager must be set before a connection can be created"); 073 } 074 075 // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a 076 // LocalXAConnectionFactory 077 if (xaDataSource == null) { 078 final ConnectionFactory connectionFactory = super.createConnectionFactory(); 079 final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), 080 getTransactionSynchronizationRegistry(), connectionFactory); 081 transactionRegistry = xaConnectionFactory.getTransactionRegistry(); 082 return xaConnectionFactory; 083 } 084 085 // Create the XADataSource instance using the configured class name if it has not been set 086 if (xaDataSourceInstance == null) { 087 Class<?> xaDataSourceClass = null; 088 try { 089 xaDataSourceClass = Class.forName(xaDataSource); 090 } catch (final Exception t) { 091 final String message = "Cannot load XA data source class '" + xaDataSource + "'"; 092 throw new SQLException(message, t); 093 } 094 095 try { 096 xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance(); 097 } catch (final Exception t) { 098 final String message = "Cannot create XA data source of class '" + xaDataSource + "'"; 099 throw new SQLException(message, t); 100 } 101 } 102 103 // finally, create the XAConnectionFactory using the XA data source 104 final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), 105 xaDataSourceInstance, getUsername(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry()); 106 transactionRegistry = xaConnectionFactory.getTransactionRegistry(); 107 return xaConnectionFactory; 108 } 109 110 @Override 111 protected DataSource createDataSourceInstance() throws SQLException { 112 final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(), 113 transactionRegistry); 114 pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); 115 return pds; 116 } 117 118 /** 119 * Creates the PoolableConnectionFactory and attaches it to the connection pool. 120 * 121 * @param driverConnectionFactory 122 * JDBC connection factory created by {@link #createConnectionFactory()} 123 * @throws SQLException 124 * if an error occurs creating the PoolableConnectionFactory 125 */ 126 @Override 127 protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory) 128 throws SQLException { 129 PoolableConnectionFactory connectionFactory = null; 130 try { 131 connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory, 132 getRegisteredJmxName()); 133 connectionFactory.setValidationQuery(getValidationQuery()); 134 connectionFactory.setValidationQueryTimeout(getValidationQueryTimeoutDuration()); 135 connectionFactory.setConnectionInitSql(getConnectionInitSqls()); 136 connectionFactory.setDefaultReadOnly(getDefaultReadOnly()); 137 connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit()); 138 connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation()); 139 connectionFactory.setDefaultCatalog(getDefaultCatalog()); 140 connectionFactory.setDefaultSchema(getDefaultSchema()); 141 connectionFactory.setCacheState(getCacheState()); 142 connectionFactory.setPoolStatements(isPoolPreparedStatements()); 143 connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn()); 144 connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements()); 145 connectionFactory.setMaxConn(getMaxConnDuration()); 146 connectionFactory.setRollbackOnReturn(getRollbackOnReturn()); 147 connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn()); 148 connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeoutDuration()); 149 connectionFactory.setFastFailValidation(getFastFailValidation()); 150 connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes()); 151 validateConnectionFactory(connectionFactory); 152 } catch (final RuntimeException e) { 153 throw e; 154 } catch (final Exception e) { 155 throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e); 156 } 157 return connectionFactory; 158 } 159 160 /** 161 * Gets the required transaction manager property. 162 * 163 * @return the transaction manager used to enlist connections 164 */ 165 public TransactionManager getTransactionManager() { 166 return transactionManager; 167 } 168 169 /** 170 * Gets the transaction registry. 171 * 172 * @return the transaction registry associating XAResources with managed connections 173 */ 174 protected synchronized TransactionRegistry getTransactionRegistry() { 175 return transactionRegistry; 176 } 177 178 /** 179 * Gets the optional TransactionSynchronizationRegistry. 180 * 181 * @return the TSR that can be used to register synchronizations. 182 * @since 2.6.0 183 */ 184 public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { 185 return transactionSynchronizationRegistry; 186 } 187 188 /** 189 * Gets the optional XADataSource class name. 190 * 191 * @return the optional XADataSource class name 192 */ 193 public synchronized String getXADataSource() { 194 return xaDataSource; 195 } 196 197 /** 198 * Gets the XADataSource instance used by the XAConnectionFactory. 199 * 200 * @return the XADataSource 201 */ 202 public synchronized XADataSource getXaDataSourceInstance() { 203 return xaDataSourceInstance; 204 } 205 206 /** 207 * Sets the required transaction manager property. 208 * 209 * @param transactionManager 210 * the transaction manager used to enlist connections 211 */ 212 public void setTransactionManager(final TransactionManager transactionManager) { 213 this.transactionManager = transactionManager; 214 } 215 216 /** 217 * Sets the optional TransactionSynchronizationRegistry property. 218 * 219 * @param transactionSynchronizationRegistry 220 * the TSR used to register synchronizations 221 * @since 2.6.0 222 */ 223 public void setTransactionSynchronizationRegistry( 224 final TransactionSynchronizationRegistry transactionSynchronizationRegistry) { 225 this.transactionSynchronizationRegistry = transactionSynchronizationRegistry; 226 } 227 228 /** 229 * Sets the optional XADataSource class name. 230 * 231 * @param xaDataSource 232 * the optional XADataSource class name 233 */ 234 public synchronized void setXADataSource(final String xaDataSource) { 235 this.xaDataSource = xaDataSource; 236 } 237 238 /** 239 * <p> 240 * Sets the XADataSource instance used by the XAConnectionFactory. 241 * </p> 242 * <p> 243 * Note: this method currently has no effect once the pool has been initialized. The pool is initialized the first 244 * time one of the following methods is invoked: <code>getConnection, setLogwriter, 245 * setLoginTimeout, getLoginTimeout, getLogWriter.</code> 246 * </p> 247 * 248 * @param xaDataSourceInstance 249 * XADataSource instance 250 */ 251 public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) { 252 this.xaDataSourceInstance = xaDataSourceInstance; 253 xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName(); 254 } 255}