You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ps...@apache.org on 2009/10/25 17:26:25 UTC

svn commit: r829616 - in /commons/proper/dbcp/trunk: ./ src/java/org/apache/commons/dbcp/ src/java/org/apache/commons/dbcp/managed/ src/test/org/apache/commons/dbcp/managed/ xdocs/

Author: psteitz
Date: Sun Oct 25 16:26:24 2009
New Revision: 829616

URL: http://svn.apache.org/viewvc?rev=829616&view=rev
Log:
Added PoolableManagedConnection and PoolableManagedConnectionFactory so that pooled managed connections
can unregister themselves from transaction registries, avoiding resource leaks reported in
JIRA: DBCP-294.

Modified BasicDataSource to expose createPoolableConnectionFactory method so that BasicManagedDataSource
can override.  Increased visibility of BasicDataSource#validateConnectionFactory from private to protected.

Reported and patched by Philippe Mouawad

Added:
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnection.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnectionFactory.java
Modified:
    commons/proper/dbcp/trunk/pom.xml
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java
    commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestBasicManagedDataSource.java
    commons/proper/dbcp/trunk/xdocs/changes.xml

Modified: commons/proper/dbcp/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/pom.xml?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/pom.xml (original)
+++ commons/proper/dbcp/trunk/pom.xml Sun Oct 25 16:26:24 2009
@@ -140,6 +140,9 @@
       <name>Dain Sundstrom</name>
       <email>dain@apache.org</email>
     </contributor>
+    <contributor>
+      <name>Philippe Mouawad</name>
+    </contributor>
   </contributors>
 
   <dependencies>

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java Sun Oct 25 16:26:24 2009
@@ -30,6 +30,7 @@
 import java.sql.SQLException;
 import javax.sql.DataSource;
 
+import org.apache.commons.pool.KeyedObjectPoolFactory;
 import org.apache.commons.pool.impl.GenericKeyedObjectPool;
 import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
 import org.apache.commons.pool.impl.GenericObjectPool;
@@ -1331,27 +1332,8 @@
                         maxOpenPreparedStatements);
         }
 
-        // Set up the poolable connection factory we will use
-        PoolableConnectionFactory connectionFactory = null;
-        try {
-            connectionFactory =
-                new PoolableConnectionFactory(driverConnectionFactory,
-                                              connectionPool,
-                                              statementPoolFactory,
-                                              validationQuery,
-                                              validationQueryTimeout,
-                                              connectionInitSqls,
-                                              defaultReadOnly,
-                                              defaultAutoCommit,
-                                              defaultTransactionIsolation,
-                                              defaultCatalog,
-                                              abandonedConfig);
-            validateConnectionFactory(connectionFactory);
-        } catch (RuntimeException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
-        }
+        // Set up the poolable connection factory
+        createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);
 
         // Create and return the pooling data source to manage the connections
         createDataSourceInstance();
@@ -1368,7 +1350,7 @@
     }
 
     /**
-     * Creates a connection factory for this datasource.  This method only
+     * Creates a JDBC connection factory for this datasource.  This method only
      * exists so subclasses can replace the implementation class.
      */
     protected ConnectionFactory createConnectionFactory() throws SQLException {
@@ -1474,8 +1456,40 @@
         dataSource.setLogWriter(logWriter);
     }
 
+    /**
+     * Creates the PoolableConnectionFactory and attaches it to the connection pool.  This method only exists
+     * so subclasses can replace the default implementation.
+     * 
+     * @param driverConnectionFactory JDBC connection factory
+     * @param statementPoolFactory statement pool factory (null if statement pooling is turned off)
+     * @param abandonedConfig abandoned connection tracking configuration (null if no tracking)
+     * @throws SQLException if an error occurs creating the PoolableConnectionFactory
+     */
+    protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory,
+            KeyedObjectPoolFactory statementPoolFactory, AbandonedConfig abandonedConfig) throws SQLException {
+        PoolableConnectionFactory connectionFactory = null;
+        try {
+            connectionFactory =
+                new PoolableConnectionFactory(driverConnectionFactory,
+                                              connectionPool,
+                                              statementPoolFactory,
+                                              validationQuery,
+                                              validationQueryTimeout,
+                                              connectionInitSqls,
+                                              defaultReadOnly,
+                                              defaultAutoCommit,
+                                              defaultTransactionIsolation,
+                                              defaultCatalog,
+                                              abandonedConfig);
+            validateConnectionFactory(connectionFactory);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
+        }
+    }
 
