Repository: commons-dbcp
Updated Branches:
  refs/heads/master b3654dbd3 -> 735d9a839


[DBCP-496] Add support for pooling CallableStatements to the
org.apache.commons.dbcp2.cpdsadapter package.

Project: http://git-wip-us.apache.org/repos/asf/commons-dbcp/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-dbcp/commit/735d9a83
Tree: http://git-wip-us.apache.org/repos/asf/commons-dbcp/tree/735d9a83
Diff: http://git-wip-us.apache.org/repos/asf/commons-dbcp/diff/735d9a83

Branch: refs/heads/master
Commit: 735d9a839110590ff1c82ff619cbd7facb34d078
Parents: b3654db
Author: Gary Gregory <garydgreg...@gmail.com>
Authored: Fri Jun 8 22:20:34 2018 -0600
Committer: Gary Gregory <garydgreg...@gmail.com>
Committed: Fri Jun 8 22:20:34 2018 -0600

----------------------------------------------------------------------
 src/changes/changes.xml                         |   3 +
 .../commons/dbcp2/DelegatingStatement.java      |  14 +-
 .../dbcp2/PoolableCallableStatement.java        |   8 +-
 .../apache/commons/dbcp2/PoolingConnection.java |  28 +-
 .../dbcp2/cpdsadapter/ConnectionImpl.java       |  93 +++++++
 .../dbcp2/cpdsadapter/DriverAdapterCPDS.java    |   9 +-
 .../dbcp2/cpdsadapter/PooledConnectionImpl.java | 267 ++++++++++++++-----
 .../datasources/TestSharedPoolDataSource.java   | 147 +++++++++-
 8 files changed, 482 insertions(+), 87 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index feeb3a6..87569dc 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -76,6 +76,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="update" issue="DBCP-495" due-to="Gary 
Gregory">
         Remove duplicate code in 
org.apache.commons.dbcp2.cpdsadapter.PStmtKeyCPDS.
       </action>
+      <action dev="ggregory" type="fix" issue="DBCP-496" due-to="Gary Gregory">
+        Add support for pooling CallableStatements to the 
org.apache.commons.dbcp2.cpdsadapter package.
+      </action>
     </release>
     <release version="2.3.0" date="2018-05-12" description="This is a minor 
release, including bug fixes and enhancements.">
       <action dev="pschumacher" type="fix" issue="DBCP-476" due-to="Gary 
Evesson, Richard Cordova">

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/DelegatingStatement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbcp2/DelegatingStatement.java 
b/src/main/java/org/apache/commons/dbcp2/DelegatingStatement.java
index 7ad0ddf..46671d5 100644
--- a/src/main/java/org/apache/commons/dbcp2/DelegatingStatement.java
+++ b/src/main/java/org/apache/commons/dbcp2/DelegatingStatement.java
@@ -174,13 +174,23 @@ public class DelegatingStatement extends AbandonedTrace 
implements Statement {
         }
     }
 
