一个连接池的例子 (二)

80酷酷网    80kuku.com

        /**
         * This method hands out the connections in round-robin order.
         * This prevents a faulty connection from locking
         * up an application entirely.  A browser 'refresh' will
         * get the next connection while the faulty
         * connection is cleaned up by the housekeeping thread.
         *
         * If the min number of threads are ever exhausted, new
         * threads are added up the the max thread count.
         * Finally, if all threads are in use, this method waits
         * 2 seconds and tries again, up to ten times.  After that, it
         * returns a null.
         */
        public Connection getConnection() {

            Connection conn=null;

            if(available){
                boolean gotOne = false;

                for(int outerloop=1; outerloop<=10; outerloop++) {

                    try  {
                        int loop=0;
                        int roundRobin = connLast + 1;
                        if(roundRobin >= currConnections) roundRobin=0;

                        do {
                            synchronized(connStatus) {
                                if((connStatus[roundRobin] < 1) &&
                                        (! connPool[roundRobin].isClosed()))
                                {
                                    conn = connPool[roundRobin];
                                    connStatus[roundRobin]=1;
                                    connLockTime[roundRobin] =
                                        System.currentTimeMillis();
                                    connLast = roundRobin;
                                    gotOne = true;
                                    break;
                                }
                                else {
                                    loop++;
                                    roundRobin++;
                                    if(roundRobin >= currConnections) roundRobin=0;
                                }
                            }
                        }
                        while((gotOne==false)&&(loop < currConnections));
                    }
                    catch (SQLException e1) {}

                    if(gotOne) {
                        break;
                    }
                    else {
                        synchronized(this) {  // Add new connections to the pool
                            if(currConnections < maxConns) {
                                try {
                                    createConn(currConnections);
                                    currConnections++;
                                }
                                catch(SQLException e) {
                                    log.println("Unable to create new connection: " + e);
                                }
                            }
                        }

                        try { Thread.sleep(2000); }
                        catch(InterruptedException e) {}
                        log.println("-----> Connections Exhausted!  Will wait and try " +
                            "again in loop " +
                            String.valueOf(outerloop));
                    }
                } // End of try 10 times loop

            }
            else {
                log.println("Unsuccessful getConnection() request during destroy()");
            } // End if(available)

            return conn;
        }

        /**
         * Returns the local JDBC ID for a connection.
         */
        public int idOfConnection(Connection conn) {
            int match;
            String tag;

            try {
                tag = conn.toString();
            }
            catch (NullPointerException e1) {
                tag = "none";
            }

            match=-1;

            for(int i=0; i< currConnections; i++) {
                if(connID[i].equals(tag)) {
                    match = i;
                    break;
                }
            }
            return match;
        }

        /**
         * Frees a connection.  Replaces connection back into the main pool for
         * reuse.
         */
        public String freeConnection(Connection conn) {
            String res="";

            int thisconn = idOfConnection(conn);
            if(thisconn >= 0) {
                connStatus[thisconn]=0;
                res = "freed " + conn.toString();
                //log.println("Freed connection " + String.valueOf(thisconn) +
                //            " normal exit: ");
            }
            else {
                log.println("----> Could not free connection!!!");
            }

            return res;
        }

