Java/Database SQL JDBC/Connection Pool

Материал из Java эксперт
Перейти к: навигация, поиск

A common intermediate format for a non-XA JDBC pool

   <source lang="java">
 

/**

*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

import java.io.Serializable; import java.util.Properties; /**

* A common intermediate format for a non-XA JDBC pool
*
* @version $Rev: 476049 $ $Date: 2006-11-16 20:35:17 -0800 (Thu, 16 Nov 2006) $
*/

public class JDBCPool extends AbstractDatabasePool {

   private String driverClass;
   private String jdbcURL;
   private String username;
   private String password;
   private Properties connectionProperties = new Properties();
   public String getDriverClass() {
       return driverClass;
   }
   public void setDriverClass(String driverClass) {
       this.driverClass = driverClass;
   }
   public String getJdbcURL() {
       return jdbcURL;
   }
   public void setJdbcURL(String jdbcURL) {
       this.jdbcURL = jdbcURL;
   }
   public String getUsername() {
       return username;
   }
   public void setUsername(String username) {
       this.username = username;
   }
   public String getPassword() {
       return password;
   }
   public void setPassword(String password) {
       this.password = password;
   }
   public Properties getConnectionProperties() {
       return connectionProperties;
   }
   public void setConnectionProperties(Properties connectionProperties) {
       this.connectionProperties = connectionProperties;
   }

} /**

*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

/**

* A common intermediate format for a database connection pool
*
* @version $Rev: 476049 $ $Date: 2006-11-16 20:35:17 -0800 (Thu, 16 Nov 2006) $
*/
abstract class AbstractDatabasePool implements Serializable {
   public final static String VENDOR_ORACLE = "Oracle";
   public final static String VENDOR_MYSQL = "MySQL";
   public final static String VENDOR_SYBASE = "Sybase";
   public final static String VENDOR_INFORMIX = "Informix";
   private String name;
   private String jndiName;
   private Integer minSize;
   private Integer maxSize;
   private Integer blockingTimeoutMillis;
   private Integer idleTimeoutMillis;
   private String newConnectionSQL;
   private String testConnectionSQL;
   private String vendor;
   private Integer statementCacheSize;
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public String getJndiName() {
       return jndiName;
   }
   public void setJndiName(String jndiName) {
       this.jndiName = jndiName;
   }
   public String getNewConnectionSQL() {
       return newConnectionSQL;
   }
   public void setNewConnectionSQL(String newConnectionSQL) {
       this.newConnectionSQL = newConnectionSQL;
   }
   public String getTestConnectionSQL() {
       return testConnectionSQL;
   }
   public void setTestConnectionSQL(String testConnectionSQL) {
       this.testConnectionSQL = testConnectionSQL;
   }
   public String getVendor() {
       return vendor;
   }
   public void setVendor(String vendor) {
       this.vendor = vendor;
   }
   public Integer getMinSize() {
       return minSize;
   }
   public void setMinSize(Integer minSize) {
       this.minSize = minSize;
   }
   public Integer getMaxSize() {
       return maxSize;
   }
   public void setMaxSize(Integer maxSize) {
       this.maxSize = maxSize;
   }
   public Integer getBlockingTimeoutMillis() {
       return blockingTimeoutMillis;
   }
   public void setBlockingTimeoutMillis(Integer blockingTimeoutMillis) {
       this.blockingTimeoutMillis = blockingTimeoutMillis;
   }
   public Integer getIdleTimeoutMillis() {
       return idleTimeoutMillis;
   }
   public void setIdleTimeoutMillis(Integer idleTimeoutMillis) {
       this.idleTimeoutMillis = idleTimeoutMillis;
   }
   public Integer getStatementCacheSize() {
       return statementCacheSize;
   }
   public void setStatementCacheSize(Integer statementCacheSize) {
       this.statementCacheSize = statementCacheSize;
   }

}


 </source>
   
  
 
  



Mini Connection Pool Manager

   <source lang="java">
 

