Source code: org/jdom/JDOMException.java
1 /*--
2
3 $Id: JDOMException.java,v 1.23 2004/02/27 11:32:57 jhunter Exp $
4
5 Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 1. Redistributions of source code must retain the above copyright
13 notice, this list of conditions, and the following disclaimer.
14
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions, and the disclaimer that follows
17 these conditions in the documentation and/or other materials
18 provided with the distribution.
19
20 3. The name "JDOM" must not be used to endorse or promote products
21 derived from this software without prior written permission. For
22 written permission, please contact <request_AT_jdom_DOT_org>.
23
24 4. Products derived from this software may not be called "JDOM", nor
25 may "JDOM" appear in their name, without prior written permission
26 from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28 In addition, we request (but do not require) that you include in the
29 end-user documentation provided with the redistribution and/or in the
30 software itself an acknowledgement equivalent to the following:
31 "This product includes software developed by the
32 JDOM Project (http://www.jdom.org/)."
33 Alternatively, the acknowledgment may be graphical using the logos
34 available at http://www.jdom.org/images/logos.
35
36 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 SUCH DAMAGE.
48
49 This software consists of voluntary contributions made by many
50 individuals on behalf of the JDOM Project and was originally
51 created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52 Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53 on the JDOM Project, please see <http://www.jdom.org/>.
54
55 */
56
57 package org.jdom;
58
59 import java.io.*;
60 import java.lang.reflect.*;
61 import java.rmi.*;
62 import java.sql.*;
63
64 import org.xml.sax.*;
65
66 /**
67 * The top level exception that JDOM classes can throw. Its subclasses add
68 * specificity to the problems that can occur using JDOM. This single exception
69 * can be caught to handle all JDOM specific problems (some methods may throw
70 * {@link java.io.IOException} and such).
71 *
72 * @version $Revision: 1.23 $, $Date: 2004/02/27 11:32:57 $
73 * @author Brett McLaughlin
74 * @author Jason Hunter
75 */
76 public class JDOMException extends Exception {
77
78 private static final String CVS_ID =
79 "@(#) $RCSfile: JDOMException.java,v $ $Revision: 1.23 $ $Date: 2004/02/27 11:32:57 $ $Name: jdom_1_0 $";
80
81 /** A wrapped <code>Throwable</code> */
82 private Throwable cause;
83
84 /**
85 * This will create an <code>Exception</code>.
86 */
87 public JDOMException() {
88 super("Error occurred in JDOM application.");
89 }
90
91 /**
92 * This will create an <code>Exception</code> with the given message.
93 *
94 * @param message <code>String</code> message indicating
95 * the problem that occurred.
96 */
97 public JDOMException(String message) {
98 super(message);
99 }
100
101 /**
102 * This will create an <code>Exception</code> with the given message
103 * and wrap another <code>Exception</code>. This is useful when
104 * the originating <code>Exception</code> should be held on to.
105 *
106 * @param message <code>String</code> message indicating
107 * the problem that occurred.
108 * @param cause <code>Throwable</code> that caused this
109 * to be thrown.
110 */
111 public JDOMException(String message, Throwable cause) {
112 super(message);
113 this.cause = cause;
114 }
115
116 /**
117 * Intializes the cause of this exception to be the specified value.
118 *
119 * @param cause <code>Throwable</code> that caused this
120 * to be thrown.
121 * @return a pointer to this throwable
122 */
123 // Created to match the JDK 1.4 Throwable method.
124 public Throwable initCause(Throwable cause) {
125 this.cause = cause;
126 return this;
127 }
128
129 /**
130 * This returns the message for the <code>Exception</code>. If
131 * there are one or more nested exceptions, their messages
132 * are appended.
133 *
134 * @return <code>String</code> - message for <code>Exception</code>.
135 */
136 public String getMessage() {
137 // Get this exception's message.
138 String msg = super.getMessage();
139
140 Throwable parent = this;
141 Throwable child;
142
143 // Look for nested exceptions.
144 while((child = getNestedException(parent)) != null) {
145 // Get the child's message.
146 String msg2 = child.getMessage();
147
148 // Special case: If a SAXException has no message of its own, but has a
149 // nested exception, then it returns the nested exception's message as its
150 // message. We don't want to add that message twice.
151 if (child instanceof SAXException) {
152 Throwable grandchild = ((SAXException)child).getException();
153 // If the SAXException tells us that it's message is identical to
154 // its nested exception's message, then we skip it, so we don't
155 // add it twice.
156 if (grandchild != null && msg2 != null && msg2.equals(grandchild.getMessage())) {
157 msg2 = null;
158 }
159 }
160
161 // If we found a message for the child exception, we append it.
162 if (msg2 != null) {
163 if (msg != null) {
164 msg += ": " + msg2;
165 } else {
166 msg = msg2;
167 }
168 }
169
170 // Any nested JDOMException will append its own children,
171 // so we need to break out of here.
172 if (child instanceof JDOMException) {
173 break;
174 }
175 parent = child;
176 }
177
178 // Return the completed message.
179 return msg;
180 }
181
182 /**
183 * This prints the stack trace of the <code>Exception</code>. If
184 * there is a root cause, the stack trace of the root
185 * <code>Exception</code> is printed right after.
186 */
187 public void printStackTrace() {
188 // Print the stack trace for this exception.
189 super.printStackTrace();
190
191 Throwable parent = this;
192 Throwable child;
193
194 // Print the stack trace for each nested exception.
195 while((child = getNestedException(parent)) != null) {
196 System.err.print("Caused by: ");
197 child.printStackTrace();
198 // Any nested JDOMException will print its own children,
199 // so we need to break out of here.
200 if (child instanceof JDOMException) {
201 break;
202 }
203 parent = child;
204 }
205 }
206
207 /**
208 * Prints the stack trace of the <code>Exception</code> to the given
209 * PrintStream. If there is a root cause, the stack trace of the root
210 * <code>Exception</code> is printed right after.
211 *
212 * @param s PrintStream to print to
213 */
214 public void printStackTrace(PrintStream s) {
215 // Print the stack trace for this exception.
216 super.printStackTrace(s);
217
218 Throwable parent = this;
219 Throwable child;
220
221 // Print the stack trace for each nested exception.
222 while((child = getNestedException(parent)) != null) {
223 s.print("Caused by: ");
224 child.printStackTrace(s);
225 // Any nested JDOMException will print its own children,
226 // so we need to break out of here.
227 if (child instanceof JDOMException) {
228 break;
229 }
230 parent = child;
231 }
232 }
233
234 /**
235 * Prints the stack trace of the <code>Exception</code> to the given
236 * PrintWriter. If there is a root cause, the stack trace of the root
237 * <code>Exception</code> is printed right after.
238 *
239 * @param w PrintWriter to print to
240 */
241 public void printStackTrace(PrintWriter w) {
242 // Print the stack trace for this exception.
243 super.printStackTrace(w);
244
245 Throwable parent = this;
246 Throwable child;
247
248 // Print the stack trace for each nested exception.
249 while((child = getNestedException(parent)) != null) {
250 w.print("Caused by: ");
251 child.printStackTrace(w);
252 // Any nested JDOMException will print its own children,
253 // so we need to break out of here.
254 if (child instanceof JDOMException) {
255 break;
256 }
257 parent = child;
258 }
259 }
260
261 /**
262 * This will return the root cause <code>Throwable</code>, or null
263 * if one does not exist.
264 *
265 * @return <code>Throwable</code> - the wrapped <code>Throwable</code>.
266 */
267 public Throwable getCause() {
268 return cause;
269 }
270
271 // If this Throwable has a nested (child) exception, then we return it.
272 // Otherwise we return null.
273 private static Throwable getNestedException(Throwable parent) {
274 if (parent instanceof JDOMException) {
275 return ((JDOMException)parent).getCause();
276 }
277
278 if (parent instanceof SAXException) {
279 return ((SAXException)parent).getException();
280 }
281
282 if (parent instanceof SQLException) {
283 return ((SQLException)parent).getNextException();
284 }
285
286 if (parent instanceof InvocationTargetException) {
287 return ((InvocationTargetException)parent).getTargetException();
288 }
289
290 if (parent instanceof ExceptionInInitializerError) {
291 return ((ExceptionInInitializerError)parent).getException();
292 }
293
294 if (parent instanceof RemoteException) {
295 return ((RemoteException)parent).detail;
296 }
297
298 // These classes are not part of standard JDK 1.1 or 1.2, so we
299 // use reflection to access them.
300
301 Throwable nestedException = getNestedException(parent, "javax.naming.NamingException", "getRootCause");
302 if (nestedException != null) {
303 return nestedException;
304 }
305
306 nestedException = getNestedException(parent, "javax.servlet.ServletException", "getRootCause");
307 if (nestedException != null) {
308 return nestedException;
309 }
310
311 return null;
312 }
313
314 // This method uses reflection to obtain the nest exception of a Throwable. We use reflection
315 // because the desired class may not exist in the currently-running VM.
316 private static Throwable getNestedException(
317 Throwable parent, String className, String methodName) {
318 try {
319 // See if this Throwable is of the desired type, by using isAssignableFrom().
320 Class testClass = Class.forName(className);
321 Class objectClass = parent.getClass();
322 if (testClass.isAssignableFrom(objectClass)) {
323 // Use reflection to call the specified method.
324 Class[] argClasses = new Class[0];
325 Method method = testClass.getMethod(methodName, argClasses);
326 Object[] args = new Object[0];
327 return (Throwable)method.invoke(parent, args);
328 }
329 }
330 catch(Exception ex) {
331 // Most likely, the desired class is not available in this VM. That's fine.
332 // Even if it's caused by something else, we don't want to display an error
333 // here, since we're already in the process of trying to display the original
334 // error - another error here will just confuse things.
335 }
336
337 return null;
338 }
339 }