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