// Test program for the MiniConnectionPoolManager class. import java.io.PrintWriter; import java.lang.Thread; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Random; import javax.sql.ConnectionPoolDataSource; import biz.source_code.miniConnectionPoolManager.MiniConnectionPoolManager; public class TestMiniConnectionPoolManager { private static final int maxConnections = 8; // number of connections private static final int noOfThreads = 50; // number of worker threads private static final int processingTime = 30; // total processing time of the test program in seconds private static final int threadPauseTime1 = 100; // max. thread pause time in microseconds, without a connection private static final int threadPauseTime2 = 100; // max. thread pause time in microseconds, with a connection private static MiniConnectionPoolManager poolMgr; private static WorkerThread[] threads; private static boolean shutdownFlag; private static Object shutdownObj = new Object(); private static Random random = new Random(); private static class WorkerThread extends Thread {

  public int threadNo;
  public void run() {threadMain (threadNo); }};

private static ConnectionPoolDataSource createDataSource() throws Exception {

  // Version for H2:
     org.h2.jdbcx.JdbcDataSource dataSource = new org.h2.jdbcx.JdbcDataSource();
     dataSource.setURL ("jdbc:h2:file:c:/temp/temp_TestMiniConnectionPoolManagerDB;DB_CLOSE_DELAY=-1");
  // Version for Apache Derby:
  /*
     org.apache.derby.jdbc.EmbeddedConnectionPoolDataSource dataSource = new org.apache.derby.jdbc.EmbeddedConnectionPoolDataSource();
     dataSource.setDatabaseName ("c:/temp/temp_TestMiniConnectionPoolManagerDB");
     dataSource.setCreateDatabase ("create");
     dataSource.setLogWriter (new PrintWriter(System.out));
  */
  // Versioo for JTDS:
  /*
     net.sourceforge.jtds.jdbcx.JtdsDataSource dataSource = new net.sourceforge.jtds.jdbcx.JtdsDataSource();
     dataSource.setAppName ("TestMiniConnectionPoolManager");
     dataSource.setDatabaseName ("Northwind");
     dataSource.setServerName ("localhost");
     dataSource.setUser ("sa");
     dataSource.setPassword (System.getProperty("saPassword"));
  */
  // Version for the Microsoft SQL Server driver (sqljdbc.jar):
  /*
     // The sqljdbc 1.1 documentation, chapter "Using Connection Pooling", recommends to use
     // SQLServerXADataSource instead of SQLServerConnectionPoolDataSource, even when no
     // distributed transactions are used.
     com.microsoft.sqlserver.jdbc.SQLServerXADataSource dataSource = new com.microsoft.sqlserver.jdbc.SQLServerXADataSource();
     dataSource.setApplicationName ("TestMiniConnectionPoolManager");
     dataSource.setDatabaseName ("Northwind");
     dataSource.setServerName ("localhost");
     dataSource.setUser ("sa");
     dataSource.setPassword (System.getProperty("saPassword"));
     dataSource.setLogWriter (new PrintWriter(System.out));
  */
  return dataSource; }

public static void main (String[] args) throws Exception {

  System.out.println ("Program started.");
  ConnectionPoolDataSource dataSource = createDataSource();
  poolMgr = new MiniConnectionPoolManager(dataSource,maxConnections);
  initDb();
  startWorkerThreads();
  pause (processingTime*1000000);
  System.out.println ("\nStopping threads.");
  stopWorkerThreads();
  System.out.println ("\nAll threads stopped.");
  poolMgr.dispose();
  System.out.println ("Program completed."); }

private static void startWorkerThreads() {

  threads = new WorkerThread[noOfThreads];
  for (int threadNo=0; threadNo<noOfThreads; threadNo++) {
     WorkerThread thread = new WorkerThread();
     threads[threadNo] = thread;
     thread.threadNo = threadNo;
     thread.start(); }}

private static void stopWorkerThreads() throws Exception {

  setShutdownFlag();
  for (int threadNo=0; threadNo<noOfThreads; threadNo++) {
     threads[threadNo].join(); }}

private static void setShutdownFlag() {

  synchronized (shutdownObj) {
     shutdownFlag = true;
     shutdownObj.notifyAll(); }}

private static void threadMain (int threadNo) {

  try {
     threadMain2 (threadNo); }
   catch (Throwable e) {
     System.out.println ("\nException in thread "+threadNo+": "+e);
     e.printStackTrace (System.out);
     setShutdownFlag(); }}

private static void threadMain2 (int threadNo) throws Exception {

  // System.out.println ("Thread "+threadNo+" started.");
  while (true) {
     if (!pauseRandom(threadPauseTime1)) return;
     threadTask (threadNo); }}

private static void threadTask (int threadNo) throws Exception {

  Connection conn = null;
  try {
     conn = poolMgr.getConnection();
     if (shutdownFlag) return;
     System.out.print (threadNo+" ");
     incrementThreadCounter (conn,threadNo);
     pauseRandom (threadPauseTime2); }
   finally {
     if (conn != null) conn.close(); }}

private static boolean pauseRandom (int maxPauseTime) throws Exception {

  return pause (random.nextInt(maxPauseTime)); }

private static boolean pause (int pauseTime) throws Exception {

  synchronized (shutdownObj) {
     if (shutdownFlag) return false;
     if (pauseTime <= 0) return true;
     int ms = pauseTime / 1000;
     int ns = (pauseTime % 1000) * 1000;
     shutdownObj.wait (ms,ns); }
  return true; }

private static void initDb() throws SQLException {

  Connection conn = null;
  try {
     conn = poolMgr.getConnection();
     System.out.println ("initDb connected");
     initDb2 (conn); }
   finally {
     if (conn != null) conn.close(); }
  System.out.println ("initDb done"); }

private static void initDb2 (Connection conn) throws SQLException {

  execSqlNoErr (conn,"drop table temp");
  execSql (conn,"create table temp (threadNo integer, ctr integer)");
  for (int i=0; i<noOfThreads; i++)
     execSql (conn,"insert into temp values("+i+",0)"); }

private static void incrementThreadCounter (Connection conn, int threadNo) throws SQLException {

  execSql (conn,"update temp set ctr = ctr + 1 where threadNo="+threadNo); }

private static void execSqlNoErr (Connection conn, String sql) {

  try {
     execSql (conn,sql); }
    catch (SQLException e) {}}

private static void execSql (Connection conn, String sql) throws SQLException {

  Statement st = null;
  try {
     st = conn.createStatement();
     st.executeUpdate(sql); }
   finally {
     if (st != null) st.close(); }}

} // end class TestMiniConnectionPoolManager

// Copyright 2007 Christian d"Heureuse, www.source-code.biz // // This module is free software: you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. See http://www.gnu.org/licenses/lgpl.html. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. package biz.source_code.miniConnectionPoolManager; import java.util.concurrent.Semaphore; import java.util.Stack; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.util.concurrent.TimeUnit; import javax.sql.ConnectionPoolDataSource; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; import javax.sql.PooledConnection; /**

  • A simple standalone JDBC connection pool manager.
  • The public methods of this class are thread-safe.
  • <p>
  • Author: Christian d"Heureuse (
  • <p>
  • 2007-06-21: Constructor with a timeout parameter added.
  • /

