com.atomikos.jdbc.ConnectionPool Maven / Gradle / Ivy
/**
* Copyright (C) 2000-2010 Atomikos
*
* This code ("Atomikos TransactionsEssentials"), by itself,
* is being distributed under the
* Apache License, Version 2.0 ("License"), a copy of which may be found at
* http://www.atomikos.com/licenses/apache-license-2.0.txt .
* You may not use this file except in compliance with the License.
*
* While the License grants certain patent license rights,
* those patent license rights only extend to the use of
* Atomikos TransactionsEssentials by itself.
*
* This code (Atomikos TransactionsEssentials) contains certain interfaces
* in package (namespace) com.atomikos.icatch
* (including com.atomikos.icatch.Participant) which, if implemented, may
* infringe one or more patents held by Atomikos.
* It should be appreciated that you may NOT implement such interfaces;
* licensing to implement these interfaces must be obtained separately from Atomikos.
*
* 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.
*/
package com.atomikos.jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Vector;
import javax.sql.PooledConnection;
import com.atomikos.icatch.system.Configuration;
/**
*
*
*
* A connection pooling implementation. This class is NOT meant to be used by
* the application directly. Rather, a DataSource object should wrap this class.
*
* @deprecated As of release 3.3, the newer pool should be used instead.
*
*/
public class ConnectionPool implements Runnable
{
// timeout_ after which connections are tested for liveness and removed if
// not ok
private long timeout_;
private Vector pool_;
// contains the available objects
private int maxSize_;
// the maximum INITIAL size of the pool_
private ConnectionFactory source_;
// where to get a new connection from.
private boolean timerNeeded_ = true;
// keeps timer thread running
private String testQuery_;
//test SQL to validate connection
private boolean testOnBorrow_;
//test connections before use?
/**
* Constructor.
*
* @param size
* The size of the pool.
* @param connSource
* The ConnectionFactory to use.
* @param seconds
* The timeout for old connections in the pool.
* @param testQuery
* A test query to validate connection liveness.
* @param testOnBorrow
* Should connections be tested when borrowed or not?
* If true then connections will be tested when gotten:
*
* - for a connection from the pool, a failing test will retry with another connection
* - for new connections (i.e. when pool is empty), a failing test will be thrown to the application
*
*/
public ConnectionPool ( int size , ConnectionFactory connSource ,
int connectionTimeout , String testQuery , boolean testOnBorrow ) throws SQLException
{
testQuery_ = testQuery;
timeout_ = connectionTimeout * 1000;
source_ = connSource;
pool_ = new Vector ();
maxSize_ = size;
testOnBorrow_ = testOnBorrow;
for ( int i = 0; i < maxSize_; i++ ) {
XPooledConnection pc = source_.getPooledConnection ();
if ( pc != null ) {
pool_.addElement ( pc );
} else
throw new SQLException (
"ConnectionPool constructor: null PooledConnection" );
}// for
Thread timer = new Thread ( this );
timer.setDaemon ( true );
timer.start ();
}
/**
* Get the current size of the available pool.
*
* @return int The current size. Does NOT include connections that are
* currently being used!
*/
public int getSize ()
{
return pool_.size ();
}
/**
* Get a PooledConnection instance from the pool.
*
* @return PooledConnection The instance.
* @exception SQLException
* On error.
*/
public synchronized PooledConnection getPooledConnection ()
throws SQLException
{
PooledConnection retVal;
if ( pool_.isEmpty () ) {
retVal = ( XPooledConnection ) source_.getPooledConnection ();
Configuration
.logWarning ( "JDBC ConnectionPool exhausted - allocated new connection: "
+ retVal.toString () );
if ( testOnBorrow_ ) {
//test, but DON'T CLOSE handle since the application will still need it!
test ( retVal , false );
}
//this is a NEW connection -> failure of test is DBMS problem -> propagate to application
}
else {
// here we are if pool not empty
retVal = (XPooledConnection) pool_.firstElement ();
if ( !pool_.removeElement ( retVal ) )
throw new SQLException ( "Unable to take connection out of pool?" );
if ( testOnBorrow_ ) {
try {
//test, but DON'T CLOSE handle since the application will still need it!
test ( retVal , false );
}
catch ( SQLException retry ) {
retVal = getPooledConnection();
}
}
}
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "JDBC ConnectionPool: using connection: "
+ retVal );
return retVal;
}
protected synchronized void putInPool ( PooledConnection pc )
{
pool_.addElement ( pc );
notifyAll ();
}
/**
* Puts back a connection after usage. This method should be called by
* DataSource objects.
*
* @param conn
* The connection. If the connection has been invalidated, or if
* the pool is large enough, then this method will actually close
* the connection.
*/
public synchronized void putBack ( XPooledConnection conn )
{
if ( !conn.getInvalidated () && getSize () < maxSize_ ) {
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "Putting connection back in pool: "
+ conn.toString () );
putInPool ( conn );
} else {
try {
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "Pool: closing connection: "
+ conn.toString () );
conn.close ();
// closes underlying connection
} catch ( SQLException err ) {
}
}
}
protected synchronized Enumeration getOldConnections ()
{
Vector returnVals = new Vector ();
java.util.Date now = new java.util.Date ();
// select those candidates that have not been used during the timeout_
// interval
Enumeration enumm = pool_.elements ();
while ( enumm.hasMoreElements () ) {
XPooledConnection pc = (XPooledConnection) enumm.nextElement ();
if ( (now.getTime () - pc.getLastUse ().getTime ()) > timeout_ )
returnVals.addElement ( pc );
}// while
// remove candidates from available pool_, for cleanup inspection
enumm = returnVals.elements ();
while ( enumm.hasMoreElements () ) {
pool_.removeElement ( enumm.nextElement () );
}// while
return returnVals.elements ();
}
public void run ()
{
Enumeration enumm;
try {
while ( timerNeeded_ ) {
Thread.sleep ( timeout_ );
enumm = getOldConnections ();
// re-check if not shutdown, or the pool will
// restore connections
while ( timerNeeded_ && enumm.hasMoreElements () ) {
XPooledConnection pc = (XPooledConnection) enumm
.nextElement ();
try {
if ( !pc.getInvalidated () ) {
test ( pc , true );
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "ConnectionPool: connection is fine, keeping it in pool: " + pc);
// here we are if connection still OK
putInPool ( pc );
}// if
else if ( getSize () < maxSize_ ) {
// replace old connection, but only if pool is
// shrinking
PooledConnection newConn = source_
.getPooledConnection ();
putInPool ( newConn );
Configuration
.logDebug ( "ConnectionPool: replacing invalidated connection "
+ pc.toString () );
}
}// try
catch ( SQLException sqlErr ) {
// here we are if connection has a problem
// discard it and put new one in pool_
if ( getSize () < maxSize_ ) {
PooledConnection newConn = source_
.getPooledConnection ();
putInPool ( newConn );
Configuration
.logDebug ( "ConnectionPool: connection invalid, replacing it: "
+ pc.toString (), sqlErr );
}
}
}// while
}// while
}// try
catch ( Exception e ) {
}
// System.out.println ( "Connectionpool: thread exiting");
}
/**
* Tests the pc for (re)usability.
* @param pc The pooled connection to test.
* @param closeConnectionAfterTest Whether to close the connection handle or not.
* @throws SQLException On error - in that case, the connection will have been closed and should not be used.
*/
private void test ( PooledConnection pc , boolean closeConnectionAfterTest ) throws SQLException
{
Connection connection = null;
boolean cleanup = false;
try {
connection = pc.getConnection();
if ( testQuery_ == null || "".equals ( testQuery_ ) ) {
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "ConnectionPool: no query to test connection, trying getMetaData(): " + connection );
connection.getMetaData();
return;
}
if ( Configuration.isDebugLoggingEnabled() ) Configuration.logDebug ( "ConnectionPool: trying query '" + testQuery_ + "' on connection " + connection );
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery ( testQuery_ );
rs.close();
stmt.close();
} catch ( Exception e ) {
//catch any Exception - cf case 22198
Configuration.logWarning ( "ConnectionPool: error testing connection" , e );
cleanup = true;
//rethrow to fail test!
SQLException sqlErr = new SQLException ( e.getMessage() );
sqlErr.initCause ( e );
throw sqlErr;
}
finally {
try {
if ( connection != null && ( closeConnectionAfterTest || cleanup ) ) connection.close();
if ( cleanup ) pc.close();
}
catch ( SQLException error ) {
Configuration.logWarning ( "ConnectionPool: error closing connection during test" , error );
//ignore; let original SQLException be thrown if any
}
}
}
/**
* To be called when the pool of connections is no longer needed. This
* method will close all connections in the pool.
*/
public synchronized void cleanup ()
{
timerNeeded_ = false;
if ( pool_ == null )
return; // null if called twice
Enumeration enumm = pool_.elements ();
try {
while ( enumm.hasMoreElements () ) {
PooledConnection pc = (PooledConnection) enumm.nextElement ();
pc.close ();
}// while
}// try
catch ( SQLException e ) {
}
pool_ = null;
}
public void finalize () throws Throwable
{
try {
cleanup ();
} finally {
super.finalize ();
}
}
public PrintWriter getLogWriter () throws SQLException
{
return source_.getLogWriter ();
}
public void setLogWriter ( PrintWriter pw ) throws SQLException
{
source_.setLogWriter ( pw );
}
public void setLoginTimeout ( int secs ) throws SQLException
{
source_.setLoginTimeout ( secs );
}
public int getLoginTimeout () throws SQLException
{
return source_.getLoginTimeout ();
}
}