⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 googoostatementcache.java

📁 c3p0数据库连接池实现源码
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* * Distributed as part of c3p0 v.0.9.1-pre6 * * Copyright (C) 2005 Machinery For Change, Inc. * * Author: Steve Waldman <swaldman@mchange.com> * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1, as  * published by the Free Software Foundation. * * This software 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 software; see the file LICENSE.  If not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */package com.mchange.v2.c3p0.stmt;import java.util.*;import java.sql.*;import java.lang.reflect.*;import com.mchange.v2.async.AsynchronousRunner;import com.mchange.v2.sql.SqlUtils;import com.mchange.v2.log.*;import com.mchange.v1.db.sql.StatementUtils;import com.mchange.v2.holders.ChangeNotifyingSynchronizedIntHolder; //req'd only for oracle bug workaroundpublic abstract class GooGooStatementCache{    private final static MLogger logger = MLog.getLogger( GooGooStatementCache.class );    /* MT: protected by this's lock */    // contains all statements in the cache,     // organized by connection    ConnectionStatementManager cxnStmtMgr;    // contains all statements in the cache,     // bound to the keys that produced them    HashMap stmtToKey      = new HashMap();    // maps all known keys to their set of statements    // and to a queue of statements, if any, available    // for checkout    HashMap keyToKeyRec    = new HashMap();        // contains all checked out statements -- in the cache,     // but not currently available for checkout, nor for    // culling in case of overflow    HashSet checkedOut = new HashSet();        int stmt_count;    /* MT: end protected by this' lock */    /* MT: protected by its own lock */    AsynchronousRunner blockingTaskAsyncRunner;    // This set is used to ensure that multiple threads    // do not try to remove the same statement from the    // cache, if for example a Statement is both deathmarched    // away and its parent Connection is closed.    //    // ALL ACCESS SHOULD BE EXPLICITLY SYNCHRONIZED    // ON removalPending's lock!    HashSet removalPending = new HashSet();    /* MT: end protected by its own lock */    public GooGooStatementCache(AsynchronousRunner blockingTaskAsyncRunner)    { 	this.blockingTaskAsyncRunner = blockingTaskAsyncRunner; 	this.cxnStmtMgr = createConnectionStatementManager();    }    abstract ConnectionStatementManager createConnectionStatementManager();    public synchronized Object checkoutStatement( Connection physicalConnection,						  Method stmtProducingMethod, 						  Object[] args )	throws SQLException    {	Object out = null;	StatementCacheKey key = StatementCacheKey.find( physicalConnection, 							stmtProducingMethod, 							args );	LinkedList l = checkoutQueue( key );	if (l == null || l.isEmpty()) //we need a new statement	    {		// we might wait() here... 		// don't presume atomicity before and after!		out = acquireStatement( physicalConnection, stmtProducingMethod, args );		if ( prepareAssimilateNewStatement( physicalConnection ) )		    assimilateNewCheckedOutStatement( key, physicalConnection, out );		// else case: we can't assimilate the statement...		// so, we just return our newly created statement, without caching it.		// on check-in, it will simply be destroyed... this is an "overload statement"	    }	else //okay, we can use an old one	    {  		if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)		    logger.finest(this.getClass().getName() + " ----> CACHE HIT");  		    //System.err.println("-------------> CACHE HIT!");		out = l.get(0);		l.remove(0);		if (! checkedOut.add( out ))		    throw new RuntimeException("Internal inconsistency: " +					       "Checking out a statement marked " + 					       "as already checked out!");		removeStatementFromDeathmarches( out, physicalConnection );	    }	if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)	    { 		//System.err.print("checkoutStatement(): ");		//printStats();		if (logger.isLoggable(MLevel.FINEST))		    logger.finest("checkoutStatement: " + statsString());	    }	return out;    }    public synchronized void checkinStatement( Object pstmt )	throws SQLException    {	if (checkedOut == null) //we're closed	    {		synchronousDestroyStatement( pstmt );		return;	    }	else if (! checkedOut.remove( pstmt ) )	    {		if (! ourResource( pstmt ) ) //this is not our resource, or it is an overload statement		    destroyStatement( pstmt ); // so we just destroy		//in the else case, it's already checked-in, so we ignore				return;	    }	try	    { refreshStatement( (PreparedStatement) pstmt ); }	catch (Exception e)	    {		if (Debug.DEBUG)		    {// 			System.err.println("Problem with checked-in Statement, discarding.");// 			e.printStackTrace();			if (logger.isLoggable(MLevel.INFO))			    logger.log(MLevel.INFO, "Problem with checked-in Statement, discarding.", e);		    }				// swaldman -- 2004-01-31: readd problem statement to checkedOut for consistency		// the statement is not yet checked-in, but it is removed from checked out, and this		// violates the consistency assumption of removeStatement(). Thanks to Zach Scott for		// calling attention to this issue.		checkedOut.add( pstmt );		removeStatement( pstmt, true ); //force destruction of the statement even though it appears checked-out		return;	    }	StatementCacheKey key = (StatementCacheKey) stmtToKey.get( pstmt );	if (Debug.DEBUG && key == null)	    throw new RuntimeException("Internal inconsistency: " +				       "A checked-out statement has no key associated with it!");	LinkedList l = checkoutQueue( key );	l.add( pstmt );	addStatementToDeathmarches( pstmt, key.physicalConnection );	if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)	    {// 		System.err.print("checkinStatement(): ");// 		printStats();		if (logger.isLoggable(MLevel.FINEST))		    logger.finest("checkinStatement(): " + statsString());	    }    }    public synchronized void checkinAll(Connection pcon)	throws SQLException    {	//new Exception("checkinAll()").printStackTrace();	Set stmtSet = cxnStmtMgr.statementSet( pcon );	if (stmtSet != null)	    {		for (Iterator ii = stmtSet.iterator(); ii.hasNext(); )		    {			Object stmt = ii.next();			if (checkedOut.contains( stmt ))			    checkinStatement( stmt );		    }	    }	if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)	    {// 		System.err.print("checkinAll(): ");// 		printStats();		if (logger.isLoggable(MLevel.FINEST))		    logger.log(MLevel.FINEST, "checkinAll(): " + statsString());	    }    }    /*     * we only selectively sync' parts of this method, because we wish to wait for     * Statements to be destroyed without holding up the StatementCache by keeping     * the lock.     *     * THIS IS BECAUSE ORACLE DEADLOCKS IF A STATEMENT IS CLOSING AT THE SAME TIME AS     * ITS PARENT CONNECTION IS CLOSING. (THIS IS AN ORACLE BUG, AND I _HATE_ DRAMATICALLY     * COMPLICATING MY CODE TO AVOID IT.) Oh, okay. That's a bit overdone. Lots of drivers     * don't like the old behavior.     *     * we have a double-lock acqusition here -- we acquire a counter's lock, than our own.     * the other thread that contends the counter's lock is the async task thread in the      * destroy statement method. ensure that statement-destroying task does not acquire      * this' lock prior to requiring the counter's lock to avoid deadlock.     *     */    public void closeAll(Connection pcon) throws SQLException    {// 	System.err.println( this + ": closeAll( " + pcon + " )" );// 	new Exception("closeAll()").printStackTrace();	if (! this.isClosed())	    {		Set cSet = null;		synchronized(this)		    { cSet = cxnStmtMgr.statementSet( pcon ); }				if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)		    {			if (logger.isLoggable(MLevel.FINEST))			    {				logger.log(MLevel.FINEST, "ENTER METHOD: closeAll( " + pcon + " )! -- num_connections: " + 					   cxnStmtMgr.getNumConnectionsWithCachedStatements());				logger.log(MLevel.FINEST, "Set of statements for connection: " + cSet + (cSet != null ? "; size: " + cSet.size() : ""));			    }		    }				ChangeNotifyingSynchronizedIntHolder counter = new ChangeNotifyingSynchronizedIntHolder(0, true);		synchronized ( counter )		    {			if (cSet != null)			    {				//the removeStatement(...) removes from cSet, so we can't be iterating over cSet directly				Set stmtSet = new HashSet( cSet );				int sz = stmtSet.size();				//System.err.println("SIZE FOR CONNECTION SET: " + stmtSet.size());				for (Iterator ii = stmtSet.iterator(); ii.hasNext(); )				    {					Object stmt = ii.next();										synchronized ( this )					    { removeStatement( stmt, true, counter ); }				    }				try				    {					while (counter.getValue() < sz) 					    {						//System.err.println( "counter.getValue(): " + counter.getValue() + "; sz: " + sz );						counter.wait();					    }					//System.err.println( "FINAL: counter.getValue(): " + counter.getValue() + "; sz: " + sz );				    }				catch (InterruptedException e)				    {					if (logger.isLoggable(MLevel.WARNING))					    logger.warning("Unexpected interupt(). [A thread closing all Statements for a Connection in a Statement cache" +							   " will no longer wait for all Statements to close, but will move on and let them close() asynchronously." +							   " This is harmless in general, but may lead to a transient deadlock (which the thread pool will notice " +							   " and resolve) under some database drivers.]");				    }			    }		    }		if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)		    {			if (logger.isLoggable(MLevel.FINEST))			    logger.finest("closeAll(): " + statsString());		    }	    }// 	else// 	    {// 		if (logger.isLoggable(MLevel.FINE))// 		    logger.log(MLevel.FINE, // 			       this + ":  call to closeAll() when statment cache is already closed! [not harmful! debug only!]", // 			       new Exception("DUPLICATE CLOSE DEBUG STACK TRACE."));// 	    }    }    public synchronized void close() 	throws SQLException    {	//System.err.println( this + ": close()" );	if (! isClosed())	    {		for (Iterator ii = stmtToKey.keySet().iterator(); ii.hasNext(); )		    synchronousDestroyStatement( ii.next() );				cxnStmtMgr       = null;		stmtToKey        = null;		keyToKeyRec      = null;		checkedOut       = null;		stmt_count       = -1;	    }	else	    {		if (logger.isLoggable(MLevel.FINE))		    logger.log(MLevel.FINE, this + ": duplicate call to close() [not harmful! -- debug only!]", new Exception("DUPLICATE CLOSE DEBUG STACK TRACE."));	    }    }    public synchronized boolean isClosed()    { return cxnStmtMgr == null; }    /* non-public methods that needn't be called with this' lock below */         private void destroyStatement( final Object pstmt )    { destroyStatement( pstmt, null ); }    /*     * ORACLE BUG WORKAROUND ( counter crap ) -- see closeAll(Connection c)     *     * NOTE: plan on reverting this method to fully sync'ed, no counter or waiting on counter      * involved if Oracle ever fixes their damned bug. See c3p0-0.9.0-pre5 for last simpler     * version.     */    private void destroyStatement( final Object pstmt, final ChangeNotifyingSynchronizedIntHolder counter )    { 	Runnable r;	if (counter == null)	    {		r = new Runnable()		    {			public void run()			{ StatementUtils.attemptClose( (PreparedStatement) pstmt ); }		    };	    }	else	    {		r = new Runnable()		    {			// this method MUSTN'T try to obtain this' lock prior to obtaining the 			// counter's lock, or a potential deadlock may result. 			//			// See closeAll(Connection c) comments.			public void run()			{ 			    synchronized( counter )				{				    StatementUtils.attemptClose( (PreparedStatement) pstmt ); 				    counter.increment();				}			}		    };	    }	blockingTaskAsyncRunner.postRunnable(r);    }    private void synchronousDestroyStatement( final Object pstmt )    { StatementUtils.attemptClose( (PreparedStatement) pstmt ); }    /* end non-public methods that needn't be called with this' lock */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -