KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > services > context > ContextService


1 /*
2
3    Derby - Class org.apache.derby.iapi.services.context.ContextService
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.services.context;
23
24 import org.apache.derby.iapi.services.monitor.Monitor;
25 import org.apache.derby.iapi.services.sanity.SanityManager;
26 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
27
28 import java.util.Hashtable JavaDoc;
29 import java.util.Enumeration JavaDoc;
30
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33
34 /**
35     A set of static methods to supply easier access to contexts.
36 */

37 public final class ContextService //OLD extends Hashtable
38
{
39
40     private static ContextService factory;
41     private HeaderPrintWriter errorStream;
42
43     /**
44         Maintains a list of all the contexts that this thread has created
45         and/or used. The object stored in the thread local varys according
46         how this thread has been used and will be one of:
47
48         <UL>
49         <LI> null - the thread no affiliation with a context manager.
50
51         <LI> ContextManager - the current thread has used or is using
52             this context manager. If ContextManager.activeThread equals
53             the current thread then the thread is currently active with
54             the ContextManager. In this case ContextManager.activeCount
55             will be greater than zero and represent the level of nested
56             setCurrentContextmanager calls.
57             If ContextManager.activeThread is null then no other thread
58             is using the Contextmanager, if ContextManager.activeThread
59             is not-null and not equal to the current thread then some
60             other thread is using the context. It is assumed that
61             only a single thread can be using a ContextManager at any time
62             and this is enforced by synchronization outside the ContextManager.
63             E.g for JDBC connections, synchronization at the JDBC level.
64
65         <LI> java.util.Stack containing ContextManagers - the current
66         thread is actively using multiple different ContextManagers,
67         with nesting. All ContextManagers in the stack will have
68         activeThread set to the current thread, and their activeCount
69         set to -1. This is beacause nesting is soley represented by
70         the stack, with the current context manager on top of the stack.
71         This supports multiple levels of nesting across two stacks, e.g.
72         C1->C2->C2->C1->C2.
73         </UL>
74
75         This thread local is used to find the current context manager. Basically it provides
76         fast access to a list of candidate contexts. If one of the contexts has its activeThread
77         equal to the current thread then it is the current context manager.
78
79         If the thread has pushed multiple contexts (e.g. open a new non-nested Cloudscape connection
80         from a server side method) then threadContextList will contain a Stack. The value for each cm
81         will be a push order, with higher numbers being more recently pushed.
82
83         To support the case where a single context manager is pushed twice (nested connection),
84         the context manager keeps track of the number of times it has been pushed (set). Note that
85         our synchronization requires that a single context can only be accessed by a single thread at a time.
86         In the JDBC layer this is enforced by the synchronization on the connection object.
87
88         <P>
89         There are two cases we are trying to optimise.
90         <UL>
91         <LI> Typical JDBC client program where there a Connection is always executed using a single thread.
92             In this case this variable will contain the Connection's context manager
93         <LI> Typical application server pooled connection where a single thread may use a connection from a pool
94         for the lifetime of the request. In this case this variable will contain a WeakReference.
95         </UL>
96         <BR>
97         Single thread for Connection exection.
98         <pre>
99         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 JavaDoc threadContextList = new ThreadLocal JavaDoc();
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 JavaDoc 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 JavaDoc();
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         Find the context with the given name in the context service factory
215         loaded for the system.
216
217         @return The requested context, null if it doesn't exist.
218     */

219     public static Context getContext(String JavaDoc contextId) {
220
221         ContextManager cm = getFactory().getCurrentContextManager();
222
223         if( cm == null)
224             return null;
225         
226         return cm.getContext(contextId);
227     }
228
229     /**
230         Find the context with the given name in the context service factory
231         loaded for the system.
232
233         This version will not do any debug checking, but return null
234         quietly if it runs into any problems.
235
236         @return The requested context, null if it doesn't exist.
237     */

238     public static Context getContextOrNull(String JavaDoc contextId) {
239         ContextService csf = factory;
240
241         if (csf == null)
242             return null;
243         
244         ContextManager cm = csf.getCurrentContextManager();
245
246         if (cm == null)
247             return null;
248
249         return cm.getContext(contextId);
250     }
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 JavaDoc tcl = threadContextList;
266         if (tcl == null) {
267             // The context service is already stopped.
268
return null;
269         }
270
271         Object JavaDoc list = tcl.get();
272
273         if (list instanceof ContextManager) {
274             
275             Thread JavaDoc 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 JavaDoc stack = (java.util.Stack JavaDoc) 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 JavaDoc 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.THROWASSERT("resetCurrentContextManager - mismatch threads - current" + Thread.currentThread() + " - cm's " + cm.activeThread);
308             }
309
310             if (getCurrentContextManager() != cm) {
311                 SanityManager.THROWASSERT("resetCurrentContextManager - mismatch contexts - " + Thread.currentThread());
312             }
313
314             if (cm.activeCount < -1) {
315                 SanityManager.THROWASSERT("resetCurrentContextManager - invalid count - current" + Thread.currentThread() + " - count " + cm.activeCount);
316             }
317
318             if (cm.activeCount == 0) {
319                 SanityManager.THROWASSERT("resetCurrentContextManager - invalid count - current" + Thread.currentThread() + " - count " + cm.activeCount);
320             }
321
322             if (cm.activeCount > 0) {
323                 if (tcl.get() != cm)
324                     SanityManager.THROWASSERT("resetCurrentContextManager - invalid thread local " + Thread.currentThread() + " - object " + tcl.get());
325
326             }
327         }
328
329         if (cm.activeCount != -1) {
330             if (--cm.activeCount == 0) {
331                 cm.activeThread = null;
332                 
333                 // If the ContextManager is empty
334
// then don't keep a reference to it
335
// when it is not in use. The ContextManager
336
// has been closed (most likely) and this
337
// is now unwanted. Keeping the reference
338
// would hold onto memory and increase the
339
// chance of holding onto a another reference
340
// will could cause issues for future operations.
341
if (cm.isEmpty())
342                     tcl.set(null);
343                     
344             }
345             return;
346         }
347
348         java.util.Stack JavaDoc stack = (java.util.Stack JavaDoc) tcl.get();
349
350         Object JavaDoc oldCM = stack.pop();
351
352         ContextManager nextCM = (ContextManager) stack.peek();
353
354         boolean seenMultipleCM = false;
355         boolean seenCM = false;
356         for (int i = 0; i < stack.size(); i++) {
357
358             Object JavaDoc stackCM = stack.elementAt(i);
359             if (stackCM != nextCM)
360                 seenMultipleCM = true;
361
362             if (stackCM == cm)
363                 seenCM = true;
364         }
365
366         if (!seenCM) {
367             cm.activeThread = null;
368             cm.activeCount = 0;
369         }
370
371         if (!seenMultipleCM)
372         {
373             // all the context managers on the stack
374
// are the same so reduce to a simple count.
375
nextCM.activeCount = stack.size();
376             tcl.set(nextCM);
377         }
378     }
379
380     /**
381      * The current thread (passed in a me) is setting associateCM
382      * to be its current context manager. Sets the thread local
383      * variable threadContextList to reflect associateCM being
384      * the current ContextManager.
385      *
386      * @return True if the nesting level is to be represented in
387      * the ContextManager.activeCount field. False if not.
388      *
389      * @see ContextManager#activeCount
390      * @see ContextManager#activeThread
391     */

392     private boolean addToThreadList(Thread JavaDoc me, ContextManager associateCM) {
393
394         ThreadLocal JavaDoc tcl = threadContextList;
395
396         if (tcl == null) {
397             // The context service is already stopped.
398
return false;
399         }
400
401         Object JavaDoc list = tcl.get();
402
403         // Already set up to reflect associateCM ContextManager
404
if (associateCM == list)
405             return true;
406
407         // Not currently using any ContextManager
408
if (list == null)
409         {
410             tcl.set(associateCM);
411             return true;
412         }
413
414         java.util.Stack JavaDoc stack;
415         if (list instanceof ContextManager) {
416             
417             // Could be two situations:
418
// 1. Single ContextManager not in use by this thread
419
// 2. Single ContextManager in use by this thread (nested call)
420

421             ContextManager threadsCM = (ContextManager) list;
422             if (me == null)
423                 me = Thread.currentThread();
424             
425             if (threadsCM.activeThread != me) {
426                 // Not nested, just a CM left over
427
// from a previous execution.
428
tcl.set(associateCM);
429                 return true;
430             }
431             
432             // Nested, need to create a Stack of ContextManagers,
433
// the top of the stack will be the active one.
434
stack = new java.util.Stack JavaDoc();
435             tcl.set(stack);
436             
437             // The stack represents the true nesting
438
// of ContextManagers, splitting out nesting
439
// of a single ContextManager into multiple
440
// entries in the stack.
441
for (int i = 0; i < threadsCM.activeCount; i++)
442             {
443                 stack.push(threadsCM);
444             }
445             threadsCM.activeCount = -1;
446         }
447         else
448         {
449             // existing stack, nesting represented
450
// by stack entries, not activeCount.
451
stack = (java.util.Stack JavaDoc) list;
452         }
453
454         stack.push(associateCM);
455         associateCM.activeCount = -1;
456
457         if (SanityManager.DEBUG) {
458
459             if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
460
461                 if (stack.size() > 10)
462                     System.out.println("memoryLeakTrace:ContextService:threadLocal " + stack.size());
463             }
464         }
465
466         return false;
467     }
468
469     /**
470      * Link the current thread to the passed in Contextmanager
471      * so that a subsequent call to getCurrentContextManager by
472      * the current Thread will return cm.
473      * ContextManagers are tied to a Thread while the thread
474      * is executing Derby code. For example on most JDBC method
475      * calls the ContextManager backing the Connection object
476      * is tied to the current Thread at the start of the method
477      * and reset at the end of the method. Once the Thread
478      * has completed its Derby work the method resetCurrentContextManager
479      * must be called with the same ContextManager to break the link.
480      * Note that a subsquent use of the ContextManager may be on
481      * a separate Thread, the Thread is only linked to the ContextManager
482      * between the setCurrentContextManager and resetCurrentContextManager calls.
483      * <BR>
484      * ContextService supports nesting of calls by a single Thread, either
485      * with the same ContextManager or a different ContextManager.
486      * <UL>
487      * <LI>The same ContextManager would be pushed during a nested JDBC call in
488      * a procedure or function.
489      * <LI>A different ContextManager would be pushed during a call on
490      * a different embedded JDBC Connection in a procedure or function.
491      * </UL>
492      */

