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