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