//文件:DbConnectionDefaultPool.java的第三部分


        /**
         * Returns the age of a connection -- the time since it was handed out to
         * an application.
         */
        public long getAge(Connection conn) { // Returns the age of the connection in millisec.
            int thisconn = idOfConnection(conn);
            return System.currentTimeMillis() - connLockTime[thisconn];
        }

        private void createConn(int i) throws SQLException {
             Date now = new Date();
             try {
                Class.forName (dbDriver);
                Properties dbProp = new Properties();
                //log.println("Creating.....");
                dbProp.put("user", dbLogin);
                dbProp.put("password", dbPassword);
                dbProp.put("characterEncoding","gb2112");
                //dbProp.put("useUnicode", "true");
        
                connPool[i] = DriverManager.getConnection
                          (dbServer,dbProp);
                //log.println("Created Ok...");
                connStatus[i]=0;
                connID[i]=connPool[i].toString();
                connLockTime[i]=0;
                connCreateDate[i] =  now.getTime();
            }
            catch (ClassNotFoundException e2) {}
                
            log.println(now.toString() + "  Opening connection " + String.valueOf(i) +
                    " " + connPool[i].toString() + ":");
        }
    
        /**
         * Shuts down the housekeeping thread and closes all connections
         * in the pool. Call this method from the destroy() method of the servlet.
         */

        /**
         * Multi-phase shutdown.  having following sequence:
         * <OL>
         * <LI><code>getConnection()</code> will refuse to return connections.
         * <LI>The housekeeping thread is shut down.

         *    Up to the time of <code>millis</code> milliseconds after shutdown of
         *    the housekeeping thread, <code>freeConnection()</code> can still be
         *    called to return used connections.
         * <LI>After <code>millis</code> milliseconds after the shutdown of the
         *    housekeeping thread, all connections in the pool are closed.
         * <LI>If any connections were in use while being closed then a
         *    <code>SQLException</code> is thrown.
         * <LI>The log is closed.
         * </OL>

         * Call this method from a servlet destroy() method.
         *
         * param      millis   the time to wait in milliseconds.
         * exception  SQLException if connections were in use after
         * <code>millis</code>.
         */
        public void destroy(int millis) throws SQLException {
    
            // Checking for invalid negative arguments is not necessary,
            // Thread.join() does this already in runner.join().

            // Stop issuing connections
            available=false;

            // Shut down the background housekeeping thread
            runner.interrupt();

            // Wait until the housekeeping thread has died.
            try { runner.join(millis); }
            catch(InterruptedException e){} // ignore
        
            // The housekeeping thread could still be running
            // (e.g. if millis is too small). This case is ignored.
            // At worst, this method will throw an exception with the
            // clear indication that the timeout was too short.

            long startTime=System.currentTimeMillis();

            // Wait for freeConnection() to return any connections
            // that are still used at this time.
            int useCount;
            while((useCount=getUseCount())>0 && System.currentTimeMillis() - startTime <=  millis) {
                try { Thread.sleep(500); }
                catch(InterruptedException e) {} // ignore
            }

            // Close all connections, whether safe or not
            for(int i=0; i < currConnections; i++) {
                try {
                    connPool[i].close();
                }
                catch (SQLException e1)
                {
                    log.println("Cannot close connections on Destroy");
                }
            }

            if(useCount > 0) {
                //bt-test successful
                String msg="Unsafe shutdown: Had to close "+useCount+
                    " active DB connections after "+millis+"ms";
                log.println(msg);
                // Close all open files
                log.close();
                // Throwing following Exception is essential because servlet authors
                // are likely to have their own error logging requirements.
                throw new SQLException(msg);
            }

            // Close all open files
            log.close();

        }//End destroy()


        /**
         * Less safe shutdown.  Uses default timeout value.
         * This method simply calls the <code>destroy()</code> method
         * with a <code>millis</code>
         * value of 10000 (10 seconds) and ignores <code>SQLException</code>
         * thrown by that method.
         * see     #destroy(int)
         */
        public void destroy() {
            try {
                destroy(10000);
            }
            catch(SQLException e) {}
        }

        /**
         * Returns the number of connections in use.
         */
        // This method could be reduced to return a counter that is
        // maintained by all methods that update connStatus.
        // However, it is more efficient to do it this way because:
        // Updating the counter would put an additional burden on the most
        // frequently used methods; in comparison, this method is
        // rarely used (although essential).
        public int getUseCount() {
            int useCount=0;
            synchronized(connStatus) {
                for(int i=0; i < currConnections; i++) {
                    if(connStatus[i] > 0) { // In use
                        useCount++;
                    }
                }
            }
            return useCount;
        }//End getUseCount()

        /**
         * Returns the number of connections in the dynamic pool.
         */
        public int getSize() {
            return currConnections;
        }//End getSize()

    }

    /**
     * An implementation of the Connection interface that wraps an underlying
     * Connection object. It releases the connection back to a connection pool
     * when Connection.close() is called.
     */
    public class ConnectionWrapper  implements Connection {

        private Connection connection;
        private ConnectionPool connectionPool;

        public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) {
            this.connection = connection;
            this.connectionPool = connectionPool;
        }

        /**
         * Instead of closing the underlying connection, we simply release
         * it back into the pool.
         */
        public void close() throws SQLException {
            connectionPool.freeConnection(this.connection);
            //Release object references. Any further method calls on the
            //connection will fail.
            connection = null;
            connectionPool = null;
        }

        public Statement createStatement() throws SQLException {
            return connection.createStatement();
        }

        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return connection.prepareStatement(sql);
        }

        public CallableStatement prepareCall(String sql) throws SQLException {
            return connection.prepareCall(sql);
        }

        public String nativeSQL(String sql) throws SQLException {
            return connection.nativeSQL(sql);
        }

        public void setAutoCommit(boolean autoCommit) throws SQLException {
            connection.setAutoCommit(autoCommit);
        }

        public boolean getAutoCommit() throws SQLException {
            return connection.getAutoCommit();
        }

        public void commit() throws SQLException {
            connection.commit();
        }

        public void rollback() throws SQLException {
            connection.rollback();
        }

        public boolean isClosed() throws SQLException {
            return connection.isClosed();
        }

        public DatabaseMetaData getMetaData() throws SQLException {
            return connection.getMetaData();
        }

        public void setReadOnly(boolean readOnly) throws SQLException {
            connection.setReadOnly(readOnly);
        }

        public boolean isReadOnly() throws SQLException {
            return connection.isReadOnly();
        }

        public void setCatalog(String catalog) throws SQLException {
            connection.setCatalog(catalog);
        }

        public String getCatalog() throws SQLException {
            return connection.getCatalog();
        }

        public void setTransactionIsolation(int level) throws SQLException {
            connection.setTransactionIsolation(level);
        }

        public int getTransactionIsolation() throws SQLException {
            return connection.getTransactionIsolation();
        }

        public SQLWarning getWarnings() throws SQLException {
            return connection.getWarnings();
        }

        public void clearWarnings() throws SQLException {
            connection.clearWarnings();
        }

        public Statement createStatement(int resultSetType, int resultSetConcurrency)
                throws SQLException
        {
            return connection.createStatement(resultSetType, resultSetConcurrency);
        }

        public PreparedStatement prepareStatement(String sql, int resultSetType,
                int resultSetConcurrency) throws SQLException
        {
            return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
        }

        public CallableStatement prepareCall(String sql, int resultSetType,
                int resultSetConcurrency) throws SQLException
        {
            return prepareCall(sql, resultSetType, resultSetConcurrency);
        }

        public Map getTypeMap() throws SQLException {
            return connection.getTypeMap();
        }

        public void setTypeMap(Map map) throws SQLException {
            connection.setTypeMap(map);
        }

    }
}//文件:DbConnectionManager.java

