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