Mega Code Archive

 
Categories / Java / Database SQL JDBC
 

Your own connection pool

/*  * Copyright Aduna (http://www.aduna-software.com/) (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.commit();     }     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();       }     }   } }