Source code: org/apache/derby/iapi/services/context/ContextManager.java
1 /*
2
3 Derby - Class org.apache.derby.iapi.services.context.ContextManager
4
5 Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 */
20
21 package org.apache.derby.iapi.services.context;
22
23 import org.apache.derby.iapi.services.sanity.SanityManager;
24 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
25 import org.apache.derby.iapi.services.stream.PrintWriterGetHeader;
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.reference.SQLState;
37 import org.apache.derby.iapi.services.i18n.LocaleFinder;
38 import java.io.PrintWriter;
39
40 import java.util.Hashtable;
41 import java.util.Stack;
42 import java.util.Vector;
43 import java.util.Locale;
44
45 /**
46 *
47 * The ContextManager collects contexts as they are
48 * created. It maintains stacks of contexts by
49 * named ids, so that the top context of a given
50 * type can be returned. It also maintains a global
51 * stack so that contexts can be traversed in the
52 * order they were created.
53 * <p>
54 * The first implementation of the context manager
55 * assumes there is only one thread to worry about
56 * and that the user(s) of the class only create one
57 * instance of ContextManager.
58 */
59
60 public class ContextManager
61
62 {
63
64
65 private final Stack holder;
66
67 /*
68 * ContextManager interface
69 */
70
71 public void pushContext(Context newContext)
72 {
73 checkInterrupt();
74
75 String contextId = newContext.getIdName();
76
77 Stack idStack = (Stack) ctxTable.get(contextId);
78
79 // if the stack is null, create a new one.
80 if (idStack == null)
81 {
82 idStack = new Stack();
83 ctxTable.put(contextId,idStack);
84 }
85
86 // add to top of id's stack
87 idStack.push(newContext);
88
89 // add to top of global stack too
90 holder.push(newContext);
91 }
92
93 /**
94 * @see org.apache.derby.iapi.services.context.ContextManager#getContext
95 */
96 public Context getContext(String contextId)
97 {
98 checkInterrupt();
99
100 Stack idStack = (Stack)ctxTable.get(contextId);
101
102 if (SanityManager.DEBUG)
103 SanityManager.ASSERT( idStack == null ||
104 idStack.empty() ||
105 ((Context)idStack.peek()).getIdName().equals(contextId));
106
107 if (idStack == null ||
108 idStack.empty())
109 {
110 return null;
111 }
112
113
114
115 return (Context) idStack.peek();
116 }
117
118 /**
119 * @see org.apache.derby.iapi.services.context.ContextManager#popContext
120 */
121 public void popContext()
122 {
123 checkInterrupt();
124
125 Context theContext;
126 String contextId;
127 Stack idStack;
128
129 // no contexts to remove, so we're done.
130 if (holder.empty())
131 {
132 return;
133 }
134
135 // remove the top context from the global stack
136 theContext = (Context) holder.pop();
137
138 // now find its id and remove it from there, too
139 contextId = theContext.getIdName();
140 idStack = (Stack)ctxTable.get(contextId);
141
142 if (SanityManager.DEBUG)
143 SanityManager.ASSERT( idStack != null &&
144 (! idStack.empty()) &&
145 ((Context)idStack.peek()).getIdName() == contextId);
146
147 idStack.pop();
148 }
149
150 void popContext(Context theContext)
151 {
152 checkInterrupt();
153
154 Stack idStack;
155
156 if (SanityManager.DEBUG)
157 SanityManager.ASSERT(!holder.empty());
158
159 // first, remove it from the global stack.
160 // to do this we treat it like its vector supertype.
161 int index = holder.lastIndexOf(theContext);
162 if (index != -1)
163 holder.removeElementAt(index);
164 else if (SanityManager.DEBUG) {
165 //SanityManager.THROWASSERT("Popped non-existent context by id " + theContext + " type " + theContext.getIdName());
166 }
167
168 // now remove it from its id's stack.
169 idStack = (Stack) ctxTable.get(theContext.getIdName());
170 boolean wasThere = idStack.removeElement(theContext);
171 if (SanityManager.DEBUG) {
172 //if (!wasThere)
173 // SanityManager.THROWASSERT("Popped non-existent stack by id " + theContext + " type " + theContext.getIdName());
174 }
175 }
176
177 /**
178 @return true if the context manager is shutdown, false otherwise.
179 */
180 public boolean cleanupOnError(Throwable error)
181 {
182 if (shutdown)
183 return true;
184
185 if (errorStringBuilder == null)
186 errorStringBuilder = new ErrorStringBuilder(errorStream.getHeader());
187
188 ThreadDeath seenThreadDeath = null;
189 if (error instanceof ThreadDeath)
190 seenThreadDeath = (ThreadDeath) error;
191
192 if (error instanceof PassThroughException)
193 error = ((PassThroughException) error).getException();
194
195 boolean reportError = reportError(error);
196
197 if (reportError)
198 {
199 ContextImpl lcc = null;
200 StringBuffer sb = null;
201 if (! shutdown)
202 {
203 // report an id for the message if possible
204 lcc = (ContextImpl) getContext(org.apache.derby.iapi.reference.ContextId.LANG_CONNECTION);
205 if (lcc != null) {
206 sb = lcc.appendErrorInfo();
207 }
208 }
209
210 String cleanup = "Cleanup action starting";
211
212 if (sb != null) {
213 sb.append(cleanup);
214 cleanup = sb.toString();
215 }
216
217 errorStringBuilder.appendln(cleanup);
218
219 if (!shutdown) // Do this only during normal processing.
220 {
221 ContextImpl sc = (ContextImpl) getContext(org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
222 // Output the SQL statement that failed in the log file.
223 if (sc != null)
224 {
225 sb = sc.appendErrorInfo();
226 if (sb != null)
227 errorStringBuilder.appendln(sb.toString());
228 }
229 }
230 }
231
232 /*
233 REVISIT RESOLVE
234 Ensure that the traversal of the stack works in all
235 cases where contexts can pop themselves *and*
236 contexts can pop other contexts off the stack.
237 */
238
239
240 forever: for (;;) {
241
242 int errorSeverity = error instanceof StandardException ?
243 ((StandardException) error).getSeverity() :
244 ExceptionSeverity.NO_APPLICABLE_SEVERITY;
245 if (reportError) {
246 errorStringBuilder.stackTrace(error);
247 flushErrorString();
248 }
249
250
251 boolean lastHandler = false;
252
253
254 /*
255 Walk down the stack, calling
256 cleanup on each context. We use
257 the vector interface to do this.
258 */
259 cleanup: for (int index = holder.size() - 1; index >= 0; index--) {
260
261 try {
262 if (lastHandler)
263 {
264 break;
265 }
266
267 Context ctx = ((Context) holder.elementAt(index));
268 lastHandler = ctx.isLastHandler(errorSeverity);
269
270 ctx.cleanupOnError(error);
271 }
272 catch (StandardException se) {
273
274 if (error instanceof StandardException) {
275
276 if (se.getSeverity() > ((StandardException) error).getSeverity()) {
277 // Ok, error handling raised a more severe error,
278 // restart with the more severe error
279 error = se;
280 reportError = reportError(se);
281 if (reportError) {
282 errorStream.println("New exception raised during cleanup " + error.getMessage());
283 errorStream.flush();
284 }
285 continue forever;
286 }
287 }
288
289 if (reportError(se)) {
290 errorStringBuilder.appendln("Less severe exception raised during cleanup (ignored) " + se.getMessage());
291 errorStringBuilder.stackTrace(se);
292 flushErrorString();
293 }
294
295 /*
296 For a less severe error, keep with the last error
297 */
298 continue cleanup;
299 }
300 catch (Throwable t) {
301 reportError = reportError(t);
302
303
304 if (error instanceof StandardException) {
305 /*
306 Ok, error handling raised a more severe error,
307 restart with the more severe error
308 A Throwable after a StandardException is always
309 more severe.
310 */
311 error = t;
312 if (reportError) {
313 errorStream.println("New exception raised during cleanup " + error.getMessage());
314 errorStream.flush();
315 }
316 continue forever;
317 }
318
319
320 if (reportError) {
321 errorStringBuilder.appendln("Equally severe exception raised during cleanup (ignored) " + t.getMessage());
322 errorStringBuilder.stackTrace(t);
323 flushErrorString();
324 }
325
326 if (t instanceof ThreadDeath) {
327 if (seenThreadDeath != null)
328 throw seenThreadDeath;
329
330 seenThreadDeath = (ThreadDeath) t;
331 }
332
333 /*
334 For a less severe error, just continue with the last
335 error
336 */
337 continue cleanup;
338 }
339 }
340
341 if (reportError) {
342 errorStream.println("Cleanup action completed");
343 errorStream.flush();
344 }
345
346 if (seenThreadDeath != null)
347 throw seenThreadDeath;
348
349 return false;
350 }
351
352 }
353
354
355 synchronized boolean setInterrupted(Context c) {
356
357 boolean interruptMe = (c == null) || holder.contains(c);
358
359 if (interruptMe) {
360 this.shutdown = true;
361 }
362 return interruptMe;
363 }
364
365 /**
366 Check to see if we have been interrupted. If we have then
367 a ShutdownException will be thrown. This will be either the
368 one passed to interrupt or a generic one if some outside
369 source interrupted the thread.
370 */
371 private void checkInterrupt() {
372 if (shutdown) {
373 // system must have changed underneath us
374 throw new ShutdownException();
375 }
376 }
377
378 /**
379 Set the locale for this context.
380 */
381 public void setLocaleFinder(LocaleFinder finder) {
382 this.finder = finder;
383 }
384
385 private Locale messageLocale;
386
387 public void setMessageLocale(String localeID) throws StandardException {
388 this.messageLocale = Monitor.getLocaleFromString(localeID);
389 }
390
391 public Locale getMessageLocale()
392 {
393 if (messageLocale != null)
394 return messageLocale;
395 else if (finder != null) {
396 try {
397 return finder.getCurrentLocale();
398 } catch (StandardException se) {
399
400 }
401 }
402 return Locale.getDefault();
403 }
404
405 /**
406 * Flush the built up error string to whereever
407 * it is supposed to go, and reset the error string
408 */
409 private void flushErrorString()
410 {
411 errorStream.print(errorStringBuilder.get().toString());
412 errorStream.flush();
413 errorStringBuilder.reset();
414 }
415
416 /*
417 ** Class methods
418 */
419
420 private boolean reportError(Throwable t) {
421
422 if (t instanceof StandardException) {
423
424 StandardException se = (StandardException) t;
425
426 switch (se.report()) {
427 case StandardException.REPORT_DEFAULT:
428 int level = se.getSeverity();
429 return (level >= logSeverityLevel) ||
430 (level == ExceptionSeverity.NO_APPLICABLE_SEVERITY);
431
432 case StandardException.REPORT_NEVER:
433 return false;
434
435 case StandardException.REPORT_ALWAYS:
436 default:
437 return true;
438 }
439 }
440
441 return !(t instanceof ShutdownException);
442
443 }
444
445 /**
446 * constructor specifying the hash table size and load
447 * factor for the hashed-by-id context stacks.
448 */
449 ContextManager(ContextService csf, HeaderPrintWriter stream)
450 {
451 errorStream = stream;
452 ctxTable = new Hashtable();
453 owningCsf = csf;
454
455 logSeverityLevel = PropertyUtil.getSystemInt(Property.LOG_SEVERITY_LEVEL,
456 SanityManager.DEBUG ? 0 : ExceptionSeverity.SESSION_SEVERITY);
457
458 holder = new Stack();
459 }
460
461 private final Hashtable ctxTable;
462
463 final ContextService owningCsf;
464
465 private int logSeverityLevel;
466
467 private HeaderPrintWriter errorStream;
468 private ErrorStringBuilder errorStringBuilder;
469
470 private boolean shutdown;
471 private LocaleFinder finder;
472
473 final Stack cmStack = new Stack();
474
475 Thread activeThread;
476 int activeCount;
477 }