You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ma...@apache.org on 2009/10/05 22:33:45 UTC

svn commit: r821997 - in /commons/proper/dbcp/trunk/src: java/org/apache/commons/dbcp/cpdsadapter/ test/org/apache/commons/dbcp/datasources/

Author: markt
Date: Mon Oct  5 20:33:44 2009
New Revision: 821997

URL: http://svn.apache.org/viewvc?rev=821997&view=rev
Log:
Fix DBCP-160
Based on a patch by Mark Riley
This patch:
- Changes to ConnectionImpl so it extends DelegatingConnection
- Adds statement pooling support (with test cases) to all prepareStatement() methods

It differs from Mark's original patch in that:
- It doesn't change DelegatingConnection
- It adds test cases and fixes for statement pooling
- Most of the implementation in ConnectionImpl is left to the super class

Modified:
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java
    commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java
    commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java
    commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java?rev=821997&r1=821996&r2=821997&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java Mon Oct  5 20:33:44 2009
@@ -17,57 +17,35 @@
 
 package org.apache.commons.dbcp.cpdsadapter;
 
-import java.util.Map;
 import java.sql.Connection;
-import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
-import java.sql.CallableStatement;
-import java.sql.Statement;
-import java.sql.SQLWarning;
 import java.sql.SQLException;
-/* JDBC_4_ANT_KEY_BEGIN */
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.ClientInfoStatus;
-import java.sql.Clob;
-import java.sql.NClob;
-import java.sql.SQLClientInfoException;
-import java.sql.SQLXML;
-import java.sql.Struct;
-import java.util.Collections;
-import java.util.Properties;
-/* JDBC_4_ANT_KEY_END */
+
+import org.apache.commons.dbcp.DelegatingConnection;
+import org.apache.commons.dbcp.DelegatingPreparedStatement;
 
 /**
  * This class is the <code>Connection</code> that will be returned
  * from <code>PooledConnectionImpl.getConnection()</code>.  
  * Most methods are wrappers around the jdbc 1.x <code>Connection</code>.  
- * A few exceptions include preparedStatement, close and isClosed.
+ * A few exceptions include preparedStatement and close.
  * In accordance with the jdbc specification this Connection cannot
  * be used after closed() is called.  Any further usage will result in an
  * SQLException.
+ * 
+ * ConnectionImpl extends DelegatingConnection to enable access to the
+ * underlying connection.
  *
  * @author John D. McNally
  * @version $Revision$ $Date$
  */
