Package org.enhydra.jdbc.standard

Source Code of org.enhydra.jdbc.standard.StandardConnectionHandle

/*
* 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.standard;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.sql.Connection;
import org.enhydra.jdbc.core.CoreConnection;
import org.enhydra.jdbc.util.LRUCache;

/**
* This is an implementation of java.sql.Connection which simply
* delegates almost everything to an underlying physical implemention
* of the same interface.
*
* It relies on a StandardPooledConnection to create it and to supply the
* physical connection and a cache of PreparedStatements. This class will
* try to re-use PreparedStatements wherever possible and will add to the
* cache when totally new PreparedStatements get created.
*/
public class StandardConnectionHandle extends CoreConnection {

  StandardPooledConnection pooledCon;
  // the pooled connection that created this object
  protected Hashtable masterPrepStmtCache;
  // the hashtable of caches, indexed by physical connection
  int preparedStmtCacheSize; // the size of the connection-specific cache
  protected LRUCache preparedStatementCache = null;
  // prepared statements indexed by SQL string
  public Hashtable inUse; // prepared statements that are currently in use
  private boolean closed; // set true when this connection has been closed
  public boolean isReallyUsed = false;

  /**
   * Constructor.
   */
  public StandardConnectionHandle(
    StandardPooledConnection pooledCon,
    Hashtable preparedStatementCache,
    int preparedStmtCacheSize) {
    super(pooledCon.getPhysicalConnection()); // get the real connection
    this.pooledCon = pooledCon; // first save parameters
    masterPrepStmtCache = preparedStatementCache;
    this.preparedStmtCacheSize = preparedStmtCacheSize;
    log = pooledCon.dataSource.log;
    setupPreparedStatementCache();
    inUse = new Hashtable(10, 0.5f);

    log.debug(
      "StandardConnectionHandle:new StandardConnectionHandle with "
        + preparedStmtCacheSize
        + " prepared statement");
  }

  protected void setupPreparedStatementCache() {
    log.debug("StandardConnectionHandle:setupPreparedStatementCache start");
    if (preparedStmtCacheSize == 0) {
      log.debug(
        "StandardConnectionHandle:setupPreparedStatementCache return with 0");
      preparedStatementCache = null;
      return;
    }
    if (con == null)
      log.warn("Connection is null");
    else {
      preparedStatementCache =
        (LRUCache) masterPrepStmtCache.get(con.toString());
      if (preparedStatementCache == null) {
        preparedStatementCache =
          new PreparedStatementCache(preparedStmtCacheSize);
        preparedStatementCache.setLogger(log);
        masterPrepStmtCache.put(con.toString(), preparedStatementCache);
        log.debug(
          "StandardConnectionHandle:setupPreparedStatementCache "
            + "preparedStatementCache.size(lru)='"
            + preparedStatementCache.LRUSize()
            + "' "
            + "preparedStatementCache.size(cache)='"
            + preparedStatementCache.cacheSize()
            + "' "
            + "masterPrepStmtCache.size='"
            + masterPrepStmtCache.size()
            + "' ");
      } else preparedStatementCache.setLogger(log);
    }
    log.debug("StandardConnectionHandle:setupPreparedStatementCache end");
  }

  /**
   * Pre-invokation of the delegation, in case of connection is
   * closed, we throw an exception
   */
  public void preInvoke() throws SQLException {
    if (closed)
      throw new SQLException("Connection is closed");
  }

  /**
   * Exception management : catch or throw the exception
   */
  public void catchInvoke(SQLException e) throws SQLException {
    //ConnectionEvent event = new ConnectionEvent (pooledCon);// create event associate with the connection
    //pooledCon.connectionErrorOccurred(event);    // ppoled have to be closed
    throw (e); // throw the exception
  }

  /**
   * Closes this StandardConnectionHandle and prevents it
   * from being reused. It also returns used PreparedStatements
   * to the PreparedStatement cache and notifies all listeners.
   */
  synchronized public void close() throws SQLException {
    log.debug("StandardConnectionHandle:close");
    // Note - we don't check to see if already closed. Some servers get confused.
    closed = true; // connection now closed
    Enumeration keys = inUse.keys(); // get any prepared statements in use
    while (keys.hasMoreElements()) { // while more prepared statements used
      Object key = keys.nextElement(); // get next key
      returnToCache(key); // return prepared statement to cache
    }
    pooledCon.closeEvent(); // notify listeners

                if (preparedStatementCache != null)
            preparedStatementCache.cleanupAll();
                if ((preparedStatementCache != null) && (masterPrepStmtCache != null) && (log != null))
    log.debug(
      "StandardConnectionHandle:close "
        + "preparedStatementCache.size(lru)='"
        + preparedStatementCache.LRUSize()
        + "' "
        + "preparedStatementCache.size(cache)='"
        + preparedStatementCache.cacheSize()
        + "' "
        + "masterPrepStmtCache.size='"
        + masterPrepStmtCache.size()
        + "' ");
  }

  /**
   * Removes a prepared statement from the inUse list
   * and returns it to the cache.
   */
  void returnToCache(Object key, Connection theCon) {
    Object value = inUse.remove(key);
    // remove key/value from used statements
    if (value != null) {
      LRUCache theCache =
        (LRUCache) masterPrepStmtCache.get(theCon.toString());
      theCache.put(key, value); // place back in cache, ready for re-use
    }
  }

  void returnToCache(Object key) {
    returnToCache(key, con);
  }

  /**
   * Checks to see if a prepared statement with the same concurrency
   * has already been created. If not, then a new prepared statement
   * is created and added to the cache.
   *
   * If a prepared statement is found in the cache then it is removed
   * from the cache and placed on the "inUse" list. This ensures that
   * if multiple threads use the same StandardConnectionHandle, or a single
   * thread does multiple prepares using the same SQL, then DIFFERENT
   * prepared statements will be returned.
   */
  synchronized PreparedStatement checkPreparedCache(
    String sql,
    int type,
    int concurrency,
    int holdability)
    throws SQLException {
    log.debug(
      "StandardConnectionHandle:checkPreparedCache sql='" + sql + "'");
    PreparedStatement ret = null; // the return value
    // NOTE - We include the Connection in the lookup key. This has no
    // effect here but is needed by StandardXAConnection where the the physical
    // Connection used can vary over time depending on the global transaction.
    String lookupKey = sql + type + concurrency;
    // used to lookup statements
    if (preparedStatementCache != null) {
      Object obj = preparedStatementCache.get(lookupKey);
      // see if there's a PreparedStatement already
      if (obj != null) { // if there is
        ret = (PreparedStatement) obj; // use as return value
        try {
          ret.clearParameters(); // make it look like new
        } catch (SQLException e) {
          // Bad statement, so we have to create a new one
          ret = createPreparedStatement(sql, type, concurrency, holdability);
        }

        preparedStatementCache.remove(lookupKey);
        // make sure it cannot be re-used
        inUse.put(lookupKey, ret);
        // make sure it gets reused by later delegates
      } else { // no PreparedStatement ready
        ret = createPreparedStatement(sql, type, concurrency, holdability);
        inUse.put(lookupKey, ret);
        // will get saved in prepared statement cache
      }
    } else {
      ret = createPreparedStatement(sql, type, concurrency, holdability);
    }
    // We don't actually give the application a real PreparedStatement. Instead
    // they get a StandardPreparedStatement that delegates everything except
    // PreparedStatement.close();

    ret = new StandardPreparedStatement(this, ret, lookupKey);
    return ret;
  }


