Java/Database SQL JDBC/Connection Pool
Содержание
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>