-class ConnectionImpl implements Connection {
-    private static final String CLOSED 
-            = "Attempted to use Connection after closed() was called.";
-
-/* JDBC_4_ANT_KEY_BEGIN */
-    private static final Map<String, ClientInfoStatus> EMPTY_FAILED_PROPERTIES =
-        Collections.<String, ClientInfoStatus>emptyMap();
-/* JDBC_4_ANT_KEY_END */
+class ConnectionImpl extends DelegatingConnection {
 
-    /** The JDBC database connection. */
-    private final Connection connection;
+    private final boolean accessToUnderlyingConnectionAllowed;
 
     /** The object that instantiated this object */
      private final PooledConnectionImpl pooledConnection;
 
-    /** Marks whether is Connection is still usable. */
-    boolean isClosed;
-
     /**
      * Creates a <code>ConnectionImpl</code>. 
      *
@@ -75,49 +53,12 @@
      * @param connection The JDBC 1.x Connection to wrap.
      */
     ConnectionImpl(PooledConnectionImpl pooledConnection, 
-            Connection connection) {
+            Connection connection,
+            boolean accessToUnderlyingConnectionAllowed) {
+        super(connection);
         this.pooledConnection = pooledConnection;
-        this.connection = connection;
-        isClosed = false;
-    }
-
-    /**
-     * The finalizer helps prevent <code>ConnectionPool</code> leakage.
-     */
-    protected void finalize() throws Throwable {
-        if (!isClosed) {
-            // If this DBConnection object is finalized while linked
-            // to a ConnectionPool, it means that it was taken from a pool
-            // and not returned.  We log this fact, close the underlying
-            // Connection, and return it to the ConnectionPool.
-            throw new SQLException("A ConnectionImpl was finalized "
-                      + "without being closed which will cause leakage of "
-                      + " PooledConnections from the ConnectionPool.");
-        }
-    }
-
-    /**
-     * Throws an SQLException, if isClosed() is true
-     */
-    private void assertOpen() throws SQLException {
-        if (isClosed) {
-            throw new SQLException(CLOSED);
-        }
-    }
-
-    // ***********************************************************************
-    // java.sql.Connection implementation using wrapped Connection
-    // ***********************************************************************
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void clearWarnings() throws SQLException {
-        assertOpen();
-        connection.clearWarnings();
+        this.accessToUnderlyingConnectionAllowed =
+            accessToUnderlyingConnectionAllowed;
     }
 
     /**
@@ -130,170 +71,14 @@
      * @exception SQLException The database connection couldn't be closed.
      */
     public void close() throws SQLException {
-        if (!isClosed) {
-            isClosed = true;
+        if (!_closed) {
+            _closed = true;
+            passivate();
             pooledConnection.notifyListeners();
         }
     }
 
     /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void commit() throws SQLException {
-        assertOpen();
-        connection.commit();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public Statement createStatement() throws SQLException {
-        assertOpen();
-        return connection.createStatement();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public Statement createStatement(int resultSetType, 
-                                     int resultSetConcurrency) 
-            throws SQLException {
-        assertOpen();
-        return connection
-                .createStatement(resultSetType, resultSetConcurrency);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public boolean getAutoCommit() throws SQLException {
-        assertOpen();
-        return connection.getAutoCommit();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public String getCatalog() throws SQLException {
-        assertOpen();
-        return connection.getCatalog();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public DatabaseMetaData getMetaData() throws SQLException {
-        assertOpen();
-        return connection.getMetaData();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public int getTransactionIsolation() throws SQLException {
-        assertOpen();
-        return connection.getTransactionIsolation();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public Map getTypeMap() throws SQLException {
-        assertOpen();
-        return connection.getTypeMap();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public SQLWarning getWarnings() throws SQLException {
-        assertOpen();
-        return connection.getWarnings();
-    }
-
-    /**
-     * Returns true after close() is called, and false prior to that.
-     *
-     * @return a <code>boolean</code> value
-     */
-    public boolean isClosed() {
-        return isClosed;
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public boolean isReadOnly() throws SQLException {
-        assertOpen();
-        return connection.isReadOnly();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public String nativeSQL(String sql) throws SQLException {
-        assertOpen();
-        return connection.nativeSQL(sql);
-    }    
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public CallableStatement prepareCall(String sql) throws SQLException {
-        assertOpen();
-        return connection.prepareCall(sql);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public CallableStatement prepareCall(String sql, int resultSetType, 
-                                         int resultSetConcurrency) 
-            throws SQLException {
-        assertOpen();
-        return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
-    }
-
-    /**
      * If pooling of <code>PreparedStatement</code>s is turned on in the
      * {@link DriverAdapterCPDS}, a pooled object may be returned, otherwise
      * delegate to the wrapped jdbc 1.x {@link java.sql.Connection}.
@@ -302,8 +87,15 @@
      * in the wrapped connection.
      */
     public PreparedStatement prepareStatement(String sql) throws SQLException {
-        assertOpen();
-        return pooledConnection.prepareStatement(sql);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement
+                (this, pooledConnection.prepareStatement(sql));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
+        }
     }
 
     /**
@@ -317,246 +109,101 @@
     public PreparedStatement prepareStatement(String sql, int resultSetType, 
                                               int resultSetConcurrency) 
             throws SQLException {
-        assertOpen();
-        return pooledConnection
-            .prepareStatement(sql, resultSetType, resultSetConcurrency);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void rollback() throws SQLException {
-        assertOpen();
-        connection.rollback();
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void setAutoCommit(boolean b) throws SQLException {
-        assertOpen();
-        connection.setAutoCommit(b);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void setCatalog(String catalog) throws SQLException {
-        assertOpen();
-        connection.setCatalog(catalog);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void setReadOnly(boolean readOnly) throws SQLException {
-        assertOpen();
-        connection.setReadOnly(readOnly);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void setTransactionIsolation(int level) throws SQLException {
-        assertOpen();
-        connection.setTransactionIsolation(level);
-    }
-
-    /**
-     * Pass thru method to the wrapped jdbc 1.x {@link java.sql.Connection}.
-     *
-     * @exception SQLException if this connection is closed or an error occurs
-     * in the wrapped connection.
-     */
-    public void setTypeMap(Map map) throws SQLException {
-        assertOpen();
-        connection.setTypeMap(map);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement
+                (this, pooledConnection.prepareStatement
+                    (sql,resultSetType,resultSetConcurrency));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
+        }
     }
 
-    // ------------------- JDBC 3.0 -----------------------------------------
-    // Will be commented by the build process on a JDBC 2.0 system
-
 /* JDBC_3_ANT_KEY_BEGIN */
-
-    public int getHoldability() throws SQLException {
-        assertOpen();
-        return connection.getHoldability();
-    }
-
-    public void setHoldability(int holdability) throws SQLException {
-        assertOpen();
-        connection.setHoldability(holdability);
-    }
-
-    public java.sql.Savepoint setSavepoint() throws SQLException {
-        assertOpen();
-        return connection.setSavepoint();
-    }
-
-    public java.sql.Savepoint setSavepoint(String name) throws SQLException {
-        assertOpen();
-        return connection.setSavepoint(name);
-    }
-
-    public void rollback(java.sql.Savepoint savepoint) throws SQLException {
-        assertOpen();
-        connection.rollback(savepoint);
-    }
-
-    public void releaseSavepoint(java.sql.Savepoint savepoint) 
-            throws SQLException {
-        assertOpen();
-        connection.releaseSavepoint(savepoint);
-    }
-
-    public Statement createStatement(int resultSetType,
-                                     int resultSetConcurrency,
-                                     int resultSetHoldability)
-            throws SQLException {
-        assertOpen();
-        return connection.createStatement(resultSetType, resultSetConcurrency,
-                                     resultSetHoldability);
-    }
-
     public PreparedStatement prepareStatement(String sql, int resultSetType,
                                               int resultSetConcurrency,
                                               int resultSetHoldability)
             throws SQLException {
-        assertOpen();
-        return connection.prepareStatement(sql, resultSetType,
-                                      resultSetConcurrency,
-                                      resultSetHoldability);
-    }
-
-    public CallableStatement prepareCall(String sql, int resultSetType,
-                                         int resultSetConcurrency,
-                                         int resultSetHoldability)
-            throws SQLException {
-        assertOpen();
-        return connection.prepareCall(sql, resultSetType,
-                                 resultSetConcurrency,
-                                 resultSetHoldability);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement(this,
+                    pooledConnection.prepareStatement(sql, resultSetType,
+                            resultSetConcurrency, resultSetHoldability));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
+        }
     }
 
     public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
             throws SQLException {
-        assertOpen();
-        return connection.prepareStatement(sql, autoGeneratedKeys);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement(this,
+                    pooledConnection.prepareStatement(sql, autoGeneratedKeys));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
+        }
     }
 
     public PreparedStatement prepareStatement(String sql, int columnIndexes[])
             throws SQLException {
-        assertOpen();
-        return connection.prepareStatement(sql, columnIndexes);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement(this,
+                    pooledConnection.prepareStatement(sql, columnIndexes));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
+        }
     }
 
     public PreparedStatement prepareStatement(String sql, String columnNames[])
             throws SQLException {
-        assertOpen();
-        return connection.prepareStatement(sql, columnNames);
-    }
-/* JDBC_3_ANT_KEY_END */
-/* JDBC_4_ANT_KEY_BEGIN */
-
-    public boolean isWrapperFor(Class<?> iface) throws SQLException {
-        return iface.isAssignableFrom(getClass()) ||
-                connection.isWrapperFor(iface);
-    }
-
-    public <T> T unwrap(Class<T> iface) throws SQLException {
-        if (iface.isAssignableFrom(getClass())) {
-            return iface.cast(this);
-        } else if (iface.isAssignableFrom(connection.getClass())) {
-            return iface.cast(connection);
-        } else {
-            return connection.unwrap(iface);
+        checkOpen();
+        try {
+            return new DelegatingPreparedStatement(this,
+                    pooledConnection.prepareStatement(sql, columnNames));
+        }
+        catch (SQLException e) {
+            handleException(e);
+            return null;
         }
     }
+/* JDBC_3_ANT_KEY_END */
 
-    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
-        assertOpen();
-        return connection.createArrayOf(typeName, elements);
-    }
-
-    public Blob createBlob() throws SQLException {
-        assertOpen();
-        return connection.createBlob();
-    }
-
-    public Clob createClob() throws SQLException {
-        assertOpen();
-        return connection.createClob();
-    }
-
-    public NClob createNClob() throws SQLException {
-        assertOpen();
-        return connection.createNClob();
-    }
-
-    public SQLXML createSQLXML() throws SQLException {
-        assertOpen();
-        return connection.createSQLXML();
-    }
-
-    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
-        assertOpen();
-        return connection.createStruct(typeName, attributes);
-    }
+    //
+    // Methods for accessing the delegate connection
+    //
 
-    public boolean isValid(int timeout) throws SQLException {
-        assertOpen();
-        return connection.isValid(timeout);
+    /**
+     * If false, getDelegate() and getInnermostDelegate() will return null.
+     * @return if false, getDelegate() and getInnermostDelegate() will return null
+     */
+    public boolean isAccessToUnderlyingConnectionAllowed() {
+        return accessToUnderlyingConnectionAllowed;
     }
 
-    public void setClientInfo(String name, String value) throws SQLClientInfoException {
-        try {
-            assertOpen();
-            connection.setClientInfo(name, value);
-        }
-        catch (SQLClientInfoException e) {
-            throw e;
-        }
-        catch (SQLException e) {
-            throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
+    public Connection getDelegate() {
+        if (isAccessToUnderlyingConnectionAllowed()) {
+            return getDelegateInternal();
+        } else {
+            return null;
         }
     }
 
-    public void setClientInfo(Properties properties) throws SQLClientInfoException {
-        try {
-            assertOpen();
-            connection.setClientInfo(properties);
-        }
-        catch (SQLClientInfoException e) {
-            throw e;
-        }
-        catch (SQLException e) {
-            throw new SQLClientInfoException("Connection is closed.", EMPTY_FAILED_PROPERTIES, e);
+    public Connection getInnermostDelegate() {
+        if (isAccessToUnderlyingConnectionAllowed()) {
+            return super.getInnermostDelegateInternal();
+        } else {
+            return null;
         }
     }
 
-    public Properties getClientInfo() throws SQLException {
-        assertOpen();
-        return connection.getClientInfo();
-    }
-
-    public String getClientInfo(String name) throws SQLException {
-        assertOpen();
-        return connection.getClientInfo(name);
-    }
-/* JDBC_4_ANT_KEY_END */
 }

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java?rev=821997&r1=821996&r2=821997&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java Mon Oct  5 20:33:44 2009
@@ -128,6 +128,11 @@
         DriverManager.getDrivers();
     }
 
+    /** 
+     * Controls access to the underlying connection 
+     */
+    private boolean accessToUnderlyingConnectionAllowed = false; 
+    
     /**
      * Default no-arg constructor for Serialization
      */
@@ -184,29 +189,35 @@
         // Workaround for buggy WebLogic 5.1 classloader - ignore the
         // exception upon first invocation.
         try {
+            PooledConnectionImpl pci = null;
             if (connectionProperties != null) {
                 connectionProperties.put("user", username);
                 connectionProperties.put("password", password);
-                return new PooledConnectionImpl(
+                pci = new PooledConnectionImpl(
                         DriverManager.getConnection(getUrl(), connectionProperties), 
-                        stmtPool); 
+                        stmtPool);
             } else {
-                return new PooledConnectionImpl(
+                pci = new PooledConnectionImpl(
                         DriverManager.getConnection(getUrl(), username, password), 
-                        stmtPool ); 
+                        stmtPool);
             }
+            pci.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
+            return pci;
         }
         catch (ClassCircularityError e)
         {
+            PooledConnectionImpl pci = null;
             if (connectionProperties != null) {
-                return new PooledConnectionImpl(
+                pci = new PooledConnectionImpl(
                         DriverManager.getConnection(getUrl(), connectionProperties), 
-                        stmtPool); 
+                        stmtPool);
             } else {
-                return new PooledConnectionImpl(
+                pci = new PooledConnectionImpl(
                         DriverManager.getConnection(getUrl(), username, password), 
-                        stmtPool ); 
+                        stmtPool);
             }
+            pci.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
+            return pci;
         }
     }
 
@@ -645,6 +656,26 @@
     }
     
     /**
+     * Returns the value of the accessToUnderlyingConnectionAllowed property.
+     * 
+     * @return true if access to the underlying is allowed, false otherwise.
+     */
+    public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
+        return this.accessToUnderlyingConnectionAllowed;
+    }
+
+    /**
+     * Sets the value of the accessToUnderlyingConnectionAllowed property.
+     * It controls if the PoolGuard allows access to the underlying connection.
+     * (Default: false)
+     * 
+     * @param allow Access to the underlying connection is granted when true.
+     */
+    public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
+        this.accessToUnderlyingConnectionAllowed = allow;
+    }
+    
+    /**
      * Returns the maximun number of prepared statements.
      * 
      * @return maxPrepartedStatements value

Modified: commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java?rev=821997&r1=821996&r2=821997&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java (original)
+++ commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java Mon Oct  5 20:33:44 2009
@@ -20,7 +20,7 @@
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
-import java.util.Iterator;
+import java.util.Arrays;
 import java.util.Vector;
 
 import javax.sql.ConnectionEvent;
@@ -81,6 +81,10 @@
     /** My pool of {*link PreparedStatement}s. */
     protected KeyedObjectPool pstmtPool = null;
 
+    /** 
+     * Controls access to the underlying connection 
+     */
+    private boolean accessToUnderlyingConnectionAllowed = false; 
 
     /**
      * Wrap the real connection.
@@ -90,7 +94,7 @@
         if (connection instanceof DelegatingConnection) {
             this.delegatingConnection = (DelegatingConnection) connection;
         } else {
-            this.delegatingConnection = new DelegatingConnection(connection);   
+            this.delegatingConnection = new DelegatingConnection(connection);
         }
         eventListeners = new Vector();
         isClosed = false;
@@ -173,7 +177,8 @@
         }
 
         // the spec requires that this return a new Connection instance.
-        logicalConnection = new ConnectionImpl(this, connection);
+        logicalConnection = new ConnectionImpl(
+                this, connection, isAccessToUnderlyingConnectionAllowed());
         return logicalConnection;
     }
 
@@ -264,6 +269,110 @@
         }
     }
 
+/* JDBC_3_ANT_KEY_BEGIN */
+    /**
+     * Create or obtain a {*link PreparedStatement} from my pool.
+     * @return a {*link PoolablePreparedStatement}
+     */
+    PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) 
+            throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareStatement(sql, autoGeneratedKeys);
+        } else {
+            try {
+                return (PreparedStatement) pstmtPool.borrowObject(
+                    createKey(sql,autoGeneratedKeys));
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
+            }
+        }
+    }
+
+    PreparedStatement prepareStatement(String sql, int resultSetType,
+            int resultSetConcurrency, int resultSetHoldability)
+    throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareStatement(sql, resultSetType,
+                    resultSetConcurrency, resultSetHoldability);
+        } else {
+            try {
+                return (PreparedStatement) pstmtPool.borrowObject(
+                    createKey(sql, resultSetType, resultSetConcurrency,
+                            resultSetHoldability));
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
+            }
+        }
+    }
+
+    PreparedStatement prepareStatement(String sql, int columnIndexes[])
+    throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareStatement(sql, columnIndexes);
+        } else {
+            try {
+                return (PreparedStatement) pstmtPool.borrowObject(
+                    createKey(sql, columnIndexes));
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
+            }
+        }
+    }
+
+    PreparedStatement prepareStatement(String sql, String columnNames[])
+    throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareStatement(sql, columnNames);
+        } else {
+            try {
+                return (PreparedStatement) pstmtPool.borrowObject(
+                    createKey(sql, columnNames));
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new SQLNestedException("Borrow prepareStatement from pool failed", e);
+            }
+        }
+    }
+
+    /**
+     * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
+     */
+    protected Object createKey(String sql, int autoGeneratedKeys) {
+        return new PStmtKey(normalizeSQL(sql), autoGeneratedKeys);
+    }
+
+    /**
+     * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
+     */
+    protected Object createKey(String sql, int resultSetType,
+            int resultSetConcurrency, int resultSetHoldability) {
+        return new PStmtKey(normalizeSQL(sql), resultSetType,
+                resultSetConcurrency, resultSetHoldability);
+    }
+
+    /**
+     * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
+     */
+    protected Object createKey(String sql, int columnIndexes[]) {
+        return new PStmtKey(normalizeSQL(sql), columnIndexes);
+    }
+
+    /**
+     * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
+     */
+    protected Object createKey(String sql, String columnNames[]) {
+        return new PStmtKey(normalizeSQL(sql), columnNames);
+    }
+
+/* JDBC_3_ANT_KEY_END */
+
     /**
      * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
@@ -272,7 +381,7 @@
         return new PStmtKey(normalizeSQL(sql), resultSetType,
                             resultSetConcurrency);
     }
-
+    
     /**
      * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
@@ -362,13 +471,37 @@
     }
 
     /**
+     * Returns the value of the accessToUnderlyingConnectionAllowed property.
+     * 
+     * @return true if access to the underlying is allowed, false otherwise.
+     */
+    public synchronized boolean isAccessToUnderlyingConnectionAllowed() {
+        return this.accessToUnderlyingConnectionAllowed;
+    }
+
+    /**
+     * Sets the value of the accessToUnderlyingConnectionAllowed property.
+     * It controls if the PoolGuard allows access to the underlying connection.
+     * (Default: false)
+     * 
+     * @param allow Access to the underlying connection is granted when true.
+     */
+    public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) {
+        this.accessToUnderlyingConnectionAllowed = allow;
+    }
+    
+    /**
      * A key uniquely identifying {*link PreparedStatement}s.
      */
     class PStmtKey {
         protected String _sql = null;
         protected Integer _resultSetType = null;
         protected Integer _resultSetConcurrency = null;
-
+        protected Integer _autoGeneratedKeys = null;
+        protected Integer _resultSetHoldability = null;
+        protected int _columnIndexes[] = null;
+        protected String _columnNames[] = null;
+        
         PStmtKey(String sql) {
             _sql = sql;
         }
@@ -379,12 +512,40 @@
             _resultSetConcurrency = new Integer(resultSetConcurrency);
         }
 
