[Onejava Studio Web][JAVA]进行改进的新版连接池范例

优化了一些性能,增加了处理unclean Connection的功能,还有一个功能正在调试中,即取得Connection时,连接池已满,使用Thread进行等待,该接口已经实现,但还没完成测试完毕。
回复标题:一个功能十分强大的数据库连接池,Netwiser改写自(Java Servlet Server Programming),实现了Singleton模式。(1101877)


内容:package com.dreamwork.base;

/**
* Title: DreamWork
* Description:
* Copyright: Copyright (c) 2001
* Company: DreamWork
* @author James
* @version 1.0
*/



import java.sql.*;
import java.util.*;
import java.io.*;
import com.dreamwork.base.exception.ConfigLoadException;



public class ConnectionPool implements TimerListener
{
// Keep a static ConnectionPool for singleton
private static ConnectionPool connectionPool;
// JDBC Driver name
String jdbcDriver;

String jdbcUser ="sa";

String jdbcPassword ="";

// JDBC Connection URL
String jdbcConnectionURL;

// 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;

// The connection pool scan interval;
int scanInterval;

// 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 Timer object
Timer timer;

/**
* Constructor
*/
private ConnectionPool() throws Exception
{
initialize();
}

private ConnectionPool(String config) throws Exception{
initialize(config);
}

/**
* <p>Initializes the ConnectionPool object using
* 'database.conf' under the WEB-INF/classes as the configuration file
*
* @return true if the ConnectionPool was initialized
* properly
*/

private boolean initialize() throws Exception
{
return initialize("database.conf");
}

/**
* <p>Initializes the ConnectionPool object with the specified
* configuration file
*
* @param config Configuration file name
* @return true if the ConnectionPool was initialized
* properly
*/
private 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
Logger.println("Creating the pool....");
createPool();

// Start our timer so we can timeout connections. The
// clock cycle will be 20 seconds
timer = new Timer(this, scanInterval);
timer.start();
}

return rc;
}

/**
* <p>Destroys the pool and it's contents. Closes any open
* JDBC connections and frees all resources
* This method is invoked by the Servlet or other class to destroy and rebuild the ConnectionPool
*/
public void destroy()
{
try {
// Stop our timer thread
if (timer != null)
{
timer.destroy();
timer = null;
}
// 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;
connectionPool = null;
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* Create single ConnectionPool if there is not ConnectionPool
* @param none
* @return Static ConnectionPool
*/

public static ConnectionPool getInstance() throws Exception {
if (connectionPool == null){
connectionPool = new ConnectionPool();
}
return connectionPool;
}

/**
* <p>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;
}

java.sql.Connection con = null;
ConnectionObject connectionObject = null;
//temp variable for object exchange
ConnectionObject co = null;
int poolSize = pool.size();

// Get the next available connection
for (int i = 0; i < poolSize; i++)
{

// Get the ConnectionObject from the pool
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;
}
} //end of for

// 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 {
Logger.println("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); //set the last time accessed
con = connectionObject.con;
}

return con; //if maximum number of connections exceeded, return null
}//end of getConnection method

/**
* <p>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 aCon)
{
// Find the connection in the pool
int index = find(aCon);

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))
{
Logger.println("Connection use count exceeded");
removeFromPool(index);
}
else
{
// Clear the use count and reset the time last used
touch(co);
co.inUse = false;
}
}
}

/**
* <p>Prints the contents of the connection pool to the
* standard output device, test the Pool content
*/
public void printPool()
{
System.out.println("--ConnectionPool--");
if (pool != null)
{
for (int i = 0; i < pool.size(); i++)
{
ConnectionObject co = (ConnectionObject) pool.elementAt(i);
System.out.println("" + i + "=" + co);
}
}
}

/**
* <p>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() && (index >= 0))
{

// Get the ConnectionObject and close the connection
ConnectionObject co = (ConnectionObject)pool.elementAt(index);
close(co);

// Remove the element from the pool
pool.removeElementAt(index);
}
}
}

/**
* <p>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;
}
}
}

/**
* <p>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 com.dreamwork.base.exception.ConfigLoadException
{
Config lc = new Config();
boolean rc = false ;
try
{
jdbcProperties = lc.getConfigProperties(name);
if (jdbcProperties == null) rc = false;
else
{
jdbcDriver = consume(jdbcProperties, "DriverName");
jdbcUser = consume(jdbcProperties,"UserName");
jdbcPassword = consume(jdbcProperties,"UserPassword");
jdbcConnectionURL = consume(jdbcProperties,"URL");
connectionPoolSize = consumeInt(jdbcProperties,"ConnectionPoolSize");
connectionPoolMax = consumeInt(jdbcProperties, "ConnectionPoolMax");
connectionUseCount = consumeInt(jdbcProperties,"ConnectionUseCount");
connectionTimeout = consumeInt(jdbcProperties, "ConnectionTimeout");
scanInterval = consumeInt(jdbcProperties,"ScanInterval");
rc = true;
}
}catch(ConfigLoadException e){
Logger.println("<Exception>[ConnectionPool]Exception occured while loading config"+e.getMessage()); // Clean memory
throw new com.dreamwork.base.exception.ConfigLoadException("Exception while loading the config");
}catch(Exception e){
Logger.println("<Exception>[ConnectionPool]Exception occured while loading config" + e.getMessage()); // Clean memory
throw new com.dreamwork.base.exception.ConfigLoadException("Exception while loading the config");
}
jdbcProperties = null;
return rc;
}
/**
* <p>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(java.util.Properties p, String key)
{
String s = null;

if ((p != null) && (key != null))
{
// Get the value of the key
s = p.getProperty(key);
}
return s;
}

/**
* <p>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(java.util.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 (NumberFormatException ex) {
Logger.println("Exception occured while convert the Integer in ConnectionPool " + ex.getMessage());
}
}
return n;
}

/**
* <p>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 (jdbcDriver == null)
{
throw new Exception("JDBCDriver property not found");
}
if (jdbcConnectionURL == 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)
{
Logger.println("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
Logger.println("JDBCDriver = " + jdbcDriver);
Logger.println("JDBCConnectionURL = " + jdbcConnectionURL);
Logger.println("UserName = "+jdbcUser);
Logger.println("Password = "+jdbcPassword);
Logger.println("ConnectionPoolSize = " + connectionPoolSize);
Logger.println("ConnectionPoolMax = " + connectionPoolMax);
Logger.println("ConnectionUseCount = " + connectionUseCount);
Logger.println("ConnectionTimeout = " + connectionTimeout + " seconds");
Logger.println("ScanInterval = " + scanInterval + " seconds");


// 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
Logger.println("Registering " + jdbcDriver);

java.sql.Driver d = (java.sql.Driver)Class.forName(jdbcDriver).newInstance();

// Create the vector for the pool
pool = new java.util.Vector();

// Bring the pool to the minimum size
fillPool(connectionPoolSize);
}

/**
* <p>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 , the order from 0
if (size == pool.size())
{
index = size - 1;
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return index;
}

/**
* <p>Brings the pool to the given size
*
* @size Size of the pool entry
*/
private synchronized void fillPool(int size) throws Exception
{

// If the only properties present are the user id and
// password, get the connection using them instead of
// the properties object
// If username and userid are null, then we use the default value
if (jdbcUser == null) jdbcUser = "sa";
if (jdbcPassword == null) jdbcPassword = "";

// Loop while we need to create more connections
while (pool.size() < size) {

ConnectionObject co = new ConnectionObject();

// Create the connection
co.con = DriverManager.getConnection(jdbcConnectionURL, jdbcUser, jdbcPassword);

// 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
java.sql.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))
{
Logger.println("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);
}
}

/**
* <p>Find the given connection in the pool
*
* @return Index into the pool, or -1 if not found
*/
private int find(java.sql.Connection aCon)
{
int index = -1;

// Find the matching Connection in the pool
if ((aCon != null) && (pool != null))
{
for (int i = 0; i < pool.size(); i++)
{
ConnectionObject co = (ConnectionObject) pool.elementAt(i);
if (co.con == aCon)
{
index = i;
break;
}
}
}
return index;
}

/**
* <p>Called by the timer each time a clock cycle expires.
* This gives us the opportunity to timeout connections
*/
public synchronized void TimerEvent(Object object)
{
// 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 ((connectionTimeout > 0) && (co.lastAccess +(connectionTimeout * 1000) < now))
{
//removeFromPool(i);
// Do not remove the connection from the ConnectionPool
touch(co);
co.inUse = false;
}

// Remove any connections that are no longer open
try
{
// If the connection is closed, remove it from the pool
// If the Connection has warning, close and remove it from pool
if (co.con.isClosed()){
Logger.println("Connection closed unexpectedly");
removeFromPool(i);
}else if(!isClean(co.con)){
Logger.println("Connection is not clear, remove it");
removeFromPool(i);
}
}
catch (Exception ex) {
Logger.println("Exception occured while remove from ConnectionPool");
}
}

// 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)
{
Logger.println("Exception occured while filling the Connection Pool in Timerevent " + ex.getMessage());
}
printPool();
}

/**
* <p>Sets the last access time for the given ConnectionObject
*/
private void touch(ConnectionObject co)
{

if (co != null)
{
co.lastAccess = System.currentTimeMillis();
}
}

/**
* Adjust the Connection is clear or not
* Clear return ture
* Whether return false
* @param <code>Connection</code> aCon
* @return <code>boolean</code>
*/
private boolean isClean(Connection aCon)
{
try{
if (aCon.getWarnings().getErrorCode() != 0){
Logger.println(aCon.getWarnings().getMessage());
Logger.println(String.valueOf(aCon.getWarnings().getErrorCode()));
return false;
}
else return true;
}catch (SQLException e){
return false;
}
}

public static void main(String arg[]) throws Exception
{
ConnectionPool connectionPool = ConnectionPool.getInstance();
System.out.println("Hello World");
connectionPool.printPool();
}

} //End of class ConnectionPool.java


class ConnectionObject
{
// The JDBC Connection
public java.sql.Connection con;

// true if this connection is currently in use
public boolean inUse;

// The last time (in milliseconds) that this connection was used
public long lastAccess;

// The number of times this connection has been used
public int useCount;

/**
* <p>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) {
Logger.println("Exception occured while verify the ConnectionObject in ConnectionObject " + ex.getMessage());
}

return available;
}

/**
* <p>Convert the object contents to a String
*/
public String toString()
{
return "Connection=" + con + ",inUse=" + inUse +",lastAccess=" + lastAccess + ",useCount=" + useCount;
}
}//End of ConntectionObject.java