⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connectionpoolmanager.java

📁 java编写的数据库连接池的源代码。包含有连接池的源码。
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
	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
 *
 * &lt;poolname&gt;.url*        The JDBC URL for the database
 * &lt;poolname&gt;.user        Database username for login
 * &lt;poolname&gt;.password    Database password for login
 * &lt;poolname&gt;.maxpool     The maximum number of pooled connections (0 if none)
 * &lt;poolname&gt;.maxconn     The maximum number of possible connections (0 if no limit)
 * &lt;poolname&gt;.init        The initial number of connections to create (default:0)
 * &lt;poolname&gt;.expiry      The connection expiry time in seconds (0 if no expiry)
 * &lt;poolname&gt;.validator   Class name of connection validator (optional)
 * &lt;poolname&gt;.decoder     Class name of password decoder (optional)
 * &lt;poolname&gt;.cache       Whether to cache Statements (optional, default:true)
 * &lt;poolname&gt;.async       Whether to use asynchronous connection destruction (optional, default:false)
 * &lt;poolname&gt;.debug       Whether to log debug info (optional, default:false)
 * &lt;poolname&gt;.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 + -