public class MiniConnectionPoolManager { private ConnectionPoolDataSource dataSource; private int maxConnections; private int timeout; private PrintWriter logWriter; private Semaphore semaphore; private Stack<PooledConnection> recycledConnections; private int activeConnections; private PoolConnectionEventListener poolConnectionEventListener; private boolean isDisposed; /**

  • Thrown in {@link #getConnection()} when no free connection becomes available within timeout seconds.
  • /

public static class TimeoutException extends RuntimeException {

  private static final long serialVersionUID = 1;
  public TimeoutException () {
     super ("Timeout while waiting for a free database connection."); }}

/**

  • Constructs a MiniConnectionPoolManager object with a timeout of 60 seconds.
  • @param dataSource the data source for the connections.
  • @param maxConnections the maximum number of connections.
  • /

public MiniConnectionPoolManager (ConnectionPoolDataSource dataSource, int maxConnections) {

  this (dataSource, maxConnections, 60); }

/**

  • Constructs a MiniConnectionPoolManager object.
  • @param dataSource the data source for the connections.
  • @param maxConnections the maximum number of connections.
  • @param timeout the maximum time in seconds to wait for a free connection.
  • /

public MiniConnectionPoolManager (ConnectionPoolDataSource dataSource, int maxConnections, int timeout) {

  this.dataSource = dataSource;
  this.maxConnections = maxConnections;
  this.timeout = timeout;
  try {
     logWriter = dataSource.getLogWriter(); }
   catch (SQLException e) {}
  if (maxConnections < 1) throw new IllegalArgumentException("Invalid maxConnections value.");
  semaphore = new Semaphore(maxConnections,true);
  recycledConnections = new Stack<PooledConnection>();
  poolConnectionEventListener = new PoolConnectionEventListener(); }

/**

  • Closes all unused pooled connections.
  • /

public synchronized void dispose() throws SQLException {

  if (isDisposed) return;
  isDisposed = true;
  SQLException e = null;
  while (!recycledConnections.isEmpty()) {
     PooledConnection pconn = recycledConnections.pop();
     try {
        pconn.close(); }
      catch (SQLException e2) {
         if (e == null) e = e2; }}
  if (e != null) throw e; }

/**

  • Retrieves a connection from the connection pool.
  • If maxConnections connections are already in use, the method
  • waits until a connection becomes available or timeout seconds elapsed.
  • When the application is finished using the connection, it must close it
  • in order to return it to the pool.
  • @return a new Connection object.
  • @throws TimeoutException when no connection becomes available within timeout seconds.
  • /

public Connection getConnection() throws SQLException {

  // This routine is unsynchronized, because semaphore.tryAcquire() may block.
  synchronized (this) {
     if (isDisposed) throw new IllegalStateException("Connection pool has been disposed."); }
  try {
     if (!semaphore.tryAcquire(timeout,TimeUnit.SECONDS))
        throw new TimeoutException(); }
   catch (InterruptedException e) {
     throw new RuntimeException("Interrupted while waiting for a database connection.",e); }
  boolean ok = false;
  try {
     Connection conn = getConnection2();
     ok = true;
     return conn; }
   finally {
     if (!ok) semaphore.release(); }}

private synchronized Connection getConnection2() throws SQLException {

  if (isDisposed) throw new IllegalStateException("Connection pool has been disposed.");   // test again with lock
  PooledConnection pconn;
  if (!recycledConnections.empty()) {
     pconn = recycledConnections.pop(); }
   else {
     pconn = dataSource.getPooledConnection(); }
  Connection conn = pconn.getConnection();
  activeConnections++;
  pconn.addConnectionEventListener (poolConnectionEventListener);
  assertInnerState();
  return conn; }

private synchronized void recycleConnection (PooledConnection pconn) {

  if (isDisposed) { disposeConnection (pconn); return; }
  if (activeConnections <= 0) throw new AssertionError();
  activeConnections--;
  semaphore.release();
  recycledConnections.push (pconn);
  assertInnerState(); }

private synchronized void disposeConnection (PooledConnection pconn) {

  if (activeConnections <= 0) throw new AssertionError();
  activeConnections--;
  semaphore.release();
  closeConnectionNoEx (pconn);
  assertInnerState(); }

private void closeConnectionNoEx (PooledConnection pconn) {

  try {
     pconn.close(); }
   catch (SQLException e) {
     log ("Error while closing database connection: "+e.toString()); }}

private void log (String msg) {

  String s = "MiniConnectionPoolManager: "+msg;
  try {
     if (logWriter == null)
        System.err.println (s);
      else
        logWriter.println (s); }
   catch (Exception e) {}}

private void assertInnerState() {

  if (activeConnections < 0) throw new AssertionError();
  if (activeConnections+recycledConnections.size() > maxConnections) throw new AssertionError();
  if (activeConnections+semaphore.availablePermits() > maxConnections) throw new AssertionError(); }

private class PoolConnectionEventListener implements ConnectionEventListener {

  public void connectionClosed (ConnectionEvent event) {
     PooledConnection pconn = (PooledConnection)event.getSource();
     pconn.removeConnectionEventListener (this);
     recycleConnection (pconn); }
  public void connectionErrorOccurred (ConnectionEvent event) {
     PooledConnection pconn = (PooledConnection)event.getSource();
     pconn.removeConnectionEventListener (this);
     disposeConnection (pconn); }}

/**

  • Returns the number of active (open) connections of this pool.
  • This is the number of Connection objects that have been
  • issued by {@link #getConnection()} for which Connection.close()
  • has not yet been called.
  • @return the number of active connections.
    • /

public synchronized int getActiveConnections() {

  return activeConnections; }

} // end class MiniConnectionPoolManager


 </source>
   
  
 
  



Pooled Connection Example

   <source lang="java">
 

import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; public class MainClass {

 public static void main(String[] args) {
   Connection connection = null;
   Statement statement = null;
   ResultSet resultSet = null;
   try {
     connection = getConnection();
     // Do work with connection
     statement = connection.createStatement();
     String selectEmployeesSQL = "SELECT * FROM employees";
     resultSet = statement.executeQuery(selectEmployeesSQL);
     while (resultSet.next()) {
       printEmployee(resultSet);
     }
   } catch (Exception e) {
     e.printStackTrace();
   } finally {
     if (resultSet != null) {
       try {
         resultSet.close();
       } catch (SQLException e) {
       } // nothing we can do
     }
     if (statement != null) {
       try {
         statement.close();
       } catch (SQLException e) {
       } // nothing we can do
     }
     if (connection != null) {
       try {
         connection.close();
       } catch (SQLException e) {
       } // nothing we can do
     }
   }
 }
 private static Connection getConnection() throws NamingException, SQLException {
   InitialContext initCtx = createContext();
   String jndiName = "HrDS";
   ConnectionPoolDataSource dataSource = (ConnectionPoolDataSource) initCtx.lookup(jndiName);
   PooledConnection pooledConnection = dataSource.getPooledConnection();
   return pooledConnection.getConnection(); // Obtain connection from pool
 }
 private static InitialContext createContext() throws NamingException {
   Properties env = new Properties();
   env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
   env.put(Context.PROVIDER_URL, "rmi://localhost:1099");
   InitialContext context = new InitialContext(env);
   return context;
 }
 private static void printEmployee(ResultSet resultSet) throws SQLException {
   System.out.print(resultSet.getInt("employee_id")+", ");
   System.out.print(resultSet.getString("last_name")+", ");
   System.out.print(resultSet.getString("first_name")+", ");
   System.out.println(resultSet.getString("email"));
 }

}


 </source>
   
  
 
  



Your own connection pool

   <source lang="java">
 

/*

* Copyright Aduna (http://www.aduna-software.ru/) (c) 1997-2006.
*
* Licensed under the Aduna BSD-style license.
*/

import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; public class ConnectionPool { /*--------------------------------------------------+ | Variables | +--------------------------------------------------*/

 protected List<PoolConnection> _connections;
 protected String _url;
 protected String _user;
 protected String _password;
 /**
  * Indicates whether the ConnectionPool should check the status of
  * connections (closed, has warnings) before they are returned.
  **/
 protected boolean _checkConnections = true;
 protected long _cleaningInterval =  30 * 1000; // 30 seconds
 protected long _maxIdleTime = 30 * 1000; // 30 seconds
 protected long _maxUseTime = -1; // disabled by default
 protected boolean _draining = false;
 protected PoolCleaner _cleaner;

/*--------------------------------------------------+ | Constructors | +--------------------------------------------------*/

 public ConnectionPool(String url, String user, String password) {
   _url = url;
   _user = user;
   _password = password;
   _connections = new ArrayList<PoolConnection>();
 }
 /**
  * Sets the flag that determines whether the the status of connections
  * (closed, has warnings) is checked before they are returned by
  * getConnection(). With some jdbc-drivers, the extra checks can have
  * a large performance penalty. Default value is "true".
  **/
 public void setCheckConnections(boolean checkConnections) {
   _checkConnections = checkConnections;
 }
 /**
  * Sets the interval for the pool cleaner to come into action. The pool
  * cleaner checks the connection pool every so many milliseconds for
  * connections that should be removed. The default interval is 30 seconds.
  * @param cleaningInterval The interval in milliseconds.
  **/
 public void setCleaningInterval(long cleaningInterval) {
   _cleaningInterval = cleaningInterval;
 }
 /**
  * Sets the maximum time that a connection is allowed to be idle. A
  * connection that has been idle for a longer time will be removed
  * by the pool cleaner the next time it check the pool. The default
  * value is 30 seconds.
  *
  * @param maxIdleTime The maximum idle time in milliseconds.
  **/
 public void setMaxIdleTime(long maxIdleTime) {
   _maxIdleTime = maxIdleTime;
 }
 /**
  * Sets the maximum time that a connection is allowed to be used. A
  * connection that has been used for a longer time will be forced to
  * close itself, even if it is still in use. Normally, this time should
  * only be reached in case an program "forgets" to close a connection.
  * The maximum time is switched of by default.
  *
  * @param maxUseTime The maximum time a connection can be used in
  * milliseconds, or a negative value if there is no maximum.
  **/
 public void setMaxUseTime(long maxUseTime) {
   _maxUseTime = maxUseTime;
 }

/*--------------------------------------------------+ | Methods | +--------------------------------------------------*/

 public Connection getConnection()
   throws SQLException
 {
   if (_draining) {
     throw new SQLException("ConnectionPool was drained.");
   }
   // Try reusing an existing Connection
   synchronized (_connections) {
     PoolConnection pc = null;
     for (int i = 0; i < _connections.size(); i++) {
       pc = _connections.get(i);
       if (pc.lease()) {
         // PoolConnection is available
         if (!_checkConnections) {
           return pc;
         }
         else {
           // Check the status of the connection
           boolean isHealthy = true;
           try {
             if (pc.isClosed() && pc.getWarnings() != null) {
               // If something happend to the connection, we
               // don"t want to use it anymore.
               isHealthy = false;
             }
           }
           catch(SQLException sqle) {
             // If we can"t even ask for that information, we
             // certainly don"t want to use it anymore.
             isHealthy = false;
           }
           if (isHealthy) {
             return pc;
           }
           else {
             try {
               pc.expire();
             }
             catch(SQLException sqle) {
               // ignore
             }
             _connections.remove(i);
           }
         }
       }
     }
   }
   // Create a new Connection
   Connection con = DriverManager.getConnection(_url, _user, _password);
   PoolConnection pc = new PoolConnection(con);
   pc.lease();
   // Add it to the pool
   synchronized (_connections) {
     _connections.add(pc);
     if (_cleaner == null) {
       // Put a new PoolCleaner to work
       _cleaner = new PoolCleaner(_cleaningInterval);
       _cleaner.start();
     }
   }
   return pc;
 }
 public void removeExpired() {
   PoolConnection pc;
   long maxIdleDeadline = System.currentTimeMillis() - _maxIdleTime;
   long maxUseDeadline = System.currentTimeMillis() - _maxUseTime;
   synchronized (_connections) {
     // Check all connections
     for (int i = _connections.size() - 1; i >= 0; i--) {
       pc = _connections.get(i);
       if (!pc.inUse() && pc.getTimeClosed() < maxIdleDeadline) {
         // Connection has been idle too long, close it.
         _connections.remove(i);
         try {
           pc.expire();
         }
         catch (SQLException ignore) {
         }
       }
       else if (
         _maxUseTime >= 0 && // don"t check if disabled
         pc.inUse() &&
         pc.getTimeOpened() < maxUseDeadline)
       {
         // Connection has been used too long, close it.
         // Print the location where the connetion was acquired
         // as it probably forgot to close the connection (which
         // is a bug).
         System.err.println("Warning: forced closing of a connection that has been in use too long.");
         System.err.println("Connection was acquired in:");
         pc.printStackTrace();
         System.err.println();
         _connections.remove(i);
         try {
           pc.expire();
         }
         catch (SQLException ignore) {
         }
       }
     }
     // Stop the PoolCleaner if the pool is empty.
     if (_connections.size() == 0 && _cleaner != null) {
       _cleaner.halt();
       _cleaner = null;
     }
   }
 }
 public int getPoolSize() {
   synchronized (_connections) {
     return _connections.size();
   }
 }
 /**
  * Drains the pool. After the ConnectionPool has been drained it will not
  * give out any more connections and all existing connections will be
  * closed. This action cannot be reversed, so a ConnectionPool will become
  * unusable once it has been drained.
  **/
 public void drain() {
   _draining = true;
   if (_cleaner != null) {
     _cleaner.halt();
   }
   synchronized (_connections) {
     for (int i = _connections.size() - 1; i >= 0; i--) {
       PoolConnection pc = _connections.get(i);
       if (pc.inUse()) {
         System.err.println("Warning: forced closing of a connection still in use.");
         System.err.println("Connection was acquired in:");
         pc.printStackTrace();
         System.err.println();
       }
       _connections.remove(i);
       try {
         pc.expire();
       }
       catch (SQLException ignore) {
       }
     }
   }
 }
 protected void finalize() {
   drain();
 }

/*--------------------------------------------+ | inner class PoolConnection | +--------------------------------------------*/

 /**
  * Wrapper around java.sql.Connection
  **/
 static class PoolConnection implements Connection {
 /*----------------------------------+
 | Variables                         |
 +----------------------------------*/
   protected Connection _conn;
   protected boolean _inUse;
   protected boolean _autoCommit;
   /** Time stamp for the last time the connection was opened. **/
   protected long _timeOpened;
   /** Time stamp for the last time the connection was closed. **/
   protected long _timeClosed;
   private Throwable _throwable;
 /*----------------------------------+
 | Constructors                      |
 +----------------------------------*/
   public PoolConnection(Connection conn) {
     _conn = conn;
     _inUse = false;
     _autoCommit = true;
   }
 /*----------------------------------+
 | PoolConnection specific methods   |
 +----------------------------------*/
   /**
    * Tries to lease this connection. If the attempt was successful (the
    * connection was available), a flag will be set marking this connection
    * "in use", and this method will return "true". If the connection was
    * already in use, this method will return "false".
    **/
   public synchronized boolean lease() {
     if (_inUse) {
       return false;
     }
     else {
       _inUse = true;
       _timeOpened = System.currentTimeMillis();
       return true;
     }
   }
   /**
    * Checks if the connection currently is used by someone.
    **/
   public boolean inUse() {
     return _inUse;
   }
   /**
    * Returns the time stamp of the last time this connection was
    * opened/leased.
    **/
   public synchronized long getTimeOpened() {
     return _timeOpened;
   }
   /**
    * Returns the time stamp of the last time this connection was
    * closed.
    **/
   public synchronized long getTimeClosed() {
     return _timeClosed;
   }
   /**
    * Expires this connection and closes the underlying connection to the
    * database. Once expired, a connection can no longer be used.
    **/
   public void expire()
     throws SQLException
   {
     _conn.close();
     _conn = null;
   }
   public void printStackTrace() {
     _throwable.printStackTrace(System.err);
   }
 /*----------------------------------+
 | Wrapping methods for Connection   |
 +----------------------------------*/
   public synchronized void close()
     throws SQLException
   {
     // Multiple calls to close?
     if (_inUse) {
       _timeClosed = System.currentTimeMillis();
       _inUse = false;
       if (_autoCommit == false) {
         // autoCommit has been set to false by this user,
         // restore the default "autoCommit = true"
         setAutoCommit(true);
       }
     }
   }
   public Statement createStatement()
     throws SQLException
   {
     _throwable = new Throwable();
     return _conn.createStatement();
   }
   public PreparedStatement prepareStatement(String sql)
     throws SQLException
   {
     _throwable = new Throwable();
     return _conn.prepareStatement(sql);
   }
   public CallableStatement prepareCall(String sql)
     throws SQLException
   {
     return _conn.prepareCall(sql);
   }
   public String nativeSQL(String sql)
     throws SQLException
   {
     return _conn.nativeSQL(sql);
   }
   public void setAutoCommit(boolean autoCommit)
     throws SQLException
   {
     _conn.setAutoCommit(autoCommit);
     _autoCommit = _conn.getAutoCommit();
   }
   public boolean getAutoCommit()
     throws SQLException
   {
     return _conn.getAutoCommit();
   }
   public void commit()
     throws SQLException
   {
     _conn.rumit();
   }
   public void rollback()
     throws SQLException
   {
     _conn.rollback();
   }
   public boolean isClosed()
     throws SQLException
   {
     return _conn.isClosed();
   }
   public DatabaseMetaData getMetaData()
     throws SQLException
   {
     return _conn.getMetaData();
   }
   public void setReadOnly(boolean readOnly)
     throws SQLException
   {
     _conn.setReadOnly(readOnly);
   }
   public boolean isReadOnly()
     throws SQLException
   {
     return _conn.isReadOnly();
   }
   public void setCatalog(String catalog)
     throws SQLException
   {
     _conn.setCatalog(catalog);
   }
   public String getCatalog()
     throws SQLException
   {
     return _conn.getCatalog();
   }
   public void setTransactionIsolation(int level)
     throws SQLException
   {
     _conn.setTransactionIsolation(level);
   }
   public int getTransactionIsolation()
     throws SQLException
   {
     return _conn.getTransactionIsolation();
   }
   public SQLWarning getWarnings()
     throws SQLException
   {
     return _conn.getWarnings();
   }
   public void clearWarnings()
     throws SQLException
   {
     _conn.clearWarnings();
   }
   public Statement createStatement(
     int resultSetType, int resultSetConcurrency)
     throws SQLException
   {
     return _conn.createStatement(resultSetType, resultSetConcurrency);
   }
   public PreparedStatement prepareStatement(
     String sql, int resultSetType, int resultSetConcurrency)
     throws SQLException
   {
     return _conn.prepareStatement(sql, resultSetType, resultSetConcurrency);
   }
   public CallableStatement prepareCall(
     String sql, int resultSetType, int resultSetConcurrency)
     throws SQLException
   {
     return _conn.prepareCall(sql, resultSetType, resultSetConcurrency);
   }
   public Map<String,Class<?>> getTypeMap()
     throws SQLException
   {
     return _conn.getTypeMap();
   }
   public void setTypeMap(Map<String,Class<?>> map)
     throws SQLException
   {
     _conn.setTypeMap(map);
   }

/*

* The following methods are new methods from java.sql.Connection that
* were added in JDK1.4. These additions are incompatible with older JDK
* versions.
*/
   public void setHoldability(int holdability)
     throws SQLException
   {
     _conn.setHoldability(holdability);
   }
   public int getHoldability()
     throws SQLException
   {
     return _conn.getHoldability();
   }
   public Savepoint setSavepoint()
     throws SQLException
   {
     return _conn.setSavepoint();
   }
   public Savepoint setSavepoint(String name)
     throws SQLException
   {
     return _conn.setSavepoint(name);
   }
   public void rollback(Savepoint savepoint)
     throws SQLException
   {
     _conn.rollback(savepoint);
   }
   public void releaseSavepoint(Savepoint savepoint)
     throws SQLException
   {
     _conn.releaseSavepoint(savepoint);
   }
   public Statement createStatement(
       int resultSetType,
       int resultSetConcurrency,
       int resultSetHoldability)
     throws SQLException
   {
     return _conn.createStatement(
       resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public PreparedStatement prepareStatement(
       String sql,
       int resultSetType,
       int resultSetConcurrency,
       int resultSetHoldability)
     throws SQLException
   {
     return _conn.prepareStatement(
       sql, resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public CallableStatement prepareCall(
       String sql,
       int resultSetType,
       int resultSetConcurrency,
       int resultSetHoldability)
     throws SQLException
   {
     return _conn.prepareCall(
       sql, resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public PreparedStatement prepareStatement(
       String sql, int autoGenerateKeys)
     throws SQLException
   {
     return _conn.prepareStatement(sql, autoGenerateKeys);
   }
   public PreparedStatement prepareStatement(
       String sql, int[] columnIndexes)
     throws SQLException
   {
     return _conn.prepareStatement(sql, columnIndexes);
   }
   public PreparedStatement prepareStatement(
       String sql, String[] columnNames)
     throws SQLException
   {
     return _conn.prepareStatement(sql, columnNames);
   }
   public Clob createClob() throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public Blob createBlob() throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public NClob createNClob() throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public SQLXML createSQLXML() throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public boolean isValid(int timeout) throws SQLException {
     // TODO Auto-generated method stub
     return false;
   }
   public void setClientInfo(String name, String value) throws SQLClientInfoException {
     // TODO Auto-generated method stub
     
   }
   public void setClientInfo(Properties properties) throws SQLClientInfoException {
     // TODO Auto-generated method stub
     
   }
   public String getClientInfo(String name) throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public Properties getClientInfo() throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public <T> T unwrap(Class<T> iface) throws SQLException {
     // TODO Auto-generated method stub
     return null;
   }
   public boolean isWrapperFor(Class<?> iface) throws SQLException {
     // TODO Auto-generated method stub
     return false;
   }
 }

/*--------------------------------------------+ | inner class PoolCleaner | +--------------------------------------------*/

 class PoolCleaner extends Thread {
   protected long _cleaningInterval;
   protected boolean _mustStop;
   public PoolCleaner(long cleaningInterval) {
     if (cleaningInterval < 0) {
       throw new IllegalArgumentException("cleaningInterval must be >= 0");
     }
     _mustStop = false;
     _cleaningInterval = cleaningInterval;
     setDaemon(true);
   }
   public void run() {
     while (!_mustStop) {
       try {
         sleep(_cleaningInterval);
       }
       catch (InterruptedException ignore) {
       }
       if (_mustStop) {
         break;
       }
       removeExpired();
     }
   }
   public void halt() {
     _mustStop = true;
     synchronized (this) {
       this.interrupt();
     }
   }
 }

}


 </source>