org.enhydra.jdbc.pool.StandardPoolDataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.enhydra.jdbc.pool.StandardPoolDataSource.java

Source

/*
 * XAPool: Open Source XA JDBC Pool
 * Copyright (C) 2003 Objectweb.org
 * Initial Developer: Lutris Technologies Inc.
 * Contact: xapool-public@lists.debian-sf.objectweb.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */
package org.enhydra.jdbc.pool;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import org.enhydra.jdbc.core.CoreDataSource;
import org.enhydra.jdbc.core.JdbcThreadFactory;
import org.enhydra.jdbc.util.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * StandardPoolDataSource class allows to make some operations on
 * PooledConnection. It implements PoolHelper for the 3 methods :<p>
 * create   : create a PooledConnection<p>
 * create(user,password)   : create a PooledConnection with an other user/password<p>
 * testThisObject : check if the object is still valid<p>
 * checkThisObject : check if the object is closed<p>
 * expire   : kill the object<p>
 */
public class StandardPoolDataSource extends CoreDataSource
        implements DataSource, PoolHelper, ConnectionEventListener {

    public ConnectionPoolDataSource cpds; // object to build PooledConnection
    public GenericPool pool; // pool to store StandardDataSource object
    public String dataSourceName; // jndi name for DataSource Factory
    public String jdbcTestStmt;
    // JDBC test statement for checkLevelObject=1 or 2
    public boolean onOff; // If the pool is started or not
    public Context ictx; // the initial context
    public Log glog = LogFactory.getLog("org.enhydra.jdbc.xapool");

    /**
     * Constructor
     */
    public StandardPoolDataSource() {
        log = new Logger(glog);
        super.setLogWriter(log);

        pool = new GenericPool(this);
        // create the pool with StandardPoolDataSource object
        pool.setLogger(log);
        onOff = false;
        dataSourceName = null;
    }

    /**
     * Constructor
     */
    public StandardPoolDataSource(int initSize) { // with an init Max size
        log = new Logger(glog);
        super.setLogWriter(log);

        pool = new GenericPool(this, initSize);
        // create the pool with StandardPoolDataSource object
        pool.setLogger(log);
        onOff = false;
        dataSourceName = null;
    }

    /**
     * Constructor
     */
    public StandardPoolDataSource(ConnectionPoolDataSource cc) {
        cpds = cc;
        log = new Logger(glog);

        super.setLogWriter(log);
        pool = new GenericPool(this);
        // create the pool with StandardPoolDataSource object
        pool.setLogger(log);
        try {
            cpds.setLogWriter(log);
        } catch (SQLException sqle) {

        }

        onOff = false;
        dataSourceName = null;
    }

    /**
     * Constructor
     */
    public StandardPoolDataSource(ConnectionPoolDataSource cc, int initSize) { // with an init Max size
        cpds = cc;
        log = new Logger(glog);
        super.setLogWriter(log);

        pool = new GenericPool(this, initSize);
        // create the pool with StandardPoolDataSource object
        pool.setLogger(log);

        onOff = false;
        dataSourceName = null;
    }

    /**
     * Set up the data source name, get the initial context,
     * and lookup in JNDI to obtain a reference of the DataSourceName
     * this method must be called before a getConnection (in this case
     * an exception is returned
     */
    public void setDataSourceName(String dataSourceName) {
        log.debug("StandardPoolDataSource:setDataSourceName");
        this.dataSourceName = dataSourceName; // set up the data source name
        /*
          synchronized(this) {
              if (onOff) {
                  pool.stop();
                  onOff = false;
              }
          }
          */
    }

    public String getDataSourceName() {
        return dataSourceName; // return the dataSourceName (jndi mechanism)
    }

    /**
     * getConnection allows to get an object from the pool and returns it
     * to the user. In this case, we return an PooledConnection
     */
    public Connection getConnection() throws SQLException {
        return getConnection(getUser(), getPassword());
    }

    /**
     * getConnection allows to get an object from the pool and returns it
     * to the user. In this case, we return an PooledConnection
     */
    public Connection getConnection(String _user, String _password) throws SQLException {
        log.debug("StandardPoolDataSource:getConnection");
        Connection ret = null;
        PooledConnection con = null;

        synchronized (this) {
            if (!onOff) {
                log.debug("StandardPoolDataSource:getConnection must configure the pool...");
                pool.start(); // the pool starts now
                onOff = true; // and is initialized
                log.debug("StandardPoolDataSource:getConnection pool config : \n" + pool.toString());
            }
        }

        try {
            try {
                log.debug("StandardPoolDataSource:getConnection Try to give a " + "connection (checkOut)");
                con = (PooledConnection) pool.checkOut(_user, _password);
                // get a connection from the pool
                log.debug("StandardPoolDataSource:getConnection checkOut return" + "a new connection");
            } catch (Exception e) {
                e.printStackTrace();
                log.debug(
                        "StandardPoolDataSource:getConnection SQLException in StandardPoolDataSource:getConnection"
                                + e);
                throw new SQLException(
                        "SQLException in StandardPoolDataSource:getConnection no connection available " + e);
            }

            ret = con.getConnection();
        } catch (Exception e) {
            log.debug("StandardPoolDataSource:getConnection exception" + e);
            e.printStackTrace();
            SQLException sqle = new SQLException(
                    "SQLException in StandardPoolDataSource:getConnection exception: " + e);
            if (e instanceof SQLException)
                sqle.setNextException((SQLException) e);
            if (con != null) {
                pool.checkIn(con);
            }
            throw sqle;
        }
        log.debug("StandardPoolDataSource:getConnection return a connection");
        return ret;
    }

    /**
     * connectionErrorOccurred and connectionClosed are methods
     * from ConnectionEventListener interface
     *
     * Invoked when a fatal connection error occurs,
     * just before an SQLException is thrown to the application
     */
    public void connectionErrorOccurred(ConnectionEvent event) {
        Object obj = event.getSource();
        PooledConnection pc = (PooledConnection) obj;
        pool.nextGeneration(pc);
        pool.removeLockedObject(pc); // remove the object from the locked pool
        expire(pc); // kill the connection (from super)
        log.debug("StandardXAPoolDataSource:connectionErrorOccurred remove the object from the pool");
    }

    /**
     * Invoked when the application calls close()
     * on its representation of the connection
     */
    public void connectionClosed(ConnectionEvent event) {
        log.debug("StandardPoolDataSource:connectionClosed close the connection");
        Object obj = event.getSource();
        pool.checkIn(obj);
    }

    /**
     * object specific work to kill the object
     */
    public void expire(Object o) {
        log.debug("StandardPoolDataSource:expire expire a connection, remove from the pool");
        if (o == null)
            return;
        try {
            PooledConnection pooledCon = (PooledConnection) o;
            pooledCon.close(); // call close() of PooledConnection
            pooledCon.removeConnectionEventListener(this);
            log.debug("StandardPoolDataSource:expire close the connection");
        } catch (java.sql.SQLException e) {
            log.error("StandardPoolDataSource:expire Error java.sql.SQLException in StandardPoolDataSource:expire");
        }
    }

    /**
     * This method tests if a connection is closed or not
     */
    public boolean checkThisObject(Object o) {

        PooledConnection con;
        Connection ret;
        log.debug("StandardPoolDataSource:checkThisObject verify the current object");
        try {
            con = (PooledConnection) o;
            ret = con.getConnection(); // get the connection from the pool
            if (ret.isClosed()) {
                return false;
            }
            try {
                ret.close();
            } catch (Exception e) {
                log.error("StandardPoolDataSource:checkThisObject can't closed the connection: " + e);
            }

            return true;
        } catch (java.sql.SQLException e) {
            log.error(
                    "StandardPoolDataSource:checkThisObject Error java.sql.SQLException in StandardPoolDataSource:checkThisObject");
            return false;
        }
    }

    /**
     * This method tests if a connection is valid or not
     */
    public boolean testThisObject(Object o) {
        Connection ret = null;
        log.debug("StandardPoolDataSource:testThisObject verify the current object");
        try {
            PooledConnection con = (PooledConnection) o;
            ret = con.getConnection();
            Statement s = ret.createStatement();
            s.execute(jdbcTestStmt);
            s.close();
            try {
                ret.close();
            } catch (Exception e) {
                log.error("StandardPoolDataSource:checkThisObject can't closed the connection: " + e);
            }
            return true;
        } catch (java.sql.SQLException e) {
            log.error(
                    "StandardPoolDataSource:checkThisObject Error java.sql.SQLException in StandardPoolDataSource:testThisObject");
            return false;
        }
    }

    public GenerationObject create() throws SQLException {
        return create(getUser(), getPassword());
    }

    public GenerationObject create(String _user, String _password) throws SQLException {
        log.debug("StandardPoolDataSource:create create a connection for the pool");
        GenerationObject genObject;
        PooledConnection pooledCon = cpds.getPooledConnection(_user, _password);
        // get the pooled connection

        pooledCon.addConnectionEventListener(this);
        // add it to the event listener
        log.debug("StandardPoolDataSource:create create a object for the pool");
        genObject = new GenerationObject(pooledCon, pool.getGeneration(), _user, _password);
        return genObject; // return a connection
    }

    /**
     * stop method to switch off the pool
     */
    public void stopPool() {
        pool.stop();
        onOff = false;
        log.debug("StandardPoolDataSource:stopPool stop now the pool");
    }

    public void shutdown(boolean force) {
        stopPool();
    }

    /**
     * set the logwriter for the current object, the logwriter will be use by
     * the current object and by the generic pool
     * @param logWriter a PrintWriter object
     */
    public void setLogWriter(PrintWriter logWriter) {
        pool.setLogger(log);
        super.setLogger(log);
    }

    /**
     * set the debug flag
     * @param debug a boolean flag
     */
    public void setDebug(boolean debug) {
        super.setDebug(debug);
        pool.setDebug(debug);
    }

    /**
     * set the minimum size of the pool
     * @param minSize minimum size of the pool
     * @throws Exception
     */
    public void setMinSize(int minSize) throws Exception {
        pool.setMinSize(minSize);
    }

    /**     
     * set the maximum size of the pool
     * @param maxSize maximum size of the pool
     * @throws Exception
     */
    public void setMaxSize(int maxSize) throws Exception {
        pool.setMaxSize(maxSize);
    }

    /**
     * set the life time of the pooled objects
     * @param lifeTime life time of the pooled objects (in milliseconds)
     */
    public void setLifeTime(long lifeTime) {
        pool.setLifeTime(lifeTime);
    }

    /**
     * set the sleep time of pooled objects
     * @param sleepTime sleep time of the pooled objects (in milliseconds)
     */
    public void setSleepTime(long sleepTime) {
        pool.setSleepTime(sleepTime);
    }

    /**
     * set the garbage collection option
     * @param gc true: the garbage collector will be launched when clean up of the
     * pool, else false
     */
    public void setGC(boolean gc) {
        pool.setGC(gc);
    }

    /**
     * set the check level of the pooled object before using them
     * @param checkLevelObject (<br>
     * 0 = no special checking
     * 1 = just a check on an object
     * 2 = test the object
     * 3 = just a check on an object (for all the objects)
     * 4 = test the object (for all the objects)
     */
    public void setCheckLevelObject(int checkLevelObject) {
        pool.setCheckLevelObject(checkLevelObject);
    }

    /**
     * set the String to test the jdbc connection before using it
     * @param jdbcTestStmt an sql statement
     */
    public void setJdbcTestStmt(String jdbcTestStmt) {
        this.jdbcTestStmt = jdbcTestStmt;
    }

    /**
     * set the generation number for future connection, the generation number
     * is used to identify a group a created objects
     * @param generation an integer value which represents a generation
     */
    public void setGeneration(int generation) {
        pool.setGeneration(generation);
    }

    /**
     * set the global time the pool can wait for a free object
     * @param deadLock in milliseconds
     */
    public void setDeadLockMaxWait(long deadLock) {
        pool.setDeadLockMaxWait(deadLock);
    }

    /**
     * set the time before 2 tries when trying to obtain an object from the pool
     * @param loopWait in milliseconds
     */
    public void setDeadLockRetryWait(long loopWait) {
        pool.setDeadLockRetryWait(loopWait);
    }

    public PrintWriter getLogWriter() {
        return log;
    }

    public int getMinSize() {
        return pool.getMinSize();
    }

    public int getMaxSize() {
        return pool.getMaxSize();
    }

    public long getLifeTime() {
        return pool.getLifeTime();
    }

    public long getSleepTime() {
        return pool.getSleepTime();
    }

    public int getGeneration() {
        return pool.generation;
    }

    public boolean isGC() {
        return pool.isGC();
    }

    public int getLockedObjectCount() {
        return pool.getLockedObjectCount();
    }

    public int getUnlockedObjectCount() {
        return pool.getUnlockedObjectCount();
    }

    public int getCheckLevelObject() {
        return pool.getCheckLevelObject();
    }

    public String getJdbcTestStmt() {
        return jdbcTestStmt;
    }

    public long getDeadLockMaxWait() {
        return pool.getDeadLockMaxWait();
    }

    public long getDeadLockRetryWait() {
        return pool.getDeadLockRetryWait();
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("StandardPoolDataSource:\n");
        sb.append("     data source name=<" + this.dataSourceName + ">\n");
        sb.append("     jdbc test stmt=<" + this.jdbcTestStmt + ">\n");
        sb.append("     user=<" + this.user + ">\n");
        if (this.cpds != null)
            sb.append(this.cpds.toString());
        sb.append(pool.toString());

        return sb.toString();
    }

    /**
     * Retrieves the Reference of this object. Used at binding time by JNDI
     * to build a reference on this object.
     *
     * @return  The non-null Reference of this object.
     * @exception  NamingException  If a naming exception was encountered while
     * retrieving the reference.
     */
    public Reference getReference() throws NamingException {
        log.debug("StandardPoolDataSource:getReference return a reference of the object");
        Reference ref = super.getReference();
        ref.add(new StringRefAddr("checkLevelObject", Integer.toString(getCheckLevelObject())));
        ref.add(new StringRefAddr("lifeTime", Long.toString(getLifeTime())));
        ref.add(new StringRefAddr("jdbcTestStmt", getJdbcTestStmt()));
        ref.add(new StringRefAddr("maxSize", Integer.toString(getMaxSize())));
        ref.add(new StringRefAddr("minSize", Integer.toString(getMinSize())));
        ref.add(new StringRefAddr("dataSourceName", getDataSourceName()));
        return ref;
    }

    /* (non-Javadoc)
     * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
     */
    public Object getObjectInstance(Object refObj, Name name, Context nameCtx, Hashtable env) throws Exception {

        super.getObjectInstance(refObj, name, nameCtx, env);
        Reference ref = (Reference) refObj;
        this.setLifeTime(Long.parseLong((String) ref.get("lifeTime").getContent()));
        this.setJdbcTestStmt((String) ref.get("jdbcTestStmt").getContent());
        this.setMaxSize(Integer.parseInt((String) ref.get("maxSize").getContent()));
        this.setMinSize(Integer.parseInt((String) ref.get("minSize").getContent()));
        this.setDataSourceName((String) ref.get("dataSourceName").getContent());
        InitialContext ictx = new InitialContext(env);
        cpds = (ConnectionPoolDataSource) ictx.lookup(this.dataSourceName);
        return this;
    }

    /**
     * Override this so that the pool's tf gets set as well
     */
    public void setThreadFactory(JdbcThreadFactory tf) {
        super.setThreadFactory(tf);
        pool.setThreadFactory(tf);
    }

    public GenericPool getPool() {
        return pool;
    }

    public void removeUnlockedObject(GenerationObject obj) {
        pool.removeUnlockedObject(obj);
    }

    public GenericPool getUsedPool() {
        return pool;
    }

    public void fullRemoveLockedObject(GenerationObject obj) {
        pool.fullRemoveLockedObject(obj);
    }

    public void setMaxLifeTime(long maxLifeTime) {
        pool.setMaxLifeTime(maxLifeTime);
    }

    public long getMaxLifeTime() {
        return pool.getMaxLifeTime();
    }
}