-    private static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception {
+    protected static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception {
         Connection conn = null;
         try {
             conn = (Connection) connectionFactory.makeObject();

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableConnectionFactory.java Sun Oct 25 16:26:24 2009
@@ -580,10 +580,10 @@
 
     public Object makeObject() throws Exception {
         Connection conn = _connFactory.createConnection();
-        initializeConnection(conn);
         if (conn == null) {
             throw new IllegalStateException("Connection factory returned null from createConnection");
         }
+        initializeConnection(conn);
         if(null != _stmtPoolFactory) {
             KeyedObjectPool stmtpool = _stmtPoolFactory.createPool();
             conn = new PoolingConnection(conn,stmtpool);
@@ -592,7 +592,7 @@
         return new PoolableConnection(conn,_pool,_config);
     }
 
-    private void initializeConnection(Connection conn) throws SQLException {
+    protected void initializeConnection(Connection conn) throws SQLException {
         Collection sqls = _connectionInitSqls;
         if(conn.isClosed()) {
             throw new SQLException("initializeConnection: connection closed");

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/BasicManagedDataSource.java Sun Oct 25 16:26:24 2009
@@ -17,25 +17,28 @@
  */
 package org.apache.commons.dbcp.managed;
 
+import org.apache.commons.dbcp.AbandonedConfig;
 import org.apache.commons.dbcp.BasicDataSource;
 import org.apache.commons.dbcp.ConnectionFactory;
+import org.apache.commons.dbcp.PoolableConnectionFactory;
 import org.apache.commons.dbcp.PoolingDataSource;
 import org.apache.commons.dbcp.SQLNestedException;
+import org.apache.commons.pool.KeyedObjectPoolFactory;
 
 import javax.sql.XADataSource;
 import javax.transaction.TransactionManager;
 import java.sql.SQLException;
 
 /**
- * BasicManagedDataSource is an extension of BasicDataSource which
+ * <p>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
+ * full two-phase-commit XA connections or one-phase-commit
+ * local connections.  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
+ * <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
@@ -44,9 +47,6 @@
  * 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
@@ -135,4 +135,36 @@
         ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
         dataSource.setLogWriter(logWriter);
     }
+    
+    /**
+     * Creates the PoolableConnectionFactory and attaches it to the connection pool.
+     *
+     * @param driverConnectionFactory JDBC connection factory created by {@link #createConnectionFactory()}
+     * @param statementPoolFactory statement pool factory (null if statement pooling is turned off)
+     * @param abandonedConfig abandoned connection tracking configuration (null if no tracking)
+     * @throws SQLException if an error occurs creating the PoolableConnectionFactory
+     */
+    protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory,
+            KeyedObjectPoolFactory statementPoolFactory, AbandonedConfig abandonedConfig) throws SQLException {
+        PoolableConnectionFactory connectionFactory = null;
+        try {
+            connectionFactory =
+                new PoolableManagedConnectionFactory((XAConnectionFactory) driverConnectionFactory,
+                                              connectionPool,
+                                              statementPoolFactory,
+                                              validationQuery,
+                                              validationQueryTimeout,
+                                              connectionInitSqls,
+                                              defaultReadOnly,
+                                              defaultAutoCommit,
+                                              defaultTransactionIsolation,
+                                              defaultCatalog,
+                                              abandonedConfig);
+            validateConnectionFactory(connectionFactory);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
+        }
+    }
 }

Added: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnection.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnection.java?rev=829616&view=auto
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnection.java (added)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnection.java Sun Oct 25 16:26:24 2009
@@ -0,0 +1,72 @@
+/*
+ * 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 java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.commons.dbcp.AbandonedConfig;
+import org.apache.commons.dbcp.PoolableConnection;
+import org.apache.commons.pool.ObjectPool;
+
+/**
+ * PoolableConnection that unregisters from TransactionRegistry on Connection real destroy.
+ * 
+ * @see PoolableConnection
+ * @version $Revision$ $Date$
+ */
+public class PoolableManagedConnection extends PoolableConnection {
+    private TransactionRegistry transactionRegistry;
+
+    /**
+     * Create a PoolableManagedConnection.
+     * 
+     * @param transactionRegistry transaction registry 
+     * @param conn underlying connection
+     * @param pool connection pool
+     * @param config abandoned configuration settings
+     */
+    public PoolableManagedConnection(TransactionRegistry transactionRegistry, Connection conn, ObjectPool pool, AbandonedConfig config) {
+        super(conn, pool, config);
+        this.transactionRegistry = transactionRegistry;
+    }
+    
+    /**
+     * Create a PoolableManagedConnection.
+     * 
+     * @param transactionRegistry transaction registry 
+     * @param conn underlying connection
+     * @param pool connection pool
+     */
+    public PoolableManagedConnection(TransactionRegistry transactionRegistry, Connection conn, ObjectPool pool) {
+        super(conn, pool);
+        this.transactionRegistry = transactionRegistry;
+    }
+
+
+    /**
+     * Actually close the underlying connection.
+     */
+    public void reallyClose() throws SQLException {
+        try {
+            super.reallyClose();
+        } finally {
+            transactionRegistry.unregisterConnection(this);
+        }
+    }
+}

Added: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnectionFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnectionFactory.java?rev=829616&view=auto
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnectionFactory.java (added)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/PoolableManagedConnectionFactory.java Sun Oct 25 16:26:24 2009
@@ -0,0 +1,116 @@
+/*
+ * 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 java.sql.Connection;
+import java.util.Collection;
+
+import org.apache.commons.dbcp.AbandonedConfig;
+import org.apache.commons.dbcp.PoolableConnectionFactory;
+import org.apache.commons.dbcp.PoolingConnection;
+import org.apache.commons.pool.KeyedObjectPool;
+import org.apache.commons.pool.KeyedObjectPoolFactory;
+import org.apache.commons.pool.ObjectPool;
+
+/**
+ * A {@link PoolableConnectionFactory} that creates {@link PoolableManagedConnection}s.
+ * 
+ * @version $Revision$ $Date$
+ */
+public class PoolableManagedConnectionFactory extends PoolableConnectionFactory {
+
+    /** Transaction registry associated with connections created by this factory */
+    private TransactionRegistry transactionRegistry;
+    
+    /**
+     * Create a PoolableManagedConnectionFactory and attach it to a connection pool.
+     * 
+     * @param connFactory XAConnectionFactory
+     * @param pool connection pool 
+     * @param stmtPoolFactory the {@link KeyedObjectPoolFactory} to use to create {@link KeyedObjectPool}s for pooling
+     * {@link java.sql.PreparedStatement}s, or <tt>null</tt> to disable {@link java.sql.PreparedStatement} pooling
+     * @param validationQuery a query to use to {@link #validateObject validate} {@link Connection}s.
+     * Should return at least one row. Using <tt>null</tt> turns off validation.
+     * @param defaultReadOnly the default "read only" setting for borrowed {@link Connection}s
+     * @param defaultAutoCommit the default "auto commit" setting for returned {@link Connection}s
+     */
+    public PoolableManagedConnectionFactory(XAConnectionFactory connFactory,
+            ObjectPool pool, KeyedObjectPoolFactory stmtPoolFactory,
+            String validationQuery, boolean defaultReadOnly,
+            boolean defaultAutoCommit) {
+        super(connFactory, pool, stmtPoolFactory, validationQuery,
+                defaultReadOnly, defaultAutoCommit);
+        this.transactionRegistry = connFactory.getTransactionRegistry();
+    }
+    
+    /**
+     * Create a PoolableManagedConnectionFactory and attach it to a connection pool.
+     * 
+     * @param connFactory XAConnectionFactory
+     * @param pool connection pool 
+     * @param stmtPoolFactory the {@link KeyedObjectPoolFactory} to use to create {@link KeyedObjectPool}s for pooling
+     * {@link java.sql.PreparedStatement}s, or <tt>null</tt> to disable {@link java.sql.PreparedStatement} pooling
+     * @param validationQuery a query to use to {@link #validateObject validate} {@link Connection}s.
+     * Should return at least one row. Using <tt>null</tt> turns off validation.
+     * @param validationQueryTimeout the number of seconds that validation queries will wait for database response
+     * before failing. Use a value less than or equal to 0 for no timeout.
+     * @param connectionInitSqls a Collection of SQL statements to initialize {@link Connection}s.
+     * Using <tt>null</tt> turns off initialization.
+     * @param defaultReadOnly the default "read only" setting for borrowed {@link Connection}s
+     * @param defaultAutoCommit the default "auto commit" setting for returned {@link Connection}s
+     * @param defaultTransactionIsolation the default "Transaction Isolation" setting for returned {@link Connection}s
+     * @param defaultCatalog the default "catalog" setting for returned {@link Connection}s
+     * @param config the AbandonedConfig if tracing SQL objects
+     */
+    public PoolableManagedConnectionFactory(XAConnectionFactory connFactory,
+            ObjectPool pool,
+            KeyedObjectPoolFactory stmtPoolFactory,
+            String validationQuery,
+            int validationQueryTimeout,
+            Collection connectionInitSqls,
+            Boolean defaultReadOnly,
+            boolean defaultAutoCommit,
+            int defaultTransactionIsolation,
+            String defaultCatalog,
+            AbandonedConfig config) {
+        super(connFactory, pool, stmtPoolFactory, validationQuery, validationQueryTimeout, connectionInitSqls,
+                defaultReadOnly, defaultAutoCommit, defaultTransactionIsolation, defaultCatalog, config);
+        this.transactionRegistry = connFactory.getTransactionRegistry();
+    }
+
+    /**
+     * Uses the configured XAConnectionFactory to create a {@link PoolableManagedConnection}.
+     * Throws <code>IllegalStateException</code> if the connection factory returns null.
+     * Also initializes the connection using configured initialization sql (if provided)
+     * and sets up a prepared statement pool associated with the PoolableManagedConnection
+     * if statement pooling is enabled.
+     */
+    synchronized public Object makeObject() throws Exception {
+        Connection conn = _connFactory.createConnection();
+        if (conn == null) {
+            throw new IllegalStateException("Connection factory returned null from createConnection");
+        }
+        initializeConnection(conn);
+        if(null != _stmtPoolFactory) {
+            KeyedObjectPool stmtpool = _stmtPoolFactory.createPool();
+            conn = new PoolingConnection(conn,stmtpool);
+            stmtpool.setFactory((PoolingConnection)conn);
+        }
+        return new PoolableManagedConnection(transactionRegistry,conn,_pool,_config);
+    }
+
+}

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/managed/TransactionRegistry.java Sun Oct 25 16:26:24 2009
@@ -17,16 +17,18 @@
  */
 package org.apache.commons.dbcp.managed;
 
-import javax.transaction.xa.XAResource;
-import javax.transaction.TransactionManager;
-import javax.transaction.Transaction;
-import javax.transaction.SystemException;
-import javax.transaction.Status;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
+
+
 /**
  * TransactionRegistry tracks Connections and XAResources in a transacted environment for a single XAConnectionFactory.
  * </p>
@@ -112,5 +114,13 @@
             return cache;
         }
     }
+
+    /**
+     * Unregisters a destroyed connection from {@link TransactionRegistry}
+     * @param connection
+     */
+    public synchronized void unregisterConnection(Connection connection) {
+        xaResources.remove(connection);
+    }
 }
 

Modified: commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestBasicManagedDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestBasicManagedDataSource.java?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestBasicManagedDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/managed/TestBasicManagedDataSource.java Sun Oct 25 16:26:24 2009
@@ -17,8 +17,11 @@
  */
 package org.apache.commons.dbcp.managed;
 
+import java.sql.SQLException;
 import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.dbcp.PoolableConnection;
 import org.apache.commons.dbcp.TestBasicDataSource;
+import org.apache.commons.dbcp.managed.LocalXAConnectionFactory.LocalXAResource;
 import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -44,4 +47,34 @@
     public void testHashCode() throws Exception {
         // TODO reenable... hashcode doesn't work when accessToUnderlyingConnectionAllowed is false
     }
+    
+    /**
+     * JIRA: DBCP-294
+     * Verify that PoolableConnections created by BasicManagedDataSource unregister themselves
+     * when reallyClosed.
+     */
+    public void testReallyClose() throws Exception {
+        BasicManagedDataSource basicManagedDataSource = new BasicManagedDataSource();
+        basicManagedDataSource.setTransactionManager(new TransactionManagerImpl());
+        basicManagedDataSource.setDriverClassName("org.apache.commons.dbcp.TesterDriver");
+        basicManagedDataSource.setUrl("jdbc:apache:commons:testdriver");
+        basicManagedDataSource.setUsername("username");
+        basicManagedDataSource.setPassword("password");
+        basicManagedDataSource.setMaxIdle(1); 
+        // Create and register a connection
+        ManagedConnection conn = (ManagedConnection) basicManagedDataSource.getConnection();
+        basicManagedDataSource.transactionRegistry.registerConnection(conn, new LocalXAResource(conn));
+        // Create another connection and return it to the pool
+        ManagedConnection conn2 = (ManagedConnection) basicManagedDataSource.getConnection();
+        conn2.close();
+        conn.close(); // No room at the inn - this will trigger reallyClose(), which should unregister
+        try {
+            basicManagedDataSource.transactionRegistry.getXAResource(conn);
+            fail("Expecting SQLException - XAResources orphaned");
+        } catch (SQLException ex) {
+            // expected
+        }     
+        conn2.close();
+        basicManagedDataSource.close();
+    }
 }

Modified: commons/proper/dbcp/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/xdocs/changes.xml?rev=829616&r1=829615&r2=829616&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/xdocs/changes.xml (original)
+++ commons/proper/dbcp/trunk/xdocs/changes.xml Sun Oct 25 16:26:24 2009
@@ -42,6 +42,11 @@
      new features as well as bug fixes and instrumentation.  Some bug fixes
      will change semantics (e.g. connection close will become idempotent).
      The minimum JDK level will be increased to 1.4">
+      <action dev="psteitz" type="fix" issue="DBCP-294" due-to="Philippe Mouawad">
+        Added PoolableManagedConnection and PoolableManagedConnectionFactory so that
+        pooled managed connections can unregister themselves from transaction registries,
+        avoiding resource leaks.
+      </action>
       <action dev="psteitz" type="update" issue="DBCP-276">
         Added connectionProperties property to DriverAdapterCPDS.
       </action>