You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by jm...@apache.org on 2002/08/05 08:42:01 UTC

cvs commit: jakarta-commons/dbcp/src/test/org/apache/commons/dbcp/jdbc2pool TestJdbc2PoolDataSource.java

jmcnally    2002/08/04 23:42:01

  Modified:    dbcp     STATUS.html build.properties.sample build.xml
                        project.xml
               dbcp/src/test/org/apache/commons/dbcp TestAll.java
  Added:       dbcp/src/java/org/apache/commons/dbcp/cpdsadapter
                        ConnectionImpl.java DriverAdapterCPDS.java
                        PoolablePreparedStatementStub.java
                        PooledConnectionImpl.java package.html
               dbcp/src/java/org/apache/commons/dbcp/jdbc2pool
                        CPDSConnectionFactory.java Jdbc2PoolDataSource.java
                        KeyedCPDSConnectionFactory.java PoolKey.java
                        UserPassKey.java package.html
               dbcp/src/test/org/apache/commons/dbcp/jdbc2pool
                        TestJdbc2PoolDataSource.java
  Log:
  import of jdbc2pool code from sandbox.
  
  Revision  Changes    Path
  1.4       +2 -1      jakarta-commons/dbcp/STATUS.html
  
  Index: STATUS.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/STATUS.html,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- STATUS.html	23 May 2002 12:11:24 -0000	1.3
  +++ STATUS.html	5 Aug 2002 06:42:01 -0000	1.4
  @@ -71,6 +71,7 @@
   <li>Morgan Delagrange
   <li>Geir Magnusson Jr.
   <li>Craig R. McClanahan
  +<li>John McNally
   <li>Ignacio J. Ortega
   <li>Rodney Waldhoff
   <li>David Weinrich
  
  
  
  1.5       +3 -0      jakarta-commons/dbcp/build.properties.sample
  
  Index: build.properties.sample
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/build.properties.sample,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- build.properties.sample	29 Jun 2002 17:53:28 -0000	1.4
  +++ build.properties.sample	5 Aug 2002 06:42:01 -0000	1.5
  @@ -2,6 +2,9 @@
   base.path=/usr/local
   commons-collections.home=${base.path}/commons-collections-2.0
   commons-collections.jar=${commons-collections.home}/commons-collections.jar
  +# commons-lang.jar - Commons-Lang Classpath
  +commons-lang.home=${basedir}/../../jakarta-commons-sandbox/lang/dist
  +commons-lang.jar=${commons-lang.home}/commons-lang-1.0-b1.jar
   # commons-pool.jar - Commons-Pool Classpath
   commons-pool.home=${base.path}/commons-pool-1.0
   commons-pool.jar=${commons-pool.home}/commons-pool.jar
  
  
  
  1.11      +7 -2      jakarta-commons/dbcp/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/build.xml,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- build.xml	20 Jul 2002 18:02:27 -0000	1.10
  +++ build.xml	5 Aug 2002 06:42:01 -0000	1.11
  @@ -42,7 +42,7 @@
         <property name="cp" value=""/>
   
         <!-- now combine the classpaths -->
  -      <property name="classpath" value="${cp}:${commons-pool.jar}:${commons-collections.jar}:${jdbc20ext.jar}:${junit.jar}:${jndi.jar}"/>
  +      <property name="classpath" value="${cp}:${commons-pool.jar}:${commons-collections.jar}:${jdbc20ext.jar}:${junit.jar}:${jndi.jar}:${commons-lang.jar}"/>
   
         <property name="name" value="commons-dbcp"/>
         <property name="Name" value="Commons-DBCP"/>
  @@ -202,6 +202,7 @@
      </target>
   
      <target name="prepare-jdbc3" if="jdbc3.present">
  +      <echo message="Uncommenting jdbc3 additional methods."/>
         <replace dir="${dest.src}" token="/* JDBC_3_ANT_KEY" value=""/>
         <replace dir="${dest.src}" token="JDBC_3_ANT_KEY */" value=""/>
      </target>
  @@ -215,6 +216,10 @@
                deprecation="true"
                optimize="true">
           <exclude name="org/apache/commons/dbcp/BasicDataSourceFactory.java"
  +               unless="jndi.present"/>
  +        <exclude name="org/apache/commons/dbcp/jdbc2pool/**"
  +               unless="jndi.present"/>
  +        <exclude name="org/apache/commons/dbcp/cpdsadapter/**"
                  unless="jndi.present"/>
         </javac>
      </target>
  
  
  
  1.4       +11 -0     jakarta-commons/dbcp/project.xml
  
  Index: project.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/project.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- project.xml	25 Jul 2002 02:36:45 -0000	1.3
  +++ project.xml	5 Aug 2002 06:42:01 -0000	1.4
  @@ -33,6 +33,12 @@
         <organization></organization>
       </developer>
       <developer>
  +      <name>John McNally</name>
  +      <id></id>
  +      <email></email>
  +      <organization></organization>
  +    </developer>
  +    <developer>
         <name>Rodney Waldoff</name>
         <id>rwaldof</id>
         <email></email>
  @@ -51,6 +57,11 @@
       <dependency>
         <id>commons-collections</id>
         <version>2.0</version>
  +    </dependency>
  +
  +    <dependency>
  +      <id>commons-lang</id>
  +      <version>1.0-b1</version>
       </dependency>
       
       <dependency>
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/ConnectionImpl.java
  
  Index: ConnectionImpl.java
  ===================================================================
  package org.apache.commons.dbcp.cpdsadapter;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  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;
  
  /**
   * 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.
   * In accordance with the jdbc specification this Connection cannot
   * be used after closed() is called.  Any further usage will result in an
   * SQLException.
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: ConnectionImpl.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  class ConnectionImpl 
      implements Connection
  {
      private static final String CLOSED = 
          "Attempted to use Connection after closed() was called.";
  
      /**
       * The JDBC database connection.
       */
      private Connection connection;
  
      /**
       * The object that instantiated this object
       */
       private PooledConnectionImpl pooledConnection;
  
      /**
       * Marks whether is Connection is still usable.
       */
      boolean isClosed;
  
      /**
       * Creates a <code>ConnectionImpl</code>. 
       *
       * @param pooledConnection The PooledConnection that is calling the ctor.
       * @param connection The JDBC 1.x Connection to wrap.
       */
      ConnectionImpl(PooledConnectionImpl pooledConnection, 
                     Connection 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();
      }
  
      /**
       * Marks the Connection as closed, and notifies the pool that the
       * pooled connection is available.
       * In accordance with the jdbc specification this Connection cannot
       * be used after closed() is called.  Any further usage will result in an
       * SQLException.
       *
       * @exception SQLException The database connection couldn't be closed.
       */
      public void close()
          throws SQLException
      {
          isClosed = true;
          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}.
       *
       * @exception SQLException if this connection is closed or an error occurs
       * in the wrapped connection.
       */
      public PreparedStatement prepareStatement(String sql)
          throws SQLException
      {
          assertOpen();
          return pooledConnection.prepareStatement(sql);
      }
  
      /**
       * 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}.
       *
       * @exception SQLException if this connection is closed or an error occurs
       * in the wrapped connection.
       */
      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);
      }
  
      // ------------------- JDBC 3.0 -----------------------------------------
      // Will be uncommented by the build process on a JDBC 3.0 system
  
  /* JDBC_3_ANT_KEY
  
      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);
      }
  
      public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
          throws SQLException {
          assertOpen();
          return connection.prepareStatement(sql, autoGeneratedKeys);
      }
  
      public PreparedStatement prepareStatement(String sql, int columnIndexes[])
          throws SQLException {
          assertOpen();
          return connection.prepareStatement(sql, columnIndexes);
      }
  
      public PreparedStatement prepareStatement(String sql, String columnNames[])
          throws SQLException {
          assertOpen();
          return connection.prepareStatement(sql, columnNames);
      }
  
  JDBC_3_ANT_KEY */
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/DriverAdapterCPDS.java
  
  Index: DriverAdapterCPDS.java
  ===================================================================
  package org.apache.commons.dbcp.cpdsadapter;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
   
  
  import java.util.Hashtable;
  import  java.io.PrintWriter;
  import  java.io.Serializable;
  import java.sql.DriverManager;
  import java.sql.SQLException;
  import javax.sql.PooledConnection;
  import javax.sql.ConnectionPoolDataSource;
  import javax.naming.Name;
  import javax.naming.Context;
  import javax.naming.Referenceable;
  import javax.naming.spi.ObjectFactory;
  import javax.naming.Reference;
  import javax.naming.RefAddr;
  import javax.naming.StringRefAddr;
  import javax.naming.NamingException;
  
  import org.apache.commons.pool.KeyedObjectPool;
  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
  
  /**
   * <p>
   * An adapter for jdbc drivers that do not include an implementation
   * of {@link javax.sql.ConnectionPoolDataSource}, but still include a 
   * {@link java.sql.DriverManager} implementation.  
   * <code>ConnectionPoolDataSource</code>s are not used within general 
   * applications.  They are used by <code>DataSource</code> implementations
   * that pool <code>Connection</code>s, such as 
   * {@link org.apache.commons.dbcp.jdbc2pool.Jdbc2PoolDataSource}.  A J2EE
   * container will normally provide some method of initializing the
   * <code>ConnectionPoolDataSource</code> whose attributes are presented
   * as bean getters/setters and then deploying it via JNDI.  It is then
   * available as a source of physical connections to the database, when
   * the pooling <code>DataSource</code> needs to create a new 
   * physical connection.
   * </p>
   *
   * <p>
   * Although normally used within a JNDI environment, the DriverAdapterCPDS
   * can be instantiated and initialized as any bean and then attached
   * directly to a pooling <code>DataSource</code>. 
   * <code>Jdbc2PoolDataSource</code> can use the 
   * <code>ConnectionPoolDataSource</code> with or without the use of JNDI.
   * </p>
   *
   * <p>
   * The DriverAdapterCPDS also provides <code>PreparedStatement</code> pooling
   * which is not generally available in jbdc2 
   * <code>ConnectionPoolDataSource</code> implementation, but is 
   * addressed within the jdbc3 specification.  The <code>PreparedStatement</code>
   * pool in DriverAdapterCPDS has been in the dbcp package for some time, but
   * it has not undergone extensive testing in the configuration used here.
   * It should be considered experimental and can be toggled with the 
   * poolPreparedStatements attribute.
   * </p>
   *
   * <p>
   * The <a href="package-summary.html">package documentation</a> contains an 
   * example using catalina and JNDI.  The <a 
   * href="../jdbc2pool/package-summary.html">jdbc2pool package documentation</a>
   * shows how to use <code>DriverAdapterCPDS</code> as a source for
   * <code>Jdbc2PoolDataSource</code> without the use of JNDI.
   * </p>
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: DriverAdapterCPDS.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  public class DriverAdapterCPDS
      implements ConnectionPoolDataSource, Referenceable, Serializable, 
                 ObjectFactory
  {
      private static final String GET_CONNECTION_CALLED = 
          "A PooledConnection was already requested from this source, " + 
          "further initialization is not allowed.";
  
      /**
       * Default no-arg constructor for Serialization
       */
      public DriverAdapterCPDS() 
      {
      }
  
      /**
       * Attempt to establish a database connection using the default
       * user and password.
       */
      public PooledConnection getPooledConnection() 
          throws SQLException
      {
          return getPooledConnection(getUser(), getPassword());
      }
                       
      /**
       * Attempt to establish a database connection.
       */
      public PooledConnection getPooledConnection(String username, 
                                                  String password)
          throws SQLException
      {
          getConnectionCalled = true;
          /*
          public GenericKeyedObjectPool(KeyedPoolableObjectFactory factory, 
          int maxActive, byte whenExhaustedAction, long maxWait, 
          int maxIdle, boolean testOnBorrow, boolean testOnReturn, 
          long timeBetweenEvictionRunsMillis, 
          int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, 
          boolean testWhileIdle) {
          */
          KeyedObjectPool stmtPool = null;
          if (isPoolPreparedStatements()) 
          {
              stmtPool = new GenericKeyedObjectPool(null,
                  getMaxActive(), GenericKeyedObjectPool.WHEN_EXHAUSTED_GROW, 0, 
                  getMaxIdle(), false, false, getTimeBetweenEvictionRunsMillis(),
                  getNumTestsPerEvictionRun(), 
                  getMinEvictableIdleTimeMillis(), false);            
          }
          
          // Workaround for buggy WebLogic 5.1 classloader - ignore the
          // exception upon first invocation.
          try
          {
              return new PooledConnectionImpl(
                  DriverManager.getConnection( getUrl(),
                                               username,
                                               password ), stmtPool );
          }
          catch( ClassCircularityError e )
          {
              return new PooledConnectionImpl(
                  DriverManager.getConnection( getUrl(),
                                               username,
                                               password ), stmtPool );
          }
      }
  
      // ----------------------------------------------------------------------
      // Referenceable implementation 
  
      /**
       * <CODE>Referenceable</CODE> implementation.
       */
      public Reference getReference() 
          throws NamingException 
      {
          // this class implements its own factory
          String factory = getClass().getName();
          
          Reference ref = new Reference(getClass().getName(), factory, null);
  
          ref.add(new StringRefAddr("description", getDescription()));
          ref.add(new StringRefAddr("driver", getDriver()));
          ref.add(new StringRefAddr("loginTimeout", 
                                    String.valueOf(getLoginTimeout())));
          ref.add(new StringRefAddr("password", getPassword()));
          ref.add(new StringRefAddr("user", getUser()));
          ref.add(new StringRefAddr("url", getUrl()));
  
          ref.add(new StringRefAddr("poolPreparedStatements", 
                                    String.valueOf(isPoolPreparedStatements())));
          ref.add(new StringRefAddr("maxActive", 
                                    String.valueOf(getMaxActive())));
          ref.add(new StringRefAddr("maxIdle", 
                                    String.valueOf(getMaxIdle())));
          ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", 
              String.valueOf(getTimeBetweenEvictionRunsMillis())));
          ref.add(new StringRefAddr("numTestsPerEvictionRun", 
              String.valueOf(getNumTestsPerEvictionRun())));
          ref.add(new StringRefAddr("minEvictableIdleTimeMillis", 
              String.valueOf(getMinEvictableIdleTimeMillis())));
  
          return ref;
      }
  
  
      // ----------------------------------------------------------------------
      // ObjectFactory implementation 
  
      /**
       * implements ObjectFactory to create an instance of this class
       */ 
      public Object getObjectInstance(Object refObj, Name name, 
                                      Context context, Hashtable env) 
          throws Exception 
      {
          // The spec says to return null if we can't create an instance 
          // of the reference
          DriverAdapterCPDS cpds = null;
          if (refObj instanceof Reference) 
          {
              Reference ref = (Reference)refObj;
              if (ref.getClassName().equals(getClass().getName())) 
              {
                  RefAddr ra = ref.get("description");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDescription(ra.getContent().toString());
                  }
  
                  ra = ref.get("driver");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDriver(ra.getContent().toString());
                  }
                  ra = ref.get("url");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setUrl(ra.getContent().toString());
                  }
                  ra = ref.get("user");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setUser(ra.getContent().toString());
                  }
                  ra = ref.get("password");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setPassword(ra.getContent().toString());
                  }
  
                  ra = ref.get("poolPreparedStatements");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setPoolPreparedStatements(
                          Boolean.getBoolean(ra.getContent().toString()));
                  }
                  ra = ref.get("maxActive");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setMaxActive(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("maxIdle");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setMaxIdle(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("timeBetweenEvictionRunsMillis");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setTimeBetweenEvictionRunsMillis(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("numTestsPerEvictionRun");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setNumTestsPerEvictionRun(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("minEvictableIdleTimeMillis");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setMinEvictableIdleTimeMillis(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  cpds = this;
              }
          }
              return cpds;
      }
  
      /**
       * Throws an IllegalStateException, if a PooledConnection has already
       * been requested.
       */
      private void assertInitializationAllowed()
          throws IllegalStateException 
      {
          if (getConnectionCalled) 
          {
              throw new IllegalStateException(GET_CONNECTION_CALLED);
          }
      }
  
      // ----------------------------------------------------------------------
      // Properties
      
      /**
       * Get the value of description.  This property is here for use by
       * the code which will deploy this datasource.  It is not used
       * internally.
       *
       * @return value of description.
       */
      public String getDescription() 
      {
          return description;
      }
      
      /**
       * Set the value of description.  This property is here for use by
       * the code which will deploy this datasource.  It is not used
       * internally.
       *
       * @param v  Value to assign to description.
       */
      public void setDescription(String  v) 
      {
          this.description = v;
      }
  
      /**
       * Get the value of password for the default user.
       * @return value of password.
       */
      public String getPassword() 
      {
          return password;
      }
      
      /**
       * Set the value of password for the default user.
       * @param v  Value to assign to password.
       */
      public void setPassword(String  v) 
      {
          assertInitializationAllowed();
          this.password = v;
      }
  
      /**
       * Get the value of url used to locate the database for this datasource.
       * @return value of url.
       */
      public String getUrl() 
      {
          return url;
      }
      
      /**
       * Set the value of url used to locate the database for this datasource.
       * @param v  Value to assign to url.
       */
      public void setUrl(String  v) 
      {
          assertInitializationAllowed();
          this.url = v;
      }
  
      /**
       * Get the value of default user (login or username).
       * @return value of user.
       */
      public String getUser() 
      {
          return user;
      }
      
      /**
       * Set the value of default user (login or username).
       * @param v  Value to assign to user.
       */
      public void setUser(String  v) 
      {
          assertInitializationAllowed();
          this.user = v;
      }
  
      /**
       * Get the driver classname.
       * @return value of driver.
       */
      public String getDriver() 
      {
          return driver;
      }
      
      /**
       * Set the driver classname.  Setting the driver classname cause the 
       * driver to be registered with the DriverManager.
       * @param v  Value to assign to driver.
       */
      public void setDriver(String  v)
          throws ClassNotFoundException
      {
          assertInitializationAllowed();
          this.driver = v;
          // make sure driver is registered
          Class.forName(v);
      }
      
      /**
       * Gets the maximum time in seconds that this data source can wait 
       * while attempting to connect to a database. NOT USED.
       */
      public int getLoginTimeout() 
      {
          return loginTimeout;
      }
                             
      /**
       * Get the log writer for this data source. NOT USED.
       */
      public PrintWriter getLogWriter() 
      {
          return logWriter;
      }
                             
      /**
       * Sets the maximum time in seconds that this data source will wait 
       * while attempting to connect to a database. NOT USED.
       */
      public void setLoginTimeout(int seconds)
      {
          loginTimeout = seconds;
      } 
                             
      /**
       * Set the log writer for this data source. NOT USED.
       */
      public void setLogWriter(java.io.PrintWriter out)
      {
          logWriter = out;
      } 
  
  
      // ------------------------------------------------------------------
      // PreparedStatement pool properties
  
      
      /**
       * Flag to toggle the pooling of <code>PreparedStatement</code>s
       * @return value of poolPreparedStatements.
       */
      public boolean isPoolPreparedStatements() 
      {
          return poolPreparedStatements;
      }
      
      /**
       * Flag to toggle the pooling of <code>PreparedStatement</code>s
       * @param v  true to pool statements.
       */
      public void setPoolPreparedStatements(boolean  v) 
      {
          assertInitializationAllowed();
          this.poolPreparedStatements = v;
      }
  
      /**
       * The maximum number of active statements that can be allocated from
       * this pool at the same time, or zero for no limit.
       */
      public int getMaxActive() {
          return (this.maxActive);
      }
  
      /**
       * The maximum number of active statements that can be allocated from
       * this pool at the same time, or zero for no limit.
       */
      public void setMaxActive(int maxActive) {
          assertInitializationAllowed();
          this.maxActive = maxActive;
      }
  
      /**
       * The maximum number of statements that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       */
      public int getMaxIdle() {
          return (this.maxIdle);
      }
  
      /**
       * The maximum number of statements that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       */
      public void setMaxIdle(int maxIdle) {
          assertInitializationAllowed();
          this.maxIdle = maxIdle;
      }
  
      /**
       * Returns the number of milliseconds to sleep between runs of the
       * idle object evictor thread.
       * When non-positive, no idle object evictor thread will be
       * run.
       *
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getTimeBetweenEvictionRunsMillis() {
          return _timeBetweenEvictionRunsMillis;
      }
  
      /**
       * Sets the number of milliseconds to sleep between runs of the
       * idle object evictor thread.
       * When non-positive, no idle object evictor thread will be
       * run.
       *
       * *see #getTimeBetweenEvictionRunsMillis
       */
      public void 
          setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
          assertInitializationAllowed();
              _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
      }
  
      /**
       * Returns the number of statements to examine during each run of the
       * idle object evictor thread (if any).
       *
       * *see #setNumTestsPerEvictionRun
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getNumTestsPerEvictionRun() {
          return _numTestsPerEvictionRun;
      }
  
      /**
       * Sets the number of statements to examine during each run of the
       * idle object evictor thread (if any).
       * <p>
       * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
       * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
       * idle objects will be tested per run.
       *
       * *see #getNumTestsPerEvictionRun
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
          assertInitializationAllowed();
          _numTestsPerEvictionRun = numTestsPerEvictionRun;
      }
  
      /**
       * Returns the minimum amount of time a statement may sit idle in the pool
       * before it is eligible for eviction by the idle object evictor
       * (if any).
       *
       * *see #setMinEvictableIdleTimeMillis
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getMinEvictableIdleTimeMillis() {
          return _minEvictableIdleTimeMillis;
      }
  
      /**
       * Sets the minimum amount of time a statement may sit idle in the pool
       * before it is eligable for eviction by the idle object evictor
       * (if any).
       * When non-positive, no objects will be evicted from the pool
       * due to idle time alone.
       *
       * *see #getMinEvictableIdleTimeMillis
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public void 
          setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
          assertInitializationAllowed();
          _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
      }
  
      /** Description */
      private String description;
      /** Password */
      private String password;
      /** Url name */
      private String url;
      /** User name */
      private String user;
      /** Driver class name */
      private String driver;
  
      /** Login TimeOut in seconds */
      private int loginTimeout;
      /** Log stream */
      private PrintWriter logWriter = null;
  
      // PreparedStatement pool properties
      private boolean poolPreparedStatements;
      private int maxActive = 10;
      private int maxIdle = 10;
      private int _timeBetweenEvictionRunsMillis = -1;
      private int _numTestsPerEvictionRun = -1;
      private int _minEvictableIdleTimeMillis = -1;
  
      private boolean getConnectionCalled = false;
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/PoolablePreparedStatementStub.java
  
  Index: PoolablePreparedStatementStub.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/PoolablePreparedStatementStub.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   * $Revision: 1.1 $
   * $Date: 2002/08/05 06:42:01 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.dbcp.cpdsadapter;
  
  import java.sql.PreparedStatement;
  import java.sql.Connection;
  import java.sql.SQLException;
  import org.apache.commons.pool.KeyedObjectPool;
  import org.apache.commons.dbcp.PoolablePreparedStatement;
  
  /**
   * A {@link PoolablePreparedStatement} stub since activate and passivate
   * are declared protected and we need to be able to call them within this
   * package.
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: PoolablePreparedStatementStub.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  class PoolablePreparedStatementStub 
      extends PoolablePreparedStatement {
  
      /**
       * Constructor
       * @param stmt my underlying {*link PreparedStatement}
       * @param key my key" as used by {*link KeyedObjectPool}
       * @param pool the {*link KeyedObjectPool} from which I was obtained.
       * @param conn the {*link Connection} from which I was created
       */
      public PoolablePreparedStatementStub(PreparedStatement stmt, Object key, 
          KeyedObjectPool pool, Connection conn) {
          super(stmt, key, pool, conn);
      }
  
      protected void activate() {
          super.activate();
      }
  
      protected void passivate() throws SQLException {
          super.passivate();
      }
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/PooledConnectionImpl.java
  
  Index: PooledConnectionImpl.java
  ===================================================================
  package org.apache.commons.dbcp.cpdsadapter;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  import java.util.Map;
  import java.util.Iterator;
  import java.util.EventObject;
  import java.util.Vector;
  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;
  import javax.sql.ConnectionEvent;
  import javax.sql.ConnectionEventListener;
  import javax.sql.PooledConnection;
  
  import org.apache.commons.dbcp.*;
  import org.apache.commons.pool.KeyedObjectPool;
  import org.apache.commons.pool.KeyedPoolableObjectFactory;
  
  /**
   * Implementation of PooledConnection that is returned by
   * PooledConnectionDataSource.
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: PooledConnectionImpl.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  class PooledConnectionImpl 
      implements PooledConnection, KeyedPoolableObjectFactory
  {
      private static final String CLOSED = 
          "Attempted to use PooledConnection after closed() was called.";
  
      /**
       * The JDBC database connection that represents the physical db connection.
       */
      private Connection connection = null;
  
      /**
       * The JDBC database logical connection.
       */
      private Connection logicalConnection = null;
  
      /**
       * ConnectionEventListeners
       */
      private Vector eventListeners;
  
      /**
       * flag set to true, once close() is called.
       */
      boolean isClosed;
  
      /** My pool of {*link PreparedStatement}s. */
      protected KeyedObjectPool pstmtPool = null;
  
  
      /**
       * Wrap the real connection.
       */
      PooledConnectionImpl(Connection connection, KeyedObjectPool pool)
      {
          this.connection = connection;
          eventListeners = new Vector();
          isClosed = false;
          if (pool != null) 
          {
              pstmtPool = pool;
              pstmtPool.setFactory(this);            
          }
      }
  
      /**
       * Add an event listener.
       */
      public void addConnectionEventListener(ConnectionEventListener listener)
      {
          if ( !eventListeners.contains(listener) )
          {
              eventListeners.add(listener);
          }
      }
  
      /**
       * Closes the physical connection and marks this 
       * <code>PooledConnection</code> so that it may not be used 
       * to generate any more logical <code>Connection</code>s.
       *
       * @exception SQLException if an error occurs
       */
      public void close()
          throws SQLException
      {        
          isClosed = true;
          if (pstmtPool != null) 
          {
              try
              {
                  try
                  {
                      pstmtPool.close();
                  }
                  finally
                  {
                      pstmtPool = null;
                      connection.close();
                  }
              }
              catch (Exception e)
              {
                  if (e instanceof RuntimeException) 
                  {
                      throw (RuntimeException)e;
                  }
                  else 
                  {
                      throw new SQLException(e.getMessage());
                  }
              }
          }
      }
  
      /**
       * Throws an SQLException, if isClosed() is true
       */
      private void assertOpen()
          throws SQLException 
      {
          if ( isClosed ) 
          {
              throw new SQLException(CLOSED);
          }
      }
  
      /**
       * Returns a JDBC connection.
       *
       * @return The database connection.
       */
      public Connection getConnection()
          throws SQLException
      {
          assertOpen();
          // make sure the last connection is marked as closed
          if ( logicalConnection != null && !logicalConnection.isClosed() ) 
          {
              // should notify pool of error so the pooled connection can
              // be removed !FIXME!
              throw new SQLException("PooledConnection was reused, without" +
                                     "its previous Connection being closed.");
          }
  
          // the spec requires that this return a new Connection instance.
          logicalConnection = new ConnectionImpl(this, connection);
          return logicalConnection;
      }
  
      /**
       * Remove an event listener.
       */
      public void removeConnectionEventListener(ConnectionEventListener listener)
      {
          eventListeners.remove(listener);
      }
  
      /**
       * Closes the physical connection and checks that the logical connection
       * was closed as well.
       */
      protected void finalize()
          throws Throwable
      {
          // Closing the Connection ensures that if anyone tries to use it,
          // an error will occur.
          try
          {
              connection.close();
          }
          catch (Exception ignored)
          {
          }
  
          // make sure the last connection is marked as closed
          if ( logicalConnection != null && !logicalConnection.isClosed() ) 
          {
              throw new SQLException("PooledConnection was gc'ed, without" +
                                     "its last Connection being closed.");
          }        
      }
  
      /**
       * sends a connectionClosed event.
       */
      void notifyListeners()
      {
          ConnectionEvent event = new ConnectionEvent(this);
          Iterator i = eventListeners.iterator();
          while ( i.hasNext() ) 
          {
              ((ConnectionEventListener)i.next()).connectionClosed(event);
          }
      }
  
      // -------------------------------------------------------------------
      // The following code implements a PreparedStatement pool
  
      /**
       * Create or obtain a {*link PreparedStatement} from my pool.
       * @return a {*link PoolablePreparedStatement}
       */
      PreparedStatement prepareStatement(String sql) 
          throws SQLException 
      {
          if (pstmtPool == null) 
          {
              return connection.prepareStatement(sql);
          }
          else 
          {
              try {
                  return (PreparedStatement)
                      pstmtPool.borrowObject(createKey(sql));
              } catch(RuntimeException e) {
                  throw e;
              } catch(Exception e) {
                  throw new SQLException(e.toString());
              }
          }
      }
  
      /**
       * Create or obtain a {*link PreparedStatement} from my pool.
       * @return a {*link PoolablePreparedStatement}
       */
      PreparedStatement prepareStatement(String sql, int resultSetType, 
                                         int resultSetConcurrency) 
          throws SQLException 
      {
          if (pstmtPool == null) 
          {
              return connection.prepareStatement(sql);
          }
          else 
          {
              try {
                  return (PreparedStatement) pstmtPool.borrowObject(
                      createKey(sql,resultSetType,resultSetConcurrency));
              } catch(RuntimeException e) {
                  throw e;
              } catch(Exception e) {
                  throw new SQLException(e.toString());
              }
          }
      }
  
      /**
       * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
       */
      protected Object createKey(String sql, int resultSetType, 
                                 int resultSetConcurrency) {
          return new PStmtKey(normalizeSQL(sql), resultSetType,
                              resultSetConcurrency);
      }
  
      /**
       * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments.
       */
      protected Object createKey(String sql) {
          return new PStmtKey(normalizeSQL(sql));
      }
  
      /**
       * Normalize the given SQL statement, producing a
       * cannonical form that is semantically equivalent to the original.
       */
      protected String normalizeSQL(String sql) {
          return sql.trim();
      }
  
      /**
       * My {*link KeyedPoolableObjectFactory} method for creating
       * {*link PreparedStatement}s.
       * @param obj the key for the {*link PreparedStatement} to be created
       */
      public Object makeObject(Object obj) {
          try {
              if(null == obj || !(obj instanceof PStmtKey)) {
                  throw new IllegalArgumentException();
              } else {
                  // _openPstmts++;
                  PStmtKey key = (PStmtKey)obj;
                  if(null == key._resultSetType && null == 
                     key._resultSetConcurrency) 
                  {
                      return new PoolablePreparedStatementStub(
                          connection.prepareStatement(key._sql),
                          key, pstmtPool, connection);
                  } else {
                      return new PoolablePreparedStatementStub(
                          connection.prepareStatement(key._sql,
                          key._resultSetType.intValue(),
                          key._resultSetConcurrency.intValue()),
                          key, pstmtPool, connection);
                  }
              }
          } catch(Exception e) {
              throw new RuntimeException(e.toString());
          }
      }
  
      /**
       * My {*link KeyedPoolableObjectFactory} method for destroying
       * {*link PreparedStatement}s.
       * @param key ignored
       * @param obj the {*link PreparedStatement} to be destroyed.
       */
      public void destroyObject(Object key, Object obj) {
          //_openPstmts--;
          try {
              ((DelegatingPreparedStatement)obj).getInnermostDelegate().close();
          } catch(SQLException e) {
              // ignored
          } catch(NullPointerException e) {
              // ignored
          } catch(ClassCastException e) {
              try {
                  ((PreparedStatement)obj).close();
              } catch(SQLException e2) {
                  // ignored
              } catch(ClassCastException e2) {
                  // ignored
              }
          }
      }
  
      /**
       * My {*link KeyedPoolableObjectFactory} method for validating
       * {*link PreparedStatement}s.
       * @param key ignored
       * @param obj ignored
       * @return <tt>true</tt>
       */
      public boolean validateObject(Object key, Object obj) {
          return true;
      }
  
      /**
       * My {*link KeyedPoolableObjectFactory} method for activating
       * {*link PreparedStatement}s.
       * @param key ignored
       * @param obj ignored
       */
      public void activateObject(Object key, Object obj) {
          ((PoolablePreparedStatementStub)obj).activate();
      }
  
      /**
       * My {*link KeyedPoolableObjectFactory} method for passivating
       * {*link PreparedStatement}s.  Currently invokes {*link PreparedStatement#clearParameters}.
       * @param key ignored
       * @param obj a {*link PreparedStatement}
       */
      public void passivateObject(Object key, Object obj) {
          try {
              ((PreparedStatement)obj).clearParameters();
              ((PoolablePreparedStatementStub)obj).passivate();
          } catch(SQLException e) {
              // ignored
          } catch(NullPointerException e) {
              // ignored
          } catch(ClassCastException e) {
              // ignored
          }
      }
  
      /**
       * A key uniquely identifying {*link PreparedStatement}s.
       */
      class PStmtKey {
          protected String _sql = null;
          protected Integer _resultSetType = null;
          protected Integer _resultSetConcurrency = null;
  
          PStmtKey(String sql) {
              _sql = sql;
          }
  
          PStmtKey(String sql, int resultSetType, int resultSetConcurrency) {
              _sql = sql;
              _resultSetType = new Integer(resultSetType);
              _resultSetConcurrency = new Integer(resultSetConcurrency);
          }
  
          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))
                        );
              } catch(ClassCastException e) {
                  return false;
              } catch(NullPointerException e) {
                  return false;
              }
          }
  
          public int hashCode() {
              return(null == _sql ? 0 : _sql.hashCode());
          }
  
          public String toString() {
              StringBuffer buf = new StringBuffer();
              buf.append("PStmtKey: sql=");
              buf.append(_sql);
              buf.append(", resultSetType=");
              buf.append(_resultSetType);
              buf.append(", resultSetConcurrency=");
              buf.append(_resultSetConcurrency);
              return buf.toString();
          }
      }
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/cpdsadapter/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <body>
  
  <p>
  This package contains one public class which is a 
  <code>ConnectionPoolDataSource</code> (CPDS) implementation that can be used to
  adapt older <code>Driver</code> based jdbc implementations. Below is an
  example of setting up the CPDS to be available via JNDI in the
  catalina servlet container. 
  
  <p>In server.xml, the following would be added to the &lt;Context&gt; for your
  webapp:
  </p>
  
  <source><![CDATA[
  
   <Resource name="jdbc/bookstoreCPDS" auth="Container"
              type="org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS"/>
    <ResourceParams name="jdbc/bookstoreCPDS">
      <parameter>
        <name>factory</name>
        <value>org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS</value>
      </parameter>
          <parameter><name>user</name><value>root</value></parameter>
          <parameter><name>password</name><value></value></parameter>
          <parameter>
              <name>driver</name>
              <value>org.gjt.mm.mysql.Driver</value></parameter>
          <parameter>
               <name>url</name>
               <value>jdbc:mysql://localhost:3306/bookstore</value>
          </parameter>
  
    </ResourceParams>
  
  ]]></source>
  
  <p>
  In web.xml.  Note that elements must be given in the order of the dtd 
  described in the servlet specification:
  </p>
  
  <source><![CDATA[
  <resource-ref>
    <description>
      Resource reference to a factory for java.sql.Connection
      instances that may be used for talking to a particular
      database that is configured in the server.xml file.
    </description>
    <res-ref-name>
      jdbc/bookstoreCPDS
    </res-ref-name>
    <res-type>
      org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS
    </res-type>
    <res-auth>
      Container
    </res-auth>
  </resource-ref>
  ]]></source>
  
  <p>
  Catalina deploys all objects configured similarly to above within the
  <strong>java:comp/env</strong> namespace.
  </p>
  
  </body>
  </html>
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/CPDSConnectionFactory.java
  
  Index: CPDSConnectionFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/CPDSConnectionFactory.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   * $Revision: 1.1 $
   * $Date: 2002/08/05 06:42:01 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.dbcp.jdbc2pool;
  
  import java.util.Map;
  import java.util.HashMap;
  import java.sql.*;
  import javax.sql.ConnectionPoolDataSource;
  import javax.sql.PooledConnection;
  import javax.sql.ConnectionEvent;
  import javax.sql.ConnectionEventListener;
  
  import org.apache.commons.pool.*;
  import org.apache.commons.dbcp.*;
  
  /**
   * A {*link PoolableObjectFactory} that creates
   * {*link PoolableConnection}s.
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: CPDSConnectionFactory.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  class CPDSConnectionFactory 
      implements PoolableObjectFactory, ConnectionEventListener {
      /**
       * Create a new <tt>PoolableConnectionFactory</tt>.
       * @param cpds the ConnectionPoolDataSource from which to obtain PooledConnection's
       * @param pool the {*link ObjectPool} in which to pool those {*link Connection}s
       * @param validationQuery a query to use to {*link #validateObject validate} {*link Connection}s.  Should return at least one row. May be <tt>null</tt>
       * @param username 
       * @param password
       */
      public CPDSConnectionFactory(ConnectionPoolDataSource cpds, 
                                   ObjectPool pool, 
                                   String validationQuery,
                                   String username,
                                   String password) {
          _cpds = cpds;
          _pool = pool;
          _pool.setFactory(this);
          _validationQuery = validationQuery;
          _username = username;
          _password = password;
      }
  
      /**
       * Sets the {*link ConnectionFactory} from which to obtain base {*link Connection}s.
       * @param connFactory the {*link ConnectionFactory} from which to obtain base {*link Connection}s
       */
      synchronized public void setCPDS(ConnectionPoolDataSource cpds) {
          _cpds = cpds;
      }
  
      /**
       * Sets the query I use to {*link #validateObject validate} {*link Connection}s.
       * Should return at least one row.
       * May be <tt>null</tt>
       * @param validationQuery a query to use to {*link #validateObject validate} {*link Connection}s.
       */
      synchronized public void setValidationQuery(String validationQuery) {
          _validationQuery = validationQuery;
      }
  
      /**
       * Sets the {*link ObjectPool} in which to pool {*link Connection}s.
       * @param pool the {*link ObjectPool} in which to pool those {*link Connection}s
       */
      synchronized public void setPool(ObjectPool pool) 
          throws SQLException
      {
          if(null != _pool && pool != _pool) {
              try
              {
                  _pool.close();
              }
              catch (Exception e)
              {
                  if (e instanceof RuntimeException) 
                  {
                      throw (RuntimeException)e;
                  }
                  else 
                  {
                      throw new SQLException(e.getMessage());
                  }
              }
          }
          _pool = pool;
      }
  
      public ObjectPool getPool() {
          return _pool;
      }
  
  
      synchronized public Object makeObject() {
          PooledConnection pc = null;
          try
          {
              if ( _username == null ) 
              {
                  pc = _cpds.getPooledConnection();
              }
              else 
              {
                  pc = _cpds.getPooledConnection(_username, _password);
              }
              // should we add this object as a listener or the pool.
              // consider the validateObject method in decision
              pc.addConnectionEventListener(this);
          }
          catch (SQLException e)
          {
              throw new RuntimeException(e.getMessage());
          }
          return pc;
      }
  
      public void destroyObject(Object obj) {
          if(obj instanceof PooledConnection) {
              try {
                  ((PooledConnection)obj).close();
              } catch(RuntimeException e) {
                  throw e;
              } catch(SQLException e) {
                  ; // ignored
              }
          }
      }
  
      public boolean validateObject(Object obj) {
          boolean valid = false;
          if(obj instanceof PooledConnection) {
              PooledConnection pconn = (PooledConnection)obj;
              String query = _validationQuery;
              if(null != query) {
                  Connection conn = null;
                  Statement stmt = null;
                  ResultSet rset = null;
                  // logical Connection from the PooledConnection must be closed
                  // before another one can be requested and closing it will 
                  // generate an event. Keep track so we know not to return
                  // the PooledConnection 
                  validatingMap.put(pconn, null);
                  try {
                      conn = pconn.getConnection();
                      stmt = conn.createStatement();
                      rset = stmt.executeQuery(query);
                      if(rset.next()) {
                          valid = true;
                      } else {
                          valid = false;
                      }
                  } catch(Exception e) {
                      valid = false;
                  }
                  finally 
                  {
                      try {
                          rset.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      try {
                          stmt.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      try {
                          conn.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      validatingMap.remove(pconn);
                  }
              } else {
                  valid = true;
              }
          } else {
              valid = false;
          }
          return valid;
      }
  
      public void passivateObject(Object obj) {
      }
  
      public void activateObject(Object obj) {
      }
  
      // ***********************************************************************
      // java.sql.ConnectionEventListener implementation
      // ***********************************************************************
  
      /**
       * This will be called if the Connection returned by the getConnection
       * method came from a PooledConnection, and the user calls the close()
       * method of this connection object. What we need to do here is to
       * release this PooledConnection from our pool...
       */
      public void connectionClosed(ConnectionEvent event) 
      {
          PooledConnection pc = (PooledConnection)event.getSource();
          // if this event occured becase we were validating, ignore it
          // otherwise return the connection to the pool.
          if (!validatingMap.containsKey(pc)) 
          {
              try
              {
                  _pool.returnObject(pc);
              }
              catch (Exception e)
              {
                  destroyObject(pc);
                  System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + 
                                     "NOT BE RETURNED TO THE POOL");
              }
          }
      }
  
      /**
       * If a fatal error occurs, close the underlying physical connection so as
       * not to be returned in the future
       */
      public void connectionErrorOccurred(ConnectionEvent event) 
      {
          PooledConnection pc = (PooledConnection)event.getSource();
          try 
          {
              System.err
                  .println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR");
              //remove this from the listener list because we are no more 
              //interested in errors since we are about to close this connection
              pc.removeConnectionEventListener(this);
          }
          catch (Exception ignore) 
          {
              // ignore
          }
  
          destroyObject(pc);
      }
  
  
      protected ConnectionPoolDataSource _cpds = null;
      protected String _validationQuery = null;
      protected ObjectPool _pool = null;
      protected String _username = null;
      protected String _password = null;
      private Map validatingMap = new HashMap();
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/Jdbc2PoolDataSource.java
  
  Index: Jdbc2PoolDataSource.java
  ===================================================================
  package org.apache.commons.dbcp.jdbc2pool;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
   
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Properties;
  
  import java.util.Hashtable;
  import java.io.PrintWriter;
  import java.io.Serializable;
  import java.io.ByteArrayOutputStream;
  import java.io.ObjectOutputStream;
  import java.io.IOException;
  import java.sql.Connection;
  import java.sql.SQLException;
  import javax.sql.DataSource;
  import javax.sql.ConnectionPoolDataSource;
  import javax.sql.PooledConnection;
  import javax.naming.Name;
  import javax.naming.Context;
  import javax.naming.InitialContext;
  import javax.naming.Referenceable;
  import javax.naming.Reference;
  import javax.naming.RefAddr;
  import javax.naming.BinaryRefAddr;
  import javax.naming.StringRefAddr;
  import javax.naming.NamingException;
  import javax.naming.spi.ObjectFactory;
  
  import org.apache.commons.collections.FastHashMap;
  import org.apache.commons.lang.SerializationUtils;
  import org.apache.commons.pool.ObjectPool;
  import org.apache.commons.pool.impl.GenericObjectPool;
  import org.apache.commons.pool.KeyedObjectPool;
  import org.apache.commons.pool.impl.GenericKeyedObjectPool;
  import org.apache.commons.pool.impl.StackObjectPool;
  
  /**
   * <p>
   * A pooling <code>DataSource</code> appropriate for deployment within
   * J2EE environment.  There are many configuration options.  Multiple users
   * can share a common set of parameters, such as a single maximum number
   * of Connections.  The pool can also support individual pools per user, if the
   * deployment environment can support initialization of mapped properties.
   * So for example, a pool of admin or write-access Connections can be
   * guaranteed a certain number of connections, separate from a maximum
   * set for read-only connections. 
   * </p>
   *
   * <p>
   * A J2EE container will normally provide some method of initializing the
   * <code>DataSource</code> whose attributes are presented
   * as bean getters/setters and then deploying it via JNDI.  It is then
   * available to an application as a source of pooled logical connections to 
   * the database.  The pool needs a source of physical connections.  This
   * source is in the form of a <code>ConnectionPoolDataSource</code> that
   * can be specified via the {@link #setDataSourceName(String)} used to
   * lookup the source via JNDI.
   * </p>
   *
   * <p>
   * Although normally used within a JNDI environment, Jdbc2PoolDataSource
   * can be instantiated and initialized as any bean.  In this case the 
   * <code>ConnectionPoolDataSource</code> will likely be instantiated in
   * a similar manner.  The source can then be attached directly to this
   * pool using the 
   * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method.
   * </p>
   *
   * <p>
   * If this <code>DataSource</code> 
   * is requested via JNDI multiple times, it maintains
   * state between lookups.  Also, multiple instances can be deployed using 
   * different backend <code>ConnectionPoolDataSource</code> sources.  
   * </p>
   *
   * <p>
   * The dbcp package contains an adapter, 
   * {@link org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS},
   * that can be used to allow the
   * use of Jdbc2PoolDataSource with jdbc driver implementations that
   * do not supply a <code>ConnectionPoolDataSource</code>, but still
   * provide a {@link java.sql.Driver} implementation.
   * </p>
   *
   * <p>
   * The <a href="package-summary.html">package documentation</a> contains an 
   * example using catalina and JNDI and it also contains a non-JNDI example. 
   * </p>
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: Jdbc2PoolDataSource.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  public class Jdbc2PoolDataSource
      implements DataSource, Referenceable, Serializable, ObjectFactory
  {
      private static final String GET_CONNECTION_CALLED = 
          "A Connection was already requested from this source, " + 
          "further initialization is not allowed.";
  
      private static Map dsInstanceMap = new HashMap();
  
      private static ObjectPool userPassKeyPool = 
          new StackObjectPool(new UserPassKey.Factory(), 256);
  
      private static ObjectPool poolKeyPool = 
          new StackObjectPool(new PoolKey.Factory(), 256);
  
      private boolean getConnectionCalled = false;
  
      private ConnectionPoolDataSource cpds;
      /** DataSource Name used to find the ConnectionPoolDataSource */
      private String dataSourceName;
      private boolean defaultAutoCommit;
      private int defaultMaxActive = 0;
      private int defaultMaxIdle = 0;
      private int defaultMaxWait = -1;
      private boolean defaultReadOnly;
      /** Description */
      private String description;
      /** Environment that may be used to set up a jndi initial context. */
      private Properties jndiEnvironment;
      /** Login TimeOut in seconds */
      private int loginTimeout;
      /** Log stream */
      private PrintWriter logWriter;
      private Map perUserDefaultAutoCommit;    
      private Map perUserMaxActive;    
      private Map perUserMaxIdle;    
      private Map perUserMaxWait;
      private Map perUserDefaultReadOnly;    
      private boolean _testOnBorrow;
      private boolean _testOnReturn;
      private int _timeBetweenEvictionRunsMillis;
      private int _numTestsPerEvictionRun;
      private int _minEvictableIdleTimeMillis;
      private boolean _testWhileIdle;
      private String validationQuery = null;
      private boolean testPositionSet;
  
      private boolean isNew;
      private Integer instanceKey;
  
      /**
       * Default no-arg constructor for Serialization
       */
      public Jdbc2PoolDataSource() 
      {
          isNew = true;
          defaultAutoCommit = true;
      }
  
      /**
       * Throws an IllegalStateException, if a PooledConnection has already
       * been requested.
       */
      private void assertInitializationAllowed()
          throws IllegalStateException 
      {
          if (getConnectionCalled) 
          {
              throw new IllegalStateException(GET_CONNECTION_CALLED);
          }
      }
  
      // -------------------------------------------------------------------
      // Properties
  
      /**
       * Get the value of connectionPoolDataSource.  This method will return
       * null, if the backing datasource is being accessed via jndi.
       *
       * @return value of connectionPoolDataSource.
       */
      public ConnectionPoolDataSource getConnectionPoolDataSource() 
      {
          return cpds;
      }
      
      /**
       * Set the backend ConnectionPoolDataSource.  This property should not be
       * set if using jndi to access the datasource.
       *
       * @param v  Value to assign to connectionPoolDataSource.
       */
      public void setConnectionPoolDataSource(ConnectionPoolDataSource  v) 
      {
          assertInitializationAllowed();
          if (dataSourceName != null) 
          {
              throw new IllegalStateException(
                  "Cannot set the DataSource, if JNDI is used.");
          }
          this.cpds = v;
          if (isNew) 
          {
              registerInstance();
          }
      }
  
      /**
       * Get the name of the ConnectionPoolDataSource which backs this pool.
       * This name is used to look up the datasource from a jndi service 
       * provider.
       *
       * @return value of dataSourceName.
       */
      public String getDataSourceName() 
      {
          return dataSourceName;
      }
      
      /**
       * Set the name of the ConnectionPoolDataSource which backs this pool.
       * This name is used to look up the datasource from a jndi service 
       * provider.
       *
       * @param v  Value to assign to dataSourceName.
       */
      public void setDataSourceName(String  v) 
      {
          assertInitializationAllowed();
          if (cpds != null) 
          {
              throw new IllegalStateException(
                  "Cannot set the JNDI name for the DataSource, if already " +
                  "set using setConnectionPoolDataSource.");
          }
          this.dataSourceName = v;
          if (isNew) 
          {
              registerInstance();
          }
      }
  
      
      /** 
       * Get the value of defaultAutoCommit, which defines the state of 
       * connections handed out from this pool.  The value can be changed
       * on the Connection using Connection.setAutoCommit(boolean).
       * The default is true.
       *
       * @return value of defaultAutoCommit.
       */
      public boolean isDefaultAutoCommit() 
      {
          return defaultAutoCommit;
      }
      
      /**
       * Set the value of defaultAutoCommit, which defines the state of 
       * connections handed out from this pool.  The value can be changed
       * on the Connection using Connection.setAutoCommit(boolean).
       * The default is true.
       *
       * @param v  Value to assign to defaultAutoCommit.
       */
      public void setDefaultAutoCommit(boolean  v) 
      {
          assertInitializationAllowed();
          this.defaultAutoCommit = v;
      }
  
  
      /**
       * The maximum number of active connections that can be allocated from
       * this pool at the same time, or zero for no limit.
       * This value is used for any username which is not specified
       * in perUserMaxConnections.  The default is 0.
       */
      public int getDefaultMaxActive() {
          return (this.defaultMaxActive);
      }
  
      /**
       * The maximum number of active connections that can be allocated from
       * this pool at the same time, or zero for no limit.
       * This value is used for any username which is not specified
       * in perUserMaxConnections.  The default is 0.
       */
      public void setDefaultMaxActive(int maxActive) {
          assertInitializationAllowed();
          this.defaultMaxActive = maxActive;
      }
  
  
      /**
       * The maximum number of active connections that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       * This value is used for any username which is not specified
       * in perUserMaxIdle.  The default is 0.
       */
      public int getDefaultMaxIdle() {
          return (this.defaultMaxIdle);
      }
  
      /**
       * The maximum number of active connections that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       * This value is used for any username which is not specified
       * in perUserMaxIdle.  The default is 0.
       */
      public void setDefaultMaxIdle(int defaultMaxIdle) {
          assertInitializationAllowed();
          this.defaultMaxIdle = defaultMaxIdle;
      }
  
  
      /**
       * The maximum number of milliseconds that the pool will wait (when there
       * are no available connections) for a connection to be returned before
       * throwing an exception, or -1 to wait indefinitely.  Will fail 
       * immediately if value is 0.
       * This value is used for any username which is not specified
       * in perUserMaxWait.  The default is -1.
       */
      public int getDefaultMaxWait() {
          return (this.defaultMaxWait);
      }
  
      /**
       * The maximum number of milliseconds that the pool will wait (when there
       * are no available connections) for a connection to be returned before
       * throwing an exception, or -1 to wait indefinitely.  Will fail 
       * immediately if value is 0.
       * This value is used for any username which is not specified
       * in perUserMaxWait.  The default is -1.
       */
      public void setDefaultMaxWait(int defaultMaxWait) {
          assertInitializationAllowed();
          this.defaultMaxWait = defaultMaxWait;
      }
  
  
      /**
       * Get the value of defaultReadOnly, which defines the state of 
       * connections handed out from this pool.  The value can be changed
       * on the Connection using Connection.setReadOnly(boolean).
       * The default is false.
       *
       * @return value of defaultReadOnly.
       */
      public boolean isDefaultReadOnly() 
      {
          return defaultReadOnly;
      }
      
      /**
       * Set the value of defaultReadOnly, which defines the state of 
       * connections handed out from this pool.  The value can be changed
       * on the Connection using Connection.setReadOnly(boolean).
       * The default is false.
       *
       * @param v  Value to assign to defaultReadOnly.
       */
      public void setDefaultReadOnly(boolean  v) 
      {
          assertInitializationAllowed();
          this.defaultReadOnly = v;
      }
  
      
      /**
       * Get the description.  This property is defined by jdbc as for use with
       * GUI (or other) tools that might deploy the datasource.  It serves no
       * internal purpose.
       *
       * @return value of description.
       */
      public String getDescription() 
      {
          return description;
      }
      
      /**
       * Set the description.  This property is defined by jdbc as for use with
       * GUI (or other) tools that might deploy the datasource.  It serves no
       * internal purpose.
       * 
       * @param v  Value to assign to description.
       */
      public void setDescription(String  v) 
      {
          this.description = v;
      }
          
  
      /**
       * Get the value of jndiEnvironment which is used when instantiating
       * a jndi InitialContext.  This InitialContext is used to locate the
       * backend ConnectionPoolDataSource.
       *
       * @return value of jndiEnvironment.
       */
      public String getJndiEnvironment(String key) 
      {
          String value = null;
          if (jndiEnvironment != null) 
          {
              value = jndiEnvironment.getProperty(key);
          }
          return value;
      }
      
      /**
       * Set the value of jndiEnvironment which is used when instantiating
       * a jndi InitialContext.  This InitialContext is used to locate the
       * backend ConnectionPoolDataSource.
       *
       * @param v  Value to assign to jndiEnvironment.
       */
      public void setJndiEnvironment(String key, String value) 
      {
          if (jndiEnvironment == null) 
          {
              jndiEnvironment = new Properties();
          }
          jndiEnvironment.setProperty(key, value);
      }
  
      
      /**
       * Get the value of loginTimeout.
       * @return value of loginTimeout.
       */
      public int getLoginTimeout() 
      {
          return loginTimeout;
      }
      
      /**
       * Set the value of loginTimeout.
       * @param v  Value to assign to loginTimeout.
       */
      public void setLoginTimeout(int  v) 
      {
          this.loginTimeout = v;
      }
      
      
      /**
       * Get the value of logWriter.
       * @return value of logWriter.
       */
      public PrintWriter getLogWriter() 
      {
          if (logWriter == null) 
          {
              logWriter = new PrintWriter(System.out);
          }        
          return logWriter;
      }
      
      /**
       * Set the value of logWriter.
       * @param v  Value to assign to logWriter.
       */
      public void setLogWriter(PrintWriter  v) 
      {
          this.logWriter = v;
      }
      
  
      /**
       * The keys are usernames and the value is the --.  Any 
       * username specified here will override the value of defaultAutoCommit.
       */
      public Boolean getPerUserDefaultAutoCommit(String key) 
      {
          Boolean value = null;
          if (perUserDefaultAutoCommit != null) 
          {
              value = (Boolean)perUserDefaultAutoCommit.get(key);
          }
          return value;
      }
      
      /**
       * The keys are usernames and the value is the --.  Any 
       * username specified here will override the value of defaultAutoCommit.
       */
      public void setPerUserDefaultAutoCommit(String username, Boolean value) 
      {
          assertInitializationAllowed();
          if (perUserDefaultAutoCommit == null) 
          {
              perUserDefaultAutoCommit = new HashMap();
          }
          perUserDefaultAutoCommit.put(username, value);
      }
  
      
      /**
       * The maximum number of active connections that can be allocated from
       * this pool at the same time, or zero for no limit.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxActive.
       */
      public Integer getPerUserMaxActive(String username) 
      {
          Integer value = null;
          if (perUserMaxActive != null) 
          {
              value = (Integer)perUserMaxActive.get(username);
          }
          return value;
      }
      
      /**
       * The maximum number of active connections that can be allocated from
       * this pool at the same time, or zero for no limit.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxActive.
       */
      public void setPerUserMaxActive(String username, Integer value) 
      {
          assertInitializationAllowed();
          if (perUserMaxActive == null) 
          {
              perUserMaxActive = new HashMap();
          }
          perUserMaxActive.put(username, value);
      }
  
  
      /**
       * The maximum number of active connections that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxIdle.
       */
      public Integer getPerUserMaxIdle(String username) 
      {
          Integer value = null;
          if (perUserMaxIdle != null) 
          {
              value = (Integer)perUserMaxIdle.get(username);
          }
          return value;
      }
      
      /**
       * The maximum number of active connections that can remain idle in the
       * pool, without extra ones being released, or zero for no limit.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxIdle.
       */
      public void setPerUserMaxIdle(String username, Integer value) 
      {
          assertInitializationAllowed();
          if (perUserMaxIdle == null) 
          {
              perUserMaxIdle = new HashMap();
          }
          perUserMaxIdle.put(username, value);
      }
      
      /**
       * The maximum number of milliseconds that the pool will wait (when there
       * are no available connections) for a connection to be returned before
       * throwing an exception, or -1 to wait indefinitely.  Will fail 
       * immediately if value is 0.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxWait.
       */
      public Integer getPerUserMaxWait(String username) 
      {
          Integer value = null;
          if (perUserMaxWait != null) 
          {
              value = (Integer)perUserMaxWait.get(username);
          }
          return value;
      }
      
      /**
       * The maximum number of milliseconds that the pool will wait (when there
       * are no available connections) for a connection to be returned before
       * throwing an exception, or -1 to wait indefinitely.  Will fail 
       * immediately if value is 0.
       * The keys are usernames and the value is the maximum connections.  Any 
       * username specified here will override the value of defaultMaxWait.
       */
      public void setPerUserMaxWait(String username, Integer value) 
      {
          assertInitializationAllowed();
          if (perUserMaxWait == null) 
          {
              perUserMaxWait = new HashMap();
          }
          perUserMaxWait.put(username, value);
      }
  
  
      /**
       * The keys are usernames and the value is the --.  Any 
       * username specified here will override the value of defaultReadOnly.
       */
      public Boolean getPerUserDefaultReadOnly(String username) 
      {
          Boolean value = null;
          if (perUserDefaultReadOnly != null) 
          {
              value = (Boolean)perUserDefaultReadOnly.get(username);
          }
          return value;
      }
      
      /**
       * The keys are usernames and the value is the --.  Any 
       * username specified here will override the value of defaultReadOnly.
       */
      public void setPerUserDefaultReadOnly(String username, Boolean value) 
      {
          assertInitializationAllowed();
          if (perUserDefaultReadOnly == null) 
          {
              perUserDefaultReadOnly = new HashMap();
          }
          perUserDefaultReadOnly.put(username, value);
      }
  
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * before being returned by the {*link #borrowObject}
       * method.  If the object fails to validate,
       * it will be dropped from the pool, and we will attempt
       * to borrow another.
       *
       * *see #setTestOnBorrow
       */
      public boolean getTestOnBorrow() {
          return _testOnBorrow;
      }
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * before being returned by the {*link #borrowObject}
       * method.  If the object fails to validate,
       * it will be dropped from the pool, and we will attempt
       * to borrow another.
       *
       * *see #getTestOnBorrow
       */
      public void setTestOnBorrow(boolean testOnBorrow) {
          assertInitializationAllowed();
          _testOnBorrow = testOnBorrow;
          testPositionSet = true;
      }
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * before being returned to the pool within the
       * {*link #returnObject}.
       *
       * *see #setTestOnReturn
       */
      public boolean getTestOnReturn() {
          return _testOnReturn;
      }
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * before being returned to the pool within the
       * {*link #returnObject}.
       *
       * *see #getTestOnReturn
       */
      public void setTestOnReturn(boolean testOnReturn) {
          assertInitializationAllowed();
          _testOnReturn = testOnReturn;
          testPositionSet = true;
      }
  
      /**
       * Returns the number of milliseconds to sleep between runs of the
       * idle object evictor thread.
       * When non-positive, no idle object evictor thread will be
       * run.
       *
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getTimeBetweenEvictionRunsMillis() {
          return _timeBetweenEvictionRunsMillis;
      }
  
      /**
       * Sets the number of milliseconds to sleep between runs of the
       * idle object evictor thread.
       * When non-positive, no idle object evictor thread will be
       * run.
       *
       * *see #getTimeBetweenEvictionRunsMillis
       */
      public void 
          setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
          assertInitializationAllowed();
              _timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
      }
  
      /**
       * Returns the number of objects to examine during each run of the
       * idle object evictor thread (if any).
       *
       * *see #setNumTestsPerEvictionRun
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getNumTestsPerEvictionRun() {
          return _numTestsPerEvictionRun;
      }
  
      /**
       * Sets the number of objects to examine during each run of the
       * idle object evictor thread (if any).
       * <p>
       * When a negative value is supplied, <tt>ceil({*link #numIdle})/abs({*link #getNumTestsPerEvictionRun})</tt>
       * tests will be run.  I.e., when the value is <i>-n</i>, roughly one <i>n</i>th of the
       * idle objects will be tested per run.
       *
       * *see #getNumTestsPerEvictionRun
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
          assertInitializationAllowed();
          _numTestsPerEvictionRun = numTestsPerEvictionRun;
      }
  
      /**
       * Returns the minimum amount of time an object may sit idle in the pool
       * before it is eligable for eviction by the idle object evictor
       * (if any).
       *
       * *see #setMinEvictableIdleTimeMillis
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public int getMinEvictableIdleTimeMillis() {
          return _minEvictableIdleTimeMillis;
      }
  
      /**
       * Sets the minimum amount of time an object may sit idle in the pool
       * before it is eligable for eviction by the idle object evictor
       * (if any).
       * When non-positive, no objects will be evicted from the pool
       * due to idle time alone.
       *
       * *see #getMinEvictableIdleTimeMillis
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public void 
          setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
          assertInitializationAllowed();
          _minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
      }
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * by the idle object evictor (if any).  If an object
       * fails to validate, it will be dropped from the pool.
       *
       * *see #setTestWhileIdle
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public boolean getTestWhileIdle() {
          return _testWhileIdle;
      }
  
      /**
       * When <tt>true</tt>, objects will be
       * {*link PoolableObjectFactory#validateObject validated}
       * by the idle object evictor (if any).  If an object
       * fails to validate, it will be dropped from the pool.
       *
       * *see #getTestWhileIdle
       * *see #setTimeBetweenEvictionRunsMillis
       */
      public void setTestWhileIdle(boolean testWhileIdle) {
          assertInitializationAllowed();
          _testWhileIdle = testWhileIdle;
          testPositionSet = true;
      }
  
  
      /**
       * The SQL query that will be used to validate connections from this pool
       * before returning them to the caller.  If specified, this query
       * <strong>MUST</strong> be an SQL SELECT statement that returns at least
       * one row.
       */
      public String getValidationQuery() {
          return (this.validationQuery);
      }
  
      /**
       * The SQL query that will be used to validate connections from this pool
       * before returning them to the caller.  If specified, this query
       * <strong>MUST</strong> be an SQL SELECT statement that returns at least
       * one row.  Default behavior is to test the connection when it is
       * borrowed.
       */
      public void setValidationQuery(String validationQuery) {
          assertInitializationAllowed();
          this.validationQuery = validationQuery;
          if (!testPositionSet) 
          {
              setTestOnBorrow(true);
          }
      }
  
      // ----------------------------------------------------------------------
      // DataSource implementation 
  
      /**
       * Attempt to establish a database connection.
       */
      public Connection getConnection() 
          throws SQLException
      {
          return getConnection(null, null);
      }
  
      /**
       * Attempt to establish a database connection.
       */
      synchronized public Connection getConnection(String username, String password)
          throws SQLException
      {
          if (isNew) 
          {
              throw new SQLException("Must set the ConnectionPoolDataSource " + 
                  "through setDataSourceName or setConnectionPoolDataSource " + 
                  "before calling getConnection.");
          }
          getConnectionCalled = true;
          Map pools = (Map)dsInstanceMap.get(instanceKey);
          PoolKey key = getPoolKey(username);
          Object pool = pools.get(key);
          if ( pool == null ) 
          {
              try
              {
                  registerPool(username, password);
                  pool = pools.get(key);
              }
              catch (Exception e)
              {
                  e.printStackTrace();
                  throw new SQLException(e.getMessage());
              }
          }
          returnPoolKey(key);
  
          PooledConnection pc = null;
          if (pool instanceof ObjectPool) 
          {
              try
              {
                  pc = (PooledConnection)((ObjectPool)pool).borrowObject();
              }
              catch (Exception e)
              {
                  if (e instanceof RuntimeException) 
                  {
                      throw (RuntimeException)e;
                  }
                  else 
                  {
                      throw new SQLException(e.getMessage());
                  }
              }
          }
          else // assume KeyedObjectPool
          { 
              try
              {
                  UserPassKey upkey = getPCKey(username, password);
                  pc = (PooledConnection)
                      ((KeyedObjectPool)pool).borrowObject(upkey);
                  returnPCKey(upkey);
              }
              catch (Exception e)
              {
                  if (e instanceof RuntimeException) 
                  {
                      throw (RuntimeException)e;
                  }
                  else 
                  {
                      throw new SQLException(e.getMessage());
                  }
              }
          }
          
          boolean defaultAutoCommit = isDefaultAutoCommit();
          if ( username != null ) 
          {
              Boolean userMax = 
                  getPerUserDefaultAutoCommit(username);
              if ( userMax != null ) 
              {
                  defaultAutoCommit = userMax.booleanValue();
              }
          }    
  
          boolean defaultReadOnly = isDefaultReadOnly();
          if ( username != null ) 
          {
              Boolean userMax = 
                  getPerUserDefaultReadOnly(username);
              if ( userMax != null ) 
              {
                  defaultReadOnly = userMax.booleanValue();
              }
          }    
  
          Connection con = pc.getConnection();        
          con.setAutoCommit(defaultAutoCommit);
          con.setReadOnly(defaultReadOnly);
          return con;
      }
  
      private UserPassKey getPCKey(String username, String password)
      {
          UserPassKey upk = null;
          try
          {
              upk = (UserPassKey)userPassKeyPool.borrowObject();
          }
          catch (Exception e)
          {
              getLogWriter().println("[WARN] Jdbc2PoolDataSource::getPCKey"
                  + " could not get key from pool. Created a new instance. "
                  + e.getMessage());
              upk = new UserPassKey();
          }
          upk.init(username, password);
          return upk;
      }
  
      private void returnPCKey(UserPassKey key)
      {
          if (key.isReusable()) 
          {
              try
              {
                  userPassKeyPool.returnObject(key);
              }
              catch (Exception e)
              {
                  getLogWriter().println(
                      "[WARN] Jdbc2PoolDataSource::returnPCKey could not return"
                      + " key to pool. " + e.getMessage());
              }
          }
      }
  
      private PoolKey getPoolKey(String username)
      {
          PoolKey key = null;
          try
          {
              key = (PoolKey)poolKeyPool.borrowObject();
          }
          catch (Exception e)
          {
              getLogWriter().println("[WARN] Jdbc2PoolDataSource::getPoolKey"
                  + " could not get key from pool. Created a new instance. "
                  + e.getMessage());
              key = new PoolKey();
          }
          if ( username != null && 
               (perUserMaxActive == null 
                || !perUserMaxActive.containsKey(username)) ) 
          {
              username = null;
          }
          key.init(getDataSourceName(), username);
          return key;
      }
  
      private void returnPoolKey(PoolKey key)
      {
          try
          {
              poolKeyPool.returnObject(key);
          }
          catch (Exception e)
          {
              getLogWriter().println(
                  "[WARN] Jdbc2PoolDataSource::returnPoolKey could not return"
                  + " key to pool. " + e.getMessage());
          }
      }
  
      synchronized private void registerInstance()
      {
          if (isNew) 
          {
              int max = 0;
              Iterator i = dsInstanceMap.keySet().iterator();
              while (i.hasNext()) 
              {
                  int key = ((Integer)i.next()).intValue();
                  max = Math.max(max, key);
              }
              instanceKey = new Integer(max+1);
              FastHashMap fhm = new FastHashMap();
              fhm.setFast(true);
              dsInstanceMap.put(instanceKey, fhm);
              isNew = false;
          }
      }
  
      synchronized private void registerPool(String username, String password)
           throws javax.naming.NamingException
      {
          Map pools = (Map)dsInstanceMap.get(instanceKey);
          PoolKey key = getPoolKey(username);
          if ( !pools.containsKey(key) ) 
          {
              int maxActive = getDefaultMaxActive();
              int maxIdle = getDefaultMaxIdle();
              int maxWait = getDefaultMaxWait();
  
              // The source of physical db connections
              ConnectionPoolDataSource cpds = this.cpds;
              if ( cpds == null ) 
              {            
                  Context ctx = null;
                  if ( jndiEnvironment == null ) 
                  {
                      ctx = new InitialContext();                
                  }
                  else 
                  {
                      ctx = new InitialContext(jndiEnvironment);
                  }
                  cpds = (ConnectionPoolDataSource)ctx.lookup(dataSourceName);
              }
  
              Object whicheverPool = null;
              if (perUserMaxActive != null 
                  && perUserMaxActive.containsKey(username)) 
              {                
                  Integer userMax = getPerUserMaxActive(username);
                  if ( userMax != null ) 
                  {
                      maxActive = userMax.intValue();
                  }
                  userMax = getPerUserMaxIdle(username);
                  if ( userMax != null ) 
                  {
                      maxIdle = userMax.intValue();
                  }
                  userMax = getPerUserMaxWait(username);
                  if ( userMax != null ) 
                  {
                      maxWait = userMax.intValue();
                  }
  
                  // Create an object pool to contain our PooledConnections
                  GenericObjectPool pool = new GenericObjectPool(null);
                  pool.setMaxActive(maxActive);
                  pool.setMaxIdle(maxIdle);
                  pool.setMaxWait(maxWait);
                  pool.setWhenExhaustedAction(
                      getWhenExhausted(maxActive, maxWait));
                  pool.setTestOnBorrow(getTestOnBorrow());
                  pool.setTestOnReturn(getTestOnReturn());
                  pool.setTimeBetweenEvictionRunsMillis(
                      getTimeBetweenEvictionRunsMillis());
                  pool.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
                  pool.setMinEvictableIdleTimeMillis(
                      getMinEvictableIdleTimeMillis());
                  pool.setTestWhileIdle(getTestWhileIdle());
                  
                  // Set up the factory we will use (passing the pool associates
                  // the factory with the pool, so we do not have to do so
                  // explicitly)
                  new CPDSConnectionFactory(cpds, pool, validationQuery,
                                            username, password);
                  whicheverPool = pool;
              }
              else // use default pool
              {
                  // Create an object pool to contain our PooledConnections
                  GenericKeyedObjectPool pool = new GenericKeyedObjectPool(null);
                  pool.setMaxActive(maxActive);
                  pool.setMaxIdle(maxIdle);
                  pool.setMaxWait(maxWait);
                  pool.setWhenExhaustedAction(
                      getWhenExhausted(maxActive, maxWait));
                  pool.setTestOnBorrow(getTestOnBorrow());
                  pool.setTestOnReturn(getTestOnReturn());
                  pool.setTimeBetweenEvictionRunsMillis(
                      getTimeBetweenEvictionRunsMillis());
                  pool.setNumTestsPerEvictionRun(getNumTestsPerEvictionRun());
                  pool.setMinEvictableIdleTimeMillis(
                      getMinEvictableIdleTimeMillis());
                  pool.setTestWhileIdle(getTestWhileIdle());
                  
                  // Set up the factory we will use (passing the pool associates
                  // the factory with the pool, so we do not have to do so
                  // explicitly)
                  new KeyedCPDSConnectionFactory(cpds, pool, validationQuery);
                  whicheverPool = pool;
              }
              
              // pools is a FastHashMap set to put the pool in a thread-safe way
              pools.put(key, whicheverPool);
          }        
      }
  
      private byte getWhenExhausted(int maxActive, int maxWait)
      {
          byte whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
          if (maxActive <= 0) 
          {
              whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_GROW;
          }
          else if (maxWait == 0) 
          {
              whenExhausted = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
          }
          return whenExhausted;
      }    
  
      // ----------------------------------------------------------------------
      // Referenceable implementation 
  
      /**
       * <CODE>Referenceable</CODE> implementation prepares object for
       * binding in jndi.
       */
      public Reference getReference() 
          throws NamingException
      {
          // this class implements its own factory
          String factory = getClass().getName();
          Reference ref = new Reference(getClass().getName(), factory, null);
  
          ref.add(new StringRefAddr("isNew", 
                                    String.valueOf(isNew)));
          ref.add(new StringRefAddr("instanceKey", 
              (instanceKey == null ? null : instanceKey.toString()) ));
          ref.add(new StringRefAddr("dataSourceName", getDataSourceName()));
          ref.add(new StringRefAddr("defaultAutoCommit", 
                                    String.valueOf(isDefaultAutoCommit())));
          ref.add(new StringRefAddr("defaultMaxActive", 
                                    String.valueOf(getDefaultMaxActive())));
          ref.add(new StringRefAddr("defaultMaxIdle", 
                                    String.valueOf(getDefaultMaxIdle())));
          ref.add(new StringRefAddr("defaultMaxWait", 
                                    String.valueOf(getDefaultMaxWait())));
          ref.add(new StringRefAddr("defaultReadOnly", 
                                    String.valueOf(isDefaultReadOnly())));
          ref.add(new StringRefAddr("description", getDescription()));
  
          byte[] ser = null;
          // BinaryRefAddr does not allow null byte[].
          if ( jndiEnvironment != null ) 
          {
              try
              {
                  ser = serialize(jndiEnvironment);
                  ref.add(new BinaryRefAddr("jndiEnvironment", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the jndiEnvironment properties.");
              }
          }
  
          ref.add(new StringRefAddr("loginTimeout", 
                                    String.valueOf(getLoginTimeout())));
  
          if ( perUserDefaultAutoCommit != null ) 
          {
              try
              {
                  ser = serialize((Serializable)perUserDefaultAutoCommit);
                  ref.add(new BinaryRefAddr("perUserDefaultAutoCommit", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the perUserDefaultAutoCommit properties.");
              }
          }
  
          if ( perUserMaxActive != null ) 
          {
              try
              {
                  ser = serialize((Serializable)perUserMaxActive);
                  ref.add(new BinaryRefAddr("perUserMaxActive", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the perUserMaxActive properties.");
              }
          }
  
          if ( perUserMaxIdle != null ) 
          {
              try
              {
                  ser = serialize((Serializable)perUserMaxIdle);
                  ref.add(new BinaryRefAddr("perUserMaxIdle", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the perUserMaxIdle properties.");
              }
          }
  
          if ( perUserMaxWait != null ) 
          {
              try
              {
                  ser = serialize((Serializable)perUserMaxWait);
                  ref.add(new BinaryRefAddr("perUserMaxWait", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the perUserMaxWait properties.");
              }
          }
  
          if ( perUserDefaultReadOnly != null ) 
          {
              try
              {
                  ser = serialize((Serializable)perUserDefaultReadOnly);
                  ref.add(new BinaryRefAddr("perUserDefaultReadOnly", ser));
              }
              catch (IOException ioe)
              {
                  throw new NamingException("An IOException prevented " + 
                     "serializing the perUserDefaultReadOnly properties.");
              }
          }
  
          ref.add(new StringRefAddr("testOnBorrow", 
                                    String.valueOf(getTestOnBorrow())));
          ref.add(new StringRefAddr("testOnReturn", 
                                    String.valueOf(getTestOnReturn())));
          ref.add(new StringRefAddr("timeBetweenEvictionRunsMillis", 
              String.valueOf(getTimeBetweenEvictionRunsMillis())));
          ref.add(new StringRefAddr("numTestsPerEvictionRun", 
              String.valueOf(getNumTestsPerEvictionRun())));
          ref.add(new StringRefAddr("minEvictableIdleTimeMillis", 
              String.valueOf(getMinEvictableIdleTimeMillis())));
          ref.add(new StringRefAddr("testWhileIdle", 
                                    String.valueOf(getTestWhileIdle())));
          ref.add(new StringRefAddr("validationQuery", getValidationQuery()));
          
          return ref;
      }
  
      /**
       * Converts a object to a byte array for storage/serialization.
       *
       * @param obj The Serializable to convert.
       * @return A byte[] with the converted Serializable.
       * @exception IOException, if conversion to a byte[] fails.
       */
      private static byte[] serialize(Serializable obj)
          throws IOException
      {
          byte[] byteArray = null;
          ByteArrayOutputStream baos = null;
          ObjectOutputStream out = null;
          try
          {
              // These objects are closed in the finally.
              baos = new ByteArrayOutputStream();
              out = new ObjectOutputStream(baos);
  
              out.writeObject(obj);
              byteArray = baos.toByteArray();
          }
          finally
          {
              if (out != null) 
              {
                  out.close();
              }
          }
          return byteArray;
      }
  
  
      // ----------------------------------------------------------------------
      // ObjectFactory implementation 
  
      /**
       * implements ObjectFactory to create an instance of this class
       */ 
      public Object getObjectInstance(Object refObj, Name name, 
                                      Context context, Hashtable env) 
          throws Exception 
      {
          // The spec says to return null if we can't create an instance 
          // of the reference
          Jdbc2PoolDataSource ds = null;
          if (refObj instanceof Reference) 
          {
              Reference ref = (Reference)refObj;
  	
              if (ref.getClassName().equals(getClass().getName())) 
              {   
                  RefAddr ra = ref.get("isNew");
                  if (ra != null && ra.getContent() != null) 
                  {
                      isNew = Boolean.getBoolean(ra.getContent().toString());
                  }
  
                  ra = ref.get("instanceKey");
                  if (ra != null && ra.getContent() != null) 
                  {
                      instanceKey = new Integer(ra.getContent().toString());
                  }
  
                  ra = ref.get("dataSourceName");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDataSourceName(ra.getContent().toString());
                  }
  
                  ra = ref.get("defaultAutoCommit");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDefaultAutoCommit
                          (Boolean.getBoolean(ra.getContent().toString()));
                  }
  
                  ra = ref.get("defaultMaxActive");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDefaultMaxActive(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("defaultMaxIdle");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDefaultMaxIdle(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("defaultMaxWait");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDefaultMaxWait(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("defaultReadOnly");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDefaultReadOnly
                          (Boolean.getBoolean(ra.getContent().toString()));
                  }
  
                  ra = ref.get("description");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setDescription(ra.getContent().toString());
                  }
  
                  ra = ref.get("jndiEnvironment");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      jndiEnvironment = 
                          (Properties)SerializationUtils.deserialize(serialized);
                  }
                  
                  ra = ref.get("loginTimeout");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setLoginTimeout(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("perUserDefaultAutoCommit");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      perUserDefaultAutoCommit = 
                          (Map)SerializationUtils.deserialize(serialized);
                  }
                  
                  ra = ref.get("perUserMaxActive");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      perUserMaxActive = 
                          (Map)SerializationUtils.deserialize(serialized);
                  }
  
                  ra = ref.get("perUserMaxIdle");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      perUserMaxIdle = 
                          (Map)SerializationUtils.deserialize(serialized);
                  }
  
                  ra = ref.get("perUserMaxWait");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      perUserMaxWait = 
                          (Map)SerializationUtils.deserialize(serialized);
                  }
                  
                  ra = ref.get("perUserDefaultReadOnly");
                  if (ra != null  && ra.getContent() != null) 
                  {
                      byte[] serialized = (byte[])ra.getContent();
                      perUserDefaultReadOnly = 
                          (Map)SerializationUtils.deserialize(serialized);
                  }
                  
                  ra = ref.get("testOnBorrow");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setTestOnBorrow
                          (Boolean.getBoolean(ra.getContent().toString()));
                  }
  
                  ra = ref.get("testOnReturn");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setTestOnReturn
                          (Boolean.getBoolean(ra.getContent().toString()));
                  }
  
                  ra = ref.get("timeBetweenEvictionRunsMillis");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setTimeBetweenEvictionRunsMillis(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("numTestsPerEvictionRun");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setNumTestsPerEvictionRun(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("minEvictableIdleTimeMillis");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setMinEvictableIdleTimeMillis(
                          Integer.parseInt(ra.getContent().toString()));
                  }
  
                  ra = ref.get("testWhileIdle");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setTestWhileIdle
                          (Boolean.getBoolean(ra.getContent().toString()));
                  }
                  
                  ra = ref.get("validationQuery");
                  if (ra != null && ra.getContent() != null) 
                  {
                      setValidationQuery(ra.getContent().toString());
                  }
  
                  ds = this;
              }            
          }
          
          return ds;
      }
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/KeyedCPDSConnectionFactory.java
  
  Index: KeyedCPDSConnectionFactory.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/KeyedCPDSConnectionFactory.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   * $Revision: 1.1 $
   * $Date: 2002/08/05 06:42:01 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.dbcp.jdbc2pool;
  
  import java.util.Map;
  import java.util.HashMap;
  import java.util.WeakHashMap;
  import java.sql.*;
  import javax.sql.ConnectionPoolDataSource;
  import javax.sql.PooledConnection;
  import javax.sql.ConnectionEvent;
  import javax.sql.ConnectionEventListener;
  
  import org.apache.commons.pool.*;
  import org.apache.commons.dbcp.*;
  
  /**
   * A {*link PoolableObjectFactory} that creates
   * {*link PoolableConnection}s.
   *
   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
   * @version $Id: KeyedCPDSConnectionFactory.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  class KeyedCPDSConnectionFactory 
      implements KeyedPoolableObjectFactory, ConnectionEventListener {
      /**
       * Create a new <tt>KeyedPoolableConnectionFactory</tt>.
       * @param cpds the ConnectionPoolDataSource from which to obtain PooledConnection's
       * @param pool the {*link ObjectPool} in which to pool those {*link Connection}s
       * @param validationQuery a query to use to {*link #validateObject validate} {*link Connection}s.  Should return at least one row. May be <tt>null</tt>
       */
      public KeyedCPDSConnectionFactory(ConnectionPoolDataSource cpds, 
                                        KeyedObjectPool pool, 
                                        String validationQuery) {
          _cpds = cpds;
          _pool = pool;
          _pool.setFactory(this);
          _validationQuery = validationQuery;
      }
  
      /**
       * Sets the {*link ConnectionFactory} from which to obtain base {*link Connection}s.
       * @param connFactory the {*link ConnectionFactory} from which to obtain base {*link Connection}s
       */
      synchronized public void setCPDS(ConnectionPoolDataSource cpds) {
          _cpds = cpds;
      }
  
      /**
       * Sets the query I use to {*link #validateObject validate} {*link Connection}s.
       * Should return at least one row.
       * May be <tt>null</tt>
       * @param validationQuery a query to use to {*link #validateObject validate} {*link Connection}s.
       */
      synchronized public void setValidationQuery(String validationQuery) {
          _validationQuery = validationQuery;
      }
  
      /**
       * Sets the {*link ObjectPool} in which to pool {*link Connection}s.
       * @param pool the {*link ObjectPool} in which to pool those {*link Connection}s
       */
      synchronized public void setPool(KeyedObjectPool pool) 
          throws SQLException
      {
          if(null != _pool && pool != _pool) {
              try
              {
                  _pool.close();
              }
              catch (Exception e)
              {
                  if (e instanceof RuntimeException) 
                  {
                      throw (RuntimeException)e;
                  }
                  else 
                  {
                      throw new SQLException(e.getMessage());
                  }
              }
          }
          _pool = pool;
      }
  
      public KeyedObjectPool getPool() {
          return _pool;
      }
  
      synchronized public Object makeObject(Object key) {
          UserPassKey upkey = (UserPassKey)key;
          // since we are using the key to make a new object, we will
          // declare the key invalid for reuse.
          upkey.setReusable(false);
          String username = upkey.getUsername();
          PooledConnection pc = null;
          try
          {
              if ( username == null ) 
              {
                  pc = _cpds.getPooledConnection();
              }
              else 
              {
                  pc = _cpds.getPooledConnection(username, upkey.getPassword());
              }
              // should we add this object as a listener or the pool.
              // consider the validateObject method in decision
              pc.addConnectionEventListener(this);
              pcKeyMap.put(pc, key);
          }
          catch (SQLException e)
          {
              throw new RuntimeException(e.getMessage());
          }
          return pc;
      }
  
      public void destroyObject(Object key, Object obj) {
          if(obj instanceof PooledConnection) {
              try {
                  ((PooledConnection)obj).close();
              } catch(RuntimeException e) {
                  throw e;
              } catch(SQLException e) {
                  ; // ignored
              }
          }
      }
  
      public boolean validateObject(Object key, Object obj) {
          boolean valid = false;
          if(obj instanceof PooledConnection) {
              PooledConnection pconn = (PooledConnection)obj;
              String query = _validationQuery;
              if(null != query) {
                  Connection conn = null;
                  Statement stmt = null;
                  ResultSet rset = null;
                  // logical Connection from the PooledConnection must be closed
                  // before another one can be requested and closing it will 
                  // generate an event. Keep track so we know not to return
                  // the PooledConnection 
                  validatingMap.put(pconn, null);
                  try {
                      conn = pconn.getConnection();
                      stmt = conn.createStatement();
                      rset = stmt.executeQuery(query);
                      if(rset.next()) {
                          valid = true;
                      } else {
                          valid = false;
                      }
                  } catch(Exception e) {
                      valid = false;
                  }
                  finally 
                  {
                      try {
                          rset.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      try {
                          stmt.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      try {
                          conn.close();
                      } catch(Throwable t) {
                          // ignore
                      }
                      validatingMap.remove(pconn);
                  }
              } else {
                  valid = true;
              }
          } else {
              valid = false;
          }
          return valid;
      }
  
      public void passivateObject(Object key, Object obj) {
      }
  
      public void activateObject(Object key, Object obj) {
      }
  
      // ***********************************************************************
      // java.sql.ConnectionEventListener implementation
      // ***********************************************************************
  
      /**
       * This will be called if the Connection returned by the getConnection
       * method came from a PooledConnection, and the user calls the close()
       * method of this connection object. What we need to do here is to
       * release this PooledConnection from our pool...
       */
      public void connectionClosed(ConnectionEvent event) 
      {
          PooledConnection pc = (PooledConnection)event.getSource();
          // if this event occured becase we were validating, ignore it
          // otherwise return the connection to the pool.
          if (!validatingMap.containsKey(pc)) 
          {
              Object key = pcKeyMap.get(pc);
              if (key == null) 
              {
                  throw new IllegalStateException(NO_KEY_MESSAGE);
              }            
              try
              {
                  _pool.returnObject(key, pc);
              }
              catch (Exception e)
              {
                  destroyObject(key, pc);
                  System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + 
                                     "NOT BE RETURNED TO THE POOL");
              }
          }
      }
  
      /**
       * If a fatal error occurs, close the underlying physical connection so as
       * not to be returned in the future
       */
      public void connectionErrorOccurred(ConnectionEvent event) 
      {
          PooledConnection pc = (PooledConnection)event.getSource();
          try 
          {
              System.err
                  .println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR");
              //remove this from the listener list because we are no more 
              //interested in errors since we are about to close this connection
              pc.removeConnectionEventListener(this);
          }
          catch (Exception ignore) 
          {
              // ignore
          }
  
          Object key = pcKeyMap.get(pc);
          if (key == null) 
          {
              throw new IllegalStateException(NO_KEY_MESSAGE);
          }            
          destroyObject(key, pc);
      }
  
      private static final String NO_KEY_MESSAGE = 
          "close() was called on a Connection, but " + 
          "I have no record of the underlying PooledConnection.";
  
      protected ConnectionPoolDataSource _cpds = null;
      protected String _validationQuery = null;
      protected KeyedObjectPool _pool = null;
      private Map validatingMap = new HashMap();
      private WeakHashMap pcKeyMap = new WeakHashMap();
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/PoolKey.java
  
  Index: PoolKey.java
  ===================================================================
  package org.apache.commons.dbcp.jdbc2pool;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
   
  import java.io.Serializable;
  import org.apache.commons.lang.ObjectUtils;
  import org.apache.commons.pool.BasePoolableObjectFactory;
  
  class PoolKey
      implements Serializable
  {
      private String datasourceName;
      private boolean reusable;
      private String username;
      
      PoolKey()
      {
      }
          
      /* *
       * Get the value of datasourceName.
       * @return value of datasourceName.
       * /
      public String getDatasourceName() 
      {
          return datasourceName;
      }
      
      /* *
       * Set the value of datasourceName.
       * @param v  Value to assign to datasourceName.
       * /
      public void setDatasourceName(String  v) 
      {
          this.datasourceName = v;
      }
      
      /* *
       * Get the value of username.
       * @return value of username.
       * /
      public String getUsername() 
      {
          return username;
      }
      
      /* *
       * Set the value of username.
       * @param v  Value to assign to username.
       * /
      public void setUsername(String  v) 
      {
          this.username = v;
      }
      */    
  
      /**
       * Initialize key for method with no arguments.
       *
       * @param instanceOrClass the Object on which the method is invoked.  if 
       * the method is static, a String representing the class name is used.
       * @param method the method name
       */
      void init(String username, String datasourceName) 
      {
          this.datasourceName = datasourceName;
          this.username = username;
      }
  
      public boolean equals(Object obj)
      {
          boolean equal = false;
          if ( obj instanceof PoolKey ) 
          {
              PoolKey pk = (PoolKey)obj;
              equal = ObjectUtils.equals(pk.datasourceName, datasourceName);
              equal &= ObjectUtils.equals(pk.username, username);
          }            
  
          return equal;
      }
  
      public int hashCode()
      {
          int h = 0;
          if (datasourceName != null) 
          {
              h += datasourceName.hashCode();
          }
          if (username != null) 
          {
              h += username.hashCode();
          }
          return h;
      }
  
      public String toString()
      {
          StringBuffer sb = new StringBuffer(50);
          sb.append("PoolKey(");
          sb.append(username).append(", ").append(datasourceName);
          sb.append(')');
          return sb.toString();
      }
  
      // ************* PoolableObjectFactory implementation *******************
  
      static class Factory 
          extends BasePoolableObjectFactory
      {
          /**
           * Creates an instance that can be returned by the pool.
           * @return an instance that can be returned by the pool.
           */
          public Object makeObject() 
              throws Exception
          {
              return new PoolKey();
          }
          
          /**
           * Uninitialize an instance to be returned to the pool.
           * @param obj the instance to be passivated
           */
          public void passivateObject(Object obj) 
              throws Exception
          {
              PoolKey key = (PoolKey)obj;
              key.datasourceName = null;
              key.username = null;
          }
      }
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/UserPassKey.java
  
  Index: UserPassKey.java
  ===================================================================
  package org.apache.commons.dbcp.jdbc2pool;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and 
   *    "Apache Turbine" must not be used to endorse or promote products 
   *    derived from this software without prior written permission. For 
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache Turbine", nor may "Apache" appear in their name, without 
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
   
  import java.io.Serializable;
  import org.apache.commons.lang.ObjectUtils;
  import org.apache.commons.pool.BasePoolableObjectFactory;
  
  class UserPassKey
      implements Serializable
  {
      private String password;
      private boolean reusable;
      private String username;
      
      UserPassKey()
      {
      }
          
      /**
       * Get the value of password.
       * @return value of password.
       */
      public String getPassword() 
      {
          return password;
      }
      
      /**
       * Set the value of password.
       * @param v  Value to assign to password.
       */
      public void setPassword(String  v) 
      {
          this.password = v;
      }
      
      
      /**
       * Get the value of reusable.
       * @return value of reusable.
       */
      public boolean isReusable() 
      {
          return reusable;
      }
      
      /**
       * Set the value of reusable.
       * @param v  Value to assign to reusable.
       */
      public void setReusable(boolean  v) 
      {
          this.reusable = v;
      }
  
      
      /**
       * Get the value of username.
       * @return value of username.
       */
      public String getUsername() 
      {
          return username;
      }
      
      /**
       * Set the value of username.
       * @param v  Value to assign to username.
       */
      public void setUsername(String  v) 
      {
          this.username = v;
      }
      
      /**
       * Initialize key for method with no arguments.
       *
       * @param instanceOrClass the Object on which the method is invoked.  if 
       * the method is static, a String representing the class name is used.
       * @param method the method name
       */
      void init(String username, String password) 
      {
          this.reusable = true;
          this.username = username;
          this.password = password;
      }
  
      public boolean equals(Object obj)
      {
          boolean equal = false;
          if ( obj instanceof UserPassKey ) 
          {
              UserPassKey upk = (UserPassKey)obj;
              equal = ObjectUtils.equals(upk.username, username);
          }            
  
          return equal;
      }
  
      public int hashCode()
      {
          int h = 0;
          if (username != null) 
          {
              h = username.hashCode();
          }
          return h;
      }
  
      public String toString()
      {
          StringBuffer sb = new StringBuffer(50);
          sb.append("UserPassKey(");
          sb.append(username).append(", ").append(password)
              .append(", ").append(reusable);
          sb.append(')');
          return sb.toString();
      }
  
      // ************* PoolableObjectFactory implementation *******************
  
      static class Factory 
          extends BasePoolableObjectFactory
      {
          /**
           * Creates an instance that can be returned by the pool.
           * @return an instance that can be returned by the pool.
           */
          public Object makeObject() 
              throws Exception
          {
              return new UserPassKey();
          }
          
          /**
           * Uninitialize an instance to be returned to the pool.
           * @param obj the instance to be passivated
           */
          public void passivateObject(Object obj) 
              throws Exception
          {
              UserPassKey key = (UserPassKey)obj;
              key.username = null;
              key.password = null;
          }
      }
  }
  
  
  
  1.1                  jakarta-commons/dbcp/src/java/org/apache/commons/dbcp/jdbc2pool/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <body>
  <p>
  This package contains one public class which is a <code>DataSource</code>
  implementation which provides a database connection pool.  Below are a couple
  of usage examples.  One shows deployment into a JNDI system.  The other
  is a simple example initializing the pool using common java code.  
  </p>
  
  <h2>JNDI</h2>
  
  <p>
  Most
  J2EE containers will provide some way of deploying resources into JNDI.  The
  method will vary among containers, but once the resource is available via
  JNDI, the application can access the resource in a container independent
  manner.  The following example shows deployment into catalina.  
  
  <p>In server.xml, the following would be added to the &lt;Context&gt; for your
  webapp:
  </p>
  
  <code><pre>
  
   &lt;Resource name="jdbc/bookstore" auth="Container"
              type="org.apache.commons.dbcp.jdbc2pool.Jdbc2PoolDataSource"/&gt;
    &lt;ResourceParams name="jdbc/bookstore"&gt;
      &lt;parameter&gt;
        &lt;name&gt;factory&lt;/name&gt;
        &lt;value&gt;org.apache.commons.dbcp.jdbc2pool.Jdbc2PoolDataSource&lt;/value&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;name&gt;dataSourceName&lt;/name&gt;&lt;value&gt;java:comp/env/jdbc/bookstoreCPDS&lt;/value&gt;
      &lt;/parameter&gt;
      &lt;parameter&gt;
        &lt;name&gt;defaultMaxActive&lt;/name&gt;&lt;value&gt;30&lt;/value&gt;
      &lt;/parameter&gt;
  
    &lt;/ResourceParams&gt;
  
  </pre></code>
  
  <p>
  In web.xml.  Note that elements must be given in the order of the dtd 
  described in the servlet specification:
  </p>
  
  <code><pre>
  &lt;resource-ref&gt;
    &lt;description&gt;
      Resource reference to a factory for java.sql.Connection
      instances that may be used for talking to a particular
      database that is configured in the server.xml file.
    &lt;/description&gt;
    &lt;res-ref-name&gt;
      jdbc/bookstore
    &lt;/res-ref-name&gt;
    &lt;res-type&gt;
      org.apache.commons.dbcp.jdbc2pool.Jdbc2PoolDataSource
    &lt;/res-type&gt;
    &lt;res-auth&gt;
      Container
    &lt;/res-auth&gt;
  &lt;/resource-ref&gt;
  </pre></code>
  
  
  <p>
  Catalina deploys all objects configured similarly to above within the
  <strong>java:comp/env</strong> namespace.  So the JNDI path given for
  the dataSourceName parameter is valid for a 
  <code>ConnectionPoolDataSource</code> that is deployed as given in the
  <a href"../cpdsadapter/package.html">cpdsadapter example</a>
  </p>
  
  <p>
  The <code>DataSource</code> is now available to the application as shown
  below:
  </p>
  
  <code><pre>
  
      Context ctx = new InitialContext();                
      DataSource ds = (DataSource)
          ctx.lookup("java:comp/env/jdbc/bookstore");
      Connection con = null;
      try
      {
          con = ds.getConnection();
          ... 
          use the connection
          ...
      }
      finally
      {
          if (con != null)
              con.close();
      }
  
  </pre></code>
  
  <p>
  The reference to the <code>DataSource</code> could be maintained, for 
  multiple getConnection() requests.  Or the <code>DataSource</code> can be 
  looked up in different parts of the application code.  Jdbc2PoolDataSource
  will maintain the state of the pool between different lookups.  This
  behavior may be different 
  among in other implementations.  It is in contrast to the behavior of
  {@link org.apache.commons.dbcp.BasicDataSource}, where two lookups of a pool set to a maximum of
  100 connections will lead to two independent sets of connections for a total
  of 200.  With Jdbc2PoolDataSource, you are expected to deploy different
  instances under different names, if you wish to have independent sets of
  connections.
  </p>
  
  <h2>Without JNDI</h2>
  
  <p>
  Connection pooling is useful in applications regardless of whether they run
  in a J2EE environment and Jdbc2PoolDataSource can be used within a simpler
  environment.  The example below shows DriverAdapterCPDS used as the 
  source, though any CPDS is applicable.
  </p>
  
  <code><pre>
  
  public class Pool
  {
      public static DataSource ds;
  
      static
      {
          DriverAdapterCPDS cpds = new DriverAdapterCPDS();
          cpds.setDriver("org.gjt.mm.mysql.Driver");
          cpds.setUrl("jdbc:mysql://localhost:3306/bookstore");
          cpds.setUser("foo");
          cpds.setPassword(null);
  
          Jdbc2PoolDataSource tds = new Jdbc2PoolDataSource();
          tds.setConnectionPoolDataSource(cpds);
          tds.setDefaultMaxActive(10);
          tds.setDefaultMaxWait(50);
  
          ds = tds;
      }
  }
  
  </pre></code>
  
  <p>
  This class can then be used wherever a connection is needed:
  </p>
  
  <code><pre>
      Connection con = null;
      try
      {
          con = Pool.ds.getConnection();
          ... 
          use the connection
          ...
      }
      finally
      {
          if (con != null)
              con.close();
      }
  </pre></code>
  
  </body>
  </html>
  
  
  
  1.2       +5 -3      jakarta-commons/dbcp/src/test/org/apache/commons/dbcp/TestAll.java
  
  Index: TestAll.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/dbcp/src/test/org/apache/commons/dbcp/TestAll.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestAll.java	14 Apr 2001 17:16:19 -0000	1.1
  +++ TestAll.java	5 Aug 2002 06:42:01 -0000	1.2
  @@ -62,6 +62,7 @@
   package org.apache.commons.dbcp;
   
   import junit.framework.*;
  +import org.apache.commons.dbcp.jdbc2pool.TestJdbc2PoolDataSource;
   
   /**
    * @author Rodney Waldhoff
  @@ -75,6 +76,7 @@
       public static Test suite() {
           TestSuite suite = new TestSuite();
           suite.addTest(TestManual.suite());
  +        suite.addTest(TestJdbc2PoolDataSource.suite());
           return suite;
       }
   
  
  
  
  1.1                  jakarta-commons/dbcp/src/test/org/apache/commons/dbcp/jdbc2pool/TestJdbc2PoolDataSource.java
  
  Index: TestJdbc2PoolDataSource.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/dbcp/src/test/org/apache/commons/dbcp/jdbc2pool/TestJdbc2PoolDataSource.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   * $Revision: 1.1 $
   * $Date: 2002/08/05 06:42:01 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  package org.apache.commons.dbcp.jdbc2pool;
  
  import junit.framework.*;
  import java.sql.*;
  import javax.sql.DataSource;
  import org.apache.commons.lang.exception.NestableRuntimeException;
  import org.apache.commons.pool.*;
  import org.apache.commons.pool.impl.*;
  
  import org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS;
  
  /**
   * @author Rodney Waldhoff
   * @version $Id: TestJdbc2PoolDataSource.java,v 1.1 2002/08/05 06:42:01 jmcnally Exp $
   */
  public class TestJdbc2PoolDataSource extends TestCase 
  {
      private static final int MAX_ACTIVE = 10;
      private static final int MAX_WAIT = 50;
  
      DataSource ds;
  
      public TestJdbc2PoolDataSource(String testName) {
          super(testName);
      }
  
      public static Test suite() {
          return new TestSuite(TestJdbc2PoolDataSource.class);
      }
  
      public static void main(String args[]) {
          String[] testCaseName = { TestJdbc2PoolDataSource.class.getName() };
          junit.textui.TestRunner.main(testCaseName);
      }
  
      public void setUp() throws Exception 
      {
          DriverAdapterCPDS pcds = new DriverAdapterCPDS();
          pcds.setDriver("org.apache.commons.dbcp.TesterDriver");
          pcds.setUrl("jdbc:apache:commons:testdriver");
          pcds.setUser("foo");
          pcds.setPassword(null);
  
          Jdbc2PoolDataSource tds = new Jdbc2PoolDataSource();
          tds.setConnectionPoolDataSource(pcds);
          tds.setDefaultMaxActive(MAX_ACTIVE);
          tds.setDefaultMaxWait(MAX_WAIT);
  
          ds = tds;
      }
  
      public void testSimple() throws Exception 
      {
          Connection conn = ds.getConnection();
          assertTrue(null != conn);
          PreparedStatement stmt = conn.prepareStatement("select * from dual");
          assertTrue(null != stmt);
          ResultSet rset = stmt.executeQuery();
          assertTrue(null != rset);
          assertTrue(rset.next());
          rset.close();
          stmt.close();
          conn.close();
      }
  
      public void testSimple2() 
          throws Exception 
      {
          Connection conn = ds.getConnection();
          assertTrue(null != conn);
  
          PreparedStatement stmt = 
              conn.prepareStatement("select * from dual");
          assertTrue(null != stmt);
          ResultSet rset = stmt.executeQuery();
          assertTrue(null != rset);
          assertTrue(rset.next());
          rset.close();
          stmt.close();
          
          stmt = conn.prepareStatement("select * from dual");
          assertTrue(null != stmt);
          rset = stmt.executeQuery();
          assertTrue(null != rset);
          assertTrue(rset.next());
          rset.close();
          stmt.close();
          
          conn.close();
          try 
          {
              conn.createStatement();
              fail("Can't use closed connections");
          } 
          catch(SQLException e) 
          {
              // expected
          }
  
          conn = ds.getConnection();
          assertTrue(null != conn);
  
          stmt = conn.prepareStatement("select * from dual");
          assertTrue(null != stmt);
          rset = stmt.executeQuery();
          assertTrue(null != rset);
          assertTrue(rset.next());
          rset.close();
          stmt.close();
  
          stmt = conn.prepareStatement("select * from dual");
          assertTrue(null != stmt);
          rset = stmt.executeQuery();
          assertTrue(null != rset);
          assertTrue(rset.next());
          rset.close();
          stmt.close();
          
          conn.close();
          conn = null;
      }
  
      public void testOpening() 
          throws Exception 
      {
          Connection[] c = new Connection[MAX_ACTIVE];
          // test that opening new connections is not closing previous
          for (int i=0; i<c.length; i++) 
          {
              c[i] = ds.getConnection();
              assertTrue(c[i] != null);
              for (int j=0; j<=i; j++) 
              {
                  assertTrue(!c[j].isClosed());
              }
          }
  
          for (int i=0; i<c.length; i++) 
          {
              c[i].close();
          }
      }
  
      public void testClosing() 
          throws Exception 
      {
          Connection[] c = new Connection[MAX_ACTIVE];
          // open the maximum connections
          for (int i=0; i<c.length; i++) 
          {
              c[i] = ds.getConnection();
          }
  
          // close one of the connections
          c[0].close();
          assertTrue(c[0].isClosed());
          
          // get a new connection
          c[0] = ds.getConnection();
  
          for (int i=0; i<c.length; i++) 
          {
              c[i].close();
          }
      }
  
      public void testMaxActive() 
          throws Exception 
      {
          Connection[] c = new Connection[MAX_ACTIVE];
          for (int i=0; i<c.length; i++) 
          {
              c[i] = ds.getConnection();
              assertTrue(c[i] != null);            
          }
  
          try
          {
              ds.getConnection();
              fail("Allowed to open more than DefaultMaxActive connections.");
          }
          catch(java.util.NoSuchElementException e)
          {
              // should only be able to open 10 connections, so this test should
              // throw an exception
          }
  
          for (int i=0; i<c.length; i++) 
          {
              c[i].close();
          }
      }
  
      public void testMultipleThreads()
          throws Exception
      {
          assertTrue(multipleThreads(1));
          assertTrue(!multipleThreads(2*MAX_WAIT));
      }
  
      private boolean multipleThreads(int holdTime)
          throws Exception
      {
          long startTime = System.currentTimeMillis();
          final boolean[] success = new boolean[1];
          success[0] = true;
          final PoolTest[] pts = new PoolTest[2*MAX_ACTIVE];
          ThreadGroup threadGroup = new ThreadGroup("foo")
              {
                  public void uncaughtException(Thread t, Throwable e)
                  {
                      /*
                      for (int i = 0; i < pts.length; i++)
                      {
                          System.out.println(i + ": " + pts[i].reportState());
                      }
                      */
                      for (int i = 0; i < pts.length; i++)
                      {
                          pts[i].stop();
                      }
                      
                      //e.printStackTrace();
                      success[0] = false;
                  }
              };
  
          for (int i = 0; i < pts.length; i++)
          {
              pts[i] = new PoolTest(threadGroup, holdTime);
          }
          Thread.currentThread().sleep(10*holdTime);
          for (int i = 0; i < pts.length; i++)
          {
              pts[i].stop();
          }
          long time = System.currentTimeMillis() - startTime; // - (pts.length*10*holdTime);
          System.out.println("Multithread test time = " + time + " ms");
  
          Thread.currentThread().sleep(holdTime);
          return success[0];
      }
  
      private static int currentThreadCount = 0;
  
      private class PoolTest implements Runnable
      {
          /**
           * The number of milliseconds to hold onto a database connection
           */
          private int connHoldTime;
          
          private boolean isRun;
          
          private String state;
  
          protected PoolTest(ThreadGroup threadGroup, int connHoldTime)
          {
              this.connHoldTime = connHoldTime;
              Thread thread = new Thread(threadGroup, this,
                                         "Thread+" + currentThreadCount++);
              thread.setDaemon(false);
              thread.start();
          }
          
          public void run()
          {
              Thread thread = Thread.currentThread();
              isRun = true;
              while (isRun)
              {
                  try
                  {
                      Connection conn = null;
                      state="Getting Connection";
                      conn = ds.getConnection();
                      state="Using Connection";
                      assertTrue(null != conn);
                      PreparedStatement stmt = 
                          conn.prepareStatement("select * from dual");
                      assertTrue(null != stmt);
                      ResultSet rset = stmt.executeQuery();
                      assertTrue(null != rset);
                      assertTrue(rset.next());
                      state="Holding Connection";
                      thread.sleep(connHoldTime);
                      state="Returning Connection";
                      rset.close();
                      stmt.close();
                      conn.close();
                  }
                  catch (Exception e)
                  {
                      throw new NestableRuntimeException(e);
                  }
              }
          }
          
          public void stop()
          {
              isRun = false;
          }
  
          public String reportState()
          {
              return state;
          }
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>