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 Show documentation
Show all versions of jt400 Show documentation
The Open Source version of the IBM Toolbox for Java
The newest version!
///////////////////////////////////////////////////////////////////////////////
//
// 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy