Save This Page
Home » xapool-1.5.0-src » org.enhydra.jdbc » standard » [javadoc | source]
    1   /*
    2    * XAPool: Open Source XA JDBC Pool
    3    * Copyright (C) 2003 Objectweb.org
    4    * Initial Developer: Lutris Technologies Inc.
    5    * Contact: xapool-public@lists.debian-sf.objectweb.org
    6    *
    7    * This library is free software; you can redistribute it and/or
    8    * modify it under the terms of the GNU Lesser General Public
    9    * License as published by the Free Software Foundation; either
   10    * version 2.1 of the License, or any later version.
   11    *
   12    * This library is distributed in the hope that it will be useful,
   13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15    * Lesser General Public License for more details.
   16    *
   17    * You should have received a copy of the GNU Lesser General Public
   18    * License along with this library; if not, write to the Free Software
   19    * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
   20    * USA
   21    */
   22   package org.enhydra.jdbc.standard;
   23   
   24   import java.sql.CallableStatement;
   25   import java.sql.PreparedStatement;
   26   import java.sql.SQLException;
   27   import java.util.Enumeration;
   28   import java.util.Hashtable;
   29   import java.sql.Connection;
   30   import org.enhydra.jdbc.core.CoreConnection;
   31   import org.enhydra.jdbc.util.LRUCache;
   32   
   33   /**
   34    * This is an implementation of java.sql.Connection which simply
   35    * delegates almost everything to an underlying physical implemention
   36    * of the same interface.
   37    *
   38    * It relies on a StandardPooledConnection to create it and to supply the
   39    * physical connection and a cache of PreparedStatements. This class will
   40    * try to re-use PreparedStatements wherever possible and will add to the
   41    * cache when totally new PreparedStatements get created.
   42    */
   43   public class StandardConnectionHandle extends CoreConnection {
   44   
   45   	StandardPooledConnection pooledCon;
   46   	// the pooled connection that created this object
   47   	protected Hashtable masterPrepStmtCache;
   48   	// the hashtable of caches, indexed by physical connection
   49   	int preparedStmtCacheSize; // the size of the connection-specific cache
   50   	protected LRUCache preparedStatementCache = null;
   51   	// prepared statements indexed by SQL string
   52   	public Hashtable inUse; // prepared statements that are currently in use
   53   	private boolean closed; // set true when this connection has been closed
   54   	public boolean isReallyUsed = false;
   55   
   56   	/**
   57   	 * Constructor.
   58   	 */
   59   	public StandardConnectionHandle(
   60   		StandardPooledConnection pooledCon,
   61   		Hashtable preparedStatementCache,
   62   		int preparedStmtCacheSize) {
   63   		super(pooledCon.getPhysicalConnection()); // get the real connection
   64   		this.pooledCon = pooledCon; // first save parameters
   65   		masterPrepStmtCache = preparedStatementCache;
   66   		this.preparedStmtCacheSize = preparedStmtCacheSize;
   67   		log = pooledCon.dataSource.log;
   68   		setupPreparedStatementCache();
   69   		inUse = new Hashtable(10, 0.5f);
   70   
   71   		log.debug(
   72   			"StandardConnectionHandle:new StandardConnectionHandle with "
   73   				+ preparedStmtCacheSize
   74   				+ " prepared statement");
   75   	}
   76   
   77   	protected void setupPreparedStatementCache() {
   78   		log.debug("StandardConnectionHandle:setupPreparedStatementCache start");
   79   		if (preparedStmtCacheSize == 0) {
   80   			log.debug(
   81   				"StandardConnectionHandle:setupPreparedStatementCache return with 0");
   82   			preparedStatementCache = null;
   83   			return;
   84   		}
   85   		if (con == null)
   86   			log.warn("Connection is null");
   87   		else {
   88   			preparedStatementCache =
   89   				(LRUCache) masterPrepStmtCache.get(con.toString());
   90   			if (preparedStatementCache == null) {
   91   				preparedStatementCache =
   92   					new PreparedStatementCache(preparedStmtCacheSize);
   93   				preparedStatementCache.setLogger(log);
   94   				masterPrepStmtCache.put(con.toString(), preparedStatementCache);
   95   				log.debug(
   96   					"StandardConnectionHandle:setupPreparedStatementCache "
   97   						+ "preparedStatementCache.size(lru)='"
   98   						+ preparedStatementCache.LRUSize()
   99   						+ "' "
  100   						+ "preparedStatementCache.size(cache)='"
  101   						+ preparedStatementCache.cacheSize()
  102   						+ "' "
  103   						+ "masterPrepStmtCache.size='"
  104   						+ masterPrepStmtCache.size()
  105   						+ "' ");
  106   			} else preparedStatementCache.setLogger(log);
  107   		}
  108   		log.debug("StandardConnectionHandle:setupPreparedStatementCache end");
  109   	}
  110   
  111   	/**
  112   	 * Pre-invokation of the delegation, in case of connection is
  113   	 * closed, we throw an exception
  114   	 */
  115   	public void preInvoke() throws SQLException {
  116   		if (closed)
  117   			throw new SQLException("Connection is closed");
  118   	}
  119   
  120   	/**
  121   	 * Exception management : catch or throw the exception
  122   	 */
  123   	public void catchInvoke(SQLException e) throws SQLException {
  124   		//ConnectionEvent event = new ConnectionEvent (pooledCon);// create event associate with the connection
  125   		//pooledCon.connectionErrorOccurred(event);		// ppoled have to be closed
  126   		throw (e); // throw the exception
  127   	}
  128   
  129   	/**
  130   	 * Closes this StandardConnectionHandle and prevents it
  131   	 * from being reused. It also returns used PreparedStatements
  132   	 * to the PreparedStatement cache and notifies all listeners.
  133   	 */
  134   	synchronized public void close() throws SQLException {
  135   		log.debug("StandardConnectionHandle:close");
  136   		// Note - we don't check to see if already closed. Some servers get confused.
  137   		closed = true; // connection now closed
  138   		Enumeration keys = inUse.keys(); // get any prepared statements in use
  139   		while (keys.hasMoreElements()) { // while more prepared statements used
  140   			Object key = keys.nextElement(); // get next key
  141   			returnToCache(key); // return prepared statement to cache
  142   		}
  143   		pooledCon.closeEvent(); // notify listeners
  144   
  145                   if (preparedStatementCache != null)
  146   		        preparedStatementCache.cleanupAll();
  147                   if ((preparedStatementCache != null) && (masterPrepStmtCache != null) && (log != null))
  148   		log.debug(
  149   			"StandardConnectionHandle:close "
  150   				+ "preparedStatementCache.size(lru)='"
  151   				+ preparedStatementCache.LRUSize()
  152   				+ "' "
  153   				+ "preparedStatementCache.size(cache)='"
  154   				+ preparedStatementCache.cacheSize()
  155   				+ "' "
  156   				+ "masterPrepStmtCache.size='"
  157   				+ masterPrepStmtCache.size()
  158   				+ "' ");
  159   	}
  160   
  161   	/**
  162   	 * Removes a prepared statement from the inUse list
  163   	 * and returns it to the cache.
  164   	 */
  165   	void returnToCache(Object key, Connection theCon) {
  166   		Object value = inUse.remove(key);
  167   		// remove key/value from used statements
  168   		if (value != null) {
  169   			LRUCache theCache =
  170   				(LRUCache) masterPrepStmtCache.get(theCon.toString());
  171   			theCache.put(key, value); // place back in cache, ready for re-use
  172   		}
  173   	}
  174   
  175   	void returnToCache(Object key) {
  176   		returnToCache(key, con);
  177   	}
  178   
  179   	/**
  180   	 * Checks to see if a prepared statement with the same concurrency
  181   	 * has already been created. If not, then a new prepared statement
  182   	 * is created and added to the cache.
  183   	 *
  184   	 * If a prepared statement is found in the cache then it is removed
  185   	 * from the cache and placed on the "inUse" list. This ensures that
  186   	 * if multiple threads use the same StandardConnectionHandle, or a single
  187   	 * thread does multiple prepares using the same SQL, then DIFFERENT
  188   	 * prepared statements will be returned.
  189   	 */
  190   	synchronized PreparedStatement checkPreparedCache(
  191   		String sql,
  192   		int type,
  193   		int concurrency,
  194   		int holdability)
  195   		throws SQLException {
  196   		log.debug(
  197   			"StandardConnectionHandle:checkPreparedCache sql='" + sql + "'");
  198   		PreparedStatement ret = null; // the return value
  199   		// NOTE - We include the Connection in the lookup key. This has no
  200   		// effect here but is needed by StandardXAConnection where the the physical
  201   		// Connection used can vary over time depending on the global transaction.
  202   		String lookupKey = sql + type + concurrency;
  203   		// used to lookup statements
  204   		if (preparedStatementCache != null) {
  205   			Object obj = preparedStatementCache.get(lookupKey);
  206   			// see if there's a PreparedStatement already
  207   			if (obj != null) { // if there is
  208   				ret = (PreparedStatement) obj; // use as return value
  209   				try {
  210   					ret.clearParameters(); // make it look like new
  211   				} catch (SQLException e) {
  212   					// Bad statement, so we have to create a new one
  213   					ret = createPreparedStatement(sql, type, concurrency, holdability);
  214   				}
  215   
  216   				preparedStatementCache.remove(lookupKey);
  217   				// make sure it cannot be re-used
  218   				inUse.put(lookupKey, ret);
  219   				// make sure it gets reused by later delegates
  220   			} else { // no PreparedStatement ready
  221   				ret = createPreparedStatement(sql, type, concurrency, holdability);
  222   				inUse.put(lookupKey, ret);
  223   				// will get saved in prepared statement cache
  224   			}
  225   		} else {
  226   			ret = createPreparedStatement(sql, type, concurrency, holdability);
  227   		}
  228   		// We don't actually give the application a real PreparedStatement. Instead
  229   		// they get a StandardPreparedStatement that delegates everything except
  230   		// PreparedStatement.close();
  231   
  232   		ret = new StandardPreparedStatement(this, ret, lookupKey);
  233   		return ret;
  234   	}
  235   
  236   
  237   	synchronized PreparedStatement checkPreparedCache(
  238   		String sql,
  239   		int autogeneratedkeys)
  240   		throws SQLException {
  241   		log.debug(
  242   			"StandardConnectionHandle:checkPreparedCache sql='" + sql + "'");
  243   		PreparedStatement ret = null; // the return value
  244   		// NOTE - We include the Connection in the lookup key. This has no
  245   		// effect here but is needed by StandardXAConnection where the the physical
  246   		// Connection used can vary over time depending on the global transaction.
  247   		String lookupKey = sql + autogeneratedkeys;
  248   		// used to lookup statements
  249   		if (preparedStatementCache != null) {
  250   			Object obj = preparedStatementCache.get(lookupKey);
  251   			// see if there's a PreparedStatement already
  252   			if (obj != null) { // if there is
  253   				ret = (PreparedStatement) obj; // use as return value
  254   				try {
  255   					ret.clearParameters(); // make it look like new
  256   				} catch (SQLException e) {
  257   					// Bad statement, so we have to create a new one
  258   					ret = createPreparedStatement(sql, autogeneratedkeys);
  259   				}
  260   
  261   				preparedStatementCache.remove(lookupKey);
  262   				// make sure it cannot be re-used
  263   				inUse.put(lookupKey, ret);
  264   				// make sure it gets reused by later delegates
  265   			} else { // no PreparedStatement ready
  266   				ret = createPreparedStatement(sql, autogeneratedkeys);
  267   				inUse.put(lookupKey, ret);
  268   				// will get saved in prepared statement cache
  269   			}
  270   		} else {
  271   			ret = createPreparedStatement(sql, autogeneratedkeys);
  272   		}
  273   		// We don't actually give the application a real PreparedStatement. Instead
  274   		// they get a StandardPreparedStatement that delegates everything except
  275   		// PreparedStatement.close();
  276   
  277   		ret = new StandardPreparedStatement(this, ret, lookupKey);
  278   		return ret;
  279   	}
  280   
  281   
  282   
  283   	protected PreparedStatement createPreparedStatement(
  284   		String sql,
  285   		int type,
  286   		int concurrency,
  287   		int holdability)
  288   		throws SQLException {
  289   		log.debug(
  290   			"StandardConnectionHandle:createPreparedStatement type ='"
  291   				+ type
  292   				+ "'");
  293   		if (type == 0 && holdability == 0) { // if no type or concurrency specified
  294   			return con.prepareStatement(sql); // create new prepared statement
  295   		} else if (holdability == 0) {
  296   			return con.prepareStatement(sql, type, concurrency);
  297   			// create new prepared statement
  298   		} else return con.prepareStatement(sql, type, concurrency, holdability);
  299   	}
  300   
  301   
  302   	protected PreparedStatement createPreparedStatement(
  303   		String sql,
  304   		int autogeneratedkeys)
  305   		throws SQLException {
  306   		log.debug(
  307   			"StandardConnectionHandle:createPreparedStatement autogeneratedkeys ='"
  308   				+ autogeneratedkeys
  309   				+ "'");
  310   		return con.prepareStatement(sql, autogeneratedkeys); // create new prepared statement
  311   	}
  312   
  313   	/**
  314   	 * Creates a PreparedStatement for the given SQL. If possible, the
  315   	 * statement is fetched from the cache.
  316   	 */
  317   	public PreparedStatement prepareStatement(String sql) throws SQLException {
  318   		log.debug(
  319   			"StandardConnectionHandle:prepareStatement sql='" + sql + "'");
  320   		preInvoke();
  321   		try {
  322   			return checkPreparedCache(sql, 0, 0, 0);
  323   		} catch (SQLException e) {
  324   			catchInvoke(e);
  325   		}
  326   		return null;
  327   	}
  328   
  329   	/**
  330   	 * Creates a PreparedStatement for the given SQL, type and concurrency.
  331   	 * If possible, the statement is fetched from the cache.
  332   	 */
  333   	public PreparedStatement prepareStatement(
  334   		String sql,
  335   		int resultSetType,
  336   		int resultSetConcurrency)
  337   		throws SQLException {
  338   		preInvoke();
  339   		try {
  340   			return checkPreparedCache(sql, resultSetType, resultSetConcurrency, 0);
  341   		} catch (SQLException e) {
  342   			catchInvoke(e);
  343   		}
  344   		return null;
  345   	}
  346   
  347   	public PreparedStatement prepareStatement(
  348   		String sql,
  349   		int resultSetType,
  350   		int resultSetConcurrency,
  351   		int resultSetHoldability)
  352   		throws SQLException {
  353   		preInvoke();
  354   		try {
  355   			return checkPreparedCache(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
  356   		} catch (SQLException e) {
  357   			catchInvoke(e);
  358   		}
  359   		return null;
  360   	}
  361   
  362   	public boolean isClosed() throws SQLException {
  363   		return closed;
  364   	}
  365   
  366   	public CallableStatement prepareCall(
  367   		String sql,
  368   		int resultSetType,
  369   		int resultSetConcurrency)
  370   		throws SQLException {
  371   		preInvoke();
  372   		try {
  373   			return con.prepareCall(sql, resultSetType, resultSetConcurrency);
  374   		} catch (SQLException e) {
  375   			catchInvoke(e);
  376   		}
  377   		return null;
  378   	}
  379   }

Save This Page
Home » xapool-1.5.0-src » org.enhydra.jdbc » standard » [javadoc | source]