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