001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.context.ContextService
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.context;
023:
024: import org.apache.derby.iapi.services.monitor.Monitor;
025: import org.apache.derby.iapi.services.sanity.SanityManager;
026: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
027:
028: import java.util.Hashtable;
029: import java.util.Enumeration;
030:
031: import java.util.HashSet;
032: import java.util.Iterator;
033:
034: /**
035: A set of static methods to supply easier access to contexts.
036: */
037: public final class ContextService //OLD extends Hashtable
038: {
039:
040: private static ContextService factory;
041: private HeaderPrintWriter errorStream;
042:
043: /**
044: Maintains a list of all the contexts that this thread has created
045: and/or used. The object stored in the thread local varys according
046: how this thread has been used and will be one of:
047:
048: <UL>
049: <LI> null - the thread no affiliation with a context manager.
050:
051: <LI> ContextManager - the current thread has used or is using
052: this context manager. If ContextManager.activeThread equals
053: the current thread then the thread is currently active with
054: the ContextManager. In this case ContextManager.activeCount
055: will be greater than zero and represent the level of nested
056: setCurrentContextmanager calls.
057: If ContextManager.activeThread is null then no other thread
058: is using the Contextmanager, if ContextManager.activeThread
059: is not-null and not equal to the current thread then some
060: other thread is using the context. It is assumed that
061: only a single thread can be using a ContextManager at any time
062: and this is enforced by synchronization outside the ContextManager.
063: E.g for JDBC connections, synchronization at the JDBC level.
064:
065: <LI> java.util.Stack containing ContextManagers - the current
066: thread is actively using multiple different ContextManagers,
067: with nesting. All ContextManagers in the stack will have
068: activeThread set to the current thread, and their activeCount
069: set to -1. This is beacause nesting is soley represented by
070: the stack, with the current context manager on top of the stack.
071: This supports multiple levels of nesting across two stacks, e.g.
072: C1->C2->C2->C1->C2.
073: </UL>
074:
075: This thread local is used to find the current context manager. Basically it provides
076: fast access to a list of candidate contexts. If one of the contexts has its activeThread
077: equal to the current thread then it is the current context manager.
078:
079: If the thread has pushed multiple contexts (e.g. open a new non-nested Cloudscape connection
080: from a server side method) then threadContextList will contain a Stack. The value for each cm
081: will be a push order, with higher numbers being more recently pushed.
082:
083: To support the case where a single context manager is pushed twice (nested connection),
084: the context manager keeps track of the number of times it has been pushed (set). Note that
085: our synchronization requires that a single context can only be accessed by a single thread at a time.
086: In the JDBC layer this is enforced by the synchronization on the connection object.
087:
088: <P>
089: There are two cases we are trying to optimise.
090: <UL>
091: <LI> Typical JDBC client program where there a Connection is always executed using a single thread.
092: In this case this variable will contain the Connection's context manager
093: <LI> Typical application server pooled connection where a single thread may use a connection from a pool
094: for the lifetime of the request. In this case this variable will contain a WeakReference.
095: </UL>
096: <BR>
097: Single thread for Connection exection.
098: <pre>
099: threadContextList.get() == cm
100: // while in JDBC engine code
101: cm.activeThread == Thread.currentThread();
102: cm.activeCount = 1;
103: </pre>
104:
105: <BR>
106: J2EE single thread for lifetime of execution.
107: <pre>
108: // thread executing request
109: threadContextList.get() == cm
110: // while in JDBC engine code
111: cm.activeThread == Thread.currentThread();
112: cm.activeCount = 1;
113:
114: // other threads that have recently executed
115: // the same connection can have
116: threadContextList.get() == cm
117: cm.activeThread != Thread.currentThread();
118: </pre>
119:
120: <BR>
121: Nested routine calls within single connection
122: <pre>
123: threadContextList.get() == cm
124: // Within server-side JDBC code in a
125: // function called from another function/procedure
126: // called from an applications's statement
127: // (three levels of nesting)
128: cm.activeThread == Thread.currentThread();
129: cm.activeCount = 3;
130: </pre>
131:
132: <BR>
133: Nested routine calls with the inner routine
134: using a different connection to access a Derby database.
135: Note nesting of orignal Contextmanager cm is changed
136: from an activeCount of 2 to nesting within the stack
137: once multiple ContextManagers are involved.
138: <pre>
139: threadContextList.get() == stack {cm2,cm,cm}
140: cm.activeThread == Thread.currentThread();
141: cm.activeCount = -1; // nesting in stack
142: cm2.activeThread == Thread.currentThread();
143: cm2.activeCount = -1; // nesting in stack
144: </pre>
145:
146: <BR>
147: Nested multiple ContextManagers, the code supports
148: this, though it may not be possible currently
149: to have a stack like this from SQL/JDBC.
150: <pre>
151: threadContextList.get() == stack {cm3,cm2,cm,cm2,cm,cm}
152: cm.activeThread == Thread.currentThread();
153: cm.activeCount = -1; // nesting in stack
154: cm2.activeThread == Thread.currentThread();
155: cm2.activeCount = -1; // nesting in stack
156: cm3.activeThread == Thread.currentThread();
157: cm3.activeCount = -1; // nesting in stack
158: </pre>
159: */
160: private ThreadLocal threadContextList = new ThreadLocal();
161:
162: /**
163: * Collection of all ContextManagers that are open
164: * in the complete Derby system. A ContextManager is
165: * added when it is created with newContextManager and
166: * removed when the session is closed.
167: *
168: * @see #newContextManager()
169: * @see SystemContext#cleanupOnError(Throwable)
170: */
171: private HashSet allContexts;
172:
173: /**
174: * Create a new ContextService for a Derby system.
175: * Only a single system is active at any time.
176: *
177: */
178: public ContextService() {
179:
180: // find the error stream
181: errorStream = Monitor.getStream();
182:
183: ContextService.factory = this ;
184:
185: allContexts = new HashSet();
186:
187: }
188:
189: /**
190: So it can be given to us and taken away...
191: */
192: public static void stop() {
193: // For some unknown reason, the ContextManager and
194: // ContextService objects will not be garbage collected
195: // without the next two lines.
196: ContextService fact = ContextService.factory;
197: if (fact != null) {
198: synchronized (fact) {
199: fact.allContexts = null;
200: fact.threadContextList = null;
201: ContextService.factory = null;
202: }
203: }
204: }
205:
206: public static ContextService getFactory() {
207: ContextService csf = factory;
208:
209: if (csf == null)
210: throw new ShutdownException();
211: return csf;
212: }
213:
214: /**
215: Find the context with the given name in the context service factory
216: loaded for the system.
217:
218: @return The requested context, null if it doesn't exist.
219: */
220: public static Context getContext(String contextId) {
221:
222: ContextManager cm = getFactory().getCurrentContextManager();
223:
224: if (cm == null)
225: return null;
226:
227: return cm.getContext(contextId);
228: }
229:
230: /**
231: Find the context with the given name in the context service factory
232: loaded for the system.
233:
234: This version will not do any debug checking, but return null
235: quietly if it runs into any problems.
236:
237: @return The requested context, null if it doesn't exist.
238: */
239: public static Context getContextOrNull(String contextId) {
240: ContextService csf = factory;
241:
242: if (csf == null)
243: return null;
244:
245: ContextManager cm = csf.getCurrentContextManager();
246:
247: if (cm == null)
248: return null;
249:
250: return cm.getContext(contextId);
251: }
252:
253: /**
254: * Get current Context Manager linked to the current Thread.
255: * See setCurrentContextManager for details.
256: * Note that this call can be expensive and is only
257: * intended to be used in "stateless" situations.
258: * Ideally code has a reference to the correct
259: * ContextManager from another Object, such as a pushed Context.
260: *
261: * @return ContextManager current Context Manager
262: */
263: public ContextManager getCurrentContextManager() {
264:
265: ThreadLocal tcl = threadContextList;
266: if (tcl == null) {
267: // The context service is already stopped.
268: return null;
269: }
270:
271: Object list = tcl.get();
272:
273: if (list instanceof ContextManager) {
274:
275: Thread me = Thread.currentThread();
276:
277: ContextManager cm = (ContextManager) list;
278: if (cm.activeThread == me)
279: return cm;
280: return null;
281: }
282:
283: if (list == null)
284: return null;
285:
286: java.util.Stack stack = (java.util.Stack) list;
287: return (ContextManager) (stack.peek());
288:
289: }
290:
291: /**
292: * Break the link between the current Thread and the passed
293: * in ContextManager. Called in a pair with setCurrentContextManager,
294: * see that method for details.
295: */
296: public void resetCurrentContextManager(ContextManager cm) {
297: ThreadLocal tcl = threadContextList;
298:
299: if (tcl == null) {
300: // The context service is already stopped.
301: return;
302: }
303:
304: if (SanityManager.DEBUG) {
305:
306: if (Thread.currentThread() != cm.activeThread) {
307: SanityManager
308: .THROWASSERT("resetCurrentContextManager - mismatch threads - current"
309: + Thread.currentThread()
310: + " - cm's "
311: + cm.activeThread);
312: }
313:
314: if (getCurrentContextManager() != cm) {
315: SanityManager
316: .THROWASSERT("resetCurrentContextManager - mismatch contexts - "
317: + Thread.currentThread());
318: }
319:
320: if (cm.activeCount < -1) {
321: SanityManager
322: .THROWASSERT("resetCurrentContextManager - invalid count - current"
323: + Thread.currentThread()
324: + " - count "
325: + cm.activeCount);
326: }
327:
328: if (cm.activeCount == 0) {
329: SanityManager
330: .THROWASSERT("resetCurrentContextManager - invalid count - current"
331: + Thread.currentThread()
332: + " - count "
333: + cm.activeCount);
334: }
335:
336: if (cm.activeCount > 0) {
337: if (tcl.get() != cm)
338: SanityManager
339: .THROWASSERT("resetCurrentContextManager - invalid thread local "
340: + Thread.currentThread()
341: + " - object " + tcl.get());
342:
343: }
344: }
345:
346: if (cm.activeCount != -1) {
347: if (--cm.activeCount == 0) {
348: cm.activeThread = null;
349:
350: // If the ContextManager is empty
351: // then don't keep a reference to it
352: // when it is not in use. The ContextManager
353: // has been closed (most likely) and this
354: // is now unwanted. Keeping the reference
355: // would hold onto memory and increase the
356: // chance of holding onto a another reference
357: // will could cause issues for future operations.
358: if (cm.isEmpty())
359: tcl.set(null);
360:
361: }
362: return;
363: }
364:
365: java.util.Stack stack = (java.util.Stack) tcl.get();
366:
367: Object oldCM = stack.pop();
368:
369: ContextManager nextCM = (ContextManager) stack.peek();
370:
371: boolean seenMultipleCM = false;
372: boolean seenCM = false;
373: for (int i = 0; i < stack.size(); i++) {
374:
375: Object stackCM = stack.elementAt(i);
376: if (stackCM != nextCM)
377: seenMultipleCM = true;
378:
379: if (stackCM == cm)
380: seenCM = true;
381: }
382:
383: if (!seenCM) {
384: cm.activeThread = null;
385: cm.activeCount = 0;
386: }
387:
388: if (!seenMultipleCM) {
389: // all the context managers on the stack
390: // are the same so reduce to a simple count.
391: nextCM.activeCount = stack.size();
392: tcl.set(nextCM);
393: }
394: }
395:
396: /**
397: * The current thread (passed in a me) is setting associateCM
398: * to be its current context manager. Sets the thread local
399: * variable threadContextList to reflect associateCM being
400: * the current ContextManager.
401: *
402: * @return True if the nesting level is to be represented in
403: * the ContextManager.activeCount field. False if not.
404: *
405: * @see ContextManager#activeCount
406: * @see ContextManager#activeThread
407: */
408: private boolean addToThreadList(Thread me,
409: ContextManager associateCM) {
410:
411: ThreadLocal tcl = threadContextList;
412:
413: if (tcl == null) {
414: // The context service is already stopped.
415: return false;
416: }
417:
418: Object list = tcl.get();
419:
420: // Already set up to reflect associateCM ContextManager
421: if (associateCM == list)
422: return true;
423:
424: // Not currently using any ContextManager
425: if (list == null) {
426: tcl.set(associateCM);
427: return true;
428: }
429:
430: java.util.Stack stack;
431: if (list instanceof ContextManager) {
432:
433: // Could be two situations:
434: // 1. Single ContextManager not in use by this thread
435: // 2. Single ContextManager in use by this thread (nested call)
436:
437: ContextManager threadsCM = (ContextManager) list;
438: if (me == null)
439: me = Thread.currentThread();
440:
441: if (threadsCM.activeThread != me) {
442: // Not nested, just a CM left over
443: // from a previous execution.
444: tcl.set(associateCM);
445: return true;
446: }
447:
448: // Nested, need to create a Stack of ContextManagers,
449: // the top of the stack will be the active one.
450: stack = new java.util.Stack();
451: tcl.set(stack);
452:
453: // The stack represents the true nesting
454: // of ContextManagers, splitting out nesting
455: // of a single ContextManager into multiple
456: // entries in the stack.
457: for (int i = 0; i < threadsCM.activeCount; i++) {
458: stack.push(threadsCM);
459: }
460: threadsCM.activeCount = -1;
461: } else {
462: // existing stack, nesting represented
463: // by stack entries, not activeCount.
464: stack = (java.util.Stack) list;
465: }
466:
467: stack.push(associateCM);
468: associateCM.activeCount = -1;
469:
470: if (SanityManager.DEBUG) {
471:
472: if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
473:
474: if (stack.size() > 10)
475: System.out
476: .println("memoryLeakTrace:ContextService:threadLocal "
477: + stack.size());
478: }
479: }
480:
481: return false;
482: }
483:
484: /**
485: * Link the current thread to the passed in Contextmanager
486: * so that a subsequent call to getCurrentContextManager by
487: * the current Thread will return cm.
488: * ContextManagers are tied to a Thread while the thread
489: * is executing Derby code. For example on most JDBC method
490: * calls the ContextManager backing the Connection object
491: * is tied to the current Thread at the start of the method
492: * and reset at the end of the method. Once the Thread
493: * has completed its Derby work the method resetCurrentContextManager
494: * must be called with the same ContextManager to break the link.
495: * Note that a subsquent use of the ContextManager may be on
496: * a separate Thread, the Thread is only linked to the ContextManager
497: * between the setCurrentContextManager and resetCurrentContextManager calls.
498: * <BR>
499: * ContextService supports nesting of calls by a single Thread, either
500: * with the same ContextManager or a different ContextManager.
501: * <UL>
502: * <LI>The same ContextManager would be pushed during a nested JDBC call in
503: * a procedure or function.
504: * <LI>A different ContextManager would be pushed during a call on
505: * a different embedded JDBC Connection in a procedure or function.
506: * </UL>
507: */
508: public void setCurrentContextManager(ContextManager cm) {
509:
510: if (SanityManager.DEBUG) {
511: Thread me = Thread.currentThread();
512:
513: if (cm.activeThread != null && me != cm.activeThread) {
514: SanityManager
515: .THROWASSERT("setCurrentContextManager - mismatch threads - current "
516: + me + " - cm's " + cm.activeThread);
517: }
518:
519: }
520:
521: Thread me = null;
522:
523: if (cm.activeThread == null) {
524: cm.activeThread = (me = Thread.currentThread());
525: }
526: if (addToThreadList(me, cm))
527: cm.activeCount++;
528: }
529:
530: /**
531: * It's up to the caller to track this context manager and set it
532: * in the context manager list using setCurrentContextManager.
533: * We don't keep track of it due to this call being made.
534: */
535: public ContextManager newContextManager() {
536: ContextManager cm = new ContextManager(this , errorStream);
537:
538: // push a context that will shut down the system on
539: // a severe error.
540: new SystemContext(cm);
541:
542: synchronized (this ) {
543: allContexts.add(cm);
544:
545: if (SanityManager.DEBUG) {
546:
547: if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
548:
549: if (allContexts.size() > 50)
550: System.out
551: .println("memoryLeakTrace:ContextService:allContexts "
552: + allContexts.size());
553: }
554: }
555: }
556:
557: return cm;
558: }
559:
560: public void notifyAllActiveThreads(Context c) {
561: Thread me = Thread.currentThread();
562:
563: synchronized (this ) {
564: for (Iterator i = allContexts.iterator(); i.hasNext();) {
565:
566: ContextManager cm = (ContextManager) i.next();
567:
568: Thread active = cm.activeThread;
569:
570: if (active == me)
571: continue;
572:
573: if (active == null)
574: continue;
575:
576: if (cm.setInterrupted(c))
577: active.interrupt();
578: }
579: }
580: }
581:
582: /**
583: * Remove a ContextManager from the list of all active
584: * contexts managers.
585: */
586: synchronized void removeContext(ContextManager cm) {
587: if (allContexts != null)
588: allContexts.remove(cm);
589: }
590: }
|