package com.qingtuo.db.pool;

import java.sql.*;
import java.io.*;
import java.util.*;

/**
* Central manager of database connections.
*/
public class DbConnectionManager {

    private static DbConnectionProvider connectionProvider;
    private static Object providerLock = new Object();

    /**
     * Returns a database connection from the currently active connection
     * provider.
     */
    public static Connection getConnection() {
        if (connectionProvider == null) {
            synchronized (providerLock) {
                if (connectionProvider == null) {
                    //Create the connection provider -- for now, this is hardcoded. For
                    //the next beta, I'll change this to load up the provider dynamically.
                    connectionProvider = new DbConnectionDefaultPool();
                    connectionProvider.start();
                }
            }
        }
        Connection con = connectionProvider.getConnection();
        if (con == null) {
            System.err.println("WARNING: DbConnectionManager.getConnection() failed to obtain a connection.");
        }
        return con;
    }

    /**
     * Returns the current connection provider. The only case in which this
     * method should be called is if more information about the current
     * connection provider is needed. Database connections should always be
     * obtained by calling the getConnection method of this class.
     */
    public static DbConnectionProvider getDbConnectionProvider() {
        return connectionProvider;
    }

    /**
     * Sets the connection provider. The old provider (if it exists) is shut
     * down before the new one is started. A connection provider <b>should
     * not</b> be started before being passed to the connection manager.
     */
    public static void setDbConnectionProvider(DbConnectionProvider provider) {
        synchronized (providerLock) {
            if (connectionProvider != null) {
                connectionProvider.destroy();
                connectionProvider = null;
            }
            connectionProvider = provider;
            provider.start();
        }
    }


}



分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: