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

📄 connectionpool.java

📁 带有自我调整功能Connection Pool(JDBC
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
        // if you haven't properly set up the pool, then you can't start the pool!
        if ((!this.classDriverSet)||(this.dbURL == null))
            throw new SQLException("Sorry, you have to set the class driver and database URL before starting the pool");

        connections = new Hashtable();  // build an empty connection matrix
        
        // Put our pool of Connections in the Hashtable
        for (int i = 0; i < initialConnections; i++) 
            connections.put(DriverManager.getConnection(dbURL, user, password), new ConData());

        timer = new Timer(true);  // the true means "Kill the timer if the parent (the pool) thread dies"

        timer.schedule((new ConnectionPoolTimer(this, DEBUG)), iterationTime, iterationTime*1000*60); // pass in our DEBUG flag to the timertask
    }

    /**
     * destroys the pool - <B>USE WITH CAUTION</B>
     * <P>
     * if you destroy a pool and want to use it again, you <B>MUST</B>
     * call the <code>start()</code> method again to restart the pool before trying to get a connection from it!
     * <P>
     * Why use this?  In case you want to use it in a <code>finalize()</code> method,
     * or if you want to insure that you have closed all the connections
     * in the pool for some othe reason - this will do it!  <p> NOTE: If you want to
     * make your pool available for garbage collection, it would be a good idea to
     * call <code>destroy()</code> before setting your pool object to <code>null</code>.
     * <P>
     * @throws SQLException if it does not successfully destroy the connection pool
     */
    public void destroy() throws SQLException
    {
        boolean successfulDestroy = true;
        Enumeration cons = connections.keys();
        Connection con = null;
        synchronized (connections) {
            while (cons.hasMoreElements()) 
            {
                con = (Connection)cons.nextElement();
                if ( !destroyConnection(con) ) successfulDestroy = false;
            }
        }
        connections = null;
        timer.cancel(); // lets kill the responsible timer
        timer = null;   // and make it garbage collectable

        if (!successfulDestroy) throw new SQLException("Did not successfully destroy all connections in " + this.getClass().getName() + "! Database may be unstable!");
    }

    /**
     * Returns a String containing a report of the current pool's status <P>
     * This can be useful if you want an "administrative view" of what's going on inside the connection pool.  It can
     * also be useful if you are sharing your pool with several programs, and someone isn't returning a connection - this
     * might give you some idea of the problem program.<P>
     * <table bgcolor="#DDDDDD" border="1" cellpadding="4" cellspacing="0"><tr><td>
     * <B>The report looks like this (customized with your pool's size, etc):</B>    
     * <code><pre>
     *   Current Time: Mon Feb 25 13:02:06 PST 2002
     *   Current Size: 4
     *   Initial Size: 3
     *    - IN USE   by UNIDENTIFIED (Mon Feb 25 13:02:06 PST 2002)    
     *    - IN USE   by TEST PROGRAM (Mon Feb 25 13:02:06 PST 2002)
     *    - IN USE   by UNIDENTIFIED (Mon Feb 25 13:02:06 PST 2002)
     *    + FREE     last used on Mon Feb 25 13:02:06 PST 2002
     * </pre></code></td></tr></table><P>
     * @return report of the current pool status
     */
    public String getReport ()
    {
        java.util.Date curTime = new java.util.Date();
        String returnString = NEWLINE + "Current Time: " + (new java.util.Date()) + NEWLINE;
        returnString += "Current Size: " + connections.size() + NEWLINE;
        returnString += "Initial Size: " + this.initialConnections + NEWLINE;
        Enumeration cons = connections.keys();
        Connection con = null;
        synchronized (connections) {        // sync this for no thread collision
            while (cons.hasMoreElements())  // let's step through our connections
            {
                con = (Connection)cons.nextElement();
                //let's see if there is a free connection that is over its age limit and over the initial size
                if ((((ConData)connections.get(con)).inUse == false) && ((curTime.getTime() - ((ConData)connections.get(con)).lastUsed.getTime()) > (1*1000*60* minutesIdle )) && (connections.size() > initialConnections)) 
                {
                    if (DEBUG) {
                        System.out.println("AGE: " + (curTime.getTime() - ((ConData)connections.get(con)).lastUsed.getTime()));
                        System.out.println("AGE: " + curTime + "," + ((ConData)connections.get(con)).lastUsed);
                    }
                    destroyConnection(con);  // free up excess idle connections
                }
                // otherwise, let's see if it is free and healthy
                else if (((ConData)connections.get(con)).inUse == false) 
                {
                    // So we found an unused connection.
                    try {
                        exerciseConnection(con); // make sure it's healthy
                    } 
                    catch (SQLException e) {
                        // It's unhealthy - let's replace it.
                        try {
                            con = DriverManager.getConnection(dbURL, user, password);
                        } 
                        catch (SQLException newConError) {  // shouldn't ever get here, but if so...uh oh! database went down?                        
                            System.err.println("CRITICAL ERROR: " + newConError.getMessage() ); 
                        }
                    }
                    returnString += " + FREE     last used on " + ((ConData)connections.get(con)).lastUsed + NEWLINE;
                }
                // otherwise, this connection must be checked out (in use) by something
                else 
                {
                    returnString += " - IN USE   by " + ((ConData)connections.get(con)).usedBy;
                    returnString += " (" + ((ConData)connections.get(con)).lastUsed + ")" + NEWLINE;
                }
            }
        }
        return  (returnString);
    }

    /**
     * Set how many minutes that a connection waits idle before being closed and removed from the pool.<P>
     * Connections will only be removed from the pool if there are more than the number found in <code>setInitialConnection()</code> <P>
     * @param     timeInMinutes time (in munites) to keep an idle connection alive before closing it
     */
    public void setMinutesIdle(int timeInMinutes)     { this.minutesIdle = timeInMinutes;    }

    /**
     * Set how many minutes to wait before the pool makes an integrity check of itself.<P>
     * The pool will, every <code>timeInMinutes</code> minutes, check all the connections in
     * the pool to make sure they are still doing ok.  If an unhealthy connection is found, 
     * it is closed and a new one is opened in its place.<BR>
     * <P>
     * Note that once <code>start()</code> has been issued, this method will not affect your
     * pool until you <code>destroy()</code> it and then restart it with <code>start()</code>
     * <P>
     * @param     timeInMinutes time (in munites) between connection integrity checks.
     * @see #start
     * @see #destroy
     */
    public void setHealthCheckIterationTime(int timeInMinutes)     { this.iterationTime = timeInMinutes;    }

    /**
     * Set the number of connections to initially open in the pool.<P>
     * The pool will never have fewer than this many connections established to the DB.  <P>
     * @param     initialConnections number of connections to open initially.  
     */
    public void setInitialConnections(int initialConnections)     { this.initialConnections = initialConnections;    }

    /**
     * Set the number of connections to open if none are available in the pool.<P>
     * If you try and get a connection from the pool but there are none available,
     * then the pool will open <code>incrementCount</code> new connections.  These
     * "extra" connections will be closed over time if they remain idle.       <P>
     * @param     increment number of new connections to open
     */
    public void setIncrementCount(int incrementCount)     { this.increment = incrementCount;    }

    /**
     * Set the password for the database user<P>
     * @param     password for the user given in setUser()
     */
    public void setPassword(String password)     { this.password = password;    }

    /**
     * Set the URL to the database<P>
     * Usually takes the form of <code>jdbc:&lt;type&gt;://&lt;ip&gt;:&lt;port&gt;/&lt;database&gt;</code><P>
     * @param     dbURL JDBC url of the DB
     */
    public void setURL(String databaseURL)     { this.dbURL = databaseURL;    }    // usually takes the form: jdbc:<type>://<ip>:<port>/<database>

    /**
     * Set username for the database<P>
     * @param     username username to use when creating connections to database
     */
    public void setUser(String username)     {  this.user = username;  }

    /**
     * Set the JDBC Driver Class Name.<P>
     * Note that the driver must be in your classpath.  Refer to the docs that came with your JDBC driver for the specifics.<P>
     * An example: <code>sun.jdbc.odbc.JdbcOdbcDriver</code><P>
     * @param   driverClassName JDBC Driver Class Name
     * @throws  ClassNotFoundException if the driver is not found in the classpath
     */
    public void setDriverClassName(String driverClassName) throws ClassNotFoundException
    {        
        Class.forName(driverClassName);    
        this.classDriverSet = true;     // let the pool know that it's set successfully
    }


    ///////////// PRIVATE METHODS BELOW

    // This will return a connection to the pool AND DESTROY IT from the pool
    private boolean destroyConnection (Connection toDestroy)
    {
        boolean successfulDestroy = false;
        try 
        {
            toDestroy.close();
            connections.remove(toDestroy);
            successfulDestroy = true;  // must have worked if we got here
            if (DEBUG) System.out.println("Destroyed a connection");
        } 
        catch (SQLException e) {}   // shouldn't get here - we'll throw it out and make it null - the DB will close it after a while
        toDestroy = null;           // make it available for garbage collection
        return  (successfulDestroy);
    }

    // this will insure a connection's health by running an arbitrary query on the connection
    // it will throw an exception if the connection isn't healthy
    private void exerciseConnection(Connection con) throws SQLException
    {
        if (!PRODUCTION)
        {
            //this is the simple way to do a health check - see below for a better way
            con.setAutoCommit(true);
        }
        else
        {
            // this might be better for a production environment
            Statement statmt;
            ResultSet res;
            statmt = con.createStatement();
            String statement = "show tables"; // some dumb query that should work everywhere
            res = statmt.executeQuery(statement);
            res.close();
            statmt.close();
        }
    } // end exerciseConnection


    ////////////////// INNER CLASSES
    /**
     *    This inner class encapsulates a connection's information.  It is used to
     *    internally represet if a connection is in use, and by whom it is being used
     */
    private class ConData 
    {
        private java.util.Date lastUsed = null;
        private boolean inUse = false;
        private String usedBy = "UNIDENTIFIED";

        /**
         * create a new condata object
         * @param inUse is this connection in use?
         * @param usedBy who is using this connection?
         */
        public ConData (boolean inUse, String usedBy) 
        {
            this.lastUsed = new java.util.Date();
            this.inUse = inUse;
            if (usedBy != null)
                this.usedBy = usedBy;
        }

        /** create a new blank condata object */
        public ConData () 
        {
            this(false,null);
        }
    } // end innerclass ConData

    /**    This inner class is a timertask that monitors the health and age of the connection objects.
     *    If one gets sick, then this will reissue a new one in its place.
     *    If a connection that was built to satisfy a request has outlived it's usefullness and is 
     *    causing the connection pool to exceed the desired size, then the timer will kill it off.
     */
    private class ConnectionPoolTimer extends TimerTask 
    {
        private ConnectionPool self = null;

        /** timer to keep track of connection pool health */
        public ConnectionPoolTimer (ConnectionPool parentPool, boolean DEBUG) 
        {
            this.self = parentPool;
        }

        /** thread killer - this should only happen if the main pool is already dead. */
        public boolean cancel()
        {
            try
            {    self.destroy();        } // try and have parentPool destroy itself
            catch (Exception e)        {} // throw it out
            return super.cancel();
        }

        /**
         * call the getReport() method to finger all of the connections for health
         */
        public void run ()      // used for timer instance
        {
            // returns a string, but we don't care (unless we're debugging)
            // we just call the report because it exercises the connections for us
            String report = self.getReport();
            if(DEBUG)System.out.println("THREAD DEBUG ------ " + NEWLINE + report);
        }
    }  // end innerclass ConnectionPoolTimer


} // EOF



⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -