package sql;/* * ConnectionPool * * Copyright (c) 1998-2002 Karl Moss. All Rights Reserved. * * Developed in conjunction with the book "Java Servlets Developer's * Guide" published by McGraw-Hill/Osborne Media Group * * You may study, use, modify, and distribute this software for any * purpose. * * This software is provided WITHOUT WARRANTY either expressed or * implied. * * @author Karl Moss * @date Nov 23, 2001 */import java.util.*; import java.sql.*; import java.io.InputStream; import java.io.PrintWriter;/** * This class serves as a JDBC connection repository. Since creating database * connections is one of the most time intensive aspects of JDBC, we'll create a * pool of connections. Each connection can be used and then replaced back into * the pool. * * A properties file 'ConnectionPool.cfg' will be used to specify the types of * JDBC connections to create, as well as the minimum and maximum size of the * pool. The format of the configuration file is: * * #(comment) JDBCDriver=<JDBC driver name> JDBCConnectionURL=<JDBC Connection * URL> ConnectionPoolSize=<minimum size of the pool> ConnectionPoolMax=<maximum * size of the pool, or -1 for none> ConnectionUseCount=<maximum usage count * for one connection> ConnectionTimeout=<maximum idle lifetime (in minutes) of * a connection> <other property for JDBC connection>=<value> * * Any number of additional properties may be given (such as username and * password) as required by the JDBC driver. * */ public class ConnectionPool implements Runnable { // JDBC Driver name String driverName; // JDBC Connection URL String connectionURL; // Minimum size of the pool int connectionPoolSize; // Maximum size of the pool int connectionPoolMax; // Maximum number of uses for a single connection, or -1 for // none int connectionUseCount; // Maximum connection idle time (in minutes) int connectionTimeout; // Additional JDBC properties Properties JDBCProperties; // The Connection pool. This is a vector of ConnectionObject // objects Vector pool; // The maximum number of simultaneous connections as reported // by the JDBC driver int maxConnections = -1; // Our scheduler Scheduler scheduler; // Timeout value public static int TIMEOUT_MS = 20000; /** * Initializes the ConnectionPool object using 'ConnectionPool.cfg' as the * configuration file * * @return true if the ConnectionPool was initialized properly */ public boolean initialize() throws Exception { return initialize("/sql/ConnectionPool.cfg"); } /** * Initializes the ConnectionPool object with the specified configuration * file * * @param config * Configuration file name * @return true if the ConnectionPool was initialized properly */ public boolean initialize(String config) throws Exception { // Load the configuration parameters. Any leftover parameters // from the configuration file will be considered JDBC // connection attributes to be used to establish the // JDBC connections boolean rc = loadConfig(config); if (rc) { // Properties were loaded; attempt to create our pool // of connections createPool(); // Start our timer so we can timeout connections. scheduler = new Scheduler(); scheduler.schedule(this, TIMEOUT_MS); } return rc; } /** * Destroys the pool and it's contents. Closes any open JDBC connections and * frees all resources */ public void destroy() { try { // Clear our pool if (pool != null) { // Loop throught the pool and close each connection for (int i = 0; i < pool.size(); i++) { close((ConnectionObject) pool.elementAt(i)); } } pool = null; } catch (Exception ex) { ex.printStackTrace(); } } /** * Gets an available JDBC Connection. Connections will be created if * necessary, up to the maximum number of connections as specified in the * configuration file. * * @return JDBC Connection, or null if the maximum number of connections has * been exceeded */ public synchronized Connection getConnection() { // If there is no pool it must have been destroyed if (pool == null) { return null; } Connection con = null; ConnectionObject connectionObject = null; int poolSize = pool.size(); // Get the next available connection for (int i = 0; i < poolSize; i++) { // Get the ConnectionObject from the pool ConnectionObject co = (ConnectionObject) pool.elementAt(i); // If this is a valid connection and it is not in use, // grab it if (co.isAvailable()) { connectionObject = co; break; } } // No more available connections. If we aren't at the // maximum number of connections, create a new entry // in the pool if (connectionObject == null) { if ((connectionPoolMax < 0) || ((connectionPoolMax > 0) && (poolSize < connectionPoolMax))) { // Add a new connection. int i = addConnection(); // If a new connection was created, use it if (i >= 0) { connectionObject = (ConnectionObject) pool.elementAt(i); } } else { trace("Maximum number of connections exceeded"); } } // If we have a connection, set the last time accessed, // the use count, and the in use flag if (connectionObject != null) { connectionObject.inUse = true; connectionObject.useCount++; touch(connectionObject); con = connectionObject.con; } return con; } /** * Places the connection back into the connection pool, or closes the * connection if the maximum use count has been reached * * @param Connection * object to close */ public synchronized void close(Connection con) { // Find the connection in the pool int index = find(con); if (index != -1) { ConnectionObject co = (ConnectionObject) pool.elementAt(index); // If the use count exceeds the max, remove it from // the pool. if ((connectionUseCount > 0) && (co.useCount >= connectionUseCount)) { trace("Connection use count exceeded"); removeFromPool(index); } else { // Clear the use count and reset the time last used touch(co); co.inUse = false; } } } /** * Prints the contents of the connection pool to the standard output device */ public void printPool() { printPool(new PrintWriter(System.out)); }
/** * Prints the contents of the connection pool to the given PrintWriter */ public void printPool(PrintWriter out) { out.println("--ConnectionPool--"); if (pool != null) { for (int i = 0; i < pool.size(); i++) { ConnectionObject co = (ConnectionObject) pool.elementAt(i); out.println("" + i + "=" + co); } } } /** * Returns an enumeration of the ConnectionObject objects that represent the * pool */ public Enumeration getConnectionPoolObjects() { return pool.elements(); } /** * Removes the ConnectionObject from the pool at the given index * * @param index * Index into the pool vector */ private synchronized void removeFromPool(int index) { // Make sure the pool and index are valid if (pool != null) { if (index < pool.size()) { // Get the ConnectionObject and close the connection ConnectionObject co = (ConnectionObject) pool.elementAt(index); close(co); // Remove the element from the pool pool.removeElementAt(index); } } } /** * Closes the connection in the given ConnectionObject * * @param connectObject * ConnectionObject */ private void close(ConnectionObject connectionObject) { if (connectionObject != null) { if (connectionObject.con != null) { try { // Close the connection connectionObject.con.close(); } catch (Exception ex) { // Ignore any exceptions during close } // Clear the connection object reference connectionObject.con = null; } } } /** * Loads the given configuration file. All global properties (such as * JDBCDriver) will be read and removed from the properties list. Any * leftover properties will be returned. Returns null if the properties * could not be loaded * * @param name * Configuration file name * @return true if the configuration file was loaded */ private boolean loadConfig(String name) throws Exception { boolean rc = false; // Get our class loader ClassLoader cl = getClass().getClassLoader(); // Attempt to open an input stream to the configuration file. // The configuration file is considered to be a system // resource. InputStream in; if (cl != null) { in = cl.getResourceAsStream(name); } else { in = ClassLoader.getSystemResourceAsStream(name); } // If the input stream is null, then the configuration file // was not found if (in == null) { throw new Exception("ConnectionPool configuration file '" + name + "' not found"); } else { try { JDBCProperties = new Properties(); // Load the configuration file into the properties table JDBCProperties.load(in); // Got the properties. Pull out the properties that we // are interested in driverName = consume(JDBCProperties, "JDBCDriver"); connectionURL = consume(JDBCProperties, "JDBCConnectionURL"); connectionPoolSize = consumeInt(JDBCProperties, "ConnectionPoolSize"); connectionPoolMax = consumeInt(JDBCProperties, "ConnectionPoolMax"); connectionTimeout = consumeInt(JDBCProperties, "ConnectionTimeout"); connectionUseCount = consumeInt(JDBCProperties, "ConnectionUseCount"); rc = true; } finally { // Always close the input stream if (in != null) { try { in.close(); } catch (Exception ex) { } } } } return rc; } /** * Consumes the given property and returns the value. * * @param properties * Properties table * @param key * Key of the property to retrieve and remove from the properties * table * @return Value of the property, or null if not found */ private String consume(Properties p, String key) { String s = null; if ((p != null) && (key != null)) { // Get the value of the key s = p.getProperty(key); // If found, remove it from the properties table if (s != null) { p.remove(key); } } return s; } /** * Consumes the given property and returns the integer value. * * @param properties * Properties table * @param key * Key of the property to retrieve and remove from the properties * table * @return Value of the property, or -1 if not found */ private int consumeInt(Properties p, String key) { int n = -1; // Get the String value String value = consume(p, key); // Got a value; convert to an integer if (value != null) { try { n = Integer.parseInt(value); } catch (Exception ex) { } } return n; } /** * Creates the initial connection pool. A timer thread is also created so * that connection timeouts can be handled. * * @return true if the pool was created */ private void createPool() throws Exception { // Sanity check our properties if (driverName == null) { throw new Exception("JDBCDriver property not found"); } if (connectionURL == null) { throw new Exception("JDBCConnectionURL property not found"); } if (connectionPoolSize < 0) { throw new Exception("ConnectionPoolSize property not found"); } if (connectionPoolSize == 0) { throw new Exception("ConnectionPoolSize invalid"); } if (connectionPoolMax < connectionPoolSize) { trace("WARNING - ConnectionPoolMax is invalid and will " + "be ignored"); connectionPoolMax = -1; } if (connectionTimeout < 0) { // Set the default to 30 minutes connectionTimeout = 30; } // Dump the parameters we are going to use for the pool. // We don't know what type of servlet environment we will // be running in - this may go to the console or it // may be redirected to a log file trace("JDBCDriver = " + driverName); trace("JDBCConnectionURL = " + connectionURL); trace("ConnectionPoolSize = " + connectionPoolSize); trace("ConnectionPoolMax = " + connectionPoolMax); trace("ConnectionUseCount = " + connectionUseCount); trace("ConnectionTimeout = " + connectionTimeout + " seconds"); // Also dump any additional JDBC properties Enumeration enums = JDBCProperties.keys(); while (enums.hasMoreElements()) { String key = (String) enums.nextElement(); String value = JDBCProperties.getProperty(key); trace("(JDBC Property) " + key + " = " + value); } // Attempt to create a new instance of the specified // JDBC driver. Well behaved drivers will register // themselves with the JDBC DriverManager when they // are instantiated trace("Registering " + driverName); Driver d = (Driver) Class.forName(driverName).newInstance(); // Create the vector for the pool pool = new Vector(); // Bring the pool to the minimum size fillPool(connectionPoolSize); }
/** * Adds a new connection to the pool * * @return Index of the new pool entry, or -1 if an error has occurred */ private int addConnection() { int index = -1; try { // Calculate the new size of the pool int size = pool.size() + 1; // Create a new entry fillPool(size); // Set the index pointer to the new connection if one // was created if (size == pool.size()) { index = size - 1; } } catch (Exception ex) { ex.printStackTrace(); } return index; } /** * Brings the pool to the given size */ private synchronized void fillPool(int size) throws Exception { boolean useProperties = true; String userID = null; String password = null; // If the only properties present are the user id and // password, get the connection using them instead of // the properties object if (JDBCProperties != null) { // Make sure there are only 2 properties, and they are // the user id and password if (JDBCProperties.size() == 2) { userID = getPropertyIgnoreCase(JDBCProperties, "user"); password = getPropertyIgnoreCase(JDBCProperties, "password"); // If all we've got is a user id and password then // don't use the properties if ((userID != null) && (password != null)) { useProperties = false; } } } // Loop while we need to create more connections while (pool.size() < size) { ConnectionObject co = new ConnectionObject(); // Create the connection if (useProperties) { co.con = DriverManager.getConnection(connectionURL, JDBCProperties); } else { co.con = DriverManager.getConnection(connectionURL, userID, password); } // Do some sanity checking on the first connection in // the pool if (pool.size() == 0) { // Get the maximum number of simultaneous connections // as reported by the JDBC driver DatabaseMetaData md = co.con.getMetaData(); maxConnections = md.getMaxConnections(); } // Give a warning if the size of the pool will exceed // the maximum number of connections allowed by the // JDBC driver if ((maxConnections > 0) && (size > maxConnections)) { trace("WARNING: Size of pool will exceed safe maximum of " + maxConnections); } // Clear the in use flag co.inUse = false; // Set the last access time touch(co); pool.addElement(co); } } /** * Gets a the named propery, ignoring case. Returns null if not found * * @param p * The property set * @param name * The property name * @return The value of the propery, or null if not found */ private String getPropertyIgnoreCase(Properties p, String name) { if ((p == null) || (name == null)) return null; String value = null; // Get an enumeration of the property names Enumeration enums = p.propertyNames(); // Loop through the enum, looking for the given property name while (enums.hasMoreElements()) { String pName = (String) enums.nextElement(); if (pName.equalsIgnoreCase(name)) { value = p.getProperty(pName); break; } } return value; } /** * Find the given connection in the pool * * @return Index into the pool, or -1 if not found */ private int find(Connection con) { int index = -1; // Find the matching Connection in the pool if ((con != null) && (pool != null)) { for (int i = 0; i < pool.size(); i++) { ConnectionObject co = (ConnectionObject) pool.elementAt(i); if (co.con == con) { index = i; break; } } } return index; } /** * Called by the timer each time a clock cycle expires. This gives us the * opportunity to timeout connections */ public synchronized void run() { // No pool means no work if (pool == null) { return; } // Get the current time in milliseconds long now = System.currentTimeMillis(); // Check for any expired connections and remove them for (int i = pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject) pool.elementAt(i); // If the connection is not in use and it has not been // used recently, remove it if (!co.inUse) { if ((connectionTimeout > 0) && (co.lastAccess + (connectionTimeout * 1000) < now)) { removeFromPool(i); } } } // Remove any connections that are no longer open for (int i = pool.size() - 1; i >= 0; i--) { ConnectionObject co = (ConnectionObject) pool.elementAt(i); try { // If the connection is closed, remove it from the pool if (co.con.isClosed()) { trace("Connection closed unexpectedly"); removeFromPool(i); } } catch (Exception ex) { } } // Now ensure that the pool is still at it's minimum size try { if (pool != null) { if (pool.size() < connectionPoolSize) { fillPool(connectionPoolSize); } } } catch (Exception ex) { ex.printStackTrace(); } // Reschedule ourselves scheduler.schedule(this, TIMEOUT_MS); } /** * Sets the last access time for the given ConnectionObject */ private void touch(ConnectionObject co) { if (co != null) { co.lastAccess = System.currentTimeMillis(); } } /** * Trace the given string */ private void trace(String s) { System.out.println("ConnectionPool: " + s); } }
package sql;/* * ConnectionObject * * Copyright (c) 1998-2002 Karl Moss. All Rights Reserved. * * Developed in conjunction with the book "Java Servlets Developer's * Guide" published by McGraw-Hill/Osborne Media Group * * You may study, use, modify, and distribute this software for any * purpose. * * This software is provided WITHOUT WARRANTY either expressed or * implied. * * @author Karl Moss * @date Nov 23, 2001 */ import java.sql.*;/** * Represents a single connection object which is stored * in a connection pool */ public class ConnectionObject { // The JDBC Connection protected Connection con; // true if this connection is currently in use protected boolean inUse; // The last time (in milliseconds) that this connection was used protected long lastAccess; // The number of times this connection has been used protected int useCount; public boolean isInUse() { return inUse; } public int getUseCount() { return useCount; } public long getLastAccess() { return lastAccess; } /** * Determine if the connection is available * * @return true if the connection can be used */ public boolean isAvailable() { boolean available = false; try { // To be available, the connection cannot be in use // and must be open if (con != null) { if (!inUse && !con.isClosed()) { available = true; } } } catch (Exception ex) { } return available; } /** * Convert the object contents to a String */ public String toString() { return "Connection=" + con + ",inUse=" + inUse + ",lastAccess=" + lastAccess + ",useCount=" + useCount; } }
* ConnectionPool
*
* Copyright (c) 1998-2002 Karl Moss. All Rights Reserved.
*
* Developed in conjunction with the book "Java Servlets Developer's
* Guide" published by McGraw-Hill/Osborne Media Group
*
* You may study, use, modify, and distribute this software for any
* purpose.
*
* This software is provided WITHOUT WARRANTY either expressed or
* implied.
*
* @author Karl Moss
* @date Nov 23, 2001
*/import java.util.*;
import java.sql.*;
import java.io.InputStream;
import java.io.PrintWriter;/**
* This class serves as a JDBC connection repository. Since creating database
* connections is one of the most time intensive aspects of JDBC, we'll create a
* pool of connections. Each connection can be used and then replaced back into
* the pool.
*
* A properties file 'ConnectionPool.cfg' will be used to specify the types of
* JDBC connections to create, as well as the minimum and maximum size of the
* pool. The format of the configuration file is:
*
* #(comment) JDBCDriver=<JDBC driver name> JDBCConnectionURL=<JDBC Connection
* URL> ConnectionPoolSize=<minimum size of the pool> ConnectionPoolMax=<maximum
* size of the pool, or -1 for none> ConnectionUseCount=<maximum usage count
* for one connection> ConnectionTimeout=<maximum idle lifetime (in minutes) of
* a connection> <other property for JDBC connection>=<value>
*
* Any number of additional properties may be given (such as username and
* password) as required by the JDBC driver.
*
*/
public class ConnectionPool implements Runnable {
// JDBC Driver name
String driverName; // JDBC Connection URL
String connectionURL; // Minimum size of the pool
int connectionPoolSize; // Maximum size of the pool
int connectionPoolMax; // Maximum number of uses for a single connection, or -1 for
// none
int connectionUseCount; // Maximum connection idle time (in minutes)
int connectionTimeout; // Additional JDBC properties
Properties JDBCProperties; // The Connection pool. This is a vector of ConnectionObject
// objects
Vector pool; // The maximum number of simultaneous connections as reported
// by the JDBC driver
int maxConnections = -1; // Our scheduler
Scheduler scheduler; // Timeout value
public static int TIMEOUT_MS = 20000; /**
* Initializes the ConnectionPool object using 'ConnectionPool.cfg' as the
* configuration file
*
* @return true if the ConnectionPool was initialized properly
*/
public boolean initialize() throws Exception {
return initialize("/sql/ConnectionPool.cfg");
} /**
* Initializes the ConnectionPool object with the specified configuration
* file
*
* @param config
* Configuration file name
* @return true if the ConnectionPool was initialized properly
*/
public boolean initialize(String config) throws Exception {
// Load the configuration parameters. Any leftover parameters
// from the configuration file will be considered JDBC
// connection attributes to be used to establish the
// JDBC connections
boolean rc = loadConfig(config); if (rc) {
// Properties were loaded; attempt to create our pool
// of connections
createPool(); // Start our timer so we can timeout connections.
scheduler = new Scheduler();
scheduler.schedule(this, TIMEOUT_MS);
}
return rc;
} /**
* Destroys the pool and it's contents. Closes any open JDBC connections and
* frees all resources
*/
public void destroy() {
try {
// Clear our pool
if (pool != null) {
// Loop throught the pool and close each connection
for (int i = 0; i < pool.size(); i++) {
close((ConnectionObject) pool.elementAt(i));
}
}
pool = null;
} catch (Exception ex) {
ex.printStackTrace();
}
} /**
* Gets an available JDBC Connection. Connections will be created if
* necessary, up to the maximum number of connections as specified in the
* configuration file.
*
* @return JDBC Connection, or null if the maximum number of connections has
* been exceeded
*/
public synchronized Connection getConnection() {
// If there is no pool it must have been destroyed
if (pool == null) {
return null;
} Connection con = null;
ConnectionObject connectionObject = null;
int poolSize = pool.size(); // Get the next available connection
for (int i = 0; i < poolSize; i++) { // Get the ConnectionObject from the pool
ConnectionObject co = (ConnectionObject) pool.elementAt(i); // If this is a valid connection and it is not in use,
// grab it
if (co.isAvailable()) {
connectionObject = co;
break;
}
} // No more available connections. If we aren't at the
// maximum number of connections, create a new entry
// in the pool
if (connectionObject == null) {
if ((connectionPoolMax < 0)
|| ((connectionPoolMax > 0) && (poolSize < connectionPoolMax))) {
// Add a new connection.
int i = addConnection(); // If a new connection was created, use it
if (i >= 0) {
connectionObject = (ConnectionObject) pool.elementAt(i);
}
} else {
trace("Maximum number of connections exceeded");
}
} // If we have a connection, set the last time accessed,
// the use count, and the in use flag
if (connectionObject != null) {
connectionObject.inUse = true;
connectionObject.useCount++;
touch(connectionObject);
con = connectionObject.con;
} return con;
} /**
* Places the connection back into the connection pool, or closes the
* connection if the maximum use count has been reached
*
* @param Connection
* object to close
*/
public synchronized void close(Connection con) {
// Find the connection in the pool
int index = find(con); if (index != -1) {
ConnectionObject co = (ConnectionObject) pool.elementAt(index); // If the use count exceeds the max, remove it from
// the pool.
if ((connectionUseCount > 0) && (co.useCount >= connectionUseCount)) {
trace("Connection use count exceeded");
removeFromPool(index);
} else {
// Clear the use count and reset the time last used
touch(co);
co.inUse = false;
}
}
} /**
* Prints the contents of the connection pool to the standard output device
*/
public void printPool() {
printPool(new PrintWriter(System.out));
}
* Prints the contents of the connection pool to the given PrintWriter
*/
public void printPool(PrintWriter out) {
out.println("--ConnectionPool--");
if (pool != null) {
for (int i = 0; i < pool.size(); i++) {
ConnectionObject co = (ConnectionObject) pool.elementAt(i);
out.println("" + i + "=" + co);
}
}
} /**
* Returns an enumeration of the ConnectionObject objects that represent the
* pool
*/
public Enumeration getConnectionPoolObjects() {
return pool.elements();
} /**
* Removes the ConnectionObject from the pool at the given index
*
* @param index
* Index into the pool vector
*/
private synchronized void removeFromPool(int index) {
// Make sure the pool and index are valid
if (pool != null) {
if (index < pool.size()) {
// Get the ConnectionObject and close the connection
ConnectionObject co = (ConnectionObject) pool.elementAt(index);
close(co); // Remove the element from the pool
pool.removeElementAt(index);
}
}
} /**
* Closes the connection in the given ConnectionObject
*
* @param connectObject
* ConnectionObject
*/
private void close(ConnectionObject connectionObject) {
if (connectionObject != null) {
if (connectionObject.con != null) {
try {
// Close the connection
connectionObject.con.close();
} catch (Exception ex) {
// Ignore any exceptions during close
} // Clear the connection object reference
connectionObject.con = null;
}
}
} /**
* Loads the given configuration file. All global properties (such as
* JDBCDriver) will be read and removed from the properties list. Any
* leftover properties will be returned. Returns null if the properties
* could not be loaded
*
* @param name
* Configuration file name
* @return true if the configuration file was loaded
*/
private boolean loadConfig(String name) throws Exception {
boolean rc = false; // Get our class loader
ClassLoader cl = getClass().getClassLoader(); // Attempt to open an input stream to the configuration file.
// The configuration file is considered to be a system
// resource.
InputStream in; if (cl != null) {
in = cl.getResourceAsStream(name);
} else {
in = ClassLoader.getSystemResourceAsStream(name);
} // If the input stream is null, then the configuration file
// was not found
if (in == null) {
throw new Exception("ConnectionPool configuration file '" + name
+ "' not found");
} else {
try {
JDBCProperties = new Properties(); // Load the configuration file into the properties table
JDBCProperties.load(in); // Got the properties. Pull out the properties that we
// are interested in
driverName = consume(JDBCProperties, "JDBCDriver");
connectionURL = consume(JDBCProperties, "JDBCConnectionURL");
connectionPoolSize = consumeInt(JDBCProperties,
"ConnectionPoolSize");
connectionPoolMax = consumeInt(JDBCProperties,
"ConnectionPoolMax");
connectionTimeout = consumeInt(JDBCProperties,
"ConnectionTimeout");
connectionUseCount = consumeInt(JDBCProperties,
"ConnectionUseCount");
rc = true;
} finally {
// Always close the input stream
if (in != null) {
try {
in.close();
} catch (Exception ex) {
}
}
}
}
return rc;
} /**
* Consumes the given property and returns the value.
*
* @param properties
* Properties table
* @param key
* Key of the property to retrieve and remove from the properties
* table
* @return Value of the property, or null if not found
*/
private String consume(Properties p, String key) {
String s = null; if ((p != null) && (key != null)) { // Get the value of the key
s = p.getProperty(key); // If found, remove it from the properties table
if (s != null) {
p.remove(key);
}
}
return s;
} /**
* Consumes the given property and returns the integer value.
*
* @param properties
* Properties table
* @param key
* Key of the property to retrieve and remove from the properties
* table
* @return Value of the property, or -1 if not found
*/
private int consumeInt(Properties p, String key) {
int n = -1; // Get the String value
String value = consume(p, key); // Got a value; convert to an integer
if (value != null) {
try {
n = Integer.parseInt(value);
} catch (Exception ex) {
}
}
return n;
} /**
* Creates the initial connection pool. A timer thread is also created so
* that connection timeouts can be handled.
*
* @return true if the pool was created
*/
private void createPool() throws Exception {
// Sanity check our properties
if (driverName == null) {
throw new Exception("JDBCDriver property not found");
}
if (connectionURL == null) {
throw new Exception("JDBCConnectionURL property not found");
}
if (connectionPoolSize < 0) {
throw new Exception("ConnectionPoolSize property not found");
}
if (connectionPoolSize == 0) {
throw new Exception("ConnectionPoolSize invalid");
}
if (connectionPoolMax < connectionPoolSize) {
trace("WARNING - ConnectionPoolMax is invalid and will "
+ "be ignored");
connectionPoolMax = -1;
}
if (connectionTimeout < 0) {
// Set the default to 30 minutes
connectionTimeout = 30;
} // Dump the parameters we are going to use for the pool.
// We don't know what type of servlet environment we will
// be running in - this may go to the console or it
// may be redirected to a log file
trace("JDBCDriver = " + driverName);
trace("JDBCConnectionURL = " + connectionURL);
trace("ConnectionPoolSize = " + connectionPoolSize);
trace("ConnectionPoolMax = " + connectionPoolMax);
trace("ConnectionUseCount = " + connectionUseCount);
trace("ConnectionTimeout = " + connectionTimeout + " seconds"); // Also dump any additional JDBC properties
Enumeration enums = JDBCProperties.keys();
while (enums.hasMoreElements()) {
String key = (String) enums.nextElement();
String value = JDBCProperties.getProperty(key);
trace("(JDBC Property) " + key + " = " + value);
} // Attempt to create a new instance of the specified
// JDBC driver. Well behaved drivers will register
// themselves with the JDBC DriverManager when they
// are instantiated
trace("Registering " + driverName);
Driver d = (Driver) Class.forName(driverName).newInstance(); // Create the vector for the pool
pool = new Vector(); // Bring the pool to the minimum size
fillPool(connectionPoolSize);
}
/**
* Adds a new connection to the pool
*
* @return Index of the new pool entry, or -1 if an error has occurred
*/
private int addConnection() {
int index = -1; try {
// Calculate the new size of the pool
int size = pool.size() + 1; // Create a new entry
fillPool(size); // Set the index pointer to the new connection if one
// was created
if (size == pool.size()) {
index = size - 1;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return index;
} /**
* Brings the pool to the given size
*/
private synchronized void fillPool(int size) throws Exception {
boolean useProperties = true;
String userID = null;
String password = null; // If the only properties present are the user id and
// password, get the connection using them instead of
// the properties object
if (JDBCProperties != null) {
// Make sure there are only 2 properties, and they are
// the user id and password
if (JDBCProperties.size() == 2) {
userID = getPropertyIgnoreCase(JDBCProperties, "user");
password = getPropertyIgnoreCase(JDBCProperties, "password"); // If all we've got is a user id and password then
// don't use the properties
if ((userID != null) && (password != null)) {
useProperties = false;
}
}
} // Loop while we need to create more connections
while (pool.size() < size) {
ConnectionObject co = new ConnectionObject(); // Create the connection
if (useProperties) {
co.con = DriverManager.getConnection(connectionURL,
JDBCProperties);
} else {
co.con = DriverManager.getConnection(connectionURL, userID,
password);
} // Do some sanity checking on the first connection in
// the pool
if (pool.size() == 0) {
// Get the maximum number of simultaneous connections
// as reported by the JDBC driver
DatabaseMetaData md = co.con.getMetaData();
maxConnections = md.getMaxConnections();
} // Give a warning if the size of the pool will exceed
// the maximum number of connections allowed by the
// JDBC driver
if ((maxConnections > 0) && (size > maxConnections)) {
trace("WARNING: Size of pool will exceed safe maximum of "
+ maxConnections);
} // Clear the in use flag
co.inUse = false; // Set the last access time
touch(co); pool.addElement(co);
}
} /**
* Gets a the named propery, ignoring case. Returns null if not found
*
* @param p
* The property set
* @param name
* The property name
* @return The value of the propery, or null if not found
*/
private String getPropertyIgnoreCase(Properties p, String name) {
if ((p == null) || (name == null))
return null; String value = null; // Get an enumeration of the property names
Enumeration enums = p.propertyNames(); // Loop through the enum, looking for the given property name
while (enums.hasMoreElements()) {
String pName = (String) enums.nextElement();
if (pName.equalsIgnoreCase(name)) {
value = p.getProperty(pName);
break;
}
} return value;
} /**
* Find the given connection in the pool
*
* @return Index into the pool, or -1 if not found
*/
private int find(Connection con) {
int index = -1; // Find the matching Connection in the pool
if ((con != null) && (pool != null)) {
for (int i = 0; i < pool.size(); i++) {
ConnectionObject co = (ConnectionObject) pool.elementAt(i);
if (co.con == con) {
index = i;
break;
}
}
}
return index;
} /**
* Called by the timer each time a clock cycle expires. This gives us the
* opportunity to timeout connections
*/
public synchronized void run() {
// No pool means no work
if (pool == null) {
return;
} // Get the current time in milliseconds
long now = System.currentTimeMillis(); // Check for any expired connections and remove them
for (int i = pool.size() - 1; i >= 0; i--) {
ConnectionObject co = (ConnectionObject) pool.elementAt(i); // If the connection is not in use and it has not been
// used recently, remove it
if (!co.inUse) {
if ((connectionTimeout > 0)
&& (co.lastAccess + (connectionTimeout * 1000) < now)) {
removeFromPool(i);
}
}
} // Remove any connections that are no longer open
for (int i = pool.size() - 1; i >= 0; i--) {
ConnectionObject co = (ConnectionObject) pool.elementAt(i);
try {
// If the connection is closed, remove it from the pool
if (co.con.isClosed()) {
trace("Connection closed unexpectedly");
removeFromPool(i);
}
} catch (Exception ex) {
}
} // Now ensure that the pool is still at it's minimum size
try {
if (pool != null) {
if (pool.size() < connectionPoolSize) {
fillPool(connectionPoolSize);
}
}
} catch (Exception ex) {
ex.printStackTrace();
} // Reschedule ourselves
scheduler.schedule(this, TIMEOUT_MS);
} /**
* Sets the last access time for the given ConnectionObject
*/
private void touch(ConnectionObject co) {
if (co != null) {
co.lastAccess = System.currentTimeMillis();
}
} /**
* Trace the given string
*/
private void trace(String s) {
System.out.println("ConnectionPool: " + s);
}
}
* ConnectionObject
*
* Copyright (c) 1998-2002 Karl Moss. All Rights Reserved.
*
* Developed in conjunction with the book "Java Servlets Developer's
* Guide" published by McGraw-Hill/Osborne Media Group
*
* You may study, use, modify, and distribute this software for any
* purpose.
*
* This software is provided WITHOUT WARRANTY either expressed or
* implied.
*
* @author Karl Moss
* @date Nov 23, 2001
*/
import java.sql.*;/**
* Represents a single connection object which is stored
* in a connection pool
*/
public class ConnectionObject
{
// The JDBC Connection
protected Connection con; // true if this connection is currently in use
protected boolean inUse; // The last time (in milliseconds) that this connection was used
protected long lastAccess; // The number of times this connection has been used
protected int useCount; public boolean isInUse()
{
return inUse;
} public int getUseCount()
{
return useCount;
} public long getLastAccess()
{
return lastAccess;
} /**
* Determine if the connection is available
*
* @return true if the connection can be used
*/
public boolean isAvailable()
{
boolean available = false; try
{
// To be available, the connection cannot be in use
// and must be open
if (con != null)
{
if (!inUse &&
!con.isClosed())
{
available = true;
}
}
}
catch (Exception ex)
{
} return available;
} /**
* Convert the object contents to a String
*/
public String toString()
{
return "Connection=" + con + ",inUse=" + inUse +
",lastAccess=" + lastAccess + ",useCount=" + useCount;
}
}