📄 connectionpoolmanager.java
字号:
/*
DBPool - JDBC Connection Pool Manager
Copyright (c) Giles Winstanley
*/
package snaq.db;
import java.io.*;
import java.sql.*;
import java.text.*;
import java.util.*;
import snaq.util.LogUtil;
/**
* <p>Class to provide access and management for multiple connection pools
* defined in a properties file or object.
* Clients get access to each defined instance through one of the
* static <tt>getInstance()</tt> methods and can then check-out and check-in
* database connections from the pools defined by that manager.</p>
*
* <p>Each successful call to a <tt>getInstance()</tt> method also increments
* an internal counter which keeps a record of the number of clients which hold
* a reference to that particular pool manager. When each client has finished
* using a pool manager it should call the <tt>release()</tt> method to
* let the manager know that it is no longer needed by that client, so it can
* clean up it's resources as necessary. The resources will not be released
* until the clients counter has reached zero. <em>It is therefore necessary to
* allocate and release your pool manager references carefully</em>.</p>
*
* <p>Properties for a manager can be specified in three different ways.
* <ol>
* <li>Properties file located in CLASSPATH
* <li>Properties file referenced explicitly (with a File object)
* <li>Properties object
* </ol>
*
* <ol>
* <li>A CLASSPATH located properties file can simply be accessed using the
* method <tt>getInstance(name)</tt> where <em>name</em> is the name of the
* properties file specified as a string.
* <li>To specify a properties file which is not in the CLASSPATH use the
* method <tt>getInstance(File)</tt>. This same file handle must be used
* each time you want to obtain the instance in this way.
* <li>To specify the pools using a Properties object a call must be made to
* the <tt>createInstance(Properties)</tt> method. This method creates the
* ConnectionPoolManager instance and makes it available via the <tt>getInstance()</tt>
* method.
* </ol>
* <p><b>Note:</b> The <tt>getInstance()</tt> method can return one of two
* possible instances depending on the previous calls made to the pool manager.
* If the <tt>createInstance(Properties)</tt> method has previously been
* called successfully then it will return this manually created instance.
* Otherwise it will attempt to return an instance relating to the default
* properties file (dbpool.properties) within the CLASSPATH, if it exists.</p>
*
* <p>The properties given to the manager specify which JDBC drivers to use to
* access the relevant databases, and also defines the characteristics of each
* connection pool. The properties required/allowed are as follows
* (those marked with * are mandatory):</p>
* <pre>
* drivers* Class names of required JDBC Drivers (comma/space delimited)
* logfile* Filename of logfile
*
* <poolname>.url* The JDBC URL for the database
* <poolname>.user Database username for login
* <poolname>.password Database password for login
* <poolname>.maxpool The maximum number of pooled connections (0 if none)
* <poolname>.maxconn The maximum number of possible connections (0 if no limit)
* <poolname>.init The initial number of connections to create (default:0)
* <poolname>.expiry The connection expiry time in seconds (0 if no expiry)
* <poolname>.validator Class name of connection validator (optional)
* <poolname>.decoder Class name of password decoder (optional)
* <poolname>.cache Whether to cache Statements (optional, default:true)
* <poolname>.async Whether to use asynchronous connection destruction (optional, default:false)
* <poolname>.debug Whether to log debug info (optional, default:false)
* <poolname>.prop.<em>XXX</em> Passes property <em>XXX</em> and it's value to the JDBC driver
* </pre>
*
* <p>Multiple pools can be specified provided they each use a different pool name.
* The <tt>validator</tt> property optionally specifies the name of a
* class to be used for validating the database connections. The default
* connection validation simply performs a test using <tt>Connection.isClosed()</tt>.
* This test is not 100% reliable as the Java API documentation specifies that
* it only returns true if the connection has been explicitly closed.
* If more rigorous connection validation is required you can either use the
* provided class <tt>snaq.db.AutoCommitValidator</tt> or write your own
* validation class which should implement the <tt>ConnectionValidator</tt>
* interface.</p>
*
* @see snaq.db.AutoCommitValidator
* @see snaq.db.ConnectionValidator
* @author Giles Winstanley
*/
public final class ConnectionPoolManager extends LogUtil implements Comparable
{
private static final String PROPERTIES_INSTANCE_KEY = "PROPERTIES_INSTANCE";
private static final String DEFAULT_PROPERTIES_FILE = "/dbpool.properties";
private static Hashtable managers = new Hashtable();
private static Vector drivers = new Vector();
private boolean released = false;
private Hashtable pools = new Hashtable();
protected int clients;
private Object source;
private ConnectionPoolManager(Properties props, Object src)
{
super();
this.source = src;
init(props);
}
/** Returns a descriptive string for this instance. */
public String toString()
{
if (source instanceof String)
return "ConnectionPoolManager [CLASSPATH resource:" + source + "]";
else if (source instanceof File)
return "ConnectionPoolManager [File:" + ((File)source).getAbsolutePath() + "]";
else if (source instanceof Properties)
return "ConnectionPoolManager [Properties]";
else
return "ConnectionPoolManager [Unknown]";
}
/** Compares this instances to other instances by name. */
public int compareTo(Object o) { return this.toString().compareTo(((ConnectionPoolManager)o).toString()); }
/**
* Returns an enumeration of the currently available ConnectionPoolManager instances.
* This method is included for convenience for external monitoring.
* Clients wanting to obtain an instance for using connections should NOT use
* this method.
*/
public static Enumeration instances() { return managers.elements(); }
/**
* Returns the singleton instance of the ConnectionPoolManager for the specified properties file.
* @param propsFile filename of the properties file to use (path info should not be specified; available CLASSPATH will be searched for the properties file)
* @return instance of ConnectionPoolManager relating to the specified properties file
* @throws IOException if there was an problem loading the properties
*/
public static synchronized ConnectionPoolManager getInstance(String propsFile) throws IOException
{
String s = propsFile.startsWith("/") ? propsFile : ("/" + propsFile);
Object o = managers.get(s);
ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager)o : null;
if (cpm == null || cpm.isReleased())
{
cpm = new ConnectionPoolManager(loadProperties(s), propsFile);
managers.put(s, cpm);
}
cpm.clients++;
return cpm;
}
/**
* Returns the singleton instance of the ConnectionPoolManager for the specified properties file.
* @param propsFile filename of the properties file to use (path info should not be specified; available CLASSPATH will be searched for the properties file)
* @return instance of ConnectionPoolManager relating to the specified properties file
* @throws IOException if there was an problem loading the properties
*/
public static synchronized ConnectionPoolManager getInstance(File propsFile) throws IOException
{
Object o = managers.get(propsFile);
ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager)o : null;
if (cpm == null || cpm.isReleased())
{
try
{
cpm = new ConnectionPoolManager(loadProperties(propsFile), propsFile);
managers.put(propsFile, cpm);
}
catch (IOException ioe)
{
if (ioe instanceof FileNotFoundException)
System.err.println("Unable to find the properties file " + propsFile.getAbsolutePath());
else
System.err.println("Error loading the properties file " + propsFile.getAbsolutePath());
ioe.printStackTrace();
return null;
}
}
cpm.clients++;
return cpm;
}
/**
* Returns the standard singleton instance of the ConnectionPoolManager.
* If an instance has been obtained with a user-specified Properties object
* then this instance is returned, otherwise an attempt is made to return an
* instance using the default properties file (dbpool.properties).
* @throws IOException if there was an problem loading the properties
*/
public static synchronized ConnectionPoolManager getInstance() throws IOException
{
Object o = managers.get(PROPERTIES_INSTANCE_KEY);
ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager)o : null;
if (cpm != null && !cpm.released)
cpm.clients++;
else
cpm = getInstance(DEFAULT_PROPERTIES_FILE);
return cpm;
}
/**
* Creates a singleton instance of the ConnectionPoolManager for the specified
* properties file. To subsequently use this instance user's should call the
* getInstance() method. This mechanism is used to provide the maximum
* separation between creation and use of this instance to avoid haphazard
* changes to any referenced Properties onject that may occur between calls.
* (This method can only be used successfully if no default properties
* instance exists and is in use at the time of calling.)
* @param props Properties object to use
* @throws RuntimeException if default properties instance already exists and is in use
*/
public static synchronized void createInstance(Properties props)
{
// Check for presence of default properties file instance
Object o = managers.get(DEFAULT_PROPERTIES_FILE);
ConnectionPoolManager cpm = (o != null) ? (ConnectionPoolManager)o : null;
if (cpm != null && !cpm.isReleased())
throw new RuntimeException("Default properties file instance already exists");
// Create new instance and store reference
cpm = new ConnectionPoolManager(props, props);
managers.put(PROPERTIES_INSTANCE_KEY, cpm);
}
/**
* Loads and returns a Properties object from file.
*/
private static Properties loadProperties(File propsFile) throws IOException
{
if (!propsFile.exists())
throw new FileNotFoundException(propsFile.getAbsolutePath() + " does not exist");
if (propsFile.isDirectory())
throw new IOException("Error accessing properties file - " + propsFile.getAbsolutePath() + " is a directory");
InputStream is = new FileInputStream(propsFile);
Properties props = new Properties();
props.load(is);
is.close();
return props;
}
/**
* Loads and returns a Properties object from the resource specified..
* The resource should be located in the current CLASSPATH to be found.
* @throws IOException if there was an problem loading the properties
*/
private static Properties loadProperties(String propsResource) throws IOException
{
InputStream is = ConnectionPoolManager.class.getResourceAsStream(propsResource);
Properties props = new Properties();
try
{
props.load(is);
}
catch (IOException ioe)
{
System.err.println("Unable to load the properties file. Make sure " + propsResource + " is in the CLASSPATH.");
ioe.printStackTrace();
throw ioe;
}
return props;
}
/**
* Initializes this instance with values from the given Properties object.
*/
private void init(Properties props)
{
String logFile = props.getProperty("logfile", "ConnectionPoolManager.log");
String df = props.getProperty("dateformat", "EEE MMM dd hh:mm:ss.SSS ZZZ yyyy");
try
{
setDateFormat(new SimpleDateFormat(df));
setLog(new FileOutputStream(logFile, true));
}
catch (IOException e)
{
System.err.println("Can't open the log file: " + logFile);
}
loadDrivers(props);
createPools(props);
}
/**
* Loads and registers all JDBC drivers. This is done by the
* DBConnectionManager, as opposed to the ConnectionPool,
* since many pools may share the same driver.
* @param props the connection pool properties
*/
private void loadDrivers(Properties props)
{
String driverClasses = props.getProperty("drivers");
StringTokenizer st = new StringTokenizer(driverClasses, ",: \t\n\r\f");
Enumeration current = DriverManager.getDrivers();
while (st.hasMoreElements())
{
String driverClassName = st.nextToken().trim();
try
{
// Check if driver already registered
boolean using = false;
while (current.hasMoreElements())
{
String cName = current.nextElement().getClass().getName();
if (cName.equals(driverClassName))
using = true;
}
if (!using)
{
Driver driver = (Driver)Class.forName(driverClassName).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
log("Registered JDBC driver " + driverClassName);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -