티스토리 뷰
import java.sql.DriverManager;
import java.sql.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
/**
* This class defines a pooling mechanism for JDBC Connection objects.
*
* <P>
* This class provides the user with a great deal of flexibility.
* The user can:
* <UL>
* <LI>specify the [min,max] range of number of connections in the pool
* <LI>acquire connections with three mechanisms: (a) no wait (throws
* a {@link ConnNotAvailException} if no connection is immediately available),
* (see {@link #getConnectionNoWait()})
* (b) wait indefinitely (at least until the pool is shutdown), or
* (see {@link #getConnectionWait()})
* (c) wait for a specified amount of time
* (see {@link #getConnectionWait(int)})
* <LI>specify a "courtesy shutdown wait period"
* (see {@link MaintenanceRunner#shutdownCourtesyTimeout})
* </UL>
* </P>
*
* <P>
* This class uses a "closed synchronization" idiom. All of the non-trivial
* public methods are synchronized and none of the private methods are.
* </P>
*
* <P>Revision History:
* <UL>
* <LI>v1.4 [21-Jan-2001, BB] Fixed the {@link MaintenanceRunner} behavior
* to keep the minimum number of connections in the free list.
* <LI>v1.3 [20-Jan-2001, BB] Dropped the singular <TT>getConnection</TT>
* method, but promoted the {@link #getConnectionNoWait()} and the
* {@link #getConnectionWait()} methods to public access and added the
* {@link #getConnectionWait(int)} method to round out the functionality.
* <LI>v1.2 [19-Jan-2001, BB] Implemented the {@link MaintenanceRunner}
* inner class and {@link #shutdown()} method and fixed several bugs
* <LI>v1.1 [Nov-2000, BB] Scraped most of the old code as it used old
* Collections classes and had a poorly designed "maintenance" mechanism.
* <LI>v1.0 [Oct-2000, BB] First cut; based on code from the SL-310 course
* from Sun Educational Services.
* </UL>
*
* @author Bryan Basham <b_basham@yahoo.com>
* @version 1.4 21-Jan-2001
*/
public class ConnectionPool {
/**
* This constant defines the default minimum number of connections.
*/
private static final int DEFAULT_MIN_CONN = 5;
/**
* This constant defines the default maximum number of connections.
*/
private static final int DEFAULT_MAX_CONN = 20;
//
// basic data attributes (properties)
//
/**
* This read-only property records the fully-qualified JDBC Driver class.
*/
private String jdbcDriverClass;
/**
* This read-only property records the database connection URL for this pool.
*/
private String jdbcURL;
/**
* This read-only property records the database connection user-name for this pool.
*/
private String jdbcUserName;
/**
* This read-only property records the database connection password for this pool.
*/
private String jdbcPassword;
/**
* This read-only property records the minimum number of connections in this pool.
*/
private int minimumConnections;
/**
* This read-only property records the maximum number of connections allowed in this pool.
*/
private int maximumConnections;
//
// internal implementation attributes
//
/**
* This stores the available database connections.
*/
private transient List freeConnections = new LinkedList();
/**
* This stores the connections currently being used by the client.
* The mapping is Connection-<Long, where the Long is start time
* that the connection was allocated to the client.
*/
private transient Map usedConnections = new HashMap();
/**
* This Boolean attribute is set when the {@link #shutdown()} method is called.
* It is used to tell this object that no new connections should be granted.
* It also tells the {@link MaintenanceRunner} thread to begin the shutdown
* process.
*/
private transient boolean shuttingDown = false;
/**
* This Boolean attribute is set when the {@link MaintenanceRunner} thread
* has completed the shutdown process.
*/
private transient boolean shutdownComplete = false;
/**
* This holds the thread running the MaintenanceRunner object.
*/
private transient Thread maintThread = null;
//
// Constructors
//
/**
* Constructor for the ConnectionPool.
* <P>
* It is up to the client using the ConnectionPool to register the
* necessary JDBC drivers before using this class.
* </P>
*
* @param jdbcDriverClass The JDBC fully-qualified Driver class name
* @param jdbcURL The JDBC connect string. e.g. 'jdbc:pointbase:SL314'
* @param jdbcUserName Database login name. e.g. 'Scott'
* @param jdbcPassword Database password. e.g. 'Tiger'
* @param minimumConnections Minimum number of connections to start with.
* @param maximumConnections Maximum number of connections in dynamic pool.
* @param shutdownCourtesyTimeout The "courtesy shutdown wait period"
*
* @exception ClassNotFoundException Thrown if the jdbcDriverClass is not
* found in the CLASSPATH
* @exception InstantiationException Thrown if the jdbcDriverClass is not
* a concrete class
* @exception IllegalAccessException Thrown if the jdbcDriverClass or its
* initializer is not accessible
* @exception SQLException Thrown on a JDBC exception
*/
public ConnectionPool(String jdbcDriverClass, String jdbcURL,
String jdbcUserName, String jdbcPassword,
int minimumConnections, int maximumConnections,
int shutdownCourtesyTimeout)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, SQLException {
//System.out.println("[CP] in constructor");
// Initialize basic attributes
this.jdbcDriverClass = jdbcDriverClass;
this.jdbcURL = jdbcURL;
this.jdbcUserName = jdbcUserName;
this.jdbcPassword = jdbcPassword;
this.minimumConnections = minimumConnections;
this.maximumConnections = maximumConnections;
// Register the JDBC driver with the DriveManager
Driver driver = (Driver) Class.forName(jdbcDriverClass).newInstance();
DriverManager.registerDriver(driver);
// Initialize implementation attributes
for (int i=0; i < minimumConnections; i++) {
freeConnections.add(makeConnection());
}
// Initialize the CP maintenance thread
maintThread = new Thread(new MaintenanceRunner(shutdownCourtesyTimeout));
maintThread.start();
}
/**
* Constructor for the ConnectionPool.
* <P>
* I tis up to the clien tusing the ConnectionPool to register the
* necessary JDBC drivers before using this class.
* </P>
*
* @param jdbcDriverClass JDBC fully-qualified Driver class name
* @param jdbcURL tJDBC connec tstring. e.g. 'jdbc:pointbase:SL314'
* @param jdbcUserName Database login name. e.g. 'Scott'
* @param jdbcPassword Database password. e.g. 'Tiger'
* @param minimumConnections Minimum number of connections to star twith.
* @param maximumConnections Maximum number of connections in dynamic pool.
*
* @exception ClassNotFoundException Thrown if the jdbcDriverClass is not
* found in the CLASSPATH
* @exception InstantiationException Thrown if the jdbcDriverClass is not
* a concrete class
* @exception IllegalAccessException Thrown if the jdbcDriverClass or its
* initializer is no taccessible
* @exception SQLException Thrown on a JDBC exception
*/
public ConnectionPool(String jdbcDriverClass, String jdbcURL,
String jdbcUserName, String jdbcPassword,
int minimumConnections, int maximumConnections)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, SQLException {
this(jdbcDriverClass, jdbcURL, jdbcUserName, jdbcPassword,
minimumConnections, maximumConnections,
MaintenanceRunner.DEFAULT_SHUTDOWN_TIMEOUT);
}
/**
* Constructor for the ConnectionPool.
* <P>
* I tis up to the clien tusing the ConnectionPool to register the
* necessary JDBC drivers before using this class.
* </P>
*
* @param jdbcDriverClass JDBC fully-qualified Driver class name
* @param jdbcURL tJDBC connec tstring. e.g. 'jdbc:pointbase:SL314'
* @param jdbcUserName Database login name. e.g. 'Scott'
* @param jdbcPassword Database password. e.g. 'Tiger'
*
* @exception ClassNotFoundException Thrown if the jdbcDriverClass is not
* found in the CLASSPATH
* @exception InstantiationException Thrown if the jdbcDriverClass is not
* a concrete class
* @exception IllegalAccessException Thrown if the jdbcDriverClass or its
* initializer is no taccessible
* @exception SQLException Thrown on a JDBC exception
*/
public ConnectionPool(String jdbcDriverClass, String jdbcURL,
String jdbcUserName, String jdbcPassword)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, SQLException {
this(jdbcDriverClass, jdbcURL, jdbcUserName, jdbcPassword,
DEFAULT_MAX_CONN, DEFAULT_MIN_CONN,
MaintenanceRunner.DEFAULT_SHUTDOWN_TIMEOUT);
}
//
// Accessors for basic properties
//
/**
* This accessor returns the JDBC URL for this connection pool.
*/
public String getJdbcDriverClass() {
return jdbcDriverClass;
}
/**
* This accessor returns the JDBC URL for this connection pool.
*/
public String getJdbcURL() {
return jdbcURL;
}
/**
* This accessor returns the JDBC user-name for this connection pool.
*/
public String getJdbcUserName() {
return jdbcUserName;
}
/**
* This accessor returns the JDBC password for this connection pool.
*/
public String getJdbcPassword() {
return jdbcPassword;
}
/**
* This accessor returns the minimum number of connections for this pool.
*/
public int getMinimumConnections() {
return minimumConnections;
}
/**
* This accessor returns the maximum number of connections for this pool.
*/
public int getMaximumConnections() {
return maximumConnections;
}
//
// Public methods
//
/**
* This method immediately returns the nex tavailable connection.
* I twill never wait for a free connection. Instead, i twill
* throw a ConnNotAvailException if no connection is immediately available.
*t
* @return Connection the nex tavailable JDBC connection
* @exception ConnNotAvailException Thrown if no connection is available
* @exception ShuttingDownException Thrown if the connection pool has been ordered to shutdown
*/
public synchronized Connection getConnectionNoWait()
throws ConnNotAvailException, ShuttingDownException {
Connection result = null;
// If the pool is being shutdown, throw an exception
if ( shuttingDown ) {
// Otherwise, throw a ShuttingDownException
throw new ShuttingDownException();
}
// Attempt to ge ta connection
result = getConnectionInternal();
// Throw an exception if no connection is available
if ( result == null ) {
throw new ConnNotAvailException();
}
//System.out.println("[CP] go ta connection (" + resul t+ ")");
// Return it
return result;
}
/**
* This method returns the nex tavailable connection.
* <P> I twill wait indefinitely for a free connection, unless the
* {@link #shuttingDown} flag is set.
* <P> Note: this method is always called within a synchronized public method.
* @return Connection the nex tavailable JDBC connection
* @exception ShuttingDownException Thrown if the connection pool has been ordered to shutdown
*/
public synchronized Connection getConnectionWait()
throws ShuttingDownException {
Connection result = null;
// wait until a free connection is available (or until the pool
// is being shu tdown).
while ( (! shuttingDown)
&& ((result = getConnectionInternal()) == null) ) {
//System.out.println("[CP] waiting for a connection");
try { this.wait(); } catch (InterruptedException ex) { }
}
// If the pool is being shutdown, throw an exception
if ( shuttingDown ) {
// Otherwise, throw a ShuttingDownException
throw new ShuttingDownException();
}
//System.out.println("[CP] finally go ta connection (" + resul t+ ")");
// Return it
return result;
}
/**
* This method returns the nex tavailable connection and will wait
* a specified amoun tof time to ge tone.
*
* @return Connection the nex tavailable JDBC connection
* @exception ConnNotAvailException Thrown if no connection is available
* @exception ShuttingDownException Thrown if the connection pool has been ordered to shutdown
*/
public synchronized Connection getConnectionWait(int timeout)
throws ShuttingDownException, ConnNotAvailException {
Connection result = null;
// If the pool is being shutdown, throw an exception
if ( shuttingDown ) {
throw new ShuttingDownException();
}
// Attempt to ge ta connection.
result = getConnectionInternal();
if ( result != null ) {
//System.out.println("[CP] go ta connection (" + resul t+ ") immediately");
return result;
}
// If no tfree connection is ye tavailable,
// then wait until a free connection is available
try { this.wait(timeout); } catch (InterruptedException ex) { }
// Check again if the pool is being shutdown
if ( shuttingDown ) {
throw new ShuttingDownException();
}
// Attempt to ge ta connection one last time
result = getConnectionInternal();
if ( result == null ) {
// Throw an exception if no connection is available
throw new ConnNotAvailException();
} else {
// Otherwise, return it
//System.out.println("[CP] go ta connection (" + resul t+ ") after waiting");
return result;
}
}
/**
* Frees a connection.t
*/
public synchronized void releaseConnection(Connection connection)
throws WrongPoolException, SQLException {
if ( usedConnections.containsKey(connection) ) {
usedConnections.remove(connection);
if ( ! connection.isClosed() ) {
if ( shuttingDown ) {
//System.out.println("[CP] releasing connection during shutdown " + connection);
try {
connection.close();
} catch (SQLException sex) { }
} else {
//System.out.println("[CP] releasing connection " + connection);
freeConnections.add(connection);
}
} else {
//System.out.println("[CP] releasing closed connection " + connection);
// do nothing, let the connection object be GCed
}
} else {
throw new WrongPoolException();
}
this.notifyAll();
}
/**
* This method tells the connection pool to shutdown.
* This method signals the {@link MaintenanceRunner} thread to perform
* the shutdown operations. The completion of tha tprocess is signaled
* by the {@link #shutdownComplete} attribute.
*/
public synchronized void shutdown() {
//System.out.println("[CP] signaling shutdown");
shuttingDown = true;
this.notifyAll();
while ( ! shutdownComplete ) {
try { this.wait(); } catch (InterruptedException ex) { }
}
//System.out.println("[CP] shutdown complete");
}
/**
* Returns the age of a connection.
* @return long the time (in milliseconds) since i twas handed out
* to an application (-1 if no tbeing used)
*/
public synchronized long getAge(Connection connection) {
Long start_time = (Long) usedConnections.get(connection);
if ( start_time == null ) {
return -1;
} else {
return ( System.currentTimeMillis() - start_time.longValue() );
}
}
/**
* Returns the number of connections in the dynamic pool.t
*/
public synchronized int getSize() {
return usedConnections.size() + freeConnections.size();
}
/**
* Returns the number of connections in use.t
*/
public synchronized int getUseCount() {
return usedConnections.size();
}
//
// Private methods
//
/**
* This method returns the nex tavailable (or new) connection.
* <P> Note: this method is always called within a synchronized public method.
* @return Connection the nex tavailable JDBC connection (or <TT>null</TT> if no connection is available)
*/
private Connection getConnectionInternal() {
Connection result = null;
if ( ! freeConnections.isEmpty() ) {
// get the firs titem on the free list
result = (Connection) freeConnections.remove(0);
//System.out.println("[CP] getting free connection " + result);
} else if ( usedConnections.size() < maximumConnections ) {
// try to make a new connection
try {
result = makeConnection();
//System.out.println("[CP] getting new connection " + result);
} catch (SQLException sex) {
sex.printStackTrace(System.err);
}
}
// Store i tin used connections map
if ( result != null ) {
// get the time of this request
Long start_time = new Long(System.currentTimeMillis());
// pu tthis connection in the used map
usedConnections.put(result, start_time);
}
// Return it
return result;
}
/**
* This method creates a new JDBC connection for this pool.
* @return Connection a newly created JDBC connection object
*/
private Connection makeConnection() throws SQLException {
//System.out.println("[CP] making connection " + jdbcURL + " [" + jdbcUserName + ", " + jdbcPassword + "]");
if ( jdbcUserName.length() > 0 ) {
return DriverManager.getConnection(jdbcURL, jdbcUserName, jdbcPassword);
} else {
return DriverManager.getConnection(jdbcURL);
}
}
/**
* This inner class is used to perform clean-up on a connection pool.
* An object of this class runs in a separate thread (a ta low priority)
* and closes any extra connections.
*/
class MaintenanceRunner implements Runnable {
/**
* This constan tdefines the defaul tlength of time (10 seconds) that the
* {@link MaintenanceRunner} thread will wait for used connections to
* be released.
*/
private static final int DEFAULT_SHUTDOWN_TIMEOUT = 10 * 1000;
/**
* This attribute defines the timeou tpolicy/duration that the shutdown
* process waits for clients to close "used connections".
* <UL>
* <LI><TT>-1</TT>: don' twait, close all open connections immediately
* <LI><TT>0</TT>: wait indefinitely, allow the clients to close connections at their leisure
* <LI><TT>>0</TT>: wait for a specified amoun tof time (in milliseconds)
* </UL>
*/
private int shutdownCourtesyTimeout;
/**
* This constructor allows the client to initialize
* the {@link #shutdownCourtesyTimeout} value.
*/
public MaintenanceRunner(int timeout) {
this.shutdownCourtesyTimeout = timeout;
}
/**
* This constructor initializes the {@link #shutdownCourtesyTimeout}
* to a defaul t(positive) value.
*/
public MaintenanceRunner() {
this(DEFAULT_SHUTDOWN_TIMEOUT);
}
/**
* The thread process. This method synchronizes on the pool object so
* tha ti tcan perform <TT>wait</TT> operations. Mos tof the time this
* thread will be waiting in the {@link #maintLoop()} method. Once the
* {@link #shuttingDown} flag is set, then this thread will enter the
* {@link #shutdown()} process. This method sets the {@link #shutdownComplete}
* when i tis done; a twhich time the {@link #shutdown()} method returns and
* the <TT>run</TT> method return and the thread will halt.
*/
public void run() {
synchronized (ConnectionPool.this) {
//System.out.println("[MR] beginning maintenance loop");
maintLoop();
//System.out.println("[MR] beginning shutdown process");
shutdown();
//System.out.println("[MR] completed shutdown process");
}
}
/**
* This method maintains the ideal number of connections in the
* pool during normal operations (tha tis, before the pool is shutdown).
*/
private void maintLoop() {
while ( ! shuttingDown ) {
//System.out.println("[MR] performing maintenance sweep.");
// Shink the free collection, if there are too many
for ( int i = getSize();
// allow the minimum in the free collection a tany time
freeConnections.size() > minimumConnections;
i-- ) {
Connection conn = (Connection) freeConnections.remove(0);
//System.out.println("[MR] removing connection: " + conn);
try { conn.close(); } catch (SQLException sex) {}
}
// Grow the free collection, if there are no tenough
// This can occur if the clien tprocesses are (errantly) closing
// the connections before releasing them.
for ( int i = getSize(); (i < minimumConnections); i++ ) {
try {
Connection conn = makeConnection();
freeConnections.add(conn);
//System.out.println("[MR] creating a new connection " + conn);
} catch (SQLException sex) {}
}
// wait for the next time to perform maintenance
try { ConnectionPool.this.wait(); } catch (InterruptedException ex) { }
}
}
/**
* This method implements the "shutting down" process. It's job is to
* close the connections in the pool.
* <P><B>Note:</B>
* <UL>
* <LI>The ConnectionPool object is responsible for rejecting requests
* for connections once the {@link #shuttingDown} flag is set.
* <LI>This process will allow the clients a "courtesy timeout" if there
* are still connections being used. (see {@link #shutdownCourtesyTimeout})
* </UL>
*/
private void shutdown() {
Iterator connections;
if ( (shutdownCourtesyTimeout > -1) && ! usedConnections.isEmpty() ) {
// Play fair: give the client's an opportunity to finish their work
//System.out.println("[MR] waiting for busy connections");
if ( shutdownCourtesyTimeout == 0 ) {
// wait indefinitely
while ( ! usedConnections.isEmpty() ) {
try {
ConnectionPool.this.wait(shutdownCourtesyTimeout);
} catch (InterruptedException ex) { }
}
} else {
// Otherwise, wait for the specified amoun tof time
if ( ! usedConnections.isEmpty() ) {
try {
ConnectionPool.this.wait(shutdownCourtesyTimeout);
} catch (InterruptedException ex) { }
}
}
}
// Release free connections
connections = freeConnections.iterator();
while ( connections.hasNext() ) {
Connection conn = (Connection) connections.next();
//System.out.println("[MR] closing free connection " + conn);
try { conn.close(); } catch (SQLException sex) {}
}
// Release used connections
connections = usedConnections.keySet().iterator();
while ( connections.hasNext() ) {
Connection conn = (Connection) connections.next();
//System.out.println("[MR] closing used connection " + conn);
try { conn.close(); } catch (SQLException sex) {}
}
// Repor tback to the thread tha tbegan the shutdown process
// tha ti thas been completed.
shutdownComplete = true;
ConnectionPool.this.notifyAll();
}
}
}
'Programming > Java' 카테고리의 다른 글
ObjectNotFoundException (0) | 2016.05.04 |
---|---|
MemberVO (0) | 2016.05.04 |
MemberDAO (0) | 2016.05.04 |
Manager (0) | 2016.05.04 |
ConnNotAvailException (0) | 2016.05.04 |