KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.iapi.services.context.ContextManager
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.sanity.SanityManager;
25 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
26
27 import org.apache.derby.iapi.error.PassThroughException;
28
29 import org.apache.derby.iapi.error.StandardException;
30 import org.apache.derby.iapi.services.monitor.Monitor;
31
32 import org.apache.derby.iapi.reference.Property;
33 import org.apache.derby.iapi.services.property.PropertyUtil;
34
35 import org.apache.derby.iapi.error.ExceptionSeverity;
36 import org.apache.derby.iapi.services.i18n.LocaleFinder;
37
38 import java.util.HashMap JavaDoc;
39 import java.util.ArrayList JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Collections JavaDoc;
42 import java.util.Locale JavaDoc;
43
44 /**
45  *
46  * The ContextManager collects contexts as they are
47  * created. It maintains stacks of contexts by
48  * named ids, so that the top context of a given
49  * type can be returned. It also maintains a global
50  * stack so that contexts can be traversed in the
51  * order they were created.
52  * <p>
53  * The first implementation of the context manager
54  * assumes there is only one thread to worry about
55  * and that the user(s) of the class only create one
56  * instance of ContextManager.
57  */

58
59 public class ContextManager
60 {
61     /**
62      * The CtxStack implement a stack on top of an ArrayList (to avoid
63      * the inherent overhead associated with java.util.Stack which is
64      * built on top of java.util.Vector, which is fully
65      * synchronized).
66      */

67     private static final class CtxStack {
68         private ArrayList JavaDoc stack_ = new ArrayList JavaDoc();
69
70         // Keeping a reference to the top element on the stack
71
// optimizes the frequent accesses to this element. The
72
// tradeoff is that pushing and popping becomes more
73
// expensive, but those operations are infrequent.
74
private Context top_ = null;
75
76         void push(Context context) {
77             stack_.add(context);
78             top_ = context;
79         }
80         void pop() {
81             stack_.remove(stack_.size()-1);
82             top_ = stack_.isEmpty() ?
83                 null : (Context) stack_.get(stack_.size()-1);
84         }
85         void remove(Context context) {
86             if (context == top_) {
87                 pop();
88                 return;
89             }
90             stack_.remove(stack_.lastIndexOf(context));
91         }
92         Context top() {
93             return top_;
94         }
95         boolean isEmpty() { return stack_.isEmpty(); }
96         List JavaDoc getUnmodifiableList() {
97             return Collections.unmodifiableList(stack_);
98         }
99     }
100
101     /**
102      * Empty ArrayList to use as void value
103      */

104     //private final ArrayList voidArrayList_ = new ArrayList(0);
105

106     /**
107      * HashMap that holds the Context objects. The Contexts are stored
108      * with a String key.
109      * @see ContextManager#pushContext(Context)
110      */

111     private final HashMap JavaDoc ctxTable = new HashMap JavaDoc();
112
113     /**
114      * List of all Contexts
115      */

116     private final ArrayList JavaDoc holder = new ArrayList JavaDoc();
117
118     /**
119      * Add a Context object to the ContextManager. The object is added
120      * both to the holder list and to a stack for the specific type of
121      * Context.
122      * @param newContext the new Context object
123      */

124     public void pushContext(Context newContext)
125     {
126         checkInterrupt();
127         final String JavaDoc contextId = newContext.getIdName();
128         CtxStack idStack = (CtxStack) ctxTable.get(contextId);
129
130         // if the stack is null, create a new one.
131
if (idStack == null) {
132             idStack = new CtxStack();
133             ctxTable.put(contextId, idStack);
134         }
135
136         // add to top of id's stack
137
idStack.push(newContext);
138
139         // add to top of global stack too
140
holder.add(newContext);
141     }
142     
143     /**
144      * Obtain the last pushed Context object of the type indicated by
145      * the contextId argument.
146      * @param contextId a String identifying the type of Context
147      * @return The Context object with the corresponding contextId, or null if not found
148      */

149     public Context getContext(String JavaDoc contextId) {
150         checkInterrupt();
151         
152         final CtxStack idStack = (CtxStack) ctxTable.get(contextId);
153         if (SanityManager.DEBUG)
154             SanityManager.ASSERT( idStack == null ||
155                                   idStack.isEmpty() ||
156                                   idStack.top().getIdName() == contextId);
157         return (idStack==null?null:idStack.top());
158     }
159
160     /**
161      * Remove the last pushed Context object, regardless of type. If
162      * there are no Context objects, no action is taken.
163      */

164     public void popContext()
165     {
166         checkInterrupt();
167         // no contexts to remove, so we're done.
168
if (holder.isEmpty()) {
169             return;
170         }
171
172         // remove the top context from the global stack
173
Context theContext = (Context) holder.remove(holder.size()-1);
174
175         // now find its id and remove it from there, too
176
final String JavaDoc contextId = theContext.getIdName();
177         final CtxStack idStack = (CtxStack) ctxTable.get(contextId);
178
179         if (SanityManager.DEBUG) {
180             SanityManager.ASSERT( idStack != null &&
181                                   (! idStack.isEmpty()) &&
182                                   idStack.top().getIdName() == contextId);
183         }
184         idStack.pop();
185     }
186
187     /**
188      * Removes the specified Context object. If
189      * the specified Context object does not exist, the call will fail.
190      * @param theContext the Context object to remove.
191      */

192     void popContext(Context theContext)
193     {
194         checkInterrupt();
195         if (SanityManager.DEBUG)
196             SanityManager.ASSERT(!holder.isEmpty());
197
198         // first, remove it from the global stack.
199
holder.remove(holder.lastIndexOf(theContext));
200
201         final String JavaDoc contextId = theContext.getIdName();
202         final CtxStack idStack = (CtxStack) ctxTable.get(contextId);
203
204         // now remove it from its id's stack.
205
idStack.remove(theContext);
206     }
207     
208     /**
209      * Is the ContextManager empty containing no Contexts.
210      */

211     final boolean isEmpty()
212     {
213         return holder.isEmpty();
214     }
215     
216     /**
217      * Return an unmodifiable list reference to the ArrayList backing
218      * CtxStack object for this type of Contexts. This method allows
219      * fast traversal of all Contexts on that stack. The first element
220      * in the List corresponds to the bottom of the stack. The
221      * assumption is that the Stack will not be modified while it is
222      * being traversed.
223      * @param contextId the type of Context stack to return.
224      * @return an unmodifiable "view" of the ArrayList backing the stack
225      * @see org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext#resetSavepoints()
226      * @see org.apache.derby.iapi.sql.conn.StatementContext#resetSavePoint()
227      */

228     public final List JavaDoc getContextStack(String JavaDoc contextId) {
229         final CtxStack cs = (CtxStack) ctxTable.get(contextId);
230         return (cs==null?Collections.EMPTY_LIST:cs.getUnmodifiableList());
231     }
232
233
234     /**
235         @return true if the context manager is shutdown, false otherwise.
236      */

237     public boolean cleanupOnError(Throwable JavaDoc error)
238     {
239         if (shutdown)
240             return true;
241
242         if (errorStringBuilder == null)
243             errorStringBuilder = new ErrorStringBuilder(errorStream.getHeader());
244
245         ThreadDeath JavaDoc seenThreadDeath = null;
246         if (error instanceof ThreadDeath JavaDoc)
247             seenThreadDeath = (ThreadDeath JavaDoc) error;
248
249         if (error instanceof PassThroughException)
250             error = ((PassThroughException) error).getException();
251
252         boolean reportError = reportError(error);
253
254         if (reportError)
255         {
256             ContextImpl lcc = null;
257             StringBuffer JavaDoc sb = null;
258             if (! shutdown)
259             {
260                 // report an id for the message if possible
261
lcc = (ContextImpl) getContext(org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION);
262                 if (lcc != null) {
263                     sb = lcc.appendErrorInfo();
264                 }
265             }
266
267             String JavaDoc cleanup = "Cleanup action starting";
268
269             if (sb != null) {
270                 sb.append(cleanup);
271                 cleanup = sb.toString();
272             }
273
274             errorStringBuilder.appendln(cleanup);
275             
276             if (!shutdown) // Do this only during normal processing.
277
{
278                 ContextImpl sc = (ContextImpl) getContext(org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
279                 // Output the SQL statement that failed in the log file.
280
if (sc != null)
281                 {
282                     sb = sc.appendErrorInfo();
283                     if (sb != null)
284                         errorStringBuilder.appendln(sb.toString());
285                 }
286             }
287         }
288         
289         /*
290           REVISIT RESOLVE
291           Ensure that the traversal of the stack works in all
292           cases where contexts can pop themselves *and*
293           contexts can pop other contexts off the stack.
294         */

295         
296
297 forever: for (;;) {
298
299             int errorSeverity = error instanceof StandardException ?
300                 ((StandardException) error).getSeverity() :
301                 ExceptionSeverity.NO_APPLICABLE_SEVERITY;
302             if (reportError) {
303                 errorStringBuilder.stackTrace(error);
304                 flushErrorString();
305             }
306
307             
308             boolean lastHandler = false;
309
310
311             /*
312                 Walk down the stack, calling
313                 cleanup on each context. We use
314                 the vector interface to do this.
315              */

316 cleanup: for (int index = holder.size() - 1; index >= 0; index--) {
317
318                 try {
319                     if (lastHandler)
320                     {
321                         break;
322                     }
323
324                     Context ctx = ((Context) holder.get(index));
325                     lastHandler = ctx.isLastHandler(errorSeverity);
326
327                     ctx.cleanupOnError(error);
328                 }
329                 catch (StandardException se) {
330     
331                     if (error instanceof StandardException) {
332     
333                         if (se.getSeverity() > ((StandardException) error).getSeverity()) {
334                             // Ok, error handling raised a more severe error,
335
// restart with the more severe error
336
error = se;
337                             reportError = reportError(se);
338                             if (reportError) {
339                                 errorStream.println("New exception raised during cleanup " + error.getMessage());
340                                 errorStream.flush();
341                             }
342                             continue forever;
343                         }
344                     }
345
346                     if (reportError(se)) {
347                         errorStringBuilder.appendln("Less severe exception raised during cleanup (ignored) " + se.getMessage());
348                         errorStringBuilder.stackTrace(se);
349                         flushErrorString();
350                     }
351
352                     /*
353                         For a less severe error, keep with the last error
354                      */

355                     continue cleanup;
356                 }
357                 catch (Throwable JavaDoc t) {
358                     reportError = reportError(t);
359
360
361                     if (error instanceof StandardException) {
362                         /*
363                             Ok, error handling raised a more severe error,
364                             restart with the more severe error
365                             A Throwable after a StandardException is always
366                             more severe.
367                          */

368                         error = t;
369                         if (reportError) {
370                             errorStream.println("New exception raised during cleanup " + error.getMessage());
371                             errorStream.flush();
372                         }
373                         continue forever;
374                     }
375
376
377                     if (reportError) {
378                         errorStringBuilder.appendln("Equally severe exception raised during cleanup (ignored) " + t.getMessage());
379                         errorStringBuilder.stackTrace(t);
380                         flushErrorString();
381                     }
382
383                     if (t instanceof ThreadDeath JavaDoc) {
384                         if (seenThreadDeath != null)
385                             throw seenThreadDeath;
386
387                         seenThreadDeath = (ThreadDeath JavaDoc) t;
388                     }
389     
390                     /*
391                         For a less severe error, just continue with the last
392                         error
393                      */

394                     continue cleanup;
395                 }
396             }
397
398             if (reportError) {
399                 errorStream.println("Cleanup action completed");
400                 errorStream.flush();
401             }
402
403             if (seenThreadDeath != null)
404                 throw seenThreadDeath;
405
406             return false;
407         }
408
409     }
410
411
412     synchronized boolean setInterrupted(Context c) {
413
414         boolean interruptMe = (c == null) || holder.contains(c);
415
416         if (interruptMe) {
417             this.shutdown = true;
418         }
419         return interruptMe;
420     }
421
422     /**
423         Check to see if we have been interrupted. If we have then
424         a ShutdownException will be thrown. This will be either the
425         one passed to interrupt or a generic one if some outside
426         source interrupted the thread.
427     */

428     private void checkInterrupt() {
429         if (shutdown) {
430             // system must have changed underneath us
431
throw new ShutdownException();
432         }
433     }
434
435     /**
436         Set the locale for this context.
437     */

438     public void setLocaleFinder(LocaleFinder finder) {
439         this.finder = finder;
440     }
441
442     private Locale JavaDoc messageLocale;
443
444     public void setMessageLocale(String JavaDoc localeID) throws StandardException {
445         this.messageLocale = Monitor.getLocaleFromString(localeID);
446     }
447
448     public Locale JavaDoc getMessageLocale()
449     {
450         if (messageLocale != null)
451             return messageLocale;
452         else if (finder != null) {
453             try {
454                 return finder.getCurrentLocale();
455             } catch (StandardException se) {
456                 
457             }
458         }
459         return Locale.getDefault();
460     }
461
462     /**
463      * Flush the built up error string to whereever
464      * it is supposed to go, and reset the error string
465      */

466     private void flushErrorString()
467     {
468         errorStream.print(errorStringBuilder.get().toString());
469         errorStream.flush();
470         errorStringBuilder.reset();
471     }
472
473     /*
474     ** Class methods
475     */

476
477     private boolean reportError(Throwable JavaDoc t) {
478
479         if (t instanceof StandardException) {
480
481             StandardException se = (StandardException) t;
482
483             switch (se.report()) {
484             case StandardException.REPORT_DEFAULT:
485                 int level = se.getSeverity();
486                 return (level >= logSeverityLevel) ||
487                     (level == ExceptionSeverity.NO_APPLICABLE_SEVERITY);
488
489             case StandardException.REPORT_NEVER:
490                 return false;
491
492             case StandardException.REPORT_ALWAYS:
493             default:
494                 return true;
495             }
496         }
497
498         return !(t instanceof ShutdownException);
499
500     }
501
502     /**
503      * Constructs a new instance. No CtxStacks are inserted into the
504      * hashMap as they will be allocated on demand.
505      * @param csf the ContextService owning this ContextManager
506      * @param stream error stream for reporting errors
507      */

508     ContextManager(ContextService csf, HeaderPrintWriter stream)
509     {
510         errorStream = stream;
511         owningCsf = csf;
512
513         logSeverityLevel = PropertyUtil.getSystemInt(Property.LOG_SEVERITY_LEVEL,
514             SanityManager.DEBUG ? 0 : ExceptionSeverity.SESSION_SEVERITY);
515     }
516
517     final ContextService owningCsf;
518
519     private int logSeverityLevel;
520
521     private HeaderPrintWriter errorStream;
522     private ErrorStringBuilder errorStringBuilder;
523
524     private boolean shutdown;
525     private LocaleFinder finder;
526
527     /**
528      * The thread that owns this ContextManager, set by
529      * ContextService.setCurrentContextManager and reset
530      * by resetCurrentContextManager. Only a single
531      * thread can be active in a ContextManager at any time,
532      * and the thread only "owns" the ContextManager while
533      * it is executing code within Derby. In the JDBC case
534      * setCurrentContextManager is called at the start of
535      * a JBDC method and resetCurrentContextManager on completion.
536      * Nesting within the same thread is supported, such as server-side
537      * JDBC calls in a Java routine or procedure. In that case
538      * the activeCount will represent the level of nesting, in
539      * some situations.
540      * <BR>
541
542      * @see ContextService#setCurrentContextManager(ContextManager)
543      * @see ContextService#resetCurrentContextManager(ContextManager)
544      * @see #activeCount
545      */

546     Thread JavaDoc activeThread;
547     
548     /**
549      * Count of the number of setCurrentContextManager calls
550      * by a single thread, for nesting situations with a single
551      * active Contextmanager. If nesting is occuring with multiple
552      * different ContextManagers then this value is set to -1
553      * and nesting is represented by entries in a stack in the
554      * ThreadLocal variable, threadContextList.
555      *
556      * @see ContextService#threadContextList
557      */

558     int activeCount;
559 }
560
Popular Tags