001: /*
002: * Distributed as part of c3p0 v.0.9.1.2
003: *
004: * Copyright (C) 2005 Machinery For Change, Inc.
005: *
006: * Author: Steve Waldman <swaldman@mchange.com>
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Lesser General Public License version 2.1, as
010: * published by the Free Software Foundation.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this software; see the file LICENSE. If not, write to the
019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: */
022:
023: package com.mchange.v2.c3p0.stmt;
024:
025: import java.util.*;
026: import java.sql.*;
027: import java.lang.reflect.*;
028: import com.mchange.v2.async.AsynchronousRunner;
029: import com.mchange.v2.sql.SqlUtils;
030: import com.mchange.v2.util.ResourceClosedException;
031: import com.mchange.v2.log.*;
032: import com.mchange.v1.db.sql.StatementUtils;
033:
034: import java.io.StringWriter;
035: import java.io.PrintWriter;
036: import java.io.IOException;
037: import com.mchange.v2.io.IndentedWriter;
038:
039: public abstract class GooGooStatementCache {
040: private final static MLogger logger = MLog
041: .getLogger(GooGooStatementCache.class);
042:
043: private final static int DESTROY_NEVER = 0;
044: private final static int DESTROY_IF_CHECKED_IN = 1 << 0;
045: private final static int DESTROY_IF_CHECKED_OUT = 1 << 1;
046: private final static int DESTROY_ALWAYS = (DESTROY_IF_CHECKED_IN | DESTROY_IF_CHECKED_OUT);
047:
048: /* MT: protected by this's lock */
049:
050: // contains all statements in the cache,
051: // organized by connection
052: ConnectionStatementManager cxnStmtMgr;
053:
054: // contains all statements in the cache,
055: // bound to the keys that produced them
056: HashMap stmtToKey = new HashMap();
057:
058: // maps all known keys to their set of statements
059: // and to a queue of statements, if any, available
060: // for checkout
061: HashMap keyToKeyRec = new HashMap();
062:
063: // contains all checked out statements -- in the cache,
064: // but not currently available for checkout, nor for
065: // culling in case of overflow
066: HashSet checkedOut = new HashSet();
067:
068: /* MT: end protected by this' lock */
069:
070: /* MT: protected by its own lock */
071:
072: AsynchronousRunner blockingTaskAsyncRunner;
073:
074: // This set is used to ensure that multiple threads
075: // do not try to remove the same statement from the
076: // cache, if for example a Statement is both deathmarched
077: // away and its parent Connection is closed.
078: //
079: // ALL ACCESS SHOULD BE EXPLICITLY SYNCHRONIZED
080: // ON removalPending's lock!
081: HashSet removalPending = new HashSet();
082:
083: /* MT: end protected by its own lock */
084:
085: public GooGooStatementCache(
086: AsynchronousRunner blockingTaskAsyncRunner) {
087: this .blockingTaskAsyncRunner = blockingTaskAsyncRunner;
088: this .cxnStmtMgr = createConnectionStatementManager();
089: }
090:
091: public synchronized int getNumStatements() {
092: return this .isClosed() ? -1 : countCachedStatements();
093: }
094:
095: public synchronized int getNumStatementsCheckedOut() {
096: return this .isClosed() ? -1 : checkedOut.size();
097: }
098:
099: public synchronized int getNumConnectionsWithCachedStatements() {
100: return isClosed() ? -1 : cxnStmtMgr
101: .getNumConnectionsWithCachedStatements();
102: }
103:
104: public synchronized String dumpStatementCacheStatus() {
105: if (isClosed())
106: return this + "status: Closed.";
107: else {
108: StringWriter sw = new StringWriter(2048);
109: IndentedWriter iw = new IndentedWriter(sw);
110: try {
111: iw.print(this );
112: iw.println(" status:");
113: iw.upIndent();
114: iw.println("core stats:");
115: iw.upIndent();
116: iw.print("num cached statements: ");
117: iw.println(this .countCachedStatements());
118: iw.print("num cached statements in use: ");
119: iw.println(checkedOut.size());
120: iw.print("num connections with cached statements: ");
121: iw.println(cxnStmtMgr
122: .getNumConnectionsWithCachedStatements());
123: iw.downIndent();
124: iw.println("cached statement dump:");
125: iw.upIndent();
126: for (Iterator ii = cxnStmtMgr.connectionSet()
127: .iterator(); ii.hasNext();) {
128: Connection pcon = (Connection) ii.next();
129: iw.print(pcon);
130: iw.println(':');
131: iw.upIndent();
132: for (Iterator jj = cxnStmtMgr.statementSet(pcon)
133: .iterator(); jj.hasNext();)
134: iw.println(jj.next());
135: iw.downIndent();
136: }
137:
138: iw.downIndent();
139: iw.downIndent();
140: return sw.toString();
141: } catch (IOException e) {
142: if (logger.isLoggable(MLevel.SEVERE))
143: logger
144: .log(
145: MLevel.SEVERE,
146: "Huh? We've seen an IOException writing to s StringWriter?!",
147: e);
148: return e.toString();
149: }
150: }
151: }
152:
153: abstract ConnectionStatementManager createConnectionStatementManager();
154:
155: public synchronized Object checkoutStatement(
156: Connection physicalConnection, Method stmtProducingMethod,
157: Object[] args) throws SQLException, ResourceClosedException {
158: try {
159: Object out = null;
160:
161: StatementCacheKey key = StatementCacheKey.find(
162: physicalConnection, stmtProducingMethod, args);
163: LinkedList l = checkoutQueue(key);
164: if (l == null || l.isEmpty()) //we need a new statement
165: {
166: // we might wait() here...
167: // don't presume atomicity before and after!
168: out = acquireStatement(physicalConnection,
169: stmtProducingMethod, args);
170:
171: if (prepareAssimilateNewStatement(physicalConnection))
172: assimilateNewCheckedOutStatement(key,
173: physicalConnection, out);
174: // else case: we can't assimilate the statement...
175: // so, we just return our newly created statement, without caching it.
176: // on check-in, it will simply be destroyed... this is an "overload statement"
177: } else //okay, we can use an old one
178: {
179: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX)
180: logger.finest(this .getClass().getName()
181: + " ----> CACHE HIT");
182: //System.err.println("-------------> CACHE HIT!");
183:
184: out = l.get(0);
185: l.remove(0);
186: if (!checkedOut.add(out))
187: throw new RuntimeException(
188: "Internal inconsistency: "
189: + "Checking out a statement marked "
190: + "as already checked out!");
191: removeStatementFromDeathmarches(out, physicalConnection);
192: }
193:
194: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
195: //System.err.print("checkoutStatement(): ");
196: //printStats();
197: if (logger.isLoggable(MLevel.FINEST))
198: logger
199: .finest("checkoutStatement: "
200: + statsString());
201: }
202:
203: return out;
204: } catch (NullPointerException npe) {
205: if (checkedOut == null) //we're closed
206: {
207: if (logger.isLoggable(MLevel.FINE))
208: logger
209: .log(
210: MLevel.FINE,
211: "A client attempted to work with a closed Statement cache, "
212: + ""
213: + "provoking a NullPointerException. c3p0 recovers, but this should be rare.",
214: npe);
215: throw new ResourceClosedException(npe);
216: } else
217: throw npe;
218: }
219: }
220:
221: public synchronized void checkinStatement(Object pstmt)
222: throws SQLException {
223: if (checkedOut == null) //we're closed
224: {
225: synchronousDestroyStatement(pstmt);
226:
227: return;
228: } else if (!checkedOut.remove(pstmt)) {
229: if (!ourResource(pstmt)) //this is not our resource, or it is an overload statement
230: destroyStatement(pstmt); // so we just destroy
231: //in the else case, it's already checked-in, so we ignore
232:
233: return;
234: }
235:
236: try {
237: refreshStatement((PreparedStatement) pstmt);
238: } catch (Exception e) {
239: if (Debug.DEBUG) {
240: // System.err.println("Problem with checked-in Statement, discarding.");
241: // e.printStackTrace();
242: if (logger.isLoggable(MLevel.INFO))
243: logger
244: .log(
245: MLevel.INFO,
246: "Problem with checked-in Statement, discarding.",
247: e);
248: }
249:
250: // swaldman -- 2004-01-31: readd problem statement to checkedOut for consistency
251: // the statement is not yet checked-in, but it is removed from checked out, and this
252: // violates the consistency assumption of removeStatement(). Thanks to Zach Scott for
253: // calling attention to this issue.
254: checkedOut.add(pstmt);
255:
256: removeStatement(pstmt, DESTROY_ALWAYS); //force destruction of the statement even though it appears checked-out
257: return;
258: }
259:
260: StatementCacheKey key = (StatementCacheKey) stmtToKey
261: .get(pstmt);
262: if (Debug.DEBUG && key == null)
263: throw new RuntimeException(
264: "Internal inconsistency: "
265: + "A checked-out statement has no key associated with it!");
266:
267: LinkedList l = checkoutQueue(key);
268: l.add(pstmt);
269: addStatementToDeathmarches(pstmt, key.physicalConnection);
270:
271: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
272: // System.err.print("checkinStatement(): ");
273: // printStats();
274: if (logger.isLoggable(MLevel.FINEST))
275: logger.finest("checkinStatement(): " + statsString());
276: }
277: }
278:
279: public synchronized void checkinAll(Connection pcon)
280: throws SQLException {
281: //new Exception("checkinAll()").printStackTrace();
282:
283: Set stmtSet = cxnStmtMgr.statementSet(pcon);
284: if (stmtSet != null) {
285: for (Iterator ii = stmtSet.iterator(); ii.hasNext();) {
286: Object stmt = ii.next();
287: if (checkedOut.contains(stmt))
288: checkinStatement(stmt);
289: }
290: }
291:
292: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
293: // System.err.print("checkinAll(): ");
294: // printStats();
295: if (logger.isLoggable(MLevel.FINEST))
296: logger.log(MLevel.FINEST, "checkinAll(): "
297: + statsString());
298: }
299: }
300:
301: /*
302: * we only selectively sync' parts of this method, because we wish to wait for
303: * Statements we wish to destroy the Statements synchronously, but without
304: * holding the pool's lock.
305: */
306: public void closeAll(Connection pcon) throws SQLException {
307: // System.err.println( this + ": closeAll( " + pcon + " )" );
308: // new Exception("closeAll()").printStackTrace();
309:
310: // assert !Thread.holdsLock( this );
311:
312: if (!this .isClosed()) {
313: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
314: if (logger.isLoggable(MLevel.FINEST)) {
315: logger
316: .log(
317: MLevel.FINEST,
318: "ENTER METHOD: closeAll( "
319: + pcon
320: + " )! -- num_connections: "
321: + cxnStmtMgr
322: .getNumConnectionsWithCachedStatements());
323: //logger.log(MLevel.FINEST, "Set of statements for connection: " + cSet + (cSet != null ? "; size: " + cSet.size() : ""));
324: }
325: }
326:
327: Set stmtSet = null;
328: synchronized (this ) {
329: Set cSet = cxnStmtMgr.statementSet(pcon);
330:
331: if (cSet != null) {
332: //the removeStatement(...) removes from cSet, so we can't be iterating over cSet directly
333: stmtSet = new HashSet(cSet);
334: //System.err.println("SIZE FOR CONNECTION SET: " + stmtSet.size());
335:
336: for (Iterator ii = stmtSet.iterator(); ii.hasNext();) {
337: Object stmt = ii.next();
338: // we remove without destroying, leaving the destruction
339: // until when we lose the pool's lock
340: removeStatement(stmt, DESTROY_NEVER);
341: }
342: }
343: }
344:
345: if (stmtSet != null) {
346: for (Iterator ii = stmtSet.iterator(); ii.hasNext();) {
347: Object stmt = ii.next();
348: synchronousDestroyStatement(stmt);
349: }
350: }
351:
352: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
353: if (logger.isLoggable(MLevel.FINEST))
354: logger.finest("closeAll(): " + statsString());
355: }
356: }
357: // else
358: // {
359: // if (logger.isLoggable(MLevel.FINER))
360: // logger.log(MLevel.FINER,
361: // this + ": call to closeAll() when statment cache is already closed! [not harmful! debug only!]",
362: // new Exception("DUPLICATE CLOSE DEBUG STACK TRACE."));
363: // }
364: }
365:
366: public synchronized void close() throws SQLException {
367: //System.err.println( this + ": close()" );
368:
369: if (!isClosed()) {
370: for (Iterator ii = stmtToKey.keySet().iterator(); ii
371: .hasNext();)
372: synchronousDestroyStatement(ii.next());
373:
374: cxnStmtMgr = null;
375: stmtToKey = null;
376: keyToKeyRec = null;
377: checkedOut = null;
378: } else {
379: if (logger.isLoggable(MLevel.FINE))
380: logger
381: .log(
382: MLevel.FINE,
383: this
384: + ": duplicate call to close() [not harmful! -- debug only!]",
385: new Exception(
386: "DUPLICATE CLOSE DEBUG STACK TRACE."));
387: }
388:
389: }
390:
391: public synchronized boolean isClosed() {
392: return cxnStmtMgr == null;
393: }
394:
395: /* non-public methods that needn't be called with this' lock below */
396:
397: private void destroyStatement(final Object pstmt) {
398: class StatementCloseTask implements Runnable {
399: public void run() {
400: StatementUtils.attemptClose((PreparedStatement) pstmt);
401: }
402: }
403:
404: Runnable r = new StatementCloseTask();
405:
406: blockingTaskAsyncRunner.postRunnable(r);
407: }
408:
409: private void synchronousDestroyStatement(final Object pstmt) {
410: StatementUtils.attemptClose((PreparedStatement) pstmt);
411: }
412:
413: /* end non-public methods that needn't be called with this' lock */
414:
415: /* non-public methods that MUST be called with this' lock */
416:
417: abstract boolean prepareAssimilateNewStatement(Connection pcon);
418:
419: abstract void addStatementToDeathmarches(Object pstmt,
420: Connection physicalConnection);
421:
422: abstract void removeStatementFromDeathmarches(Object pstmt,
423: Connection physicalConnection);
424:
425: final int countCachedStatements() {
426: return stmtToKey.size();
427: }
428:
429: private void assimilateNewCheckedOutStatement(
430: StatementCacheKey key, Connection pConn, Object ps) {
431: stmtToKey.put(ps, key);
432: HashSet ks = keySet(key);
433: if (ks == null)
434: keyToKeyRec.put(key, new KeyRec());
435: else {
436: //System.err.println("-------> Multiply prepared statement! " + key.stmtText );
437: if (logger.isLoggable(MLevel.INFO))
438: logger.info("Multiply prepared statement! "
439: + key.stmtText);
440: if (Debug.DEBUG && logger.isLoggable(MLevel.FINE))
441: logger
442: .fine("(The same statement has already been prepared by this Connection, "
443: + "and that other instance has not yet been closed, so the statement pool "
444: + "has to prepare a second PreparedStatement object rather than reusing "
445: + "the previously-cached Statement. The new Statement will be cached, in case "
446: + "you frequently need multiple copies of this Statement.)");
447: }
448: keySet(key).add(ps);
449: cxnStmtMgr.addStatementForConnection(ps, pConn);
450:
451: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
452: // System.err.println("cxnStmtMgr.statementSet( " + pConn + " ).size(): " +
453: // cxnStmtMgr.statementSet( pConn ).size());
454: if (logger.isLoggable(MLevel.FINEST))
455: logger.finest("cxnStmtMgr.statementSet( " + pConn
456: + " ).size(): "
457: + cxnStmtMgr.statementSet(pConn).size());
458: }
459:
460: checkedOut.add(ps);
461: }
462:
463: private void removeStatement(Object ps, int destruction_policy) {
464: synchronized (removalPending) {
465: if (removalPending.contains(ps))
466: return;
467: else
468: removalPending.add(ps);
469: }
470:
471: StatementCacheKey sck = (StatementCacheKey) stmtToKey
472: .remove(ps);
473: removeFromKeySet(sck, ps);
474: Connection pConn = sck.physicalConnection;
475:
476: boolean checked_in = !checkedOut.contains(ps);
477:
478: if (checked_in) {
479: removeStatementFromDeathmarches(ps, pConn);
480: removeFromCheckoutQueue(sck, ps);
481: if ((destruction_policy & DESTROY_IF_CHECKED_IN) != 0)
482: destroyStatement(ps);
483: } else {
484: checkedOut.remove(ps);
485: if ((destruction_policy & DESTROY_IF_CHECKED_OUT) != 0)
486: destroyStatement(ps);
487: }
488:
489: boolean check = cxnStmtMgr.removeStatementForConnection(ps,
490: pConn);
491: if (Debug.DEBUG && check == false) {
492: //new Exception("WARNING: removed a statement that apparently wasn't in a statement set!!!").printStackTrace();
493: if (logger.isLoggable(MLevel.WARNING))
494: logger
495: .log(
496: MLevel.WARNING,
497: this
498: + " removed a statement that apparently wasn't in a statement set!!!",
499: new Exception("LOG STACK TRACE"));
500: }
501:
502: synchronized (removalPending) {
503: removalPending.remove(ps);
504: }
505: }
506:
507: private Object acquireStatement(final Connection pConn,
508: final Method stmtProducingMethod, final Object[] args)
509: throws SQLException {
510: try {
511: final Object[] outHolder = new Object[1];
512: final SQLException[] exceptionHolder = new SQLException[1];
513:
514: class StmtAcquireTask implements Runnable {
515: public void run() {
516: try {
517: outHolder[0] = stmtProducingMethod.invoke(
518: pConn, args);
519: } catch (InvocationTargetException e) {
520: Throwable targetException = e
521: .getTargetException();
522: if (targetException instanceof SQLException)
523: exceptionHolder[0] = (SQLException) targetException;
524: else
525: exceptionHolder[0] = SqlUtils
526: .toSQLException(targetException);
527: } catch (Exception e) {
528: exceptionHolder[0] = SqlUtils.toSQLException(e);
529: } finally {
530: synchronized (GooGooStatementCache.this ) {
531: GooGooStatementCache.this .notifyAll();
532: }
533: }
534: }
535: }
536:
537: Runnable r = new StmtAcquireTask();
538: blockingTaskAsyncRunner.postRunnable(r);
539:
540: while (outHolder[0] == null && exceptionHolder[0] == null)
541: this .wait(); //give up our lock while the Statement gets prepared
542: if (exceptionHolder[0] != null)
543: throw exceptionHolder[0];
544: else {
545: Object out = outHolder[0];
546: return out;
547: }
548: } catch (InterruptedException e) {
549: throw SqlUtils.toSQLException(e);
550: }
551: }
552:
553: private KeyRec keyRec(StatementCacheKey key) {
554: return ((KeyRec) keyToKeyRec.get(key));
555: }
556:
557: private HashSet keySet(StatementCacheKey key) {
558: KeyRec rec = keyRec(key);
559: return (rec == null ? null : rec.allStmts);
560: }
561:
562: private boolean removeFromKeySet(StatementCacheKey key, Object pstmt) {
563: boolean out;
564: HashSet stmtSet = keySet(key);
565: out = stmtSet.remove(pstmt);
566: if (stmtSet.isEmpty() && checkoutQueue(key).isEmpty())
567: keyToKeyRec.remove(key);
568: return out;
569: }
570:
571: private LinkedList checkoutQueue(StatementCacheKey key) {
572: KeyRec rec = keyRec(key);
573: return (rec == null ? null : rec.checkoutQueue);
574: }
575:
576: private boolean removeFromCheckoutQueue(StatementCacheKey key,
577: Object pstmt) {
578: boolean out;
579: LinkedList q = checkoutQueue(key);
580: out = q.remove(pstmt);
581: if (q.isEmpty() && keySet(key).isEmpty())
582: keyToKeyRec.remove(key);
583: return out;
584: }
585:
586: private boolean ourResource(Object ps) {
587: return stmtToKey.keySet().contains(ps);
588: }
589:
590: private void refreshStatement(PreparedStatement ps)
591: throws Exception {
592: ps.clearParameters();
593: }
594:
595: private void printStats() {
596: //new Exception("printStats()").printStackTrace();
597: int total_size = this .countCachedStatements();
598: int checked_out_size = checkedOut.size();
599: int num_connections = cxnStmtMgr
600: .getNumConnectionsWithCachedStatements();
601: int num_keys = keyToKeyRec.size();
602: System.err.print(this .getClass().getName() + " stats -- ");
603: System.err.print("total size: " + total_size);
604: System.err.print("; checked out: " + checked_out_size);
605: System.err.print("; num connections: " + num_connections);
606: System.err.println("; num keys: " + num_keys);
607: }
608:
609: private String statsString() {
610: int total_size = this .countCachedStatements();
611: int checked_out_size = checkedOut.size();
612: int num_connections = cxnStmtMgr
613: .getNumConnectionsWithCachedStatements();
614: int num_keys = keyToKeyRec.size();
615:
616: StringBuffer sb = new StringBuffer(255);
617: sb.append(this .getClass().getName());
618: sb.append(" stats -- ");
619: sb.append("total size: ");
620: sb.append(total_size);
621: sb.append("; checked out: ");
622: sb.append(checked_out_size);
623: sb.append("; num connections: ");
624: sb.append(num_connections);
625: sb.append("; num keys: ");
626: sb.append(num_keys);
627: return sb.toString();
628: }
629:
630: private static class KeyRec {
631: HashSet allStmts = new HashSet();
632: LinkedList checkoutQueue = new LinkedList();
633: }
634:
635: protected class Deathmarch {
636: TreeMap longsToStmts = new TreeMap();
637: HashMap stmtsToLongs = new HashMap();
638:
639: long last_long = -1;
640:
641: public void deathmarchStatement(Object ps) {
642: //System.err.println("deathmarchStatement( " + ps + " )");
643: if (Debug.DEBUG) {
644: Long old = (Long) stmtsToLongs.get(ps);
645: if (old != null)
646: throw new RuntimeException(
647: "Internal inconsistency: "
648: + "A statement is being double-deathmatched. no checked-out statements should be in a deathmarch already; "
649: + "no already checked-in statement should be deathmarched!");
650: }
651:
652: Long youth = getNextLong();
653: stmtsToLongs.put(ps, youth);
654: longsToStmts.put(youth, ps);
655: }
656:
657: public void undeathmarchStatement(Object ps) {
658: Long old = (Long) stmtsToLongs.remove(ps);
659: if (Debug.DEBUG && old == null)
660: throw new RuntimeException(
661: "Internal inconsistency: "
662: + "A (not new) checking-out statement is not in deathmarch.");
663: Object check = longsToStmts.remove(old);
664: if (Debug.DEBUG && old == null)
665: throw new RuntimeException(
666: "Internal inconsistency: "
667: + "A (not new) checking-out statement is not in deathmarch.");
668: }
669:
670: public boolean cullNext() {
671: if (longsToStmts.isEmpty())
672: return false;
673: else {
674: Long l = (Long) longsToStmts.firstKey();
675: Object ps = longsToStmts.get(l);
676: if (Debug.DEBUG && Debug.TRACE == Debug.TRACE_MAX) {
677: // System.err.println("CULLING: " +
678: // ((StatementCacheKey) stmtToKey.get(ps)).stmtText);
679: if (logger.isLoggable(MLevel.FINEST))
680: logger.finest("CULLING: "
681: + ((StatementCacheKey) stmtToKey
682: .get(ps)).stmtText);
683: }
684: // we do not undeathmarch the statement ourselves, because removeStatement( ... )
685: // should remove from all deathmarches...
686: removeStatement(ps, DESTROY_ALWAYS);
687: if (Debug.DEBUG && this .contains(ps))
688: throw new RuntimeException(
689: "Inconsistency!!! Statement culled from deathmarch failed to be removed by removeStatement( ... )!");
690: return true;
691: }
692: }
693:
694: public boolean contains(Object ps) {
695: return stmtsToLongs.keySet().contains(ps);
696: }
697:
698: public int size() {
699: return longsToStmts.size();
700: }
701:
702: private Long getNextLong() {
703: return new Long(++last_long);
704: }
705: }
706:
707: protected static abstract class ConnectionStatementManager {
708: Map cxnToStmtSets = new HashMap();
709:
710: public int getNumConnectionsWithCachedStatements() {
711: return cxnToStmtSets.size();
712: }
713:
714: public Set connectionSet() {
715: return cxnToStmtSets.keySet();
716: }
717:
718: public Set statementSet(Connection pcon) {
719: return (Set) cxnToStmtSets.get(pcon);
720: }
721:
722: public int getNumStatementsForConnection(Connection pcon) {
723: Set stmtSet = statementSet(pcon);
724: return (stmtSet == null ? 0 : stmtSet.size());
725: }
726:
727: public void addStatementForConnection(Object ps, Connection pcon) {
728: Set stmtSet = statementSet(pcon);
729: if (stmtSet == null) {
730: stmtSet = new HashSet();
731: cxnToStmtSets.put(pcon, stmtSet);
732: }
733: stmtSet.add(ps);
734: }
735:
736: public boolean removeStatementForConnection(Object ps,
737: Connection pcon) {
738: boolean out;
739:
740: Set stmtSet = statementSet(pcon);
741: if (stmtSet != null) {
742: out = stmtSet.remove(ps);
743: if (stmtSet.isEmpty())
744: cxnToStmtSets.remove(pcon);
745: } else
746: out = false;
747:
748: return out;
749: }
750: }
751:
752: // i want this as optimized as possible, so i'm adopting the philosophy that all
753: // classes are abstract or final, to help enable compiler inlining...
754: protected static final class SimpleConnectionStatementManager
755: extends ConnectionStatementManager {
756: }
757:
758: protected final class DeathmarchConnectionStatementManager extends
759: ConnectionStatementManager {
760: Map cxnsToDms = new HashMap();
761:
762: public void addStatementForConnection(Object ps, Connection pcon) {
763: super .addStatementForConnection(ps, pcon);
764: Deathmarch dm = (Deathmarch) cxnsToDms.get(pcon);
765: if (dm == null) {
766: dm = new Deathmarch();
767: cxnsToDms.put(pcon, dm);
768: }
769: }
770:
771: public boolean removeStatementForConnection(Object ps,
772: Connection pcon) {
773: boolean out = super .removeStatementForConnection(ps, pcon);
774: if (out) {
775: if (statementSet(pcon) == null)
776: cxnsToDms.remove(pcon);
777: }
778: return out;
779: }
780:
781: public Deathmarch getDeathmarch(Connection pcon) {
782: return (Deathmarch) cxnsToDms.get(pcon);
783: }
784: }
785: }
|