-    protected void activate() throws SQLException {
+    /**
+     *
+     * @throws SQLException
+     * @since 2.4.0 made public, was protected in 2.3.0.
+     */
+    public void activate() throws SQLException {
         if(_stmt instanceof DelegatingStatement) {
             ((DelegatingStatement)_stmt).activate();
         }
     }
 
-    protected void passivate() throws SQLException {
+    /**
+     *
+     * @throws SQLException
+     * @since 2.4.0 made public, was protected in 2.3.0.
+     */
+    public void passivate() throws SQLException {
         if(_stmt instanceof DelegatingStatement) {
             ((DelegatingStatement)_stmt).passivate();
         }

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/PoolableCallableStatement.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/dbcp2/PoolableCallableStatement.java 
b/src/main/java/org/apache/commons/dbcp2/PoolableCallableStatement.java
index e1e4e1d..023da51 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolableCallableStatement.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolableCallableStatement.java
@@ -90,9 +90,11 @@ public class PoolableCallableStatement extends 
DelegatingCallableStatement {
     /**
      * Activates after retrieval from the pool. Adds a trace for this 
CallableStatement to the Connection
      * that created it.
+     *
+     * @since 2.4.0 made public, was protected in 2.3.0.
      */
     @Override
-    protected void activate() throws SQLException {
+    public void activate() throws SQLException {
         setClosedInternal(false);
         if (getConnectionInternal() != null) {
             getConnectionInternal().addTrace(this);
@@ -103,9 +105,11 @@ public class PoolableCallableStatement extends 
DelegatingCallableStatement {
     /**
      * Passivates to prepare for return to the pool.  Removes the trace 
associated with this CallableStatement
      * from the Connection that created it.  Also closes any associated 
ResultSets.
+     *
+     * @since 2.4.0 made public, was protected in 2.3.0.
      */
     @Override
-    protected void passivate() throws SQLException {
+    public void passivate() throws SQLException {
         setClosedInternal(true);
         if (getConnectionInternal() != null) {
             getConnectionInternal().removeTrace(this);

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java 
b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
index 8f72b31..8745950 100644
--- a/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
+++ b/src/main/java/org/apache/commons/dbcp2/PoolingConnection.java
@@ -38,34 +38,42 @@ import org.apache.commons.pool2.impl.DefaultPooledObject;
  * actually close the statement, but rather returns it to the pool.
  * (See {@link PoolablePreparedStatement}, {@link PoolableCallableStatement}.)
  *
- *
  * @see PoolablePreparedStatement
  * @author Rodney Waldhoff
  * @author Dirk Verbeeck
  * @since 2.0
  */
 public class PoolingConnection extends DelegatingConnection<Connection>
-        implements 
KeyedPooledObjectFactory<PStmtKey,DelegatingPreparedStatement> {
+        implements KeyedPooledObjectFactory<PStmtKey, 
DelegatingPreparedStatement> {
 
     /**
-     * The possible statement types.
-     * @since 2.0
+     * Statement types.
+     *
+     * @since 2.0 protected enum.
+     * @since 2.4.0  public enum.
      */
-    protected static enum StatementType {
+    public enum StatementType {
+
+        /**
+         * Callable statement.
+         */
         CALLABLE_STATEMENT,
+
+        /**
+         * Prepared statement.
+         */
         PREPARED_STATEMENT
     }
 
     /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */
-    private KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> _pstmtPool = 
null;
-
+    private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> _pstmtPool 
= null;
 
     /**
      * Constructor.
-     * @param c the underlying {@link Connection}.
+     * @param conn the underlying {@link Connection}.
      */
-    public PoolingConnection(final Connection c) {
-        super(c);
+    public PoolingConnection(final Connection conn) {
+        super(conn);
     }
 
 

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/cpdsadapter/ConnectionImpl.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/ConnectionImpl.java 
b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/ConnectionImpl.java
index 4d1c416..adcc8ef 100644
--- a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/ConnectionImpl.java
+++ b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/ConnectionImpl.java
@@ -17,10 +17,12 @@
 
 package org.apache.commons.dbcp2.cpdsadapter;
 
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
+import org.apache.commons.dbcp2.DelegatingCallableStatement;
 import org.apache.commons.dbcp2.DelegatingConnection;
 import org.apache.commons.dbcp2.DelegatingPreparedStatement;
 
@@ -84,6 +86,97 @@ class ConnectionImpl extends 
DelegatingConnection<Connection> {
     }
 
     /**
+     * If pooling of <code>CallableStatement</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}.
+     *
+     * @param sql
+     *            an SQL statement that may contain one or more '?' parameter 
placeholders. Typically this statement is
+     *            specified using JDBC call escape syntax.
+     * @return a default <code>CallableStatement</code> object containing the 
pre-compiled SQL statement.
+     * @exception SQLException
+     *                Thrown if a database access error occurs or this method 
is called on a closed connection.
+     * @since 2.4.0
+     */
+    @Override
+    public CallableStatement prepareCall(String sql) throws SQLException {
+        checkOpen();
+        try {
+            return new DelegatingCallableStatement(this, 
pooledConnection.prepareCall(sql));
+        } catch (final SQLException e) {
+            handleException(e); // Does not return
+            return null;
+        }
+    }
+
+    /**
+     * If pooling of <code>CallableStatement</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}.
+     *
+     * @param sql
+     *            a <code>String</code> object that is the SQL statement to be 
sent to the database; may contain on or
+     *            more '?' parameters.
+     * @param resultSetType
+     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            a concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @return a <code>CallableStatement</code> object containing the 
pre-compiled SQL statement that will produce
+     *         <code>ResultSet</code> objects with the given type and 
concurrency.
+     * @throws SQLException
+     *             Thrown if a database access error occurs, this method is 
called on a closed connection or the given
+     *             parameters are not <code>ResultSet</code> constants 
indicating type and concurrency.
+     * @since 2.4.0
+     */
+    @Override
+    public CallableStatement prepareCall(String sql, int resultSetType, int 
resultSetConcurrency) throws SQLException {
+        checkOpen();
+        try {
+            return new DelegatingCallableStatement(this,
+                    pooledConnection.prepareCall(sql, resultSetType, 
resultSetConcurrency));
+        } catch (final SQLException e) {
+            handleException(e); // Does not return
+            return null;
+        }
+    }
+
+    /**
+     * If pooling of <code>CallableStatement</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}.
+     *
+     * @param sql
+     *            a <code>String</code> object that is the SQL statement to be 
sent to the database; may contain on or
+     *            more '?' parameters.
+     * @param resultSetType
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @param resultSetHoldability
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
+     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @return a new <code>CallableStatement</code> object, containing the 
pre-compiled SQL statement, that will
+     *         generate <code>ResultSet</code> objects with the given type, 
concurrency, and holdability.
+     * @throws SQLException
+     *             Thrown if a database access error occurs, this method is 
called on a closed connection or the given
+     *             parameters are not <code>ResultSet</code> constants 
indicating type, concurrency, and holdability.
+     * @since 2.4.0
+     */
+    @Override
+    public CallableStatement prepareCall(String sql, int resultSetType, int 
resultSetConcurrency,
+            int resultSetHoldability) throws SQLException {
+        checkOpen();
+        try {
+            return new DelegatingCallableStatement(this,
+                    pooledConnection.prepareCall(sql, resultSetType, 
resultSetConcurrency, resultSetHoldability));
+        } catch (final SQLException e) {
+            handleException(e); // Does not return
+            return null;
+        }
+    }
+
+    /**
      * 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}.

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java 
b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
index db811b7..e16d45c 100644
--- a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
+++ b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/DriverAdapterCPDS.java
@@ -37,7 +37,8 @@ import javax.naming.spi.ObjectFactory;
 import javax.sql.ConnectionPoolDataSource;
 import javax.sql.PooledConnection;
 
-import org.apache.commons.dbcp2.PoolablePreparedStatement;
+import org.apache.commons.dbcp2.DelegatingPreparedStatement;
+import org.apache.commons.dbcp2.PStmtKey;
 import org.apache.commons.pool2.KeyedObjectPool;
 import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
 import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
@@ -186,9 +187,7 @@ public class DriverAdapterCPDS
                         getUrl(), username, pass));
             }
             
pci.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
-        }
-        catch (final ClassCircularityError e)
-        {
+        } catch (final ClassCircularityError e) {
             if (connectionProperties != null) {
                 pci = new PooledConnectionImpl(DriverManager.getConnection(
                         getUrl(), connectionProperties));
@@ -198,7 +197,7 @@ public class DriverAdapterCPDS
             }
             
pci.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
         }
-        KeyedObjectPool<PStmtKeyCPDS, PoolablePreparedStatement<PStmtKeyCPDS>> 
stmtPool = null;
+        KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> stmtPool = null;
         if (isPoolPreparedStatements()) {
             final GenericKeyedObjectPoolConfig config = new 
GenericKeyedObjectPoolConfig();
             config.setMaxTotalPerKey(Integer.MAX_VALUE);

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java 
b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
index fa46cbf..ca41608 100644
--- 
a/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
+++ 
b/src/main/java/org/apache/commons/dbcp2/cpdsadapter/PooledConnectionImpl.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.dbcp2.cpdsadapter;
 
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
@@ -28,7 +29,11 @@ import javax.sql.PooledConnection;
 import javax.sql.StatementEventListener;
 
 import org.apache.commons.dbcp2.DelegatingConnection;
+import org.apache.commons.dbcp2.DelegatingPreparedStatement;
+import org.apache.commons.dbcp2.PStmtKey;
+import org.apache.commons.dbcp2.PoolableCallableStatement;
 import org.apache.commons.dbcp2.PoolablePreparedStatement;
+import org.apache.commons.dbcp2.PoolingConnection.StatementType;
 import org.apache.commons.pool2.KeyedObjectPool;
 import org.apache.commons.pool2.KeyedPooledObjectFactory;
 import org.apache.commons.pool2.PooledObject;
@@ -42,7 +47,7 @@ import org.apache.commons.pool2.impl.DefaultPooledObject;
  * @since 2.0
  */
 class PooledConnectionImpl
-        implements PooledConnection, KeyedPooledObjectFactory<PStmtKeyCPDS, 
PoolablePreparedStatement<PStmtKeyCPDS>> {
+        implements PooledConnection, KeyedPooledObjectFactory<PStmtKey, 
DelegatingPreparedStatement> {
 
     private static final String CLOSED
             = "Attempted to use PooledConnection after closed() was called.";
@@ -74,12 +79,12 @@ class PooledConnectionImpl
             new Vector<>();
 
     /**
-     * flag set to true, once close() is called.
+     * Flag set to true, once close() is called.
      */
     private boolean isClosed;
 
     /** My pool of {@link PreparedStatement}s. */
-    private KeyedObjectPool<PStmtKeyCPDS, 
PoolablePreparedStatement<PStmtKeyCPDS>> pstmtPool = null;
+    private KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pstmtPool;
 
     /**
      * Controls access to the underlying connection
@@ -87,7 +92,7 @@ class PooledConnectionImpl
     private boolean accessToUnderlyingConnectionAllowed = false;
 
     /**
-     * Wrap the real connection.
+     * Wraps the real connection.
      * @param connection the connection to be wrapped
      */
     PooledConnectionImpl(final Connection connection) {
@@ -108,8 +113,8 @@ class PooledConnectionImpl
      * @param p ignored
      */
     @Override
-    public void activateObject(final PStmtKeyCPDS key,
-            final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p)
+    public void activateObject(final PStmtKey key,
+            final PooledObject<DelegatingPreparedStatement> p)
             throws Exception {
         p.getObject().activate();
     }
@@ -175,52 +180,75 @@ class PooledConnectionImpl
     }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql) {
-        return new PStmtKeyCPDS(normalizeSQL(sql));
+    protected PStmtKey createKey(final String sql) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull());
     }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql, final int 
autoGeneratedKeys) {
-        return new PStmtKeyCPDS(normalizeSQL(sql), autoGeneratedKeys);
+    protected PStmtKey createKey(final String sql, final int 
autoGeneratedKeys) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
autoGeneratedKeys);
     }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql, final int 
columnIndexes[]) {
-        return new PStmtKeyCPDS(normalizeSQL(sql), columnIndexes);
+    protected PStmtKey createKey(final String sql, final int columnIndexes[]) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
columnIndexes);
     }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql, final int resultSetType,
-                               final int resultSetConcurrency) {
-        return new PStmtKeyCPDS(normalizeSQL(sql), resultSetType,
-                            resultSetConcurrency);
+    protected PStmtKey createKey(final String sql, final int resultSetType,
+            final int resultSetConcurrency) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
resultSetType, resultSetConcurrency);
     }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql, final int resultSetType,
+    protected PStmtKey createKey(final String sql, final int resultSetType,
             final int resultSetConcurrency, final int resultSetHoldability) {
-        return new PStmtKeyCPDS(normalizeSQL(sql), resultSetType,
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
resultSetType,
                 resultSetConcurrency, resultSetHoldability);
     }
 
-    // -------------------------------------------------------------------
-    // The following code implements a PreparedStatement pool
+    /**
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     *
+     * @since 2.4.0
+     */
+    protected PStmtKey createKey(String sql, int resultSetType, int 
resultSetConcurrency, int resultSetHoldability,
+            StatementType statementType) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
resultSetType, resultSetConcurrency, resultSetHoldability,
+                statementType);
+    }
+    /**
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     *
+     * @since 2.4.0
+     */
+    protected PStmtKey createKey(String sql, int resultSetType, int 
resultSetConcurrency,
+            StatementType statementType) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
resultSetType, resultSetConcurrency, statementType);
+    }
+
+    /**
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     */
+    protected PStmtKey createKey(final String sql, final StatementType 
statementType) {
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
statementType);
+    }
 
     /**
-     * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
+     * Creates a {@link PooledConnectionImpl.PStmtKey} for the given arguments.
      */
-    protected PStmtKeyCPDS createKey(final String sql, final String 
columnNames[]) {
-        return new PStmtKeyCPDS(normalizeSQL(sql), columnNames);
+    protected PStmtKey createKey(final String sql, final String columnNames[]) 
{
+        return new PStmtKey(normalizeSQL(sql), getCatalogOrNull(), 
columnNames);
     }
 
     /**
@@ -230,8 +258,8 @@ class PooledConnectionImpl
      * @param p the wrapped {@link PreparedStatement} to be destroyed.
      */
     @Override
-    public void destroyObject(final PStmtKeyCPDS key,
-            final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p)
+    public void destroyObject(final PStmtKey key,
+            final PooledObject<DelegatingPreparedStatement> p)
             throws Exception {
         p.getObject().getInnermostDelegate().close();
     }
@@ -257,6 +285,14 @@ class PooledConnectionImpl
         }
     }
 
+    private String getCatalogOrNull() {
+        try {
+            return connection == null ? null : connection.getCatalog();
+        } catch (SQLException e) {
+            return null;
+        }
+    }
+
     /**
      * Returns a JDBC connection.
      *
@@ -294,33 +330,49 @@ class PooledConnectionImpl
      * {@link PreparedStatement}s.
      * @param key the key for the {@link PreparedStatement} to be created
      */
+    @SuppressWarnings("resource")
     @Override
-    public PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> 
makeObject(final PStmtKeyCPDS key) throws Exception {
+    public PooledObject<DelegatingPreparedStatement> makeObject(final PStmtKey 
key) throws Exception {
+//        if (null == key) {
+//            throw new IllegalArgumentException();
+//        }
+//        // _openPstmts++;
+//        if (null == key.getResultSetType()
+//                && null == key.getResultSetConcurrency()) {
+//            if (null == key.getAutoGeneratedKeys()) {
+//                return new DefaultPooledObject<>(new 
PoolablePreparedStatement<>(
+//                        connection.prepareStatement(key.getSql()),
+//                        key, pstmtPool, delegatingConnection));
+//            }
+//            return new DefaultPooledObject<>(new PoolablePreparedStatement<>(
+//                            connection.prepareStatement(key.getSql(),
+//                                    key.getAutoGeneratedKeys().intValue()),
+//                            key, pstmtPool, delegatingConnection));
+//        }
+//        return new DefaultPooledObject<>(new PoolablePreparedStatement<>(
+//                connection.prepareStatement(key.getSql(),
+//                        key.getResultSetType().intValue(),
+//                        key.getResultSetConcurrency().intValue()),
+//                        key, pstmtPool, delegatingConnection));
+        //
+
         if (null == key) {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("Prepared statement key is null 
or invalid.");
         }
-        // _openPstmts++;
-        if (null == key.getResultSetType()
-                && null == key.getResultSetConcurrency()) {
-            if (null == key.getAutoGeneratedKeys()) {
-                return new DefaultPooledObject<>(new 
PoolablePreparedStatement<>(
-                        connection.prepareStatement(key.getSql()),
-                        key, pstmtPool, delegatingConnection));
-            }
-            return new DefaultPooledObject<>(new PoolablePreparedStatement<>(
-                            connection.prepareStatement(key.getSql(),
-                                    key.getAutoGeneratedKeys().intValue()),
-                            key, pstmtPool, delegatingConnection));
+        if (key.getStmtType() == StatementType.PREPARED_STATEMENT ) {
+            final PreparedStatement statement = (PreparedStatement) 
key.createStatement(connection);
+            @SuppressWarnings({"rawtypes", "unchecked"}) // Unable to find way 
to avoid this
+            final PoolablePreparedStatement pps = new 
PoolablePreparedStatement(statement, key, pstmtPool, delegatingConnection);
+            return new DefaultPooledObject<DelegatingPreparedStatement>(pps);
         }
-        return new DefaultPooledObject<>(new PoolablePreparedStatement<>(
-                connection.prepareStatement(key.getSql(),
-                        key.getResultSetType().intValue(),
-                        key.getResultSetConcurrency().intValue()),
-                        key, pstmtPool, delegatingConnection));
+        final CallableStatement statement = (CallableStatement) 
key.createStatement(connection);
+        final PoolableCallableStatement pcs = new 
PoolableCallableStatement(statement, key, pstmtPool,
+                (DelegatingConnection<Connection>) delegatingConnection);
+        return new DefaultPooledObject<DelegatingPreparedStatement>(pcs);
     }
 
     /**
-     * Normalize the given SQL statement, producing a
+     * Normalizes the given SQL statement, producing a
      * canonical form that is semantically equivalent to the original.
      */
     protected String normalizeSQL(final String sql) {
@@ -345,16 +397,109 @@ class PooledConnectionImpl
      * @param p a wrapped {@link PreparedStatement}
      */
     @Override
-    public void passivateObject(final PStmtKeyCPDS key,
-            final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p)
+    public void passivateObject(final PStmtKey key,
+            final PooledObject<DelegatingPreparedStatement> p)
             throws Exception {
-        final PoolablePreparedStatement<PStmtKeyCPDS> ppss = p.getObject();
-        ppss.clearParameters();
-        ppss.passivate();
+        @SuppressWarnings("resource")
+        final DelegatingPreparedStatement dps = p.getObject();
+        dps.clearParameters();
+        dps.passivate();
+    }
+
+    /**
+     * Creates or obtains a {@link CallableStatement} from my pool.
+     *
+     * @param sql
+     *            an SQL statement that may contain one or more '?' parameter 
placeholders. Typically this statement is
+     *            specified using JDBC call escape syntax.
+     * @return a default <code>CallableStatement</code> object containing the 
pre-compiled SQL statement.
+     * @exception SQLException
+     *                Thrown if a database access error occurs or this method 
is called on a closed connection.
+     * @since 2.4.0
+     */
+    CallableStatement prepareCall(String sql) throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareCall(sql);
+        }
+        try {
+            return (CallableStatement) pstmtPool.borrowObject(createKey(sql, 
StatementType.CALLABLE_STATEMENT));
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new SQLException("Borrow prepareCall from pool failed", e);
+        }
+    }
+
+    /**
+     * Creates or obtains a {@link CallableStatement} from my pool.
+     *
+     * @param sql
+     *            a <code>String</code> object that is the SQL statement to be 
sent to the database; may contain on or
+     *            more '?' parameters.
+     * @param resultSetType
+     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            a concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @return a <code>CallableStatement</code> object containing the 
pre-compiled SQL statement that will produce
+     *         <code>ResultSet</code> objects with the given type and 
concurrency.
+     * @throws SQLException
+     *             Thrown if a database access error occurs, this method is 
called on a closed connection or the given
+     *             parameters are not <code>ResultSet</code> constants 
indicating type and concurrency.
+     * @since 2.4.0
+     */
+    CallableStatement prepareCall(String sql, int resultSetType, int 
resultSetConcurrency) throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareCall(sql, resultSetType, 
resultSetConcurrency);
+        }
+        try {
+            return (CallableStatement) pstmtPool.borrowObject(createKey(sql, 
resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT));
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new SQLException("Borrow prepareCall from pool failed", e);
+        }
+    }
+
+    /**
+     * Creates or obtains a {@link CallableStatement} from my pool.
+     *
+     * @param sql
+     *            a <code>String</code> object that is the SQL statement to be 
sent to the database; may contain on or
+     *            more '?' parameters.
+     * @param resultSetType
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @param resultSetHoldability
+     *            one of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
+     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @return a new <code>CallableStatement</code> object, containing the 
pre-compiled SQL statement, that will
+     *         generate <code>ResultSet</code> objects with the given type, 
concurrency, and holdability.
+     * @throws SQLException
+     *             Thrown if a database access error occurs, this method is 
called on a closed connection or the given
+     *             parameters are not <code>ResultSet</code> constants 
indicating type, concurrency, and holdability.
+     * @since 2.4.0
+     */
+    CallableStatement prepareCall(String sql, int resultSetType, int 
resultSetConcurrency,
+            int resultSetHoldability) throws SQLException {
+        if (pstmtPool == null) {
+            return connection.prepareCall(sql, resultSetType, 
resultSetConcurrency, resultSetHoldability);
+        }
+        try {
+            return (CallableStatement) pstmtPool.borrowObject(createKey(sql, 
resultSetType, resultSetConcurrency, resultSetHoldability, 
StatementType.CALLABLE_STATEMENT));
+        } catch (final RuntimeException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new SQLException("Borrow prepareCall from pool failed", e);
+        }
     }
 
     /**
-     * Create or obtain a {@link PreparedStatement} from my pool.
+     * Creates or obtains a {@link PreparedStatement} from my pool.
      * @param sql the SQL statement
      * @return a {@link PoolablePreparedStatement}
      */
@@ -372,7 +517,7 @@ class PooledConnectionImpl
     }
 
     /**
-     * Create or obtain a {@link PreparedStatement} from my pool.
+     * Creates or obtains a {@link PreparedStatement} from my pool.
      * @param sql an SQL statement that may contain one or more '?' IN
      *        parameter placeholders
      * @param autoGeneratedKeys a flag indicating whether auto-generated keys
@@ -411,7 +556,7 @@ class PooledConnectionImpl
     }
 
     /**
-     * Create or obtain a {@link PreparedStatement} from my pool.
+     * Creates or obtains a {@link PreparedStatement} from my pool.
      * @param sql a <code>String</code> object that is the SQL statement to
      *            be sent to the database; may contain one or more '?' IN
      *            parameters
@@ -501,7 +646,7 @@ class PooledConnectionImpl
     }
 
     public void setStatementPool(
-            final KeyedObjectPool<PStmtKeyCPDS, 
PoolablePreparedStatement<PStmtKeyCPDS>> statementPool) {
+            final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> 
statementPool) {
         pstmtPool = statementPool;
     }
 
@@ -513,8 +658,8 @@ class PooledConnectionImpl
      * @return {@code true}
      */
     @Override
-    public boolean validateObject(final PStmtKeyCPDS key,
-            final PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> p) {
+    public boolean validateObject(final PStmtKey key,
+            final PooledObject<DelegatingPreparedStatement> p) {
         return true;
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-dbcp/blob/735d9a83/src/test/java/org/apache/commons/dbcp2/datasources/TestSharedPoolDataSource.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/dbcp2/datasources/TestSharedPoolDataSource.java
 
b/src/test/java/org/apache/commons/dbcp2/datasources/TestSharedPoolDataSource.java
index e41fdcd..f436b6c 100644
--- 
a/src/test/java/org/apache/commons/dbcp2/datasources/TestSharedPoolDataSource.java
+++ 
b/src/test/java/org/apache/commons/dbcp2/datasources/TestSharedPoolDataSource.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -397,6 +398,22 @@ public class TestSharedPoolDataSource extends 
TestConnectionPool {
     // Bugzilla Bug 24136 ClassCastException in DriverAdapterCPDS
     // when setPoolPreparedStatements(true)
     @Test
+    public void testPoolPrepareCall() throws Exception {
+        pcds.setPoolPreparedStatements(true);
+
+        final Connection conn = ds.getConnection();
+        assertNotNull(conn);
+        final PreparedStatement stmt = conn.prepareCall("{call home()}");
+        assertNotNull(stmt);
+        final ResultSet rset = stmt.executeQuery();
+        assertNotNull(rset);
+        assertTrue(rset.next());
+        rset.close();
+        stmt.close();
+        conn.close();
+    }
+
+    @Test
     public void testPoolPrepareStatement() throws Exception {
         pcds.setPoolPreparedStatements(true);
 
@@ -412,6 +429,37 @@ public class TestSharedPoolDataSource extends 
TestConnectionPool {
         conn.close();
     }
 
+    // There are 3 different prepareCall statement methods so add a little
+    // complexity to reduce what would otherwise be lots of copy and paste
+    private static abstract class PrepareCallCallback {
+        protected Connection conn;
+        void setConnection(final Connection conn) {
+            this.conn = conn;
+        }
+        abstract CallableStatement getCallableStatement() throws SQLException;
+    }
+
+    private static class CscbString extends PrepareCallCallback {
+        @Override
+        CallableStatement getCallableStatement() throws SQLException {
+            return conn.prepareCall("{call home()}");
+        }
+    }
+
+    private static class CscbStringIntInt extends PrepareCallCallback {
+        @Override
+        CallableStatement getCallableStatement() throws SQLException {
+            return conn.prepareCall("{call home()}", 0, 0);
+        }
+    }
+
+    private static class CscbStringIntIntInt extends PrepareCallCallback {
+        @Override
+        CallableStatement getCallableStatement() throws SQLException {
+            return conn.prepareCall("{call home()}", 0, 0, 0);
+        }
+    }
+
     // 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 {
@@ -421,49 +469,127 @@ public class TestSharedPoolDataSource extends 
TestConnectionPool {
         }
         abstract PreparedStatement getPreparedStatement() throws SQLException;
     }
-    
+
     private static class PscbString extends PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual");
         }
     }
-    
+
     private static class PscbStringIntInt extends PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual", 0, 0);
         }
     }
-    
+
     private static class PscbStringInt extends PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual", 0);
         }
     }
-    
+
     private static class PscbStringIntArray extends PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual", new int[0]);
         }
     }
-    
+
     private static class PscbStringStringArray extends 
PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual", new String[0]);
         }
     }
-    
+
     private static class PscbStringIntIntInt extends PrepareStatementCallback {
         @Override
         PreparedStatement getPreparedStatement() throws SQLException {
             return conn.prepareStatement("select * from dual", 0, 0, 0);
         }
     }
-    
+
+    private void doTestPoolCallableStatements(final PrepareCallCallback 
callBack)
+    throws Exception {
+        final DriverAdapterCPDS myPcds = new DriverAdapterCPDS();
+        DataSource myDs = null;
+        myPcds.setDriver("org.apache.commons.dbcp2.TesterDriver");
+        myPcds.setUrl("jdbc:apache:commons:testdriver");
+        myPcds.setUser("foo");
+        myPcds.setPassword("bar");
+        myPcds.setPoolPreparedStatements(true);
+        myPcds.setMaxPreparedStatements(10);
+
+        final SharedPoolDataSource spDs = new SharedPoolDataSource();
+        spDs.setConnectionPoolDataSource(myPcds);
+        spDs.setMaxTotal(getMaxTotal());
+        spDs.setDefaultMaxWaitMillis((int) getMaxWaitMillis());
+        spDs.setDefaultTransactionIsolation(
+            Connection.TRANSACTION_READ_COMMITTED);
+
+        myDs = spDs;
+
+        Connection conn = ds.getConnection();
+        callBack.setConnection(conn);
+        CallableStatement stmt = null;
+        ResultSet rset = null;
+
+        assertNotNull(conn);
+
+        stmt = callBack.getCallableStatement();
+        assertNotNull(stmt);
+        final long l1HashCode = ((DelegatingStatement) 
stmt).getDelegate().hashCode();
+        rset = stmt.executeQuery();
+        assertNotNull(rset);
+        assertTrue(rset.next());
+        rset.close();
+        stmt.close();
+
+        stmt = callBack.getCallableStatement();
+        assertNotNull(stmt);
+        final long l2HashCode = ((DelegatingStatement) 
stmt).getDelegate().hashCode();
+        rset = stmt.executeQuery();
+        assertNotNull(rset);
+        assertTrue(rset.next());
+        rset.close();
+        stmt.close();
+
+        // statement pooling is not enabled, we should get different statements
+        assertTrue(l1HashCode != l2HashCode);
+        conn.close();
+        conn = null;
+
+        conn = myDs.getConnection();
+        callBack.setConnection(conn);
+
+        stmt = callBack.getCallableStatement();
+        assertNotNull(stmt);
+        final long l3HashCode = ((DelegatingStatement) 
stmt).getDelegate().hashCode();
+        rset = stmt.executeQuery();
+        assertNotNull(rset);
+        assertTrue(rset.next());
+        rset.close();
+        stmt.close();
+
+        stmt = callBack.getCallableStatement();
+        assertNotNull(stmt);
+        final long l4HashCode = ((DelegatingStatement) 
stmt).getDelegate().hashCode();
+        rset = stmt.executeQuery();
+        assertNotNull(rset);
+        assertTrue(rset.next());
+        rset.close();
+        stmt.close();
+
+        // prepared statement pooling is working
+        assertTrue(l3HashCode == l4HashCode);
+        conn.close();
+        conn = null;
+        spDs.close();
+    }
+
     private void doTestPoolPreparedStatements(final PrepareStatementCallback 
callBack)
     throws Exception {
         final DriverAdapterCPDS mypcds = new DriverAdapterCPDS();
@@ -543,6 +669,13 @@ public class TestSharedPoolDataSource extends 
TestConnectionPool {
     }
 
     @Test
+    public void testPoolPreparedCalls() throws Exception {
+        doTestPoolCallableStatements(new CscbString());
+        doTestPoolCallableStatements(new CscbStringIntInt());
+        doTestPoolCallableStatements(new CscbStringIntIntInt());
+    }
+
+    @Test
     public void testPoolPreparedStatements() throws Exception {
         doTestPoolPreparedStatements(new PscbString());
         doTestPoolPreparedStatements(new PscbStringIntInt());

Reply via email to