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