src.com.ibm.as400.access.AS400JDBCConnectionPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400-jdk8 Show documentation
Show all versions of jt400-jdk8 Show documentation
The Open Source version of the IBM Toolbox for Java
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400JDBCConnectionPool.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2003 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
package com.ibm.as400.access;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator; // Java2
import java.util.Vector; // Java2
/**
* Represents a pool of JDBC connections
* that are available for use by a Java program.
*
* Note: AS400JDBCConnectionPool objects are threadsafe.
*
*
The following example creates a connection pool with 10 connections.
*
* // Obtain an AS400JDBCConnectionPoolDataSource object from JNDI.
* Context context = new InitialContext(environment);
* AS400JDBCConnectionPoolDataSource datasource = (AS400JDBCConnectionPoolDataSource)context.lookup("jdbc/myDatabase");
*
* // Create an AS400JDBCConnectionPool object.
* AS400JDBCConnectionPool pool = new AS400JDBCConnectionPool(datasource);
*
* // Adds 10 connections to the pool that can be used by the application (creates the physical database connections based on the data source).
* pool.fill(10);
*
* // Get a handle to a database connection from the pool.
* Connection connection = pool.getConnection();
*
* ... Perform miscellenous queries/updates on the database.
*
* // Close the connection handle to return it to the pool.
* connection.close();
*
* ... Application works with some more connections from the pool.
*
* // Close the pool to release all resources.
* pool.close();
*
**/
public class AS400JDBCConnectionPool extends ConnectionPool implements Serializable
{
static final long serialVersionUID = 4L;
private boolean closed_; // If the pool is closed.
private AS400JDBCConnectionPoolDataSource dataSource_; // The dataSource used for the connection.
transient long lastSingleThreadRun_; // Last time maintenance was run (single-thread mode).
transient Vector activePool_; // Active connections.
transient Vector availablePool_; // Available connections.
transient Vector deadPool_; // Connections staged for removal and disconnection.
transient private PoolConnectionEventListener eventListener_; // Listener for events on pooled connections.
/**
* Constructs a default AS400JDBCConnectionPool object.
**/
public AS400JDBCConnectionPool()
{
super();
initializeTransient();
}
/**
* Constructs an AS400JDBCConnectionPool object with the specified dataSource.
* @param dataSource The AS400JDBCConnectionPoolDataSource object.
**/
public AS400JDBCConnectionPool(AS400JDBCConnectionPoolDataSource dataSource)
{
this();
try
{
setDataSource(dataSource);
}
catch (PropertyVetoException p)
{ /* will never occur. */
}
}
/**
* Removes any connections that have exceeded maximum inactivity time, replaces any connections that have aged past maximum
* usage or maximum lifetime, and removes any connections that have been in use too long.
* @exception SQLException If a database error occurs closing a connection.
**/
void cleanupConnections()
{
boolean trace = JDTrace.isTraceOn(); // @B5C
if (trace)
{
JDTrace.logInformation (this, "ConnectionPool cleanup...");
JDTrace.logInformation (this, " MaxLifeTime: " + getMaxLifetime()); // @B5C
JDTrace.logInformation (this, " MaxUseTime: " + getMaxUseTime()); // @B5C
JDTrace.logInformation (this, " MaxInactivity: " + getMaxInactivity()); // @B5C
JDTrace.logInformation (this, " PretestConnections: " + isPretestConnections());
JDTrace.logInformation (this, "Idle Connections: " + availablePool_.size()); // @B5C
JDTrace.logInformation (this, "Active Connections: " + activePool_.size()); // @B5C
JDTrace.logInformation (this, "Dead Connections: " + deadPool_.size());
}
synchronized (availablePool_)
{
synchronized (activePool_)
{
Iterator[] connections = { availablePool_.iterator(), activePool_.iterator()};
for (int i=0; i< connections.length; i++)
{
while (connections[i].hasNext())
{
AS400JDBCPooledConnection poolConnection = (AS400JDBCPooledConnection)connections[i].next();
if (trace)
JDTrace.logInformation (this, poolConnection.toString()); // @B5C
if ((!poolConnection.isInUse() && getMaxLifetime() !=-1 && poolConnection.getLifeSpan() > getMaxLifetime()) || //@B1C // inactive connections only.
(!poolConnection.isInUse() && getMaxInactivity() !=-1 && poolConnection.getInactivityTime() > getMaxInactivity())) //@B1C //@B3C
{
if (trace)
JDTrace.logInformation (this, "Removing expired connection from the pool."); // @B5C
connections[i].remove();
// Stage the connection for closing.
synchronized (deadPool_)
{
deadPool_.addElement(poolConnection);
}
// Notify listeners that the connection expired
if (poolListeners_ != null)
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(poolConnection, ConnectionPoolEvent.CONNECTION_EXPIRED); //@A5C
poolListeners_.fireConnectionExpiredEvent(poolEvent);
}
}
else if (getMaxUseTime() > 0 &&
poolConnection.getInUseTime() > getMaxUseTime()) // only valid with active connections.
{
if (trace)
JDTrace.logInformation (this, "Returning active connection to the pool."); // @B5C
poolConnection.returned(); // invalidate the connection handle
availablePool_.add(poolConnection);
connections[i].remove();
// Notify listeners that the connection expired
if (poolListeners_ != null)
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(poolConnection, ConnectionPoolEvent.CONNECTION_EXPIRED); //@A5C
poolListeners_.fireConnectionExpiredEvent(poolEvent);
}
}
} // 'while' loop
} // 'for' loop
} // synchronized (activePool_)
} // synchronized (availablePool_)
// Close the removed connections.
synchronized (deadPool_)
{
Iterator connections = deadPool_.iterator();
while (connections.hasNext())
{
AS400JDBCPooledConnection poolConnection = (AS400JDBCPooledConnection)connections.next();
if (trace)
JDTrace.logInformation (this, poolConnection.toString());
if (trace)
JDTrace.logInformation (this, "Removing dead connection from the pool.");
closePooledConnection(poolConnection);
connections.remove();
}
}
// Notify listeners that the maintenance thread was run.
if (poolListeners_ != null)
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAINTENANCE_THREAD_RUN);
poolListeners_.fireMaintenanceThreadRun(poolEvent);
}
// Check if maintenance should keep running.
synchronized (availablePool_)
{
synchronized (activePool_)
{
if (activePool_.isEmpty() && availablePool_.isEmpty())
{
if (maintenance_ != null) maintenance_.setRunning(false);
setInUse(false); // data source CAN be changed.
}
}
}
if (!isThreadUsed())
lastSingleThreadRun_ = System.currentTimeMillis();
if (trace)
{
JDTrace.logInformation (this, "ConnectionPool cleanup finished."); // @B5C
JDTrace.logInformation (this, " Idle Connections: " + availablePool_.size()); // @B5C
JDTrace.logInformation (this, " Active Connections: " + activePool_.size()); // @B5C
JDTrace.logInformation (this, " Dead Connections: " + deadPool_.size());
}
}
/**
* Closes all the database connections in the pool.
**/
public void close()
{
if (JDTrace.isTraceOn()) // @B5C
{
JDTrace.logInformation (this, "Closing the JDBC connection pool."); // @B5C
JDTrace.logInformation (this, "Available: " + availablePool_.size()); // @B5C
JDTrace.logInformation (this, "Active: " + activePool_.size()); // @B5C
}
// Close all connections in the pool.
synchronized (availablePool_)
{
synchronized (activePool_)
{
synchronized (deadPool_)
{
Iterator[] connections = { availablePool_.iterator(), activePool_.iterator(), deadPool_.iterator()};
for (int i=0; i< connections.length; i++)
{
while (connections[i].hasNext())
{
AS400JDBCPooledConnection pooledConnection = (AS400JDBCPooledConnection)connections[i].next();
closePooledConnection(pooledConnection);
connections[i].remove();
}
}
}
}
}
// Terminate the maintenance thread, if it's still alive.
if (maintenance_ != null && maintenance_.isAlive()) {
maintenance_.shutdown(); // tell the thread to terminate
}
synchronized( this) { // @A7A
if (isInUse())
setInUse(false); // data source CAN be changed.
}
// Notify the listeners.
if (poolListeners_ != null)
{
ConnectionPoolEvent event = new ConnectionPoolEvent(this, ConnectionPoolEvent.CONNECTION_POOL_CLOSED);
poolListeners_.fireClosedEvent(event);
}
closed_ = true;
}
/**
* Closes an AS400JDBCPooledConnection.
* @param pooledConnection The pooled connection.
**/
void closePooledConnection(AS400JDBCPooledConnection pooledConnection)
{
try
{
pooledConnection.close();
}
catch (SQLException e)
{
/* ignore connection is being removed anyway. */
JDTrace.logInformation (this, e.getMessage()); // @B5C
}
}
/**
* Creates a pooledConnection for the pool.
* @return An AS400JDBCPooledConnection object.
* @exception SQLException If a database error occurs.
**/
private AS400JDBCPooledConnection createPooledConnection() throws SQLException
{
if (dataSource_ == null)
throw new ExtendedIllegalStateException("dataSource", ExtendedIllegalStateException.PROPERTY_NOT_SET);
AS400JDBCPooledConnection pooledConnection = new AS400JDBCPooledConnection(dataSource_.getConnection()); //@A3C
pooledConnection.addConnectionEventListener(eventListener_);
dataSource_.log("PooledConnection created"); //@A3A
return pooledConnection;
}
/**
* Fills the connection pool with the specified number of database connections.
* @param numberOfConnections The number of connections to add to the pool.
* @exception ConnectionPoolException If a database error occurs creating a connection for the pool, or the maximum number of connections has been reached for the pool.
* @exception ExtendedIllegalArgumentException if the number of connections to fill the pool with is less than one.
**/
public void fill(int numberOfConnections) throws ConnectionPoolException
{
if (JDTrace.isTraceOn()) // @B5C
JDTrace.logInformation (this, "Filling the pool with " + numberOfConnections + " connections."); //@B5C
// Validate the numberOfConnections parameter.
if (numberOfConnections < 1)
throw new ExtendedIllegalArgumentException("numberOfConnections", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
int maxConnections = getMaxConnections();
if (maxConnections != -1)
{
if (numberOfConnections + getActiveConnectionCount() + getAvailableConnectionCount() > maxConnections)
throw new ConnectionPoolException(ConnectionPoolException.MAX_CONNECTIONS_REACHED); //@KBA fix for JTOpen Bug 3655
//@KBD throw new ExtendedIllegalArgumentException("numberOfConnections", ExtendedIllegalArgumentException.RANGE_NOT_VALID);
}
// Add connections to the pool.
try
{
synchronized (availablePool_)
{
for (int i=0; i< numberOfConnections; i++)
{ //@A5A
AS400JDBCPooledConnection poolConnection = createPooledConnection(); //@A5A
availablePool_.addElement(poolConnection); //@A5C
// Notify the listeners.
if (poolListeners_ != null)
{
ConnectionPoolEvent event = new ConnectionPoolEvent(poolConnection, ConnectionPoolEvent.CONNECTION_CREATED); //@A5M @A5C
poolListeners_.fireConnectionCreatedEvent(event); //@A5M
}
} //@A5A
}
}
catch (SQLException e)
{
if (isRunMaintenance() && maintenance_ != null)
cleanupConnections(); // re-check old connections.
throw new ConnectionPoolException(e);
}
synchronized(this) {
if (!isInUse())
{
setInUse(true); // Data source now can NOT be changed.
if (isClosed())
closed_ = false; // Set the state to OPEN if previously closed.
}
}
if (isRunMaintenance() && isThreadUsed())
{
if (maintenance_ == null)
{
synchronized(this) //@CRS
{
if (maintenance_ == null) //@CRS
{
maintenance_ = new PoolMaintenance(this);
maintenance_.start(); // Start the first time.
// Give thread a chance to start.
// if (!maintenance_.isRunning()) //@A2C
// {
// try
// { //@A2A
// Thread.sleep(10); //@A2A
// } //@A2A
// catch (InterruptedException e) //@A2A
// {
//Ignore //@A2A
// } //@A2A
// } //@A2A
// If thread has still not started, keep giving it chances for 5 minutes.
// for (int i = 1; !maintenance_.isRunning() && i<6000; i++) //@A2A
// {
// try
// { //@A2A
// Thread.sleep(50); //@A2A
// } //@A2A
// catch (InterruptedException ie) //@A2A
// {
//Ignore //@A2A
// } //@A2A
// } //@A2A
// if (!maintenance_.isRunning()) //@A2A
// JDTrace.logInformation (this, "maintenance thread failed to start"); //@A2A
} //@A2A
}
}
//@CRS else if (!maintenance_.isRunning())
if (!maintenance_.isRunning()) maintenance_.setRunning(true); // Restart. @CRS
}
else if (isRunMaintenance() && !isThreadUsed())
lastSingleThreadRun_ = System.currentTimeMillis();
}
/**
* Closes the connection pool if not explicitly closed by the caller.
* @exception Throwable If an error occurs.
**/
protected void finalize() throws Throwable
{
if (!isClosed())
close();
super.finalize();
}
/**
* Returns the number of active connections the pool has created.
* @return The number of active connections.
**/
public int getActiveConnectionCount()
{
return activePool_.size();
}
/**
* Returns the number of available PooledConnections in the pool.
* @return The number of available PooledConnections.
**/
public int getAvailableConnectionCount()
{
return availablePool_.size();
}
/**
* Returns a connection from the pool.
* Updates the pool cache.
* @return The connection.
* @exception ConnectionPoolException If a database error occurs getting the connection.
**/
public Connection getConnection() throws ConnectionPoolException
{
AS400JDBCPooledConnection pooledConnection = getPooledConnection();
Connection connection = null;
try
{
connection = pooledConnection.getConnection();
}
catch (SQLException sql)
{
throw new ConnectionPoolException(sql);
}
return connection;
}
/**
* Returns the data source used to make connections.
* @return The AS400JDBCConnectionPoolDataSource object.
**/
public AS400JDBCConnectionPoolDataSource getDataSource()
{
return dataSource_;
}
//@A3A
/**
* Returns a connection from the pool.
* Updates the pool cache.
* @return The connection.
* @exception ConnectionPoolException If a database error occurs getting the connection.
**/
AS400JDBCPooledConnection getPooledConnection() throws ConnectionPoolException
{
AS400JDBCPooledConnection pooledConnection = null;
final int maxTries = Math.max(getMaxConnections(), 1+availablePool_.size());
int numTries = 0;
while (pooledConnection == null &&
++numTries <= maxTries) // eliminate any possibility of an infinite loop
{
synchronized (availablePool_)
{
//@B2M Moved following two lines into the synchronization block.
if (availablePool_.isEmpty()) //@B2M
fill(1); // Add a new connection. //@B2M
//@CRS pooledConnection = (AS400JDBCPooledConnection)availablePool_.firstElement();
pooledConnection = (AS400JDBCPooledConnection)availablePool_.lastElement(); //@CRS -- Most recently used.
// Remove the pooled connection from the available list.
availablePool_.removeElement(pooledConnection);
}
// Pre-test the connection, if the property is set.
try
{
if (isPretestConnections() && !pooledConnection.isConnectionAlive())
{
if (JDTrace.isTraceOn())
JDTrace.logInformation (this, "Connection failed a pretest.");
synchronized (deadPool_)
{
deadPool_.addElement(pooledConnection);
}
pooledConnection = null;
}
}
catch (SQLException sql)
{
throw new ConnectionPoolException(sql);
}
if (pooledConnection != null)
{
synchronized (activePool_)
{
activePool_.addElement(pooledConnection);
}
}
}
if (pooledConnection == null)
{
JDTrace.logInformation (this, "Exceeded maximum attempts to get a valid connection: " + numTries);
throw new ConnectionPoolException(ConnectionPoolException.UNKNOWN_ERROR);
}
// Notify the listeners that a connection was released.
if (poolListeners_ != null)
{
ConnectionPoolEvent event = new ConnectionPoolEvent(pooledConnection, ConnectionPoolEvent.CONNECTION_RELEASED); //@A5C
poolListeners_.fireConnectionReleasedEvent(event);
}
return pooledConnection;
}
/**
* Initializes the transient data.
**/
private void initializeTransient()
{
eventListener_ = new PoolConnectionEventListener(this);
activePool_ = new Vector();
availablePool_ = new Vector();
deadPool_ = new Vector();
closed_ = true;
//@A1D Moved property change listener to parent; moved runMaintenance method below.
}
/**
* Indicates whether the connection pool is closed.
* @return true if closed; false otherwise.
**/
public boolean isClosed()
{
return closed_;
}
/**
* Deserializes and initializes transient data.
* @exception IOException If a file I/O error occurs.
* @exception ClassNotFoundException If a file error occurs.
**/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
initializeTransient();
}
/**
* Reduces the number of available connections down to the maximum number if necessary and possible.
* Note: A possibility still exists where the number of active connections is greater than the number of max connections.
* We will do this check in the PooledConnectionEventListener.
**/
private void reduceConnectionCount()
{
synchronized (availablePool_)
{
synchronized (activePool_)
{
int current = availablePool_.size() + activePool_.size();
int required = getMaxConnections();
if (current > required)
{
if (JDTrace.isTraceOn()) // @B5C
JDTrace.logInformation (this, "Reducing number of connections... Current: " + current + "(" + availablePool_.size() + ") " + " Max: " + required); // @B5C
int removed = 0;
int reduceBy = current - required;
while (removed < reduceBy && availablePool_.size() != 0)
{
AS400JDBCPooledConnection poolConnection = (AS400JDBCPooledConnection)availablePool_.remove(0);
removed++;
synchronized (deadPool_)
{
// Stage the connection for closing.
deadPool_.addElement(poolConnection);
}
}
}
}
}
// Close the removed connections.
synchronized (deadPool_)
{
Iterator deadConnections = deadPool_.iterator();
while (deadConnections.hasNext())
{
AS400JDBCPooledConnection poolConnection = (AS400JDBCPooledConnection)deadConnections.next();
closePooledConnection(poolConnection);
deadConnections.remove();
}
}
}
//@A1A
/**
* Run cleanupConnections().
* @param reduced true if need to check current num connections; false otherwise.
**/
void runMaintenance(boolean reduced)
{
if (maintenance_ != null && maintenance_.isRunning())
{
synchronized(maintenance_)
{
if (reduced)
reduceConnectionCount(); // Check to see if number of available connections needs adjusting.
maintenance_.notify();
}
}
}
/**
* Sets the data source used to make connections.
* @param dataSource The AS400JDBCConnectionPoolDataSource object.
* @exception PropertyVetoException If a change is vetoed.
**/
public void setDataSource(AS400JDBCConnectionPoolDataSource dataSource) throws PropertyVetoException
{
String property = "dataSource";
if (dataSource == null)
throw new NullPointerException(property);
if (isInUse())
{
JDTrace.logInformation (this, "Connection pool data source is already in use."); // @B5C
throw new ExtendedIllegalStateException(property, ExtendedIllegalStateException.PROPERTY_NOT_CHANGED);
}
AS400JDBCConnectionPoolDataSource old = dataSource_;
dataSource_ = dataSource;
if(changes_ != null) //@K1A
changes_.firePropertyChange(property, old, dataSource);
}
}