KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > util > exception > ExceptionAnalyzer


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.util.exception;
16
17 import java.beans.BeanInfo JavaDoc;
18 import java.beans.IntrospectionException JavaDoc;
19 import java.beans.Introspector JavaDoc;
20 import java.beans.PropertyDescriptor JavaDoc;
21 import java.io.CharArrayWriter JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.LineNumberReader JavaDoc;
24 import java.io.PrintStream JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.io.StringReader JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.List JavaDoc;
30
31 /**
32  * Analyzes an exception, creating one or more {@link ExceptionDescription}s from it.
33  *
34  * @author Howard Lewis Ship
35  */

36
37 public class ExceptionAnalyzer
38 {
39     private final List JavaDoc exceptionDescriptions = new ArrayList JavaDoc();
40
41     private final List JavaDoc propertyDescriptions = new ArrayList JavaDoc();
42
43     private final CharArrayWriter JavaDoc writer = new CharArrayWriter JavaDoc();
44
45     private boolean exhaustive = false;
46
47     /**
48      * If true, then stack trace is extracted for each exception. If false, the default, then stack
49      * trace is extracted for only the deepest exception.
50      */

51
52     public boolean isExhaustive()
53     {
54         return exhaustive;
55     }
56
57     public void setExhaustive(boolean value)
58     {
59         exhaustive = value;
60     }
61
62     /**
63      * Analyzes the exceptions. This builds an {@link ExceptionDescription}for the exception. It
64      * also looks for a non-null {@link Throwable}property. If one exists, then a second
65      * {@link ExceptionDescription}is created. This continues until no more nested exceptions can
66      * be found.
67      * <p>
68      * The description includes a set of name/value properties (as {@link ExceptionProperty})
69      * object. This list contains all non-null properties that are not, themselves,
70      * {@link Throwable}.
71      * <p>
72      * The name is the display name (not the logical name) of the property. The value is the
73      * <code>toString()</code> value of the property. Only properties defined in subclasses of
74      * {@link Throwable}are included.
75      * <p>
76      * A future enhancement will be to alphabetically sort the properties by name.
77      */

78
79     public ExceptionDescription[] analyze(Throwable JavaDoc exception)
80     {
81         try
82         {
83
84             while (exception != null)
85             {
86                 exception = buildDescription(exception);
87             }
88
89             ExceptionDescription[] result = new ExceptionDescription[exceptionDescriptions.size()];
90
91             return (ExceptionDescription[]) exceptionDescriptions.toArray(result);
92         }
93         finally
94         {
95             exceptionDescriptions.clear();
96             propertyDescriptions.clear();
97
98             writer.reset();
99         }
100     }
101
102     protected Throwable JavaDoc buildDescription(Throwable JavaDoc exception)
103     {
104         BeanInfo JavaDoc info;
105         Class JavaDoc exceptionClass;
106         ExceptionProperty property;
107         PropertyDescriptor JavaDoc[] descriptors;
108         PropertyDescriptor JavaDoc descriptor;
109         Throwable JavaDoc next = null;
110         int i;
111         Object JavaDoc value;
112         Method JavaDoc method;
113         ExceptionProperty[] properties;
114         ExceptionDescription description;
115         String JavaDoc stringValue;
116         String JavaDoc message;
117         String JavaDoc[] stackTrace = null;
118
119         propertyDescriptions.clear();
120
121         message = exception.getMessage();
122         exceptionClass = exception.getClass();
123
124         // Get properties, ignoring those in Throwable and higher
125
// (including the 'message' property).
126

127         try
128         {
129             info = Introspector.getBeanInfo(exceptionClass, Throwable JavaDoc.class);
130         }
131         catch (IntrospectionException JavaDoc e)
132         {
133             return null;
134         }
135
136         descriptors = info.getPropertyDescriptors();
137
138         for (i = 0; i < descriptors.length; i++)
139         {
140             descriptor = descriptors[i];
141
142             method = descriptor.getReadMethod();
143             if (method == null)
144                 continue;
145
146             try
147             {
148                 value = method.invoke(exception, null);
149             }
150             catch (Exception JavaDoc e)
151             {
152                 continue;
153             }
154
155             if (value == null)
156                 continue;
157
158             // Some annoying exceptions duplicate the message property
159
// (I'm talking to YOU SAXParseException), so just edit that out.
160

161             if (message != null && message.equals(value))
162                 continue;
163
164             // Skip Throwables ... but the first non-null
165
// found is the next exception. We kind of count
166
// on there being no more than one Throwable
167
// property per Exception.
168

169             if (value instanceof Throwable JavaDoc)
170             {
171                 if (next == null)
172                     next = (Throwable JavaDoc) value;
173
174                 continue;
175             }
176
177             stringValue = value.toString().trim();
178
179             if (stringValue.length() == 0)
180                 continue;
181
182             property = new ExceptionProperty(descriptor.getDisplayName(), value);
183
184             propertyDescriptions.add(property);
185         }
186
187         // If exhaustive, or in the deepest exception (where there's no next)
188
// the extract the stack trace.
189

190         if (next == null || exhaustive)
191             stackTrace = getStackTrace(exception);
192
193         // Would be nice to sort the properties here.
194

195         properties = new ExceptionProperty[propertyDescriptions.size()];
196
197         ExceptionProperty[] propArray = (ExceptionProperty[]) propertyDescriptions
198                 .toArray(properties);
199
200         description = new ExceptionDescription(exceptionClass.getName(), message, propArray,
201                 stackTrace);
202
203         exceptionDescriptions.add(description);
204
205         return next;
206     }
207
208     /**
209      * Gets the stack trace for the exception, and converts it into an array of strings.
210      * <p>
211      * This involves parsing the string generated indirectly from
212      * <code>Throwable.printStackTrace(PrintWriter)</code>. This method can get confused if the
213      * message (presumably, the first line emitted by printStackTrace()) spans multiple lines.
214      * <p>
215      * Different JVMs format the exception in different ways.
216      * <p>
217      * A possible expansion would be more flexibility in defining the pattern used. Hopefully all
218      * 'mainstream' JVMs are close enough for this to continue working.
219      */

220
221     protected String JavaDoc[] getStackTrace(Throwable JavaDoc exception)
222     {
223         writer.reset();
224
225         PrintWriter JavaDoc printWriter = new PrintWriter JavaDoc(writer);
226
227         exception.printStackTrace(printWriter);
228
229         printWriter.close();
230
231         String JavaDoc fullTrace = writer.toString();
232
233         writer.reset();
234
235         // OK, the trick is to convert the full trace into an array of stack frames.
236

237         StringReader JavaDoc stringReader = new StringReader JavaDoc(fullTrace);
238         LineNumberReader JavaDoc lineReader = new LineNumberReader JavaDoc(stringReader);
239         int lineNumber = 0;
240         List JavaDoc frames = new ArrayList JavaDoc();
241
242         try
243         {
244             while (true)
245             {
246                 String JavaDoc line = lineReader.readLine();
247
248                 if (line == null)
249                     break;
250
251                 // Always ignore the first line.
252

253                 if (++lineNumber == 1)
254                     continue;
255
256                 frames.add(stripFrame(line));
257             }
258
259             lineReader.close();
260         }
261         catch (IOException JavaDoc ex)
262         {
263             // Not likely to happen with this particular set
264
// of readers.
265
}
266
267         String JavaDoc result[] = new String JavaDoc[frames.size()];
268
269         return (String JavaDoc[]) frames.toArray(result);
270     }
271
272     private static final int SKIP_LEADING_WHITESPACE = 0;
273
274     private static final int SKIP_T = 1;
275
276     private static final int SKIP_OTHER_WHITESPACE = 2;
277
278     /**
279      * Sun's JVM prefixes each line in the stack trace with " <tab>at ", other JVMs don't. This
280      * method looks for and strips such stuff.
281      */

282
283     private String JavaDoc stripFrame(String JavaDoc frame)
284     {
285         char array[] = frame.toCharArray();
286
287         int i = 0;
288         int state = SKIP_LEADING_WHITESPACE;
289         boolean more = true;
290
291         while (more)
292         {
293             // Ran out of characters to skip? Return the empty string.
294

295             if (i == array.length)
296                 return "";
297
298             char ch = array[i];
299
300             switch (state)
301             {
302                 // Ignore whitespace at the start of the line.
303

304                 case SKIP_LEADING_WHITESPACE:
305
306                     if (Character.isWhitespace(ch))
307                     {
308                         i++;
309                         continue;
310                     }
311
312                     if (ch == 'a')
313                     {
314                         state = SKIP_T;
315                         i++;
316                         continue;
317                     }
318
319                     // Found non-whitespace, not 'a'
320
more = false;
321                     break;
322
323                 // Skip over the 't' after an 'a'
324

325                 case SKIP_T:
326
327                     if (ch == 't')
328                     {
329                         state = SKIP_OTHER_WHITESPACE;
330                         i++;
331                         continue;
332                     }
333
334                     // Back out the skipped-over 'a'
335

336                     i--;
337                     more = false;
338                     break;
339
340                 // Skip whitespace between 'at' and the name of the class
341

342                 case SKIP_OTHER_WHITESPACE:
343
344                     if (Character.isWhitespace(ch))
345                     {
346                         i++;
347                         continue;
348                     }
349
350                     // Not whitespace
351
more = false;
352                     break;
353             }
354
355         }
356
357         // Found nothing to strip out.
358

359         if (i == 0)
360             return frame;
361
362         return frame.substring(i);
363     }
364
365     /**
366      * Produces a text based exception report to the provided stream.
367      */

368
369     public void reportException(Throwable JavaDoc exception, PrintStream JavaDoc stream)
370     {
371         int i;
372         int j;
373         ExceptionDescription[] descriptions;
374         ExceptionProperty[] properties;
375         String JavaDoc[] stackTrace;
376         String JavaDoc message;
377
378         descriptions = analyze(exception);
379
380         for (i = 0; i < descriptions.length; i++)
381         {
382             message = descriptions[i].getMessage();
383
384             if (message == null)
385                 stream.println(descriptions[i].getExceptionClassName());
386             else
387                 stream.println(descriptions[i].getExceptionClassName() + ": "
388                         + descriptions[i].getMessage());
389
390             properties = descriptions[i].getProperties();
391
392             for (j = 0; j < properties.length; j++)
393                 stream.println(" " + properties[j].getName() + ": " + properties[j].getValue());
394
395             // Just show the stack trace on the deepest exception.
396

397             if (i + 1 == descriptions.length)
398             {
399                 stackTrace = descriptions[i].getStackTrace();
400
401                 for (j = 0; j < stackTrace.length; j++)
402                     stream.println(stackTrace[j]);
403             }
404             else
405                 stream.println();
406         }
407     }
408
409 }
Popular Tags