PooledDataSource.java :  » Database-ORM » XORM » org » xorm » datastore » sql » Java Open Source

Java Open Source » Database ORM » XORM 
XORM » org » xorm » datastore » sql » PooledDataSource.java
/*
    $Header: /cvsroot/xorm/xorm/src/org/xorm/datastore/sql/PooledDataSource.java,v 1.7 2003/07/12 00:57:29 sbendar Exp $

    This file is part of XORM.

    XORM 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
    (at your option) any later version.

    XORM 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 XORM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.xorm.datastore.sql;

import java.util.Iterator;
import java.util.Properties;
import java.util.LinkedList;
import java.util.logging.Logger;
import java.util.logging.Level;

import java.io.PrintWriter;

import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
import java.sql.Driver;

import javax.sql.DataSource;

/**
 * Wraps a JDBC driver manager and provides a pooling of connections.
 *
 * @author Scott Bendar
 * @version $Revision: 1.7 $
 */
public class PooledDataSource implements DataSource {

    private static final Logger logger =
  Logger.getLogger("org.xorm.datastore.sql");

    /**
     * Milliseconds to wait for a connection to become available
     * before waking up and logging the wait before waiting again.
     * This is overridden if the loginTimeout is less than this.
     */
    private static final int DEFAULT_TIMEOUT_MS = 5000;

    private String connectionURL = null;
    private String driverName = null;
    private int minPool = 0;
    private int maxPool = 5;
    private int loginTimeout = 0;
    private boolean checkReturnedConnection = false;
    private long idleCheck = 0;
    private Integer isolationLevel;
    private String idleCheckSQL = null;

    protected Properties props = new Properties();
    protected int connCount = 0;

    protected Driver driver = null;

    protected LinkedList availableConnections = new LinkedList();
  

    /**
     * Initializes a new pooled data source
     */
    public PooledDataSource() {
    }

    /**
     * Sets the user name to pass when making a db connection
     */
    public void setUser(String s) {
   props.setProperty("user", s);
    }
    public String getUser() {
  return props.getProperty("user");
    }

    /**
     * Sets the password to use when creating a db connection.  For
     * security reasons, there is no way to get the password.
     */
    public void setPassword(String s) {
  props.setProperty("password", s);
    }

    /**
     * Set the URL used to connect to the DB
     */
    public void setConnectionUrl(String s) {
  connectionURL = s;
    }
    public String getConnectionUrl() {
  return connectionURL;
    }

    /**
     * Set the name of the driver to load when connecting to the DB
     */
    public void setDriverName(String s) {
  driverName = s;
    }
    public String getDriverName() {
  return driverName;
    }

    /**
     * The maximum number of connections created by the data source.
     * Once the max is exceeded callers must wait for a connection to
     * become available.
     */
    public int getMaxPool() { 
  return maxPool;
    }
    public void setMaxPool(int i) {
  if (i < 1) {
      logger.info("Max pool size is disabled, setting to max int.");
      i = Integer.MAX_VALUE;
  }
  maxPool = i;
    }

    /**
     * The minimum number of connections in the pool.  It will create
     * this number of connections when the pool is first initialized
     * and the pool will never shrink below this many connections.
     */
    public int getMinPool() {
  return minPool;
    }
    public void setMinPool(int i) {
  minPool = i;
    }


    public int getLoginTimeout() {
  return loginTimeout;
    }
    /**
     * Sets the maximum time to wait in seconds for a connection
     * before throwing an exception.
     */
    public void setLoginTimeout(int timeout) {
  loginTimeout = timeout;
    }

    /**
     * If set, instructs the connection to verify its valid when the
     * caller is done with the connection (calls close).  This is used
     * for debugging purposes only to help catch folks to return closed
     * connections to the pool.
     */
    public boolean getCheckReturnedConnection() {
        return checkReturnedConnection;
    }

    /**
     * If set, instructs the connection to verify its valid when the
     * caller is done with the connection (calls close).  This is used
     * for debugging purposes only to help catch folks to return closed
     * connections to the pool.
     */
    public void setCheckReturnedConnection(boolean value) {
        checkReturnedConnection = value;
    }


    /**
     * Gets the amount of time in milliseconds a connection has been
     * idle before the pool tests the connection to see if its still
     * valid.
     *
     * -1: Never check
     * 0: Always check
     * 
     */
    public long getIdleCheck() {
        return idleCheck;
    }

    /**
     * Sets the amount of time in milliseconds a connection has been
     * idle before the pool tests the connection to see if its still
     * valid.
     *
     * -1: Never check
     * 0: Always check
     * 
     */
    public void setIdleCheck(long idle) {
        idleCheck = idle;
    }

    /**
     * Gets a sql statement to be used to check a connection.  If not
     * specified, isConnectionClosed() is called instead.
     */
    public String getIdleCheckSQL() {
        return idleCheckSQL;
    }
    
    /**
     * Sets a sql statement to be used to check a connection.  If not
     * specified, isConnectionClosed() is called instead.
     */
    public void setIdleCheckSQL(String sql) {
        idleCheckSQL = sql;
    }
    

    /**
     * Sets the transaction isolation level.  If null, no specific
     * setting is made on a Connection instance; otherwise, the wrapped
     * value is used.
     */
    public void setIsolationLevel(Integer isolationLevel) {
  this.isolationLevel = isolationLevel;
    }

    public Integer getIsolationLevel() {
  return isolationLevel;
    }

    /**
     * Gets a DB connection, actually a wrapper that allows the
     * connection to be pooled.  The method first allocates a
     * connection from the available pool, if there are none available
     * it creates a new connection unless that would put it over the
     * max pool size, in which case it forces the caller to wait.
     *
     * If the data source was configured with a loginTimeout value
     * greater than 0, it will wait for loginTimeout seconds for a
     * connection.
     */
    public Connection getConnection() throws SQLException {
    
  PooledConnection conn = null;

  int timeSpent = 0;

  // Don't wait forever, wake up and check things out periodically
  int waitTime = DEFAULT_TIMEOUT_MS;

  // If there is a fixed time we are willing to wait, just wait that
  // long
  if (loginTimeout > 0)
      waitTime = loginTimeout;

  synchronized(availableConnections) {

      // Initialize the driver the first time its used
      if (driver == null)
    initDriver();
      
      while (loginTimeout == 0 || timeSpent < loginTimeout) {
        
    long startTime = System.currentTimeMillis();

    if (availableConnections.size() > 0) {
        logger.fine("getConnection(): using available connection");
        conn = (PooledConnection)availableConnections.removeFirst();
        conn.setClosed(false);
        try {
      testConnection(conn);
      return conn;
        }
        catch (SQLException e) {
      /*
       * If it fails the test, get rid of the connection
       * so we will try and allocate a new one before
       * returning a SQLException to the caller.
       */
                logger.log(Level.SEVERE, "Connection failed test, dropping connection from pool!", e);
                dropConnection(conn);
        }
    }
    else {
        if (connCount < maxPool) {
      logger.fine("getConnection(): creating new connection");
      conn = createNewConnection();
      return conn;
        }
        else {
      logger.fine("getConnection(): waiting for available connection");
      try {
          availableConnections.wait(waitTime);
      }
      catch (InterruptedException e) {
          logger.fine("getConnection(): wait interrupted!");
      }
      timeSpent += (int)(System.currentTimeMillis() -
             startTime);
      logger.fine("getConnection(): wait timeout, wait total is " + timeSpent + " seconds");
        }
    }
      }
  }

  logger.warning("getConnection(): wait exceeded loginTimeout");
  throw new SQLException("getConnection(): wait exceeded loginTimeout");
    }

    /**
     * This function is currently not implemented as it doesn't allow
     * us to use the connections in the pool.  The workaround is to
     * create a new data source with a different user and password.
     *
     * Should this just pass back an unpooled real connection?
     */
    public Connection getConnection(String userName, String password)
  throws SQLException {

  throw new SQLException("getConnection(user,pass) not supported, create a new data source with desired user and password");
    
    }

    /**
     * The PooledDataSource class uses JDK 1.4 logging and ignores the
     * log writer.
     */
    public PrintWriter getLogWriter() {
  return null;
    }
    public void setLogWriter(PrintWriter logger) {
    }


    /**
     * Initialize the driver and create the initial connections up to
     * the min pool size.
     */
    private void initDriver() throws SQLException {
    
  if (driver != null)
      return;

  logger.finer("initDriver() -- Entry");

  try {
      Class driverClass = Class.forName(driverName);
      driver = (Driver)driverClass.newInstance();

      // Create the minimum connections
      while (connCount < minPool) {
    availableConnections.add(createNewConnection());
      }

  }
  catch (Throwable t) {
      throw new SQLException("initDriver: " + t);
  }

    }

    /**
     * Creats a new physical db connection and wraps it in our
     * PooledConnection
     */
    private PooledConnection createNewConnection() throws SQLException {

  PooledConnection conn = null;
  if (driver != null) {
      conn = new PooledConnection(this,
          driver.connect(connectionURL, props));
      if (isolationLevel != null) {
    conn.setTransactionIsolation(isolationLevel.intValue());
      }
      connCount++;
  }
    
  return conn;
    }

  /**
   * Tests if the connection is still valid or not.
   */
  private void testConnection(PooledConnection conn) throws SQLException {
        if (conn.hasReceivedException() ||
            (idleCheck >= 0 &&
             (System.currentTimeMillis() - conn.getLastUsedTime() > idleCheck))) {

            conn.clearReceivedException();

            if (!isConnectionValid(conn))
                throw new SQLException("Connection is invalid, remove from pool.");
        }
    }

    /**
     * Checks if a connection is active by running a SQL query specified
     * by IdleCheckSQL or by calling isClosed() if no sql statement has
     * been specified.
     */
    private boolean isConnectionValid(PooledConnection conn)
        throws SQLException {

        if (idleCheckSQL == null) {
            logger.fine("Checking connection using isClosed");
            return !conn.isConnectionClosed();
        }
        else {
            logger.fine("Checking connection with SQL: " + idleCheckSQL);
            Statement statement = conn.createStatement();

            try {
                statement.execute(idleCheckSQL);
            }
            finally {
                statement.close();
            }
            return true;
        }
    }

    /**
     * Makes the connection available again.  This is called by the
     * PooledConnection class when the user chooses to close a
     * connection.
     */
    protected void makeAvailable(PooledConnection conn) {
  synchronized (availableConnections) {
      availableConnections.add(conn);
      logger.fine("makeAvailable(): available=" +
      availableConnections.size() + " total=" +
      connCount);
      availableConnections.notify();
  }
    }

    /** Releases all available connections. */
    // TODO: handle closure of connections that are currently leased
    public void close() {
  synchronized (availableConnections) {
      Iterator i = availableConnections.iterator();
      while (i.hasNext()) {
    PooledConnection conn = (PooledConnection) i.next();
    conn.closeConnection();
    i.remove();
    connCount--;
      }
  }
    }

    /** remove a connection from the pool */
    protected void dropConnection(PooledConnection conn) {
        synchronized (availableConnections) {
            conn.closeConnection();
            connCount--;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.