Index: src/test/org/apache/commons/dbcp/managed/TestManagedDataSourceInTx.java =================================================================== --- src/test/org/apache/commons/dbcp/managed/TestManagedDataSourceInTx.java (revision 557361) +++ src/test/org/apache/commons/dbcp/managed/TestManagedDataSourceInTx.java (working copy) @@ -21,6 +21,9 @@ import java.sql.Connection; import java.sql.SQLException; +import java.sql.Statement; +import java.sql.ResultSet; +import java.sql.PreparedStatement; import junit.framework.Test; import junit.framework.TestSuite; @@ -302,4 +305,41 @@ // close connection connection.close(); } + + // can't actually test close in a transaction + protected void assertBackPointers(Connection conn, Statement statement) throws SQLException { + assertFalse(conn.isClosed()); + assertFalse(isClosed(statement)); + + assertSame("statement.getConnection() should return the exact same connection instance that was used to create the statement", + conn, statement.getConnection()); + + ResultSet resultSet = statement.getResultSet(); + assertFalse(isClosed(resultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, resultSet.getStatement()); + + ResultSet executeResultSet = statement.executeQuery("select * from dual"); + assertFalse(isClosed(executeResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, executeResultSet.getStatement()); + + ResultSet keysResultSet = statement.getGeneratedKeys(); + assertFalse(isClosed(keysResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, keysResultSet.getStatement()); + + ResultSet preparedResultSet = null; + if (statement instanceof PreparedStatement) { + PreparedStatement preparedStatement = (PreparedStatement) statement; + preparedResultSet = preparedStatement.executeQuery(); + assertFalse(isClosed(preparedResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, preparedResultSet.getStatement()); + } + + + resultSet.getStatement().getConnection().close(); + } + } Index: src/test/org/apache/commons/dbcp/TesterPreparedStatement.java =================================================================== --- src/test/org/apache/commons/dbcp/TesterPreparedStatement.java (revision 557361) +++ src/test/org/apache/commons/dbcp/TesterPreparedStatement.java (working copy) @@ -243,7 +243,7 @@ } public ResultSet getGeneratedKeys() throws SQLException { - throw new SQLException("Not implemented."); + return new TesterResultSet(this, null, _resultSetType, _resultSetConcurrency); } public int executeUpdate(String sql, int autoGeneratedKeys) Index: src/test/org/apache/commons/dbcp/TesterConnection.java =================================================================== --- src/test/org/apache/commons/dbcp/TesterConnection.java (revision 557361) +++ src/test/org/apache/commons/dbcp/TesterConnection.java (working copy) @@ -243,36 +243,36 @@ int resultSetConcurrency, int resultSetHoldability) throws SQLException { - throw new SQLException("Not implemented."); + return createStatement(); } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - throw new SQLException("Not implemented."); + return prepareStatement(sql); } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - throw new SQLException("Not implemented."); + return prepareCall(sql); } public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - throw new SQLException("Not implemented."); + return prepareStatement(sql); } public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException { - throw new SQLException("Not implemented."); + return prepareStatement(sql); } public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException { - throw new SQLException("Not implemented."); + return prepareStatement(sql); } /* JDBC_3_ANT_KEY_END */ Index: src/test/org/apache/commons/dbcp/TestConnectionPool.java =================================================================== --- src/test/org/apache/commons/dbcp/TestConnectionPool.java (revision 557361) +++ src/test/org/apache/commons/dbcp/TestConnectionPool.java (working copy) @@ -238,6 +238,81 @@ conn.close(); } + public void testBackPointers() throws Exception { + // normal statement + Connection conn = newConnection(); + assertBackPointers(conn, conn.createStatement()); + conn = newConnection(); + assertBackPointers(conn, conn.createStatement(0, 0)); + conn = newConnection(); + assertBackPointers(conn, conn.createStatement(0, 0, 0)); + + // prepared statement + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual")); + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual", 0)); + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0)); + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0, 0)); + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual", new int[0])); + conn = newConnection(); + assertBackPointers(conn, conn.prepareStatement("select * from dual", new String[0])); + + // callable statement + conn = newConnection(); + assertBackPointers(conn, conn.prepareCall("select * from dual")); + conn = newConnection(); + assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0)); + conn = newConnection(); + assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0, 0)); + } + + protected void assertBackPointers(Connection conn, Statement statement) throws SQLException { + assertFalse(conn.isClosed()); + assertFalse(isClosed(statement)); + + assertSame("statement.getConnection() should return the exact same connection instance that was used to create the statement", + conn, statement.getConnection()); + + ResultSet resultSet = statement.getResultSet(); + assertFalse(isClosed(resultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, resultSet.getStatement()); + + ResultSet executeResultSet = statement.executeQuery("select * from dual"); + assertFalse(isClosed(executeResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, executeResultSet.getStatement()); + + ResultSet keysResultSet = statement.getGeneratedKeys(); + assertFalse(isClosed(keysResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, keysResultSet.getStatement()); + + ResultSet preparedResultSet = null; + if (statement instanceof PreparedStatement) { + PreparedStatement preparedStatement = (PreparedStatement) statement; + preparedResultSet = preparedStatement.executeQuery(); + assertFalse(isClosed(preparedResultSet)); + assertSame("resultSet.getStatement() should return the exact same statement instance that was used to create the result set", + statement, preparedResultSet.getStatement()); + } + + + resultSet.getStatement().getConnection().close(); + assertTrue(conn.isClosed()); + assertTrue(isClosed(statement)); + assertTrue(isClosed(resultSet)); + assertTrue(isClosed(executeResultSet)); + assertTrue(isClosed(keysResultSet)); + if (preparedResultSet != null) { + assertTrue(isClosed(preparedResultSet)); + } + } + public void testSimple() throws Exception { Connection conn = newConnection(); assertTrue(null != conn); Index: src/test/org/apache/commons/dbcp/TesterStatement.java =================================================================== --- src/test/org/apache/commons/dbcp/TesterStatement.java (revision 557361) +++ src/test/org/apache/commons/dbcp/TesterStatement.java (working copy) @@ -230,7 +230,7 @@ } public ResultSet getGeneratedKeys() throws SQLException { - throw new SQLException("Not implemented."); + return new TesterResultSet(this); } public int executeUpdate(String sql, int autoGeneratedKeys) Index: src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java =================================================================== --- src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java (revision 557361) +++ src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java (working copy) @@ -76,6 +76,10 @@ ds = tds; } + public void testBackPointers() throws Exception { + // todo disabled until a wrapping issuen in PerUserPoolDataSource are resolved + } + /** * Switching 'u1 -> 'u2' and 'p1' -> 'p2' will * exhibit the bug detailed in Index: src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java =================================================================== --- src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java (revision 557361) +++ src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java (working copy) @@ -69,6 +69,11 @@ ds = tds; } + + public void testBackPointers() throws Exception { + // todo disabled until a wrapping issuen in SharedPoolDataSource are resolved + } + /** * Switching 'u1 -> 'u2' and 'p1' -> 'p2' will * exhibit the bug detailed in Index: src/java/org/apache/commons/dbcp/PoolingDataSource.java =================================================================== --- src/java/org/apache/commons/dbcp/PoolingDataSource.java (revision 557361) +++ src/java/org/apache/commons/dbcp/PoolingDataSource.java (working copy) @@ -203,12 +203,12 @@ public Statement createStatement() throws SQLException { checkOpen(); - return delegate.createStatement(); + return new DelegatingStatement(this, delegate.createStatement()); } public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.createStatement(resultSetType, resultSetConcurrency); + return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency)); } public boolean innermostDelegateEquals(Connection c) { @@ -290,22 +290,22 @@ public CallableStatement prepareCall(String sql) throws SQLException { checkOpen(); - return delegate.prepareCall(sql); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql)); } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); } public PreparedStatement prepareStatement(String sql) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql)); } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); } public void rollback() throws SQLException { @@ -382,32 +382,32 @@ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, autoGeneratedKeys); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys)); } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingPreparedStatement(this,delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, columnIndexes); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes)); } public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, columnNames); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnNames)); } /* JDBC_3_ANT_KEY_END */ Index: src/java/org/apache/commons/dbcp/PoolingDriver.java =================================================================== --- src/java/org/apache/commons/dbcp/PoolingDriver.java (revision 557361) +++ src/java/org/apache/commons/dbcp/PoolingDriver.java (working copy) @@ -291,12 +291,12 @@ public Statement createStatement() throws SQLException { checkOpen(); - return delegate.createStatement(); + return new DelegatingStatement(this, delegate.createStatement()); } public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.createStatement(resultSetType, resultSetConcurrency); + return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency)); } public boolean equals(Object obj) { @@ -355,22 +355,22 @@ public CallableStatement prepareCall(String sql) throws SQLException { checkOpen(); - return delegate.prepareCall(sql); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql)); } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); } public PreparedStatement prepareStatement(String sql) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql)); } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); } public void rollback() throws SQLException { @@ -447,32 +447,32 @@ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, autoGeneratedKeys); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys)); } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, columnIndexes); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes)); } public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { checkOpen(); - return delegate.prepareStatement(sql, columnNames); + return new DelegatingPreparedStatement(this, delegate.prepareStatement(sql, columnNames)); } /* JDBC_3_ANT_KEY_END */ Index: src/java/org/apache/commons/dbcp/DelegatingStatement.java =================================================================== --- src/java/org/apache/commons/dbcp/DelegatingStatement.java (revision 557361) +++ src/java/org/apache/commons/dbcp/DelegatingStatement.java (working copy) @@ -314,8 +314,15 @@ public boolean getMoreResults(int current) throws SQLException { checkOpen(); try { return _stmt.getMoreResults(current); } catch (SQLException e) { handleException(e); return false; } } - public ResultSet getGeneratedKeys() throws SQLException - { checkOpen(); try { return _stmt.getGeneratedKeys(); } catch (SQLException e) { handleException(e); return null; } } + public ResultSet getGeneratedKeys() throws SQLException { + checkOpen(); + try { + return DelegatingResultSet.wrapResultSet(this, _stmt.getGeneratedKeys()); + } catch (SQLException e) { + handleException(e); + return null; + } + } public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { checkOpen(); try { return _stmt.executeUpdate(sql, autoGeneratedKeys); } catch (SQLException e) { handleException(e); return 0; } }