alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/db/DBConnectionManager.java,v 1.13 2005/01/26 04:54:26 minhnn Exp $
 * $Author: minhnn $
 * $Revision: 1.13 $
 * $Date: 2005/01/26 04:54:26 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2005 by MyVietnam.net
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * All copyright notices regarding MyVietnam and MyVietnam CoreLib
 * MUST remain intact in the scripts and source code.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Correspondence and Marketing Questions can be sent to:
 * info@MyVietnam.net
 *
 * @author: Minh Nguyen  minhnn@MyVietnam.net
 * @author: Mai  Nguyen  mai.nh@MyVietnam.net
 */
package net.myvietnam.mvncore.db;

import java.sql.*;
import java.util.Enumeration;
import java.util.Vector;

import net.myvietnam.mvncore.MVNCoreConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is a Singleton that provides access to the
 * connection pool. A client gets access to the single
 * instance through the static getInstance() method
 * and can then check-out and check-in connections from a pool.
 * When the client shuts down it should call the release() method
 * to close all opened connections and do other clean up.
 */
class DBConnectionManager {

    private static Log log = LogFactory.getLog(DBConnectionManager.class);

    private static final int TIME_BETWEEN_RETRIES = 500; // O.5 second

    // static variable
    static private DBConnectionManager instance = null;       // The single instance

    // instance variable
    private DBConnectionPool pool = null;// please be careful if u want to make this variable static

    /**
     * A private constructor since this is a Singleton
     * Note: This constructor is lightweight since DBConnectionPool is lightweight,
     *       so no connection is created until the first time getConnection() is called
     */
    private DBConnectionManager(DBOptions option) {
        try {
            Class.forName(option.driverClassName).newInstance();
        } catch (Exception e) {
            log.fatal("DBConnectionManager: Unable to load driver = " + option.driverClassName);
        }

        //always new the pool because pool is an instance variable
        pool = new DBConnectionPool(option.databaseURL, option.databaseUser, option.databasePassword, option.maxConnection);
    }

    private DBConnectionManager() {
        String driverClassName = MVNCoreConfig.getDriverClassName();
        try {
            Class.forName(driverClassName).newInstance();
        } catch (Exception e) {
            log.fatal("DBConnectionManager: Unable to load driver = " + driverClassName);
        }

        String url = MVNCoreConfig.getDatabaseURL();
        String user = MVNCoreConfig.getDatabaseUser();
        String password = MVNCoreConfig.getDatabasePassword();
        int maxConnection = MVNCoreConfig.getMaxConnection();

        //always new the pool because pool is an instance variable
        pool = new DBConnectionPool(url, user, password, maxConnection);
    }

    /**
     * Returns the single instance, creating one if it's the
     * first time this method is called.
     *
     * @return DBConnectionManager The single instance.
     */
    /*
    public static synchronized DBConnectionManager getInstance() {
        if (instance == null) {
            DBOptions option = new DBOptions();
            instance = new DBConnectionManager(option);
        }
        return instance;
    }*/

    /**
     * Returns the single instance, creating one if it's the
     * first time this method is called.
     *
     * @return DBConnectionManager The single instance.
     */
    /*
    private static synchronized DBConnectionManager getInstance(DBOptions option) {
        if (instance == null) {
            if (option == null) {
                option = new DBOptions();
            }
            instance = new DBConnectionManager(option);
        }
        return instance;
    }*/

    public static synchronized DBConnectionManager getInstance(boolean useConfig) {
        if (instance == null) {
            instance = new DBConnectionManager();
        }
        return instance;
    }

    /**
     * Returns a connection to the pool.
     *
     * @param con The Connection
     */
    void freeConnection(Connection con) {
        pool.freeConnection(con);
    }

    /**
     * Returns an open connection. If no one is available, and the max
     * number of connections has not been reached, a new connection is
     * created.
     *
     * @return Connection The connection or null
     */
    Connection getConnection() {
        return pool.getConnection();
    }

    /**
     * Returns an open connection. If no one is available, and the max
     * number of connections has not been reached, a new connection is
     * created. If the max number has been reached, waits until one
     * is available or the specified time has elapsed.
     *
     * @param time The number of milliseconds to wait
     * @return Connection The connection or null
     */
    Connection getConnection(long time) {
        return pool.getConnection(time);
    }

    /**
     * Closes all open connections.
     * @return true if the pool is empty and balance
     *         false if the pool has returned some connection to outside
     */
    boolean release() {
        return pool.release();
    }

    /**
     * This inner class represents a connection pool. It creates new
     * connections on demand, up to a max number if specified.
     * It also checks to make sure that the connection is still open
     * before it is returned to a client.
     */
    class DBConnectionPool {
        private int checkedOut  = 0;//NOTE: this variable should be changed in synchronized method only
        private Vector freeConnections = new Vector();

        private int    maxConn  = 0;
        private String password = null;
        private String URL      = null;
        private String user     = null;

        /**
         * Creates new connection pool.
         * NOTE: new an instance of this class is lightweight since it does not create any connections
         *
         * @param URL The JDBC URL for the database
         * @param user The database user, or null
         * @param password The database user password, or null
         * @param maxConn The maximal number of connections, or 0 for no limit
         */
        public DBConnectionPool(String URL, String user, String password, int maxConn) {
            this.URL = URL;
            this.user = user;
            this.password = password;
            this.maxConn = maxConn;
        }

        /**
         * Checks in a connection to the pool. Notify other Threads that
         * may be waiting for a connection.
         *
         * @todo: Maybe we dont need notifyAll(); ???
         *
         * @param con The connection to check in
         */
        synchronized void freeConnection(Connection con) {
            // Put the connection at the end of the Vector
            if (con != null) {//make sure that the connection is not null
                if (checkedOut <= 0) {
                    // this means that connection is open too much
                    // There are 2 cases:
                    // 1. Not get from this connection pool (maybe get directly)
                    // 2. this connection is gotten and then the whole pool is released
                    // In these case, just close the connection
                    try {
                        log.debug("DBConnectionManager: about to close the orphan connection.");
                        con.close();
                    } catch (SQLException ex) { }
                } else {
                    // Return this connection to the pool
                    // note that we dont have to check if the connection is not connected
                    // this will be check in the getConnection method
                    freeConnections.addElement(con);
                    // FIXME: posible negative value
                    // NOTE: checkOut should never be negative here
                    checkedOut--; // NOTE: this number can be negative (in case connection does not come from the pool)
                    notifyAll(); // can I remove it ???
                }
            }
        }

        /**
         * Checks out a connection from the pool. If no free connection
         * is available, a new connection is created unless the max
         * number of connections has been reached. If a free connection
         * has been closed by the database, it's removed from the pool
         * and this method is called again recursively.
         */
        synchronized Connection getConnection() {
            Connection con = null;

            while ( (freeConnections.size() > 0) && (con == null) ) {
                // Pick the first Connection in the Vector
                // to get round-robin usage
                con = (Connection) freeConnections.firstElement();
                freeConnections.removeElementAt(0);
                try {
                    if (con.isClosed()) {
                        log.info("Removed bad connection in DBConnectionPool.");
                        con = null; // to make the while loop to continue
                    }
                } catch (SQLException e) {
                    con = null; // to make the while loop to continue
                }
            } // while

            if (con == null) {// cannot get any connection from the pool
                if (maxConn == 0 || checkedOut < maxConn) {// maxConn = 0 means unlimited connections
                    con = newConnection();
                }
            }
            if (con != null) {
                checkedOut++;
            }
            return con;
        }

        /**
         * Checks out a connection from the pool. If no free connection
         * is available, a new connection is created unless the max
         * number of connections has been reached. If a free connection
         * has been closed by the database, it's removed from the pool
         * and this method is called again recursively.
         * <P>
         * If no connection is available and the max number has been
         * reached, this method waits the specified time for one to be
         * checked in.
         *
         * @param timeout The timeout value in milliseconds
         */
        /**
         * Note that this method is not synchronized since it relies on the getConnection(void) method
         * I also believe that this method SHOULD NOT synchronized because I use #sleep() method
         * @todo: check if we should synchronize this method and use wait instead of sleep ???
         */
        Connection getConnection(long timeout) {
            long startTime = System.currentTimeMillis();
            Connection con;
            while ((con = getConnection()) == null) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                if (elapsedTime >= timeout) {
                    // Timeout has expired
                    return null;
                }

                long timeToWait = timeout - elapsedTime;
                if (timeToWait > TIME_BETWEEN_RETRIES) timeToWait = TIME_BETWEEN_RETRIES;// we dont want to wait for more than TIME_BETWEEN_RETRIES second each time
                try {
                    Thread.sleep(timeToWait);
                } catch (InterruptedException e) {}
            }
            return con;
        }

        /**
         * Closes all available connections.
         * @return true if the pool is empty and balance
         *         false if the pool has returned some connection to outside
         */
        synchronized boolean release() {
            boolean retValue = true;
            Enumeration allConnections = freeConnections.elements();
            while (allConnections.hasMoreElements()) {
                Connection con = (Connection) allConnections.nextElement();
                try {
                    con.close();
                } catch (SQLException e) {
                    log.error("Cannot close connection in DBConnectionPool.");
                }
            }
            freeConnections.removeAllElements();
            if (checkedOut != 0) {
                retValue = false;
                log.warn("DBConnectionManager: the built-in connection pool is not balanced.");
            }
            checkedOut = 0;
            return retValue;
        }

        /**
         * Creates a new connection, using a userid and password
         * if specified.
         * @todo: check if this method need synchronized
         */
        private Connection newConnection() {
            Connection con = null;
            try {
                if (user == null) {
                    con = DriverManager.getConnection(URL);
                } else {
                    con = DriverManager.getConnection(URL, user, password);
                }
                con.setAutoCommit(true);//thread 804 by trulore
            } catch (SQLException e) {
                log.error("Cannot create a new connection in DBConnectionPool. URL = " + URL, e);
                return null;
            }
            return con;
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.