Author: psteitz Date: Fri Jul 13 06:10:05 2007 New Revision: 555980 URL: http://svn.apache.org/viewvc?view=rev&rev=555980 Log: Added BasicManagedDataSource, extending BasicDataSource. Also improved extensibility of BasicDataSource by encapsulating methods to create object pool, connection factory and datasource instance previously embedded in createDataSource.
JIRA: DBCP-230 Patch provided by Dain Sundstrom Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java Modified: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml Modified: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java?view=diff&rev=555980&r1=555979&r2=555980 ============================================================================== --- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java (original) +++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java Fri Jul 13 06:10:05 2007 @@ -1135,6 +1135,65 @@ return (dataSource); } + // create factory which returns raw physical connections + ConnectionFactory driverConnectionFactory = createConectionFactory(); + + // create a pool for our connections + createConnectionPool(); + + // Set up statement pool, if desired + GenericKeyedObjectPoolFactory statementPoolFactory = null; + if (isPoolPreparedStatements()) { + statementPoolFactory = new GenericKeyedObjectPoolFactory(null, + -1, // unlimited maxActive (per key) + GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, + 0, // maxWait + 1, // maxIdle (per key) + maxOpenPreparedStatements); + } + + // Set up the poolable connection factory we will use + PoolableConnectionFactory connectionFactory = null; + try { + connectionFactory = + new PoolableConnectionFactory(driverConnectionFactory, + connectionPool, + statementPoolFactory, + validationQuery, + defaultReadOnly, + defaultAutoCommit, + defaultTransactionIsolation, + defaultCatalog, + abandonedConfig); + if (connectionFactory == null) { + throw new SQLException("Cannot create PoolableConnectionFactory"); + } + validateConnectionFactory(connectionFactory); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e); + } + + // Create and return the pooling data source to manage the connections + createDataSourceInstance(); + + try { + for (int i = 0 ; i < initialSize ; i++) { + connectionPool.addObject(); + } + } catch (Exception e) { + throw new SQLNestedException("Error preloading the connection pool", e); + } + + return dataSource; + } + + /** + * Creates a connection factory for this datasource. This method only + * exists so subclasses can replace the implementation class. + */ + protected ConnectionFactory createConectionFactory() throws SQLException { // Load the JDBC driver class if (driverClassName != null) { try { @@ -1154,7 +1213,7 @@ driver = DriverManager.getDriver(url); } catch (Throwable t) { String message = "Cannot create JDBC driver of class '" + - (driverClassName != null ? driverClassName : "") + + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'"; logWriter.println(message); t.printStackTrace(logWriter); @@ -1168,6 +1227,28 @@ setTestWhileIdle(false); } + // Set up the driver connection factory we will use + if (username != null) { + connectionProperties.put("user", username); + } else { + log("DBCP DataSource configured without a 'username'"); + } + + if (password != null) { + connectionProperties.put("password", password); + } else { + log("DBCP DataSource configured without a 'password'"); + } + + ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, url, connectionProperties); + return driverConnectionFactory; + } + + /** + * Creates a connection pool for this datasource. This method only exists + * so subclasses can replace the implementation class. + */ + protected void createConnectionPool() { // Create an object pool to contain our active connections if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) { connectionPool = new AbandonedObjectPool(null,abandonedConfig); @@ -1185,73 +1266,21 @@ connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun); connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); connectionPool.setTestWhileIdle(testWhileIdle); - - // Set up statement pool, if desired - GenericKeyedObjectPoolFactory statementPoolFactory = null; - if (isPoolPreparedStatements()) { - statementPoolFactory = new GenericKeyedObjectPoolFactory(null, - -1, // unlimited maxActive (per key) - GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, - 0, // maxWait - 1, // maxIdle (per key) - maxOpenPreparedStatements); - } - - // Set up the driver connection factory we will use - if (username != null) { - connectionProperties.put("user", username); - } else { - log("DBCP DataSource configured without a 'username'"); - } - - if (password != null) { - connectionProperties.put("password", password); - } else { - log("DBCP DataSource configured without a 'password'"); - } - - DriverConnectionFactory driverConnectionFactory = - new DriverConnectionFactory(driver, url, connectionProperties); - - // Set up the poolable connection factory we will use - PoolableConnectionFactory connectionFactory = null; - try { - connectionFactory = - new PoolableConnectionFactory(driverConnectionFactory, - connectionPool, - statementPoolFactory, - validationQuery, - defaultReadOnly, - defaultAutoCommit, - defaultTransactionIsolation, - defaultCatalog, - abandonedConfig); - if (connectionFactory == null) { - throw new SQLException("Cannot create PoolableConnectionFactory"); - } - validateConnectionFactory(connectionFactory); - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e); - } + } - // Create and return the pooling data source to manage the connections + /** + * Creates the actual data source instance. This method only exists so + * subclasses can replace the implementation class. + * + * @throws SQLException if unable to create a datasource instance + */ + protected void createDataSourceInstance() throws SQLException { dataSource = new PoolingDataSource(connectionPool); ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); dataSource.setLogWriter(logWriter); - - try { - for (int i = 0 ; i < initialSize ; i++) { - connectionPool.addObject(); - } - } catch (Exception e) { - throw new SQLNestedException("Error preloading the connection pool", e); - } - - return dataSource; } + private static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception { Connection conn = null; try { @@ -1276,7 +1305,7 @@ } } - private void log(String message) { + protected void log(String message) { if (logWriter != null) { logWriter.println(message); } Added: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java?view=auto&rev=555980 ============================================================================== --- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java (added) +++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java Fri Jul 13 06:10:05 2007 @@ -0,0 +1,138 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.dbcp.managed; + +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.PoolingDataSource; +import org.apache.commons.dbcp.SQLNestedException; + +import javax.sql.XADataSource; +import javax.transaction.TransactionManager; +import java.sql.SQLException; + +/** + * BasicManagedDataSource is an extension of BasicDataSource which + * creates ManagedConnections. This data source can create either + * create full two-phase-commit XA connections or one-phase-commit + * local connection. Both types of connections are committed or + * rolled back as part of the global transaction (a.k.a. XA + * transaction or JTA Transaction), but only XA connections can be + * recovered in the case of a system crash. + * </p> + * BasicManagedDataSource adds the TransactionManager and XADataSource + * properties. The TransactionManager property is required and is + * used to elist connections in global transactions. The XADataSource + * is optional and if set is the class name of the XADataSource class + * for a two-phase-commit JDBC driver. If the XADataSource property + * is set, the driverClassName is ignored and a DataSourceXAConnectionFactory + * is created. Otherwise, a standard DriverConnectionFactory is created + * and wrapped with a LocalXAConnectionFactory. + * </p> + * This is not the only way to combine the <em>commons-dbcp</em> and + * <em>commons-pool</em> packages, but provides a "one stop shopping" + * solution for basic requirements.</p> + * + * @see BasicDataSource + * @see ManagedConnection + * @version $Revision$ + */ +public class BasicManagedDataSource extends BasicDataSource { + protected TransactionRegistry transactionRegistry; + protected TransactionManager transactionManager; + protected String xaDataSource; + + /** + * Gets the required transaction manager property. + * @return the transaction manager used to enlist connections + */ + public TransactionManager getTransactionManager() { + return transactionManager; + } + + /** + * Sets the required transaction manager property. + * @param transactionManager the transaction manager used to enlist connections + */ + public void setTransactionManager(TransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + /** + * Gets the optional XADataSource class name. + * @return the optional XADataSource class name + */ + public String getXADataSource() { + return xaDataSource; + } + + /** + * Sets the optional XADataSource class name. + * @param xaDataSource the optional XADataSource class name + */ + public void setXADataSource(String xaDataSource) { + this.xaDataSource = xaDataSource; + } + + protected ConnectionFactory createConectionFactory() throws SQLException { + if (transactionManager == null) { + throw new SQLException("Transaction manager must be set before a connection can be created"); + } + + // If xa data source is not specified a DriverConnectionFactory is created and wrapped with a LocalXAConnectionFactory + if (xaDataSource == null) { + ConnectionFactory connectionFactory = super.createConectionFactory(); + XAConnectionFactory xaConnectionFactory = new LocalXAConnectionFactory(getTransactionManager(), connectionFactory); + transactionRegistry = xaConnectionFactory.getTransactionRegistry(); + return xaConnectionFactory; + } + + // Load the XA data source class + Class xaDataSourceClass = null; + try { + xaDataSourceClass = Class.forName(xaDataSource); + } catch (Throwable t) { + String message = "Cannot load XA data source class '" + xaDataSource + "'"; + logWriter.println(message); + t.printStackTrace(logWriter); + throw new SQLNestedException(message, t); + } + + // Create the xa data source instance + XADataSource xaDataSource = null; + try { + xaDataSource = (XADataSource) xaDataSourceClass.newInstance(); + } catch (Throwable t) { + String message = "Cannot create XA data source of class '" + xaDataSource + "'"; + logWriter.println(message); + t.printStackTrace(logWriter); + throw new SQLNestedException(message, t); + } + + // finally, create the XAConectionFactory using the XA data source + XAConnectionFactory xaConnectionFactory = new DataSourceXAConnectionFactory(getTransactionManager(), xaDataSource, username, password); + transactionRegistry = xaConnectionFactory.getTransactionRegistry(); + return xaConnectionFactory; + } + + protected void createDataSourceInstance() throws SQLException { + dataSource = new ManagedDataSource(connectionPool, transactionRegistry); + ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); + dataSource.setLogWriter(logWriter); + } +} Modified: jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java?view=diff&rev=555980&r1=555979&r2=555980 ============================================================================== --- jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java (original) +++ jakarta/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/DataSourceXAConnectionFactory.java Fri Jul 13 06:10:05 2007 @@ -33,6 +33,8 @@ public class DataSourceXAConnectionFactory implements XAConnectionFactory { protected TransactionRegistry transactionRegistry; protected XADataSource xaDataSource; + protected String username; + protected String password; /** * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database @@ -42,11 +44,50 @@ * @param xaDataSource the data source from which connections will be retrieved */ public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource) { + this(transactionManager, xaDataSource, null, null); + } + + /** + * Creates an DataSourceXAConnectionFactory which uses the specified XADataSource to create database + * connections. The connections are enlisted into transactions using the specified transaction manager. + * + * @param transactionManager the transaction manager in which connections will be enlisted + * @param xaDataSource the data source from which connections will be retrieved + * @param username the username used for authenticating new connections or null for unauthenticated + * @param password the password used for authenticating new connections + */ + public DataSourceXAConnectionFactory(TransactionManager transactionManager, XADataSource xaDataSource, String username, String password) { if (transactionManager == null) throw new NullPointerException("transactionManager is null"); if (xaDataSource == null) throw new NullPointerException("xaDataSource is null"); this.transactionRegistry = new TransactionRegistry(transactionManager); this.xaDataSource = xaDataSource; + this.username = username; + this.password = password; + } + + /** + * Gets the username used to authenticate new connections. + * @return the user name or null if unauthenticated connections are used + */ + public String getUsername() { + return username; + } + + /** + * Sets the username used to authenticate new connections. + * @param username the username used for authenticating the connection or null for unauthenticated + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Sets the password used to authenticate new connections. + * @param password the password used for authenticating the connection or null for unauthenticated + */ + public void setPassword(String password) { + this.password = password; } public TransactionRegistry getTransactionRegistry() { @@ -55,7 +96,12 @@ public Connection createConnection() throws SQLException { // create a new XAConection - XAConnection xaConnection = xaDataSource.getXAConnection(); + XAConnection xaConnection; + if (username == null) { + xaConnection = xaDataSource.getXAConnection(); + } else { + xaConnection = xaDataSource.getXAConnection(username, password); + } // get the real connection and XAResource from the connection Connection connection = xaConnection.getConnection(); Modified: jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml?view=diff&rev=555980&r1=555979&r2=555980 ============================================================================== --- jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml (original) +++ jakarta/commons/proper/dbcp/trunk/xdocs/changes.xml Fri Jul 13 06:10:05 2007 @@ -54,6 +54,12 @@ <action dev="psteitz" type="add" issue="DBCP-228" due-to="Dain Sundstrom"> Added support for pooling managed connections. </action> + <action dev="psteitz" type="add" issue="DBCP-230" due-to="Dain Sundstrom"> + Added BasicManagedDataSource, extending BasicDataSource. + Also improved extensibility of BasicDataSource by encapsulating + methods to create object pool, connection factory and datasource + instance previously embedded in createDataSource. + </action> </release> <release version="1.2.2" date="2007-04-04" description="This is a maintenance release containing bug fixes --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]