[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 |
Copyright (C) 2004 http://www.onejava.com, Java技术联盟. All Rights Reserved