+        PStmtKey(String sql, int autoGeneratedKeys) {
+            _sql = sql;
+            _autoGeneratedKeys = new Integer(autoGeneratedKeys);
+        }
+
+        PStmtKey(String sql, int resultSetType, int resultSetConcurrency,
+                int resultSetHoldability) {
+            _sql = sql;
+            _resultSetType = new Integer(resultSetType);
+            _resultSetConcurrency = new Integer(resultSetConcurrency);
+            _resultSetHoldability = new Integer(resultSetHoldability);
+        }
+
+        PStmtKey(String sql, int columnIndexes[]) {
+            _sql = sql;
+            _columnIndexes = columnIndexes;
+        }
+
+        PStmtKey(String sql, String columnNames[]) {
+            _sql = sql;
+            _columnNames = columnNames;
+        }
+
+        
         public boolean equals(Object that) {
             try {
                 PStmtKey key = (PStmtKey) that;
                 return(((null == _sql && null == key._sql) || _sql.equals(key._sql)) &&
                        ((null == _resultSetType && null == key._resultSetType) || _resultSetType.equals(key._resultSetType)) &&
-                       ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency.equals(key._resultSetConcurrency))
+                       ((null == _resultSetConcurrency && null == key._resultSetConcurrency) || _resultSetConcurrency.equals(key._resultSetConcurrency)) &&
+                       ((null == _autoGeneratedKeys && null == key._autoGeneratedKeys) || _autoGeneratedKeys.equals(key._autoGeneratedKeys)) &&
+                       ((null == _resultSetHoldability && null == key._resultSetHoldability) || _resultSetHoldability.equals(key._resultSetHoldability)) &&
+                       ((null == _columnIndexes && null == key._columnIndexes) || Arrays.equals(_columnIndexes, key._columnIndexes)) &&
+                       ((null == _columnNames && null == key._columnNames) || Arrays.equals(_columnNames, key._columnNames))
                       );
             } catch (ClassCastException e) {
                 return false;
@@ -405,6 +566,14 @@
             buf.append(_resultSetType);
             buf.append(", resultSetConcurrency=");
             buf.append(_resultSetConcurrency);
+            buf.append(", autoGeneratedKeys=");
+            buf.append(_autoGeneratedKeys);
+            buf.append(", resultSetHoldability=");
+            buf.append(_resultSetHoldability);
+            buf.append(", columnIndexes=");
+            buf.append(Arrays.toString(_columnIndexes));
+            buf.append(", columnNames=");
+            buf.append(Arrays.toString(_columnNames));
             return buf.toString();
         }
     }

Modified: commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java?rev=821997&r1=821996&r2=821997&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestPerUserPoolDataSource.java Mon Oct  5 20:33:44 2009
@@ -61,6 +61,7 @@
         pcds.setUrl("jdbc:apache:commons:testdriver");
         pcds.setUser("foo");
         pcds.setPassword("bar");
+        pcds.setAccessToUnderlyingConnectionAllowed(true);
 
         PerUserPoolDataSource tds = new PerUserPoolDataSource();
         tds.setConnectionPoolDataSource(pcds);

Modified: commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java
URL: http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java?rev=821997&r1=821996&r2=821997&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java (original)
+++ commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/datasources/TestSharedPoolDataSource.java Mon Oct  5 20:33:44 2009
@@ -59,6 +59,7 @@
         pcds.setUser("foo");
         pcds.setPassword("bar");
         pcds.setPoolPreparedStatements(false);
+        pcds.setAccessToUnderlyingConnectionAllowed(true);
 
         SharedPoolDataSource tds = new SharedPoolDataSource();
         tds.setConnectionPoolDataSource(pcds);
@@ -520,7 +521,47 @@
         conn.close();
     }
 
-    public void testPoolPreparedStatements() throws Exception {
+    // There are 6 different prepareStatement statement methods so add a little
+    // complexity to reduce what would otherwise be lots of copy and paste
+    private static abstract class PrepareStatementCallback {
+        protected Connection conn;
+        void setConnection(Connection conn) {
+            this.conn = conn;
+        }
+        abstract PreparedStatement getPreparedStatement() throws SQLException;
+    }
+    private static class PscbString extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual");
+        }
+    }
+    private static class PscbStringIntInt extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual",0,0);
+        }
+    }
+    private static class PscbStringInt extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual",0);
+        }
+    }
+    private static class PscbStringIntArray extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual", new int[0]);
+        }
+    }
+    private static class PscbStringStringArray extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual",new String[0]);
+        }
+    }
+    private static class PscbStringIntIntInt extends PrepareStatementCallback {
+        PreparedStatement getPreparedStatement() throws SQLException {
+            return conn.prepareStatement("select * from dual",0,0,0);
+        }
+    }
+    private void doTestPoolPreparedStatements(PrepareStatementCallback callBack)
+    throws Exception {
         DriverAdapterCPDS mypcds = new DriverAdapterCPDS();
         DataSource myds = null;
         mypcds.setDriver("org.apache.commons.dbcp.TesterDriver");
@@ -540,12 +581,13 @@
         myds = tds;
 
         Connection conn = ds.getConnection();
+        callBack.setConnection(conn);
         PreparedStatement stmt = null;
         ResultSet rset = null;
 
         assertTrue(null != conn);
 
-        stmt = conn.prepareStatement("select * from dual");
+        stmt = callBack.getPreparedStatement();
         assertTrue(null != stmt);
         long l1HashCode = stmt.hashCode();
         rset = stmt.executeQuery();
@@ -554,7 +596,7 @@
         rset.close();
         stmt.close();
 
-        stmt = conn.prepareStatement("select * from dual");
+        stmt = callBack.getPreparedStatement();
         assertTrue(null != stmt);
         long l2HashCode = stmt.hashCode();
         rset = stmt.executeQuery();
@@ -569,8 +611,9 @@
         conn = null;
 
         conn = myds.getConnection();
+        callBack.setConnection(conn);
 
-        stmt = conn.prepareStatement("select * from dual");
+        stmt = callBack.getPreparedStatement();
         assertTrue(null != stmt);
         long l3HashCode = stmt.hashCode();
         rset = stmt.executeQuery();
@@ -579,7 +622,7 @@
         rset.close();
         stmt.close();
 
-        stmt = conn.prepareStatement("select * from dual");
+        stmt = callBack.getPreparedStatement();
         assertTrue(null != stmt);
         long l4HashCode = stmt.hashCode();
         rset = stmt.executeQuery();
@@ -593,4 +636,12 @@
         conn.close();
         conn = null;
     }
+    public void testPoolPreparedStatements() throws Exception {
+        doTestPoolPreparedStatements(new PscbString());
+        doTestPoolPreparedStatements(new PscbStringIntInt());
+        doTestPoolPreparedStatements(new PscbStringInt());
+        doTestPoolPreparedStatements(new PscbStringIntArray());
+        doTestPoolPreparedStatements(new PscbStringStringArray());
+        doTestPoolPreparedStatements(new PscbStringIntIntInt());
+    }
 }