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