Source code: org/apache/derby/iapi/error/StandardException.java
1 /*
2
3 Derby - Class org.apache.derby.iapi.error.StandardException
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.error;
22
23 import org.apache.derby.iapi.reference.SQLState;
24
25 import org.apache.derby.impl.jdbc.EmbedSQLException;
26 import org.apache.derby.iapi.error.ExceptionSeverity;
27 import org.apache.derby.iapi.services.i18n.MessageService;
28 import org.apache.derby.iapi.services.sanity.SanityManager;
29
30 import java.sql.SQLException;
31 import java.sql.SQLWarning;
32
33 /**
34 StandardException is the root of all exceptions that are handled
35 in a standard fashion by the database code, mainly in the language code.
36 <P>
37 This class is abstract to ensure that an implementation only throws
38 a specific exception (e.g. TransactionException) which is a sub-class
39 <P>
40 A method in an iterface in a protocol under com.ibm.db2j.protocol.Database must
41 only throw a StandardException (if it needs to throw an exception).
42 This indicates that the method can throw an exception and therefore its
43 caller must ensure that any resources it allocates will be cleaned up
44 in the event of an exception in the StandardException hierarchy.
45 <P>
46 Implementations of methods that throw StandardException can have throws
47 clause that are more specific than StandardException.
48 */
49
50 public class StandardException extends Exception
51 {
52 public static final int REPORT_DEFAULT = 0;
53 public static final int REPORT_NEVER = 1;
54 public static final int REPORT_ALWAYS = 2;
55
56 /*
57 * Exception State
58 */
59 private Throwable nestedException;
60 private transient Object[] arguments;
61 private int severity;
62 private String textMessage;
63 private String sqlState;
64 private transient int report;
65
66 /*
67 ** End of constructors
68 */
69
70 protected StandardException(String messageID)
71 {
72 this(messageID, (Throwable) null, (Object[]) null);
73
74 }
75
76 protected StandardException(String messageID, Object[] args)
77 {
78 this(messageID, (Throwable) null, args);
79 }
80
81 protected StandardException(String messageID, Throwable t, Object[] args)
82 {
83 super(messageID);
84
85 this.severity = getSeverityFromIdentifier(messageID);
86 this.sqlState = getSQLStateFromIdentifier(messageID);
87 this.nestedException = t;
88 this.arguments = args;
89
90 if (SanityManager.DEBUG)
91 {
92 SanityManager.ASSERT(messageID != null,
93 "StandardException with no messageID");
94 }
95 }
96
97 /**
98 * This constructor is used when we already have the
99 * message text.
100 *
101 * @param sqlState the sql state of the message
102 * @param text the text of the message
103 */
104 private StandardException(String sqlState, String text)
105 {
106 this(sqlState);
107 textMessage = text;
108 }
109
110 /*
111 ** End of constructors
112 */
113 /**
114 * Sets the arguments for this exception.
115 */
116 private final void setArguments(Object[] arguments)
117 {
118 this.arguments = arguments;
119 }
120
121 /**
122 * Returns the arguments for this exception,
123 * if there are any.
124 */
125 public final Object[] getArguments()
126 {
127 return arguments;
128 }
129
130 /**
131 * Sets the nested exception for this exception.
132 */
133 public final void setNestedException(Throwable nestedException)
134 {
135 this.nestedException = nestedException;
136 }
137
138 /**
139 * Returns the nested exception for this exception,
140 * if there is one.
141 */
142 public final Throwable getNestedException()
143 {
144 return nestedException;
145 }
146
147 /**
148 Yes, report me. Errors that need this method to return
149 false are in the minority.
150 */
151 public final int report() {
152 return report;
153 }
154
155 /**
156 Set my report type.
157 */
158 public final void setReport(int report) {
159 this.report = report;
160 }
161
162 public final void setSeverity(int severity) {
163 this.severity = severity;
164 }
165
166
167 public final int getSeverity() {
168 return severity;
169 }
170
171 public final int getErrorCode() {
172 return severity;
173 }
174
175 /**
176 Return the 5 character SQL State.
177 If you need teh identifier that was used to create the
178 message, then use getMessageId(). getMessageId() will return the
179 string that corresponds to the field in org.apache.derby.iapi.reference.SQLState.
180 */
181 public final String getSQLState()
182 {
183 return sqlState;
184 }
185
186 /**
187 Convert a message identifer from org.apache.derby.iapi.reference.SQLState to
188 a SQLState five character string.
189 * @param messageID - the sql state id of the message from cloudscape
190 * @return String - the 5 character code of the SQLState ID to returned to the user
191 */
192 public static String getSQLStateFromIdentifier(String messageID) {
193
194 if (messageID.length() == 5)
195 return messageID;
196 return messageID.substring(0, 5);
197 }
198
199 /**
200 Get the severity given a message identifier from org.apache.derby.iapi.reference.SQLState.
201 */
202 public static int getSeverityFromIdentifier(String messageID) {
203
204 int lseverity = ExceptionSeverity.NO_APPLICABLE_SEVERITY;
205
206 switch (messageID.length()) {
207 case 5:
208 switch (messageID.charAt(0)) {
209 case '0':
210 switch (messageID.charAt(1)) {
211 case '1':
212 lseverity = ExceptionSeverity.WARNING_SEVERITY;
213 break;
214 case 'A':
215 case '7':
216 lseverity = ExceptionSeverity.STATEMENT_SEVERITY;
217 break;
218 case '8':
219 lseverity = ExceptionSeverity.SESSION_SEVERITY;
220 break;
221 }
222 break;
223 case '2':
224 case '3':
225 lseverity = ExceptionSeverity.STATEMENT_SEVERITY;
226 break;
227 case '4':
228 switch (messageID.charAt(1)) {
229 case '0':
230 lseverity = ExceptionSeverity.TRANSACTION_SEVERITY;
231 break;
232 case '2':
233 lseverity = ExceptionSeverity.STATEMENT_SEVERITY;
234 break;
235 }
236 break;
237 }
238 break;
239
240 default:
241 switch (messageID.charAt(6)) {
242 case 'M':
243 lseverity = ExceptionSeverity.SYSTEM_SEVERITY;
244 break;
245 case 'D':
246 lseverity = ExceptionSeverity.DATABASE_SEVERITY;
247 break;
248 case 'C':
249 lseverity = ExceptionSeverity.SESSION_SEVERITY;
250 break;
251 case 'T':
252 lseverity = ExceptionSeverity.TRANSACTION_SEVERITY;
253 break;
254 case 'S':
255 lseverity = ExceptionSeverity.STATEMENT_SEVERITY;
256 break;
257 case 'U':
258 lseverity = ExceptionSeverity.NO_APPLICABLE_SEVERITY;
259 break;
260 }
261 break;
262 }
263
264 return lseverity;
265 }
266
267 /*
268 ** Set of static methods to obtain exceptions.
269 **
270 ** Possible parameters:
271 ** String sqlState - SQL State
272 ** int severity - Severity of message
273 ** Throwable t - exception to wrap
274 ** Object aN - argument to error message
275 **
276 ** Calls that can be made after the exception has been created.
277 **
278 ** setExceptionCategory()
279 ** setReport()
280 */
281
282 /* specific exceptions */
283
284 public static StandardException normalClose()
285 {
286 StandardException se = newException( SQLState.NORMAL_CLOSE );
287 se.report = REPORT_NEVER;
288 return se;
289 }
290
291 public static StandardException errorClose( Throwable t )
292 {
293 StandardException se = newException( SQLState.ERROR_CLOSE, t );
294 se.report = REPORT_NEVER;
295 return se;
296 }
297
298 /* 0 arguments */
299
300 public static StandardException newException(String messageID) {
301 return new StandardException(messageID);
302 }
303 public static StandardException newException(String messageID, Throwable t) {
304 return new StandardException(messageID, t, (Object[]) null);
305 }
306
307 /* 1 argument */
308
309 public static StandardException newException(String messageID, Object a1) {
310 Object[] oa = new Object[] {a1};
311 return new StandardException(messageID, oa);
312 }
313 public static StandardException newException(String messageID, Throwable t, Object a1) {
314 Object[] oa = new Object[] {a1};
315 return new StandardException(messageID, t, oa);
316 }
317
318 /* 2 arguments */
319
320 public static StandardException newException(String messageID, Object a1, Object a2) {
321 Object[] oa = new Object[] {a1, a2};
322 return new StandardException(messageID, oa);
323 }
324 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2) {
325 Object[] oa = new Object[] {a1, a2};
326 return new StandardException(messageID, t, oa);
327 }
328
329 /* 3 arguments */
330
331 public static StandardException newException(String messageID, Object a1, Object a2, Object a3) {
332 Object[] oa = new Object[] {a1, a2, a3};
333 return new StandardException(messageID, oa);
334 }
335 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3) {
336 Object[] oa = new Object[] {a1, a2, a3};
337 return new StandardException(messageID, t, oa);
338 }
339
340 /* 4 arguments */
341
342 public static StandardException newException(String messageID, Object a1, Object a2, Object a3, Object a4) {
343 Object[] oa = new Object[] {a1, a2, a3, a4};
344 return new StandardException(messageID, oa);
345 }
346 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3, Object a4) {
347 Object[] oa = new Object[] {a1, a2, a3, a4};
348 return new StandardException(messageID, t, oa);
349 }
350
351 /* 5 arguments */
352 public static StandardException newException(String messageID, Object a1, Object a2, Object a3, Object a4, Object a5) {
353 Object[] oa = new Object[] {a1, a2, a3, a4, a5};
354 return new StandardException(messageID, oa);
355 }
356 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3, Object a4, Object a5) {
357 Object[] oa = new Object[] {a1, a2, a3, a4, a5};
358 return new StandardException(messageID, t, oa);
359 }
360
361 /* 6 arguments */
362 public static StandardException newException(String messageID, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
363 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6};
364 return new StandardException(messageID, oa);
365 }
366 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
367 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6};
368 return new StandardException(messageID, t, oa);
369 }
370
371 /* 7 arguments */
372 public static StandardException newException(String messageID, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
373 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6, a7};
374 return new StandardException(messageID, oa);
375 }
376 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
377 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6, a7};
378 return new StandardException(messageID, t, oa);
379 }
380
381 /* 8 arguments */
382 public static StandardException newException(String messageID, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) {
383 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6, a7, a8};
384 return new StandardException(messageID, oa);
385 }
386 public static StandardException newException(String messageID, Throwable t, Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8) {
387 Object[] oa = new Object[] {a1, a2, a3, a4, a5, a6, a7, a8};
388 return new StandardException(messageID, t, oa);
389 }
390
391 /**
392 * Creates a new StandardException using message text that has already been localized.
393 *
394 * @param MessageID The SQLState and severity are derived from the ID. However the text message is not.
395 * @param t The Throwable that caused this exception, null if this exception was not caused by another Throwable.
396 * @param localizedMessage The message associated with this exception.
397 * <b>It is the caller's responsibility to ensure that this message is properly localized.</b>
398 *
399 * @see org.apache.derby.iapi.tools.i18n.LocalizedResource
400 */
401 public static StandardException newPreLocalizedException( String MessageID,
402 Throwable t,
403 String localizedMessage)
404 {
405 StandardException se = new StandardException( MessageID, localizedMessage);
406 if( t != null)
407 se.nestedException = t;
408 return se;
409 }
410
411 public static StandardException unexpectedUserException(Throwable t)
412 {
413 /*
414 ** If we have a SQLException that isn't a Util
415 ** (i.e. it didn't come from cloudscape), then we check
416 ** to see if it is a valid user defined exception range
417 ** (38001-38XXX). If so, then we convert it into a
418 ** StandardException without further ado.
419 */
420 if ((t instanceof SQLException) &&
421 !(t instanceof EmbedSQLException))
422 {
423 SQLException sqlex = (SQLException)t;
424 String state = sqlex.getSQLState();
425 if ((state != null) &&
426 (state.length() == 5) &&
427 state.startsWith("38") &&
428 !state.equals("38000"))
429 {
430 StandardException se = new StandardException(state, sqlex.getMessage());
431 if (sqlex.getNextException() != null)
432 {
433 se.setNestedException(sqlex.getNextException());
434 }
435 return se;
436 }
437 }
438
439 // Look for simple wrappers for 3.0.1 - will be cleaned up in main
440 if (t instanceof EmbedSQLException) {
441 EmbedSQLException csqle = (EmbedSQLException) t;
442 if (csqle.isSimpleWrapper()) {
443 Throwable wrapped = csqle.getJavaException();
444 if (wrapped instanceof StandardException)
445 return (StandardException) wrapped;
446 }
447 }
448
449
450 // no need to wrap a StandardException
451 if (t instanceof StandardException)
452 {
453 return (StandardException) t;
454 }
455 else
456 {
457 /*
458 **
459 ** The exception at this point could be a:
460 **
461 ** standard java exception, e.g. NullPointerException
462 ** SQL Exception - from some server-side JDBC
463 ** 3rd party exception - from some application
464 ** some cloudscape exception that is not a standard exception.
465 **
466 **
467 ** We don't want to call t.toString() here, because the JVM is
468 ** inconsistent about whether it includes a detail message
469 ** with some exceptions (esp. NullPointerException). In those
470 ** cases where there is a detail message, t.toString() puts in
471 ** a colon character, even when the detail message is blank.
472 ** So, we do our own string formatting here, including the colon
473 ** only when there is a non-blank message.
474 **
475 ** The above is because our test canons contain the text of
476 ** error messages.
477 **
478 ** In addition we don't want to place the class name in an
479 ** exception when the class is from cloudscape because
480 ** the class name changes in obfuscated builds. Thus for
481 ** exceptions that are in a package below com.ibm.db2j
482 ** we use toString(). If this returns an empty or null
483 ** then we use the class name to make tracking the problem
484 ** down easier, though the lack of a message should be seen
485 ** as a bug.
486 */
487 String detailMessage;
488 boolean cloudscapeException = false;
489
490 if (t instanceof EmbedSQLException) {
491 detailMessage = ((EmbedSQLException) t).toString();
492 cloudscapeException = true;
493 }
494 else {
495 detailMessage = t.getMessage();
496 }
497
498 if (detailMessage == null)
499 {
500 detailMessage = "";
501 } else {
502 detailMessage = detailMessage.trim();
503 }
504
505 // if no message, use the class name
506 if (detailMessage.length() == 0) {
507 detailMessage = t.getClass().getName();
508 }
509 else {
510
511 if (!cloudscapeException) {
512 detailMessage = t.getClass().getName() + ": " + detailMessage;
513 }
514 }
515
516 StandardException se =
517 newException(SQLState.LANG_UNEXPECTED_USER_EXCEPTION, t, detailMessage);
518 return se;
519 }
520 }
521
522 /**
523 Similar to unexpectedUserException but makes no assumtion about
524 when the execption is being called. The error is wrapped as simply
525 as possible.
526 */
527
528 public static StandardException plainWrapException(Throwable t) {
529
530 if (t instanceof StandardException)
531 return (StandardException) t;
532
533 if (t instanceof SQLException) {
534
535 SQLException sqle = (SQLException) t;
536
537 String sqlState = sqle.getSQLState();
538 if (sqlState != null) {
539
540 StandardException se = new StandardException(sqlState, "(" + sqle.getErrorCode() + ") " + sqle.getMessage());
541 sqle = sqle.getNextException();
542 if (sqle != null)
543 se.setNestedException(plainWrapException(sqle));
544 return se;
545 }
546 }
547
548 String detailMessage = t.getMessage();
549
550 if (detailMessage == null)
551 {
552 detailMessage = "";
553 } else {
554 detailMessage = detailMessage.trim();
555 }
556
557 StandardException se =
558 newException(SQLState.JAVA_EXCEPTION, t, detailMessage, t.getClass().getName());
559 return se;
560 }
561
562 /**
563 ** A special exception to close a session.
564 */
565 public static StandardException closeException() {
566 StandardException se = newException(SQLState.CLOSE_REQUEST);
567 se.setReport(REPORT_NEVER);
568 return se;
569 }
570 /*
571 ** Message handling
572 */
573
574 /**
575 The message stored in the super class Throwable must be set
576 up object creation. At this time we cannot get any information
577 about the object itself (ie. this) in order to determine the
578 natural language message. Ie. we need to class of the objec in
579 order to look up its message, but we can't get the class of the
580 exception before calling the super class message.
581 <P>
582 Thus the message stored by Throwable and obtained by the
583 getMessage() of Throwable (ie. super.getMessage() in this
584 class) is the message identifier. The actual text message
585 is stored in this class at the first request.
586
587 */
588
589 public String getMessage() {
590 if (textMessage == null)
591 textMessage = MessageService.getCompleteMessage(getMessageId(), getArguments());
592 return textMessage;
593 }
594
595 /**
596 Return the message identifier that is used to look up the
597 error message text in the messages.properties file.
598 */
599 public final String getMessageId() {
600 return super.getMessage();
601 }
602
603
604 /**
605 Get the error code for an error given a type. The value of
606 the property messageId.type will be returned, e.g.
607 deadlock.sqlstate.
608 */
609 public String getErrorProperty(String type) {
610 return getErrorProperty(getMessageId(), type);
611 }
612
613 /**
614 Don't print the class name in the toString() method.
615 */
616 public String toString() {
617 String msg = getMessage();
618
619 return "ERROR " + getSQLState() + ": " + msg;
620 }
621
622 /*
623 ** Static methods
624 */
625
626 private static String getErrorProperty(String messageId, String type) {
627 return MessageService.getProperty(messageId, type);
628 }
629
630 public static StandardException interrupt(InterruptedException ie) {
631 StandardException se = StandardException.newException(SQLState.CONN_INTERRUPT, ie);
632 return se;
633 }
634 /*
635 ** SQL warnings
636 */
637
638 public static SQLWarning newWarning(String messageId) {
639
640 return newWarningCommon( messageId, (Object[]) null );
641
642 }
643
644 public static SQLWarning newWarning(String messageId, Object a1) {
645
646 Object[] oa = new Object[] {a1};
647
648 return newWarningCommon( messageId, oa );
649 }
650
651 public static SQLWarning newWarning(String messageId, Object a1, Object a2) {
652
653 Object[] oa = new Object[] {a1, a2};
654
655 return newWarningCommon( messageId, oa );
656 }
657
658 private static SQLWarning newWarningCommon( String messageId, Object[] oa )
659 {
660 String message = MessageService.getCompleteMessage(messageId, oa);
661 String state = StandardException.getSQLStateFromIdentifier(messageId);
662 SQLWarning sqlw = new SQLWarning(message, state, ExceptionSeverity.WARNING_SEVERITY);
663
664 return sqlw;
665 }
666 }