connectionpool.java
来自「java编写的数据库连接池的源代码。包含有连接池的源码。」· Java 代码 · 共 489 行
JAVA
489 行
/*
DBPool - JDBC Connection Pool Manager
Copyright (c) Giles Winstanley
*/
package snaq.db;
import snaq.util.*;
import java.sql.*;
import java.util.*;
/**
* Implementation of a database connection pool.
* @see snaq.db.CacheConnection
* @see snaq.db.CachedCallableStatement
* @see snaq.db.CachedPreparedStatement
* @author Giles Winstanley
*/
public class ConnectionPool extends ObjectPool
{
private String url, user, pass;
private Properties props;
private ConnectionValidator validator = new DefaultValidator();
private PasswordDecoder decoder;
private boolean cacheSS, cachePS, cacheCS;
private InitThread initer;
private List listeners = new ArrayList();
/**
* Creates new connection pool.
* @param name pool name
* @param poolSize maximum number of pooled objects, or 0 for no limit
* @param maxSize maximum number of possible objects, or 0 for no limit
* @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
* @param url JDBC connection URL
* @param username database username
* @param password password for the database username supplied
*/
public ConnectionPool(String name, int poolSize, int maxSize, long expiryTime, String url, String username, String password)
{
super(name, poolSize, maxSize, expiryTime);
this.url = url;
this.user = username;
this.pass = password;
this.props = null;
setCaching(true);
addObjectPoolListener(new EventRelay());
}
/**
* Creates new connection pool.
* @param name pool name
* @param poolSize maximum number of pooled objects, or 0 for no limit
* @param maxSize maximum number of possible objects, or 0 for no limit
* @param expiryTime expiry time (milliseconds) for pooled object, or 0 for no expiry
* @param url JDBC connection URL
* @param props connection properties
*/
public ConnectionPool(String name, int poolSize, int maxSize, long expiryTime, String url, Properties props)
{
this(name, poolSize, maxSize, expiryTime, url, null, null);
this.props = props;
this.pass = props.getProperty("password");
addObjectPoolListener(new EventRelay());
}
/**
* Initializes the given number of database connections.
* This spawns a new thread to create them in the background whilst the
* application can continue.
*/
public void init(int num)
{
if (num == 0)
return;
if (num > 0 && num <= getPoolSize())
{
initer = new InitThread(num);
initer.start();
}
else
throw new IllegalArgumentException("Invalid number of connections specifiec");
}
/**
* Creates a new database connection.
*/
protected Reusable create() throws SQLException
{
Connection con = null;
CacheConnection ccon = null;
try
{
if (props != null)
{
if (decoder != null)
props.setProperty("password", new String(decoder.decode(pass)));
con = DriverManager.getConnection(url, props);
}
else if (user != null)
{
try
{
if (decoder != null)
con = DriverManager.getConnection(url, user, new String(decoder.decode(pass)));
else
con = DriverManager.getConnection(url, user, pass);
}
catch (SQLException sqle)
{
log("Failed to connect with standard authentication...trying with just JDBC URL");
con = DriverManager.getConnection(url);
}
}
else
con = DriverManager.getConnection(url);
// Add caching wrapper to connection
ccon = new CacheConnection(this, con);
ccon.setCacheStatements(cacheSS);
ccon.setCachePreparedStatements(cachePS);
ccon.setCacheCallableStatements(cacheCS);
log("Created a new connection");
// Check for warnings
SQLWarning warn = con.getWarnings();
while (warn != null)
{
log("Warning - " + warn.getMessage());
warn = warn.getNextWarning();
}
}
catch (SQLException e)
{
log(e, "Can't create a new connection for " + url);
throw e;
}
return ccon;
}
/**
* Validates a connection.
*/
protected boolean isValid(final Reusable o)
{
if (o == null)
return false;
if (validator == null)
return true;
try
{
boolean valid = validator.isValid((Connection)o);
if (!valid)
fireValidationErrorEvent();
return valid;
}
catch (Exception e) { log(e, "Exception during validation"); return false; }
}
/**
* Sets the validator class for connections.
*/
public void setValidator(ConnectionValidator cv) { validator = cv; }
/**
* Returns the current validator class.
*/
public ConnectionValidator getValidator() { return validator; }
/**
* Sets the password decoder class.
*/
public void setPasswordDecoder(PasswordDecoder pd) { decoder = pd; }
/**
* Returns the current password decoder class.
*/
public PasswordDecoder getPasswordDecoder() { return decoder; }
/**
* Closes the given connection.
*/
protected void destroy(final Reusable o)
{
if (o == null)
return;
try
{
((CacheConnection)o).release();
log("Destroyed connection");
}
catch (SQLException e)
{
log(e, "Can't destroy connection");
}
}
/**
* Gets a connection from the pool.
*/
public Connection getConnection() throws SQLException
{
try
{
Reusable o = super.checkOut();
if (o != null)
{
CacheConnection cc = (CacheConnection)o;
cc.setOpen();
return cc;
}
return null;
}
catch (Exception e)
{
log(e, "Error getting connection");
if (e instanceof SQLException)
throw (SQLException)e;
else
{
Throwable t = e.getCause();
while (t != null)
{
log(e, "Error getting connection");
t = t.getCause();
}
throw new SQLException(e.getMessage());
}
}
}
/**
* Gets a connection from the pool.
*/
public Connection getConnection(long timeout) throws SQLException
{
try
{
Object o = super.checkOut(timeout);
if (o != null)
{
CacheConnection cc = (CacheConnection)o;
cc.setOpen();
return cc;
}
return null;
}
catch (Exception e)
{
if (e instanceof SQLException)
throw (SQLException)e;
else
{
log(e, "Error getting connection");
throw new SQLException(e.getMessage());
}
}
}
/**
* Returns a connection to the pool (for internal use only).
* Connections obtained from the pool should be returned by calling the
* close() method on the connection.
*/
protected void freeConnection(Connection c) throws SQLException
{
if (c == null || !CacheConnection.class.isInstance(c))
log("Attempt to return invalid item");
else
{
CacheConnection cc = (CacheConnection)c;
super.checkIn((Reusable)c);
}
}
/**
* Determines whether to perform statement caching.
* This applies to all types of statements (normal, prepared, callable).
*/
public void setCaching(boolean b)
{
cacheSS = cachePS = cacheCS = b;
}
/**
* Determines whether to perform statement caching.
* @param ss whether to cache Statement objects
* @param ps whether to cache PreparedStatement objects
* @param cs whether to cache CallableStatement objects
*/
public void setCaching(boolean ss, boolean ps, boolean cs)
{
cacheSS = ss;
cachePS = ps;
cacheCS = cs;
}
/**
* Cleans up resources before disposal.
*/
public void finalize()
{
super.finalize();
if (initer != null)
initer.halt();
}
//**************************
// Event-handling methods
//**************************
/**
* Adds an ConnectionPoolListener to the event notification list.
*/
public final void addConnectionPoolListener(ConnectionPoolListener x)
{
listeners.add(x);
}
/**
* Removes an ConnectionPoolListener from the event notification list.
*/
public final void removeConnectionPoolListener(ConnectionPoolListener x)
{
listeners.remove(x);
}
private final void fireValidationErrorEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.VALIDATION_ERROR);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).validationError(poolEvent);
}
private final void firePoolCheckOutEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.CHECKOUT);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).poolCheckOut(poolEvent);
}
private final void firePoolCheckInEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.CHECKIN);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).poolCheckIn(poolEvent);
}
private final void fireMaxPoolLimitReachedEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_POOL_LIMIT_REACHED);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).maxPoolLimitReached(poolEvent);
}
private final void fireMaxPoolLimitExceededEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_POOL_LIMIT_EXCEEDED);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).maxPoolLimitExceeded(poolEvent);
}
private final void fireMaxSizeLimitReachedEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_SIZE_LIMIT_REACHED);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).maxSizeLimitReached(poolEvent);
}
private final void fireMaxSizeLimitErrorEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.MAX_SIZE_LIMIT_ERROR);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).maxSizeLimitError(poolEvent);
}
private final void fireParametersChangedEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.PARAMETERS_CHANGED);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).poolParametersChanged(poolEvent);
}
private final void firePoolReleasedEvent()
{
ConnectionPoolEvent poolEvent = new ConnectionPoolEvent(this, ConnectionPoolEvent.POOL_RELEASED);
for (Iterator iter = listeners.iterator(); iter.hasNext();)
((ConnectionPoolListener)iter.next()).poolReleased(poolEvent);
}
/**
* Class to relay ObjectPoolEvents as ConnectionPoolEvents for convenience.
*/
private final class EventRelay extends ObjectPoolEventAdapter
{
public void poolCheckOut(ObjectPoolEvent evt) { firePoolCheckOutEvent(); }
public void poolCheckIn(ObjectPoolEvent evt) { firePoolCheckInEvent(); }
public void maxPoolLimitReached(ObjectPoolEvent evt) { fireMaxPoolLimitReachedEvent(); }
public void maxPoolLimitExceeded(ObjectPoolEvent evt) { fireMaxPoolLimitExceededEvent(); }
public void maxSizeLimitReached(ObjectPoolEvent evt) { fireMaxSizeLimitReachedEvent(); }
public void maxSizeLimitError(ObjectPoolEvent evt) { fireMaxSizeLimitErrorEvent(); }
public void poolParametersChanged(ObjectPoolEvent evt) { fireParametersChangedEvent(); }
public void poolReleased(ObjectPoolEvent evt) { firePoolReleasedEvent(); listeners.clear(); }
}
/**
* Default implementation of ConnectionValidator.
* This class simply checks a Connection with the <tt>isClosed()</tt> method.
*/
static class DefaultValidator implements ConnectionValidator
{
/**
* Validates a connection.
*/
public boolean isValid(Connection con)
{
try { return !con.isClosed(); }
catch (SQLException e) { return false; }
}
}
/**
* Thread to initialize connections in pool.
*/
private class InitThread extends Thread
{
private int num;
private boolean stopped = false;
private InitThread(int num) { this.num = num; }
public void halt() { stopped = true; }
/**
* Populates the pool with the given number of database connections.
* If the pool already contains open connections then they will be counted
* towards the number created by this method.
*/
public void run()
{
if (num > 0 && num <= getPoolSize() && getSize() < num)
{
log("Populating pool with " + num + " connection" + (num > 1 ? "s" : ""));
ArrayList list = new ArrayList();
Connection con = null;
boolean badCon = false;
// Create connections
while (!stopped && getSize() < num && num <= getPoolSize() && !badCon)
{
try
{
// Wait up to 3 seconds for each new connection
con = getConnection(3000);
if (con == null)
throw new SQLException();
else
{
list.add(con);
if (debug)
log("Created connection " + list.size() + " of " + num);
}
}
catch (SQLException sqle)
{
log(sqle, "Unable to initialize database connections");
badCon = true;
}
}
num = list.size();
// Free connections back to pool
Iterator iter = list.iterator();
while (iter.hasNext())
{
try { ((Connection)iter.next()).close(); }
catch (NullPointerException npe) {}
catch (Exception e) { log(e, "Problem closing connection while populating pool"); }
}
if (debug)
log("Populated pool with " + num + " connection" + (num > 1 ? "s" : ""));
}
initer = null;
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?