  synchronized PreparedStatement checkPreparedCache(
    String sql,
    int autogeneratedkeys)
    throws SQLException {
    log.debug(
      "StandardConnectionHandle:checkPreparedCache sql='" + sql + "'");
    PreparedStatement ret = null; // the return value
    // NOTE - We include the Connection in the lookup key. This has no
    // effect here but is needed by StandardXAConnection where the the physical
    // Connection used can vary over time depending on the global transaction.
    String lookupKey = sql + autogeneratedkeys;
    // used to lookup statements
    if (preparedStatementCache != null) {
      Object obj = preparedStatementCache.get(lookupKey);
      // see if there's a PreparedStatement already
      if (obj != null) { // if there is
        ret = (PreparedStatement) obj; // use as return value
        try {
          ret.clearParameters(); // make it look like new
        } catch (SQLException e) {
          // Bad statement, so we have to create a new one
          ret = createPreparedStatement(sql, autogeneratedkeys);
        }

        preparedStatementCache.remove(lookupKey);
        // make sure it cannot be re-used
        inUse.put(lookupKey, ret);
        // make sure it gets reused by later delegates
      } else { // no PreparedStatement ready
        ret = createPreparedStatement(sql, autogeneratedkeys);
        inUse.put(lookupKey, ret);
        // will get saved in prepared statement cache
      }
    } else {
      ret = createPreparedStatement(sql, autogeneratedkeys);
    }
    // We don't actually give the application a real PreparedStatement. Instead
    // they get a StandardPreparedStatement that delegates everything except
    // PreparedStatement.close();

    ret = new StandardPreparedStatement(this, ret, lookupKey);
    return ret;
  }



  protected PreparedStatement createPreparedStatement(
    String sql,
    int type,
    int concurrency,
    int holdability)
    throws SQLException {
    log.debug(
      "StandardConnectionHandle:createPreparedStatement type ='"
        + type
        + "'");
    if (type == 0 && holdability == 0) { // if no type or concurrency specified
      return con.prepareStatement(sql); // create new prepared statement
    } else if (holdability == 0) {
      return con.prepareStatement(sql, type, concurrency);
      // create new prepared statement
    } else return con.prepareStatement(sql, type, concurrency, holdability);
  }


  protected PreparedStatement createPreparedStatement(
    String sql,
    int autogeneratedkeys)
    throws SQLException {
    log.debug(
      "StandardConnectionHandle:createPreparedStatement autogeneratedkeys ='"
        + autogeneratedkeys
        + "'");
    return con.prepareStatement(sql, autogeneratedkeys); // create new prepared statement
  }

  /**
   * Creates a PreparedStatement for the given SQL. If possible, the
   * statement is fetched from the cache.
   */
  public PreparedStatement prepareStatement(String sql) throws SQLException {
    log.debug(
      "StandardConnectionHandle:prepareStatement sql='" + sql + "'");
    preInvoke();
    try {
      return checkPreparedCache(sql, 0, 0, 0);
    } catch (SQLException e) {
      catchInvoke(e);
    }
    return null;
  }

  /**
   * Creates a PreparedStatement for the given SQL, type and concurrency.
   * If possible, the statement is fetched from the cache.
   */
  public PreparedStatement prepareStatement(
    String sql,
    int resultSetType,
    int resultSetConcurrency)
    throws SQLException {
    preInvoke();
    try {
      return checkPreparedCache(sql, resultSetType, resultSetConcurrency, 0);
    } catch (SQLException e) {
      catchInvoke(e);
    }
    return null;
  }

  public PreparedStatement prepareStatement(
    String sql,
    int resultSetType,
    int resultSetConcurrency,
    int resultSetHoldability)
    throws SQLException {
    preInvoke();
    try {
      return checkPreparedCache(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    } catch (SQLException e) {
      catchInvoke(e);
    }
    return null;
  }

  public boolean isClosed() throws SQLException {
    return closed;
  }

  public CallableStatement prepareCall(
    String sql,
    int resultSetType,
    int resultSetConcurrency)
    throws SQLException {
    preInvoke();
    try {
      return con.prepareCall(sql, resultSetType, resultSetConcurrency);
    } catch (SQLException e) {
      catchInvoke(e);
    }
    return null;
  }
}
TOP

Related Classes of org.enhydra.jdbc.standard.StandardConnectionHandle

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.