blob: 0a3dae6c533739d04005745149cea9fd7e68802f [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.dbcp2.managed;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.dbcp2.Utils;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;
import java.sql.SQLException;
/**
* <p>
* BasicManagedDataSource is an extension of BasicDataSource which creates ManagedConnections. This data source can
* create either full two-phase-commit XA connections or one-phase-commit local connections. Both types of connections
* are committed or rolled back as part of the global transaction (a.k.a. XA transaction or JTA Transaction), but only
* XA connections can be recovered in the case of a system crash.
* </p>
* <p>
* BasicManagedDataSource adds the TransactionManager and XADataSource properties. The TransactionManager property is
* required and is used to enlist connections in global transactions. The XADataSource is optional and if set is the
* class name of the XADataSource class for a two-phase-commit JDBC driver. If the XADataSource property is set, the
* driverClassName is ignored and a DataSourceXAConnectionFactory is created. Otherwise, a standard
* DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory.
* </p>
*
* @see BasicDataSource
* @see ManagedConnection
* @since 2.0
*/
public class BasicManagedDataSource extends BasicDataSource {
/** Transaction Registry */
private TransactionRegistry transactionRegistry;
/** Transaction Manager */
private transient TransactionManager transactionManager;
/** XA data source class name */
private String xaDataSource;
/** XA data source instance */
private XADataSource xaDataSourceInstance;
/** Transaction Synchronization Registry */
private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;
/**
* Gets the XADataSource instance used by the XAConnectionFactory.
*
* @return the XADataSource
*/
public synchronized XADataSource getXaDataSourceInstance() {
return xaDataSourceInstance;
}
/**
* <p>
* Sets the XADataSource instance used by the XAConnectionFactory.
* </p>
* <p>
* 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 invoked: <code>getConnection, setLogwriter,
* setLoginTimeout, getLoginTimeout, getLogWriter.</code>
* </p>
*
* @param xaDataSourceInstance
* XADataSource instance
*/
public synchronized void setXaDataSourceInstance(final XADataSource xaDataSourceInstance) {
this.xaDataSourceInstance = xaDataSourceInstance;
xaDataSource = xaDataSourceInstance == null ? null : xaDataSourceInstance.getClass().getName();
}
/**
* Gets the required transaction manager property.
*
* @return the transaction manager used to enlist connections
*/
public TransactionManager getTransactionManager() {
return transactionManager;
}
/**
* Gets the optional TransactionSynchronizationRegistry.
*
* @return the TSR that can be used to register synchronizations.
* @since 2.6.0
*/
public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
return transactionSynchronizationRegistry;
}
/**
* Gets the transaction registry.
*
* @return the transaction registry associating XAResources with managed connections
*/
protected synchronized TransactionRegistry getTransactionRegistry() {
return transactionRegistry;
}
/**
* Sets the required transaction manager property.
*
* @param transactionManager
* the transaction manager used to enlist connections
*/
public void setTransactionManager(final TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Sets the optional TransactionSynchronizationRegistry property.
*
* @param transactionSynchronizationRegistry
* the TSR used to register synchronizations
* @since 2.6.0
*/
public void setTransactionSynchronizationRegistry(
final TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
}
/**
* Gets the optional XADataSource class name.
*
* @return the optional XADataSource class name
*/
public synchronized String getXADataSource() {
return xaDataSource;
}
/**
* Sets the optional XADataSource class name.
*
* @param xaDataSource
* the optional XADataSource class name
*/
public synchronized void setXADataSource(final String xaDataSource) {
this.xaDataSource = xaDataSource;
}
@Override
protected ConnectionFactory createConnectionFactory() throws SQLException {
if (transactionManager == null) {
throw new SQLException("Transaction manager must be set before a connection can be created");
}
// If xa data source is not specified a DriverConnectionFactory is created and wrapped with a
// LocalXAConnectionFactory
if (xaDataSource == null) {
final ConnectionFactory connectionFactory = super.createConnectionFactory();
final XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(),
getTransactionSynchronizationRegistry(), connectionFactory);
transactionRegistry = xaConnectionFactory.getTransactionRegistry();
return xaConnectionFactory;
}
// Create the XADataSource instance using the configured class name if it has not been set
if (xaDataSourceInstance == null) {
Class<?> xaDataSourceClass = null;
try {
xaDataSourceClass = Class.forName(xaDataSource);
} catch (final Exception t) {
final String message = "Cannot load XA data source class '" + xaDataSource + "'";
throw new SQLException(message, t);
}
try {
xaDataSourceInstance = (XADataSource) xaDataSourceClass.getConstructor().newInstance();
} catch (final Exception t) {
final String message = "Cannot create XA data source of class '" + xaDataSource + "'";
throw new SQLException(message, t);
}
}
// finally, create the XAConnectionFactory using the XA data source
final XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(),
xaDataSourceInstance, getUsername(), Utils.toCharArray(getPassword()), getTransactionSynchronizationRegistry());
transactionRegistry = xaConnectionFactory.getTransactionRegistry();
return xaConnectionFactory;
}
@Override
protected DataSource createDataSourceInstance() throws SQLException {
final PoolingDataSource<PoolableConnection> pds = new ManagedDataSource<>(getConnectionPool(),
transactionRegistry);
pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
return pds;
}
/**
* Creates the PoolableConnectionFactory and attaches it to the connection pool.
*
* @param driverConnectionFactory
* JDBC connection factory created by {@link #createConnectionFactory()}
* @throws SQLException
* if an error occurs creating the PoolableConnectionFactory
*/
@Override
protected PoolableConnectionFactory createPoolableConnectionFactory(final ConnectionFactory driverConnectionFactory)
throws SQLException {
PoolableConnectionFactory connectionFactory = null;
try {
connectionFactory = new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory,
getRegisteredJmxName());
connectionFactory.setValidationQuery(getValidationQuery());
connectionFactory.setValidationQueryTimeout(getValidationQueryTimeout());
connectionFactory.setConnectionInitSql(getConnectionInitSqls());
connectionFactory.setDefaultReadOnly(getDefaultReadOnly());
connectionFactory.setDefaultAutoCommit(getDefaultAutoCommit());
connectionFactory.setDefaultTransactionIsolation(getDefaultTransactionIsolation());
connectionFactory.setDefaultCatalog(getDefaultCatalog());
connectionFactory.setDefaultSchema(getDefaultSchema());
connectionFactory.setCacheState(getCacheState());
connectionFactory.setPoolStatements(isPoolPreparedStatements());
connectionFactory.setClearStatementPoolOnReturn(isClearStatementPoolOnReturn());
connectionFactory.setMaxOpenPreparedStatements(getMaxOpenPreparedStatements());
connectionFactory.setMaxConnLifetimeMillis(getMaxConnLifetimeMillis());
connectionFactory.setRollbackOnReturn(getRollbackOnReturn());
connectionFactory.setAutoCommitOnReturn(getAutoCommitOnReturn());
connectionFactory.setDefaultQueryTimeout(getDefaultQueryTimeout());
connectionFactory.setFastFailValidation(getFastFailValidation());
connectionFactory.setDisconnectionSqlCodes(getDisconnectionSqlCodes());
validateConnectionFactory(connectionFactory);
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new SQLException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
}
return connectionFactory;
}
}