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$
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 }