493     public void setCurrentContextManager(ContextManager cm) {
494
495
496         if (SanityManager.DEBUG) {
497             Thread JavaDoc me = Thread.currentThread();
498
499             if (cm.activeThread != null && me != cm.activeThread) {
500                 SanityManager.THROWASSERT("setCurrentContextManager - mismatch threads - current " + me + " - cm's " + cm.activeThread);
501             }
502
503         }
504
505         Thread JavaDoc me = null;
506
507         if (cm.activeThread == null) {
508             cm.activeThread = (me = Thread.currentThread());
509         }
510         if (addToThreadList(me, cm))
511             cm.activeCount++;
512     }
513
514     /**
515      * It's up to the caller to track this context manager and set it
516      * in the context manager list using setCurrentContextManager.
517      * We don't keep track of it due to this call being made.
518      */

519     public ContextManager newContextManager()
520     {
521         ContextManager cm = new ContextManager(this, errorStream);
522
523         // push a context that will shut down the system on
524
// a severe error.
525
new SystemContext(cm);
526
527         synchronized (this) {
528             allContexts.add(cm);
529             
530             if (SanityManager.DEBUG) {
531
532                 if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
533
534                     if (allContexts.size() > 50)
535                         System.out.println("memoryLeakTrace:ContextService:allContexts " + allContexts.size());
536                 }
537             }
538         }
539
540         return cm;
541     }
542
543     public void notifyAllActiveThreads(Context c) {
544         Thread JavaDoc me = Thread.currentThread();
545
546         synchronized (this) {
547             for (Iterator JavaDoc i = allContexts.iterator(); i.hasNext(); ) {
548
549                 ContextManager cm = (ContextManager) i.next();
550
551                 Thread JavaDoc active = cm.activeThread;
552
553                 if (active == me)
554                     continue;
555
556                 if (active == null)
557                     continue;
558
559                 if (cm.setInterrupted(c))
560                     active.interrupt();
561             }
562         }
563     }
564
565     /**
566      * Remove a ContextManager from the list of all active
567      * contexts managers.
568      */

569     synchronized void removeContext(ContextManager cm)
570     {
571         if (allContexts != null)
572             allContexts.remove( cm);
573     }
574 }
575
Popular Tags