Author: psteitz
Date: Sat Nov 21 20:04:53 2009
New Revision: 882981

URL: http://svn.apache.org/viewvc?rev=882981&view=rev
Log:
Made PoolingConnection pool CallableStatements.
JIRA: DBCP-204
Reported and patched by Wei Chen.

Added:
    
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
   (with props)
Modified:
    
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java
    
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolingConnection.java
    
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TestPStmtPooling.java
    commons/proper/dbcp/trunk/xdocs/changes.xml

Modified: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java
URL: 
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java?rev=882981&r1=882980&r2=882981&view=diff
==============================================================================
--- 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java 
(original)
+++ 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/BasicDataSource.java 
Sat Nov 21 20:04:53 2009
@@ -427,14 +427,15 @@
     }
 
     /**
-     * Prepared statement pooling for this pool.
+     * Prepared statement pooling for this pool. When this property is set to 
<code>true</code>
+     * both PreparedStatements and CallableStatements are pooled.
      */
     protected boolean poolPreparedStatements = false;
     
     /**
      * Returns true if we are pooling statements.
      * 
-     * @return true if prepared statements are pooled
+     * @return true if prepared and callable statements are pooled
      */
     public synchronized boolean isPoolPreparedStatements() {
         return this.poolPreparedStatements;
@@ -456,10 +457,15 @@
     }
 
     /**
-     * The maximum number of open statements that can be allocated from
+     * <p>The maximum number of open statements that can be allocated from
      * the statement pool at the same time, or non-positive for no limit.  
Since 
      * a connection usually only uses one or two statements at a time, this is
-     * mostly used to help detect resource leaks.
+     * mostly used to help detect resource leaks.</p>
+     * 
+     * <p>Note: As of version 1.3, CallableStatements (those produced by 
{...@link Connection#prepareCall})
+     * are pooled along with PreparedStatements (produced by {...@link 
Connection#prepareStatement})
+     * and <code>maxOpenPreparedStatements</code> limits the total number of 
prepared or callable statements
+     * that may be in use at a given time.</p>
      */
     protected int maxOpenPreparedStatements = 
GenericKeyedObjectPool.DEFAULT_MAX_TOTAL;
 

Added: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
URL: 
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java?rev=882981&view=auto
==============================================================================
--- 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
 (added)
+++ 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
 Sat Nov 21 20:04:53 2009
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.dbcp;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.commons.pool.KeyedObjectPool;
+
+/**
+ * A {...@link DelegatingCallableStatement} that cooperates with
+ * {...@link PoolingConnection} to implement a pool of {...@link 
CallableStatement}s.
+ * <p>
+ * The {...@link #close} method returns this statement to its containing pool. 
(See {...@link PoolingConnection}.)
+ *
+ * @see PoolingConnection
+ * @version $Revision$ $Date$
+ * @since 1.3
+ */
+public class PoolableCallableStatement extends DelegatingCallableStatement 
implements CallableStatement {
+
+       /**
+        * The {...@link KeyedObjectPool} from which this CallableStatement was 
obtained.
+        */
+       protected KeyedObjectPool _pool = null;
+
+       /**
+        * Key for this statement in the containing {...@link KeyedObjectPool}.
+        */
+       protected Object _key = null;
+
+       public PoolableCallableStatement(DelegatingConnection c, 
CallableStatement s) {
+               super(c, s);
+       }
+
+       /**
+        * Constructor.
+        * 
+        * @param stmt the underlying {...@link CallableStatement}
+        * @param key the key for this statement in the {...@link 
KeyedObjectPool}
+        * @param pool the {...@link KeyedObjectPool} from which this 
CallableStatement was obtained
+        * @param conn the {...@link Connection} that created this 
CallableStatement
+        */
+       public PoolableCallableStatement(CallableStatement stmt, Object key, 
KeyedObjectPool pool, Connection conn) {
+               super((DelegatingConnection)conn, stmt);
+               _pool = pool;
+               _key = key;
+
+               // Remove from trace now because this statement will be 
+               // added by the activate method.
+               if(_conn != null) {
+                       _conn.removeTrace(this);
+               }
+       }
+
+       /**
+     * Returns the CallableStatement to the pool.  If {...@link #isClosed()}, 
this is a No-op.
+     */
+    public void close() throws SQLException {
+        // calling close twice should have no effect
+        if (!isClosed()) {
+            try {
+                _pool.returnObject(_key,this);
+            } catch(SQLException e) {
+                throw e;
+            } catch(RuntimeException e) {
+                throw e;
+            } catch(Exception e) {
+                throw new SQLNestedException("Cannot close CallableStatement 
(return to pool failed)", e);
+            }
+        }
+    }
+
+    /**
+     * Activates after retrieval from the pool. Adds a trace for this 
CallableStatement to the Connection
+     * that created it.
+     */
+       protected void activate() throws SQLException {
+               _closed = false;
+               if( _conn != null ) {
+                       _conn.addTrace( this );
+               }
+               super.activate();
+       }
+
+       /**
+        * 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.
+        */
+       protected void passivate() throws SQLException {
+               _closed = true;
+               if( _conn != null ) {
+                       _conn.removeTrace(this);
+               }
+
+               // The JDBC spec requires that a statment close any open
+               // ResultSet's when it is closed.
+               // FIXME The PreparedStatement we're wrapping should handle 
this for us.
+               // See DBCP-10 for what could happen when ResultSets are closed 
twice.
+               List resultSets = getTrace();
+               if(resultSets != null) {
+                       ResultSet[] set = (ResultSet[])resultSets.toArray(new 
ResultSet[resultSets.size()]);
+                       for(int i = 0; i < set.length; i++) {
+                               set[i].close();
+                       }
+                       clearTrace();
+               }
+
+               super.passivate();
+       }
+
+}

Propchange: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolableCallableStatement.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolingConnection.java
URL: 
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolingConnection.java?rev=882981&r1=882980&r2=882981&view=diff
==============================================================================
--- 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolingConnection.java
 (original)
+++ 
commons/proper/dbcp/trunk/src/java/org/apache/commons/dbcp/PoolingConnection.java
 Sat Nov 21 20:04:53 2009
@@ -17,6 +17,7 @@
 
 package org.apache.commons.dbcp;
 
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
@@ -29,10 +30,12 @@
 /**
  * A {...@link DelegatingConnection} that pools {...@link PreparedStatement}s.
  * <p>
- * My {...@link #prepareStatement} methods, rather than creating a new 
{...@link PreparedStatement}
- * each time, may actually pull the {...@link PreparedStatement} from a pool 
of unused statements.
- * The {...@link PreparedStatement#close} method of the returned {...@link 
PreparedStatement} doesn't
- * actually close the statement, but rather returns it to my pool. (See 
{...@link PoolablePreparedStatement}.)
+ * The {...@link #prepareStatement} and {...@link #prepareCall} methods, 
rather than creating a new PreparedStatement
+ * each time, may actually pull the statement from a pool of unused statements.
+ * The {...@link PreparedStatement#close} method of the returned statement 
doesn't
+ * actually close the statement, but rather returns it to the pool. 
+ * (See {...@link PoolablePreparedStatement}, {...@link 
PoolableCallableStatement}.)
+ * 
  *
  * @see PoolablePreparedStatement
  * @author Rodney Waldhoff
@@ -40,9 +43,16 @@
  * @version $Revision$ $Date$
  */
 public class PoolingConnection extends DelegatingConnection implements 
Connection, KeyedPoolableObjectFactory {
-    /** My pool of {...@link PreparedStatement}s. */
+    /** Pool of {...@link PreparedStatement}s. and {...@link 
CallableStatement}s */
     protected KeyedObjectPool _pstmtPool = null;
 
+    /** Prepared Statement type */
+    private static final byte STATEMENT_PREPAREDSTMT = 0;
+    
+    /** Callable Statement type */
+    private static final byte STATEMENT_CALLABLESTMT = 1;
+     
+    
     /**
      * Constructor.
      * @param c the underlying {...@link Connection}.
@@ -54,7 +64,7 @@
     /**
      * Constructor.
      * @param c the underlying {...@link Connection}.
-     * @param pool {...@link KeyedObjectPool} of {...@link PreparedStatement}s
+     * @param pool {...@link KeyedObjectPool} of {...@link PreparedStatement}s 
and {...@link CallableStatement}s.
      */
     public PoolingConnection(Connection c, KeyedObjectPool pool) {
         super(c);
@@ -63,7 +73,7 @@
 
 
     /**
-     * Close and free all {...@link PreparedStatement}s from my pool, and
+     * Close and free all {...@link PreparedStatement}s or {...@link 
CallableStatement} from my pool, and
      * close my underlying connection.
      */
     public synchronized void close() throws SQLException {
@@ -122,6 +132,48 @@
             throw (SQLException) new SQLException("Borrow prepareStatement 
from pool failed").initCause(e);
         }
     }
+    
+    /**
+     * Create or obtain a {...@link CallableStatement} from the pool.
+     * 
+     * @param sql the sql string used to define the CallableStatement
+     * @return a {...@link PoolableCallableStatement}
+     * @throws SQLException
+     * @since 1.3
+     */
+       public CallableStatement prepareCall(String sql) throws SQLException {
+        try {
+            return (CallableStatement) (_pstmtPool.borrowObject(createKey(sql, 
STATEMENT_CALLABLESTMT)));
+        } catch (NoSuchElementException e) {
+            throw new SQLNestedException("MaxOpenCallableStatements limit 
reached", e);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new SQLNestedException("Borrow callableStatement from pool 
failed", e);
+        }
+    }
+       
+       /**
+     * Create or obtain a {...@link CallableStatement} from the pool.
+     * 
+     * @param sql the sql string used to define the CallableStatement
+     * @return a {...@link PoolableCallableStatement}
+     * @throws SQLException
+     * @since 1.3
+     */
+       public CallableStatement prepareCall(String sql, int resultSetType, int 
resultSetConcurrency) throws SQLException {
+        try {
+            return (CallableStatement) (_pstmtPool.borrowObject(createKey(sql, 
resultSetType,
+                            resultSetConcurrency, STATEMENT_CALLABLESTMT)));
+        } catch (NoSuchElementException e) {
+            throw new SQLNestedException("MaxOpenCallableStatements limit 
reached", e);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new SQLNestedException("Borrow callableStatement from pool 
failed", e);
+        }
+    }
+    
 
     // ------------------- JDBC 3.0 -----------------------------------------
     // Will be commented by the build process on a JDBC 2.0 system
@@ -166,6 +218,17 @@
         } catch (SQLException e) {}
         return new PStmtKey(normalizeSQL(sql), catalog, resultSetType, 
resultSetConcurrency);
     }
+    
+    /**
+     * Create a PStmtKey for the given arguments.
+     */
+    protected Object createKey(String sql, int resultSetType, int 
resultSetConcurrency, byte stmtType) {
+        String catalog = null;
+        try {
+            catalog = getCatalog();
+        } catch (Exception e) {}
+        return new PStmtKey(normalizeSQL(sql), catalog, resultSetType, 
resultSetConcurrency, stmtType);
+    }
 
     /**
      * Create a PStmtKey for the given arguments.
@@ -177,6 +240,17 @@
         } catch (SQLException e) {}
         return new PStmtKey(normalizeSQL(sql), catalog);
     }
+    
+    /**
+     * Create a PStmtKey for the given arguments.
+     */
+    protected Object createKey(String sql, byte stmtType) {
+        String catalog = null;
+        try {
+            catalog = getCatalog();
+        } catch (Exception e) {}
+        return new PStmtKey(normalizeSQL(sql), catalog, stmtType);
+    }
 
     /**
      * Normalize the given SQL statement, producing a
@@ -187,36 +261,42 @@
     }
 
     /**
-     * My {...@link KeyedPoolableObjectFactory} method for creating
-     * {...@link PreparedStatement}s.
+     * {...@link KeyedPoolableObjectFactory} method for creating
+     * {...@link PoolablePreparedStatement}s or {...@link 
PoolableCallableStatements}.
+     * The {...@link PStmtKey#_stmtType} field in the key determines whether
+     * a PoolablePreparedStatement or PoolableCallableStatement is created.
+     * 
      * @param obj the key for the {...@link PreparedStatement} to be created
      */
     public Object makeObject(Object obj) throws Exception {
         if(null == obj || !(obj instanceof PStmtKey)) {
             throw new IllegalArgumentException("Prepared statement key is null 
or invalid.");
         } else {
-            // _openPstmts++;
             PStmtKey key = (PStmtKey)obj;
-            if(null == key._resultSetType && null == 
key._resultSetConcurrency) {
-                return new 
PoolablePreparedStatement(getDelegate().prepareStatement(key._sql),key,_pstmtPool,this);
-            } else {
-                if (null == key._resultSetType || null == 
key._resultSetConcurrency) {
-                    throw new IllegalArgumentException("Invalid prepared 
statement key.");
-                }
-                return new 
PoolablePreparedStatement(getDelegate().prepareStatement(
-                        
key._sql,key._resultSetType.intValue(),key._resultSetConcurrency.intValue()),key,_pstmtPool,this);
+            if( null == key._resultSetType && null == 
key._resultSetConcurrency ) {
+                return key._stmtType == STATEMENT_PREPAREDSTMT ?
+                    new 
PoolablePreparedStatement(getDelegate().prepareStatement( key._sql), key, 
_pstmtPool, this) :
+                    new PoolableCallableStatement(getDelegate().prepareCall( 
key._sql ), key, _pstmtPool, this);
+
+            }else {
+                return key._stmtType == STATEMENT_PREPAREDSTMT ?
+                    new 
PoolablePreparedStatement(getDelegate().prepareStatement(
+                        key._sql, 
key._resultSetType.intValue(),key._resultSetConcurrency.intValue()), key, 
_pstmtPool, this) :
+                    new PoolableCallableStatement( getDelegate().prepareCall(
+                        key._sql,key._resultSetType.intValue(), 
key._resultSetConcurrency.intValue() ), key, _pstmtPool, this);
             }
         }
     }
 
     /**
-     * My {...@link KeyedPoolableObjectFactory} method for destroying
-     * {...@link PreparedStatement}s.
+     * {...@link KeyedPoolableObjectFactory} method for destroying
+     * PoolablePreparedStatements and PoolableCallableStatements.
+     * Closes the underlying statement.
+     * 
      * @param key ignored
-     * @param obj the {...@link PreparedStatement} to be destroyed.
+     * @param obj the pooled statement to be destroyed.
      */
     public void destroyObject(Object key, Object obj) throws Exception {
-        //_openPstmts--;
         if(obj instanceof DelegatingPreparedStatement) {
             ((DelegatingPreparedStatement)obj).getInnermostDelegate().close();
         } else {
@@ -225,8 +305,9 @@
     }
 
     /**
-     * My {...@link KeyedPoolableObjectFactory} method for validating
-     * {...@link PreparedStatement}s.
+     * {...@link KeyedPoolableObjectFactory} method for validating
+     * pooled statements. Currently always returns true.
+     * 
      * @param key ignored
      * @param obj ignored
      * @return <tt>true</tt>
@@ -236,18 +317,21 @@
     }
 
     /**
-     * My {...@link KeyedPoolableObjectFactory} method for activating
-     * {...@link PreparedStatement}s. (Currently a no-op.)
+     * {...@link KeyedPoolableObjectFactory} method for activating
+     * pooled statements.
+     * 
      * @param key ignored
-     * @param obj ignored
+     * @param obj pooled statement to be activated
      */
     public void activateObject(Object key, Object obj) throws Exception {
         ((DelegatingPreparedStatement)obj).activate();
     }
 
     /**
-     * My {...@link KeyedPoolableObjectFactory} method for passivating
-     * {...@link PreparedStatement}s.  Currently invokes {...@link 
PreparedStatement#clearParameters}.
+     * {...@link KeyedPoolableObjectFactory} method for passivating
+     * {...@link PreparedStatement}s or {...@link CallableStatement}s.
+     * Invokes {...@link PreparedStatement#clearParameters}.
+     * 
      * @param key ignored
      * @param obj a {...@link PreparedStatement}
      */
@@ -268,11 +352,25 @@
      * A key uniquely identifiying {...@link PreparedStatement}s.
      */
     static class PStmtKey {
+        
+        /** SQL defining Prepared or Callable Statement */
         protected String _sql = null;
+        
+        /** Result set type */
         protected Integer _resultSetType = null;
+        
+        /** Result set concurrency */
         protected Integer _resultSetConcurrency = null;
+        
+        /** Database catalog */
         protected String _catalog = null;
         
+        /** 
+         *  Statement type. Either STATEMENT_PREPAREDSTMT (PreparedStatement)
+         *  or STATEMENT_CALLABLESTMT (CallableStatement) 
+         */
+        protected byte _stmtType = STATEMENT_PREPAREDSTMT;
+        
         PStmtKey(String sql) {
             _sql = sql;
         }
@@ -281,6 +379,12 @@
             _sql = sql;
             _catalog = catalog;
         }
+        
+        PStmtKey(String sql, String catalog, byte stmtType) {
+            _sql = sql;
+            _catalog = catalog;
+            _stmtType = stmtType;
+        }
 
         PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
             _sql = sql;
@@ -294,6 +398,14 @@
             _resultSetType = new Integer(resultSetType);
             _resultSetConcurrency = new Integer(resultSetConcurrency);
         }
+        
+        PStmtKey(String sql, String catalog, int resultSetType, int 
resultSetConcurrency, byte stmtType) {
+            _sql = sql;
+            _catalog = catalog;
+            _resultSetType = new Integer(resultSetType);
+            _resultSetConcurrency = new Integer(resultSetConcurrency);
+            _stmtType = stmtType;
+        }
 
         public boolean equals(Object that) {
             try {
@@ -301,7 +413,8 @@
                 return( ((null == _sql && null == key._sql) || 
_sql.equals(key._sql)) &&
                         ((null == _catalog && null == key._catalog) || 
_catalog.equals(key._catalog)) &&
                         ((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)) &&
+                        (_stmtType == key._stmtType)
                       );
             } catch(ClassCastException e) {
                 return false;
@@ -327,6 +440,8 @@
             buf.append(_resultSetType);
             buf.append(", resultSetConcurrency=");
             buf.append(_resultSetConcurrency);
+            buf.append(", statmentType=");
+            buf.append(_stmtType);
             return buf.toString();
         }
     }

Modified: 
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TestPStmtPooling.java
URL: 
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TestPStmtPooling.java?rev=882981&r1=882980&r2=882981&view=diff
==============================================================================
--- 
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TestPStmtPooling.java
 (original)
+++ 
commons/proper/dbcp/trunk/src/test/org/apache/commons/dbcp/TestPStmtPooling.java
 Sat Nov 21 20:04:53 2009
@@ -70,6 +70,42 @@
         assertSame(ustmt1, ustmt2);
     }
     
+    public void testCallableStatementPooling() throws Exception {
+        new TesterDriver();
+        ConnectionFactory connFactory = new DriverManagerConnectionFactory(
+                "jdbc:apache:commons:testdriver","u1","p1");
+
+        ObjectPool connPool = new GenericObjectPool();
+        KeyedObjectPoolFactory stmtPoolFactory = new 
GenericKeyedObjectPoolFactory(null);
+
+        new PoolableConnectionFactory(connFactory, connPool, stmtPoolFactory,
+                null, false, true);
+
+        DataSource ds = new PoolingDataSource(connPool);
+
+        Connection conn = ds.getConnection();
+        Statement stmt1 = conn.prepareStatement("select 1 from dual");
+        Statement ustmt1 = ((DelegatingStatement) 
stmt1).getInnermostDelegate();
+        Statement cstmt1 = conn.prepareCall("{call home}");
+        Statement ucstmt1 = ((DelegatingStatement) 
cstmt1).getInnermostDelegate();
+        stmt1.close();  // Return to pool
+        cstmt1.close(); // ""
+        Statement stmt2 = conn.prepareStatement("select 1 from dual"); // 
Check out from pool
+        Statement ustmt2 = ((DelegatingStatement) 
stmt2).getInnermostDelegate();
+        Statement cstmt2 = conn.prepareCall("{call home}");
+        Statement ucstmt2 = ((DelegatingStatement) 
cstmt2).getInnermostDelegate();
+        stmt2.close();  // Return to pool
+        cstmt2.close(); // ""
+        assertSame(ustmt1, ustmt2);
+        assertSame(ucstmt1, ucstmt2);
+        // Verify key distinguishes Callable from Prepared Statements in the 
pool
+        Statement stmt3 = conn.prepareCall("select 1 from dual");
+        Statement ustmt3 = ((DelegatingStatement) 
stmt3).getInnermostDelegate();
+        stmt3.close();
+        assertNotSame(ustmt1, ustmt3);
+        assertNotSame(ustmt3, ucstmt1);
+    }
+    
     public void testClosePool() throws Exception {
         new TesterDriver();
         ConnectionFactory connFactory = new DriverManagerConnectionFactory(

Modified: commons/proper/dbcp/trunk/xdocs/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/dbcp/trunk/xdocs/changes.xml?rev=882981&r1=882980&r2=882981&view=diff
==============================================================================
--- commons/proper/dbcp/trunk/xdocs/changes.xml (original)
+++ commons/proper/dbcp/trunk/xdocs/changes.xml Sat Nov 21 20:04:53 2009
@@ -42,6 +42,13 @@
      new features as well as bug fixes and instrumentation.  Some bug fixes
      will change semantics (e.g. connection close will become idempotent).
      The minimum JDK level will be increased to 1.4">
+      <action dev="psteitz" type="fix" issue="DBCP-204" due-to="Wei Chen">
+        Made PoolingConnection pool CallableStatements. When BasicDataSource's 
+        poolPreparedStatements property is true, CallableStatements are now
+        pooled along with PreparedStatements. The maxOpenPreparedStatements
+        property limits the combined number of Callable and Prepared statements
+        that can be in use at a given time.
+      </action>
       <action dev="markt" type="update" issue="DBCP-305" due-to="Christopher 
Schultz">
         Use an API specific exception for logging abandoned objects to make
         scanning the logs for these exceptions simpler and to provide a better


Reply via email to