KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > StackTrace


1 /***************************************
2  * *
3  * JBoss: The OpenSource J2EE WebOS *
4  * *
5  * Distributable under LGPL license. *
6  * See terms of license at gnu.org. *
7  * *
8  ***************************************/

9
10 package org.jboss.util;
11
12 import java.io.IOException JavaDoc;
13 import java.io.ByteArrayInputStream JavaDoc;
14 import java.io.ByteArrayOutputStream JavaDoc;
15 import java.io.PrintWriter JavaDoc;
16 import java.io.PrintStream JavaDoc;
17 import java.io.InputStreamReader JavaDoc;
18 import java.io.BufferedReader JavaDoc;
19 import java.io.Serializable JavaDoc;
20
21 import java.util.List JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.ArrayList JavaDoc;
24
25 import org.jboss.util.stream.Printable;
26
27 /**
28  * Provides access to the current stack trace by parsing the output of
29  * <code>Throwable.printStackTrace()</code>.
30  *
31  * @version <tt>$Revision: 1.1.28.2 $</tt>
32  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
33  */

34 public final class StackTrace
35    implements Serializable JavaDoc, Cloneable JavaDoc, Printable
36 {
37    static final long serialVersionUID = -6077429788585907990L;
38    /** Parse all entries */
39    public static final int UNLIMITED = 0;
40
41    /** Empty prefix constant */
42    private static final String JavaDoc EMPTY_PREFIX = "";
43
44    /** List of <tt>StackTrace.Entry</tt> elements */
45    protected final List JavaDoc stack;
46
47    /**
48     * Initialize a <tt>StackTrace</tt>.
49     *
50     * @param detail Detail throwable to determine stack entries from.
51     * @param level Number of levels to go down into the trace.
52     * @param limit The maximum number of entries to parse (does not
53     * include skipped levels or the description).
54     * A value <= zero results in all entries being parsed.
55     *
56     * @throws IllegalArgumentException Invalid level or limit.
57     * @throws NestedRuntimeException Failed to create Parser.
58     * @throws NestedRuntimeException Failed to parse stack trace.
59     */

60    public StackTrace(final Throwable JavaDoc detail,
61                      final int level,
62                      final int limit)
63    {
64       if (level < 0)
65          throw new IllegalArgumentException JavaDoc("level < 0");
66       if (limit < 0)
67          throw new IllegalArgumentException JavaDoc("limit < 0");
68
69       try {
70          Parser parser = Parser.getInstance();
71          stack = parser.parse(detail, level, limit);
72       }
73       catch (InstantiationException JavaDoc e) {
74          throw new NestedRuntimeException(e);
75       }
76       catch (IOException JavaDoc e) {
77          throw new NestedRuntimeException(e);
78       }
79    }
80
81    /**
82     * Initialize a <tt>StackTrace</tt>.
83     *
84     * @param detail Detail throwable to determine stack entries from.
85     * @param level Number of levels to go down into the trace.
86     *
87     * @throws IllegalArgumentException Invalid level.
88     * @throws NestedRuntimeException Failed to create Parser.
89     * @throws NestedRuntimeException Failed to parse stack trace.
90     */

91    public StackTrace(final Throwable JavaDoc detail, final int level) {
92       this(detail, level, 0);
93    }
94
95    /**
96     * Initialize a <tt>StackTrace</tt>.
97     *
98     * @param detail Detail throwable to determine stack entries from.
99     * @param level Number of levels to go down into the trace.
100     *
101     * @throws NestedRuntimeException Failed to create Parser.
102     * @throws NestedRuntimeException Failed to parse stack trace.
103     */

104    public StackTrace(final Throwable JavaDoc detail) {
105       this(detail, 0, 0);
106    }
107
108    /**
109     * Construct a <tt>StackTrace</tt>.
110     *
111     * @param level Number of levels to go down into the trace.
112     * @param limit The maximum number of entries to parse (does not
113     * include skipped levels or the description).
114     * A value <= zero results in all entries being parsed.
115     */

116    public StackTrace(final int level, final int limit) {
117       this(new Throwable JavaDoc(), level + 1, limit);
118    }
119
120    /**
121     * Construct a <tt>StackTrace</tt>.
122     *
123     * @param level Number of levels to go down into the trace.
124     */

125    public StackTrace(final int level) {
126       this(new Throwable JavaDoc(), level + 1, UNLIMITED);
127    }
128
129    /**
130     * Construct a <tt>StackTrace</tt>.
131     */

132    public StackTrace() {
133       this(new Throwable JavaDoc(), 1, UNLIMITED);
134    }
135
136    /**
137     * Sub-trace constructor.
138     */

139    protected StackTrace(final List JavaDoc stack) {
140       this.stack = stack;
141    }
142
143    /**
144     * Check if the given object is equals to this.
145     *
146     * @param obj Object to test equality with.
147     * @return True if object is equal to this.
148     */

149    public boolean equals(final Object JavaDoc obj) {
150       if (obj == this) return true;
151
152       if (obj != null && obj.getClass() == getClass()) {
153          return ((StackTrace)obj).stack.equals(stack);
154       }
155       
156       return false;
157    }
158    
159    /**
160     * Returns a shallow cloned copy of this object.
161     *
162     * @return A shallow cloned copy of this object.
163     */

164    public Object JavaDoc clone() {
165       try {
166          return super.clone();
167       }
168       catch (CloneNotSupportedException JavaDoc e) {
169          throw new InternalError JavaDoc();
170       }
171    }
172
173    /**
174     * Returns the stack trace entry for the element at the given level.
175     *
176     * @param level Number of levels.
177     * @return Stack trace entry.
178     *
179     * @throws IndexOutOfBoundsException Invalid level index.
180     */

181    public Entry getEntry(final int level) {
182       return (Entry)stack.get(level);
183    }
184
185    /**
186     * Returns the stack trace entry for the calling method.
187     *
188     * @return Stack trace entry for calling method.
189     */

190    public Entry getCallerEntry() {
191       return getEntry(0);
192    }
193
194    /**
195     * Return the root entry for this stack trace.
196     *
197     * @return Stack trace entry for the root calling method.
198     */

199    public Entry getRootEntry() {
200       return getEntry(stack.size() - 1);
201    }
202
203    /**
204     * Returns a sub trace starting at the the given level.
205     *
206     * @param level Number of levels.
207     * @return Sub-trace.
208     */

209    public StackTrace getSubTrace(final int level) {
210       return new StackTrace(stack.subList(level, stack.size()));
211    }
212
213    /**
214     * Returns a sub trace starting at the the given level.
215     *
216     * @param level Number of levels.
217     * @param limit Limit the sub-trace. If there are less entries
218     * than the limit, the limit has no effect.
219     * @return Sub-trace.
220     */

221    public StackTrace getSubTrace(final int level, int limit) {
222       if (limit > 0) {
223          limit = Math.min(level + limit, stack.size());
224       }
225       else {
226          limit = stack.size();
227       }
228       // limit is now the ending index of the stack to return
229

230       return new StackTrace(stack.subList(level, limit));
231    }
232
233    /**
234     * Returns the stack trace starting at the calling method.
235     *
236     * @return Stack trace for calling method.
237     */

238    public StackTrace getCallerTrace() {
239       return getSubTrace(1);
240    }
241
242    /**
243     * Print this stack trace.
244     *
245     * @param writer The writer to print to.
246     * @param prefix Stack trace entry prefix.
247     */

248    public void print(final PrintWriter JavaDoc writer, final String JavaDoc prefix) {
249       Iterator JavaDoc iter = stack.iterator();
250       while (iter.hasNext()) {
251          Entry entry = (Entry)iter.next();
252          entry.print(writer, prefix);
253       }
254    }
255
256    /**
257     * Print this stack trace.
258     *
259     * @param writer The writer to print to.
260     */

261    public void print(final PrintWriter JavaDoc writer) {
262       print(writer, EMPTY_PREFIX);
263    }
264
265    /**
266     * Print this stack trace.
267     *
268     * @param stream The stream to print to.
269     * @param prefix Stack trace entry prefix.
270     */

271    public void print(final PrintStream JavaDoc stream, final String JavaDoc prefix) {
272       Iterator JavaDoc iter = stack.iterator();
273       while (iter.hasNext()) {
274          Entry entry = (Entry)iter.next();
275          entry.print(stream, prefix);
276       }
277    }
278
279    /**
280     * Print this stack trace.
281     *
282     * @param stream The stream to print to.
283     */

284    public void print(final PrintStream JavaDoc stream) {
285       print(stream, EMPTY_PREFIX);
286    }
287
288    /**
289     * Print this stack trace to <code>System.err</code>.
290     *
291     * @param prefix Stack trace entry prefix.
292     */

293    public void print(final String JavaDoc prefix) {
294       print(System.err, prefix);
295    }
296
297    /**
298     * Print this stack trace to <code>System.err</code>.
299     */

300    public void print() {
301       print(System.err);
302    }
303
304    /**
305     * Returns an iterator over all of the entries in the stack trace.
306     *
307     * @return An iterator over all of the entries in the stack trace.
308     */

309    public Iterator JavaDoc iterator() {
310       return stack.iterator();
311    }
312
313    /**
314     * Returns the number of entries (or size) of the stack trace.
315     *
316     * @return The number of entries (or size) of the stack trace.
317     */

318    public int size() {
319       return stack.size();
320    }
321
322
323    /////////////////////////////////////////////////////////////////////////
324
// Static Access //
325
/////////////////////////////////////////////////////////////////////////
326

327    /**
328     * Returns a stack trace entry for the current position in the stack.
329     *
330     * <p>The current entry refers to the method that has invoked
331     * {@link #currentEntry()}.
332     *
333     * @return Current position in the stack.
334     */

335    public static final Entry currentEntry() {
336       return new StackTrace().getCallerEntry();
337    }
338
339    /**
340     * Returns a stack trace entry for the calling methods current position
341     * in the stack.
342     *
343     * <p>Calling method in this case refers to the method that has called
344     * the method which invoked {@link #callerEntry()}.
345     *
346     * @return Calling methods current position in the stack.
347     *
348     * @throws IndexOutOfBoundsException The current entry is at bottom
349     * of the stack.
350     */

351    public static final Entry callerEntry() {
352       return new StackTrace(1).getCallerEntry();
353    }
354
355    /**
356     * Returns a stack trace entry for the root calling method of the current
357     * execution thread.
358     *
359     * @return Stack trace entry for the root calling method.
360     */

361    public static final Entry rootEntry() {
362       return new StackTrace().getRootEntry();
363    }
364
365
366    /////////////////////////////////////////////////////////////////////////
367
// StackTrace Entry //
368
/////////////////////////////////////////////////////////////////////////
369

370    /**
371     * A stack trace entry.
372     */

373    public static final class Entry
374       implements Cloneable JavaDoc, Serializable JavaDoc, Printable
375    {
376       static final long serialVersionUID = 7013023772762859280L;
377       /** Unknown element token */
378       public static final String JavaDoc UNKNOWN = "<unknown>";
379
380       /** Default package token */
381       public static final String JavaDoc DEFAULT = "<default>";
382
383       /** The fully qualified class name for this entry */
384       protected String JavaDoc className = UNKNOWN;
385
386       /** The method name for this entry */
387       protected String JavaDoc methodName = UNKNOWN;
388
389       /** The source file name for this entry */
390       protected String JavaDoc sourceFileName = UNKNOWN;
391
392       /** The source file line number for this entry */
393       protected String JavaDoc lineNumber = UNKNOWN;
394       
395       /**
396        * Construct a new StackTrace entry.
397        *
398        * @param className Fully qualified class name.
399        * @param methodName Method name.
400        * @param sourceFileName Source file name.
401        * @param lineNumber Source file line number.
402        */

403       public Entry(final String JavaDoc className,
404                    final String JavaDoc methodName,
405                    final String JavaDoc sourceFileName,
406                    final String JavaDoc lineNumber)
407       {
408          this.className = className;
409          this.methodName = methodName;
410          this.sourceFileName = sourceFileName;
411          this.lineNumber = lineNumber;
412       }
413
414       /**
415        * Construct a new StackTrace entry.
416        *
417        * @param raw The raw stack trace entry.
418        */

419       public Entry(final String JavaDoc raw) {
420          // parse the raw string
421
parse(raw);
422       }
423
424       /**
425        * Parse a raw stack trace entry.
426        *
427        * @param raw Raw stack trace.
428        */

429       protected void parse(final String JavaDoc raw) {
430          // get the class and method names
431
int j = raw.indexOf("at ") + 3;
432          int i = raw.indexOf("(");
433          if (j == -1 || i == -1) return;
434
435          String JavaDoc temp = raw.substring(j, i);
436          i = temp.lastIndexOf(".");
437          if (i == -1) return;
438
439          className = temp.substring(0, i);
440          methodName = temp.substring(i + 1);
441
442          // get the source file name
443
j = raw.indexOf("(") + 1;
444          i = raw.indexOf(":");
445          if (j == -1) return;
446          if (i == -1) {
447             i = raw.indexOf(")");
448             if (i == -1) return;
449             sourceFileName = raw.substring(j, i);
450          }
451          else {
452             sourceFileName = raw.substring(j, i);
453             // get the line number
454
j = i + 1;
455             i = raw.lastIndexOf(")");
456             if (i != -1)
457                lineNumber = raw.substring(j, i);
458             else
459                lineNumber = raw.substring(j);
460          }
461       }
462
463       /**
464        * Get the class name for this entry.
465        *
466        * @return The class name for this entry.
467        */

468       public String JavaDoc getClassName() {
469          return className;
470       }
471
472       /**
473        * Get the short class name for this entry.
474        *
475        * <p>This is a macro for
476        * <code>Classes.stripPackageName(entry.getClassName())</code></p>
477        *
478        * @return The short class name for this entry.
479        */

480       public String JavaDoc getShortClassName() {
481          return Classes.stripPackageName(className);
482       }
483
484       /**
485        * Get the method name for this entry.
486        *
487        * @return The method name for this entry.
488        */

489       public String JavaDoc getMethodName() {
490          return methodName;
491       }
492
493       /**
494        * Get the fully qualified method name for this entry.
495        *
496        * <p>This is a macro for
497        * <code>entry.getClassName() + "." + entry.getMethodName()</code></p>
498        *
499        * @return The fully qualified method name for this entry.
500        */

501       public String JavaDoc getFullMethodName() {
502          return className + "." + methodName;
503       }
504
505       /**
506        * Get the source file name for this entry.
507        *
508        * @return The source file name for this entry.
509        */

510       public String JavaDoc getSourceFileName() {
511          return sourceFileName;
512       }
513
514       /**
515        * Get the source file line number for this entry.
516        *
517        * @return The source file line number for this entry.
518        */

519       public String JavaDoc getLineNumber() {
520          return lineNumber;
521       }
522
523       /**
524        * Return a string representation of this with the given prefix.
525        *
526        * @param prefix Prefix for returned string.
527        * @return A string in the format of
528        * <code>prefixclassName.methodName(sourceFileName:lineNumber)</code>
529        * or <code>prefixclassName.methodName(sourceFileName)</code> if there
530        * is no line number.
531        */

532       public String JavaDoc toString(final String JavaDoc prefix) {
533          StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
534
535          if (prefix != null)
536             buff.append(prefix);
537
538          buff.append(className).append(".").append(methodName)
539             .append("(").append(sourceFileName);
540          
541          if (! lineNumber.equals(UNKNOWN))
542             buff.append(":").append(lineNumber);
543
544          buff.append(")");
545
546          return buff.toString();
547       }
548
549       /**
550        * Return a string representation of this.
551        *
552        * @return A string in the format of
553        * <code>className.methodName(sourceFileName:lineNumber)</code>
554        */

555       public String JavaDoc toString() {
556          return toString(EMPTY_PREFIX);
557       }
558
559       /**
560        * Return the hash code of this object.
561        *
562        * @return The hash code of this object.
563        */

564       public int hashCode() {
565          return HashCode.generate(new String JavaDoc[] {
566             className, methodName, sourceFileName, lineNumber,
567          });
568       }
569
570       /**
571        * Check the equality of a given object with this.
572        *
573        * @param obj Object to test equality with.
574        * @return True if the given object is equal to this.
575        */

576       public boolean equals(final Object JavaDoc obj) {
577          if (obj == this) return true;
578
579          if (obj != null && obj.getClass() == getClass()) {
580             Entry entry = (Entry)obj;
581             return
582                entry.className.equals(className) &&
583                entry.methodName.equals(methodName) &&
584                entry.sourceFileName.equals(sourceFileName) &&
585                entry.lineNumber.equals(lineNumber);
586          }
587
588          return false;
589       }
590
591       /**
592        * Returns a shallow cloned copy of this object.
593        *
594        * @return A shallow cloned copy of this object.
595        */

596       public Object JavaDoc clone() {
597          try {
598             return super.clone();
599          }
600          catch (CloneNotSupportedException JavaDoc e) {
601             throw new InternalError JavaDoc();
602          }
603       }
604
605       /**
606        * Print this stack trace entry.
607        *
608        * @param writer The writer to print to.
609        * @param prefix Prefix for string conversion.
610        */

611       public void print(final PrintWriter JavaDoc writer, final String JavaDoc prefix) {
612          writer.println(this.toString(prefix));
613       }
614
615       /**
616        * Print this stack trace entry.
617        *
618        * @param writer The writer to print to.
619        */

620       public void print(final PrintWriter JavaDoc writer) {
621          writer.println(this.toString());
622       }
623
624       /**
625        * Print this stack trace entry.
626        *
627        * @param stream The stream to print to.
628        * @param prefix Prefix for string conversion.
629        */

630       public void print(final PrintStream JavaDoc stream, final String JavaDoc prefix) {
631          stream.println(this.toString(prefix));
632       }
633
634       /**
635        * Print this stack trace entry.
636        *
637        * @param stream The stream to print to.
638        */

639       public void print(final PrintStream JavaDoc stream) {
640          stream.println(this.toString());
641       }
642
643       /**
644        * Print this stack trace entry to <code>System.err<code>.
645        *
646        * @param prefix Prefix for string conversion.
647        */

648       public void print(final String JavaDoc prefix) {
649          print(System.err, prefix);
650       }
651
652       /**
653        * Print this stack trace entry to <code>System.err<code>.
654        */

655       public void print() {
656          print(System.err);
657       }
658    }
659
660
661    /////////////////////////////////////////////////////////////////////////
662
// StackTrace Parser //
663
/////////////////////////////////////////////////////////////////////////
664

665    /**
666     * A parser which takes a standard Throwable and produces
667     * {@link StackTrace.Entry} objects.
668     */

669    public static class Parser
670    {
671       /**
672        * Skip the throwable description of the trace.
673        *
674        * @param reader Reader representing the trace.
675        *
676        * @throws IOException
677        */

678       protected void skipDescription(final BufferedReader JavaDoc reader)
679          throws IOException JavaDoc
680       {
681          reader.readLine();
682       }
683
684       /**
685        * Skip to the correct level of the stack (going down into the stack).
686        *
687        * @param reader Reader representing the stack trace.
688        * @param level Number of levels to go down.
689        *
690        * @throws IOException
691        */

692       protected void setLevel(final BufferedReader JavaDoc reader, final int level)
693          throws IOException JavaDoc
694       {
695          for (int i=0; i<level; i++) {
696             reader.readLine();
697          }
698       }
699
700       /**
701        * Read a throwable stack trace as an array of bytes.
702        *
703        * @param detail Throwable to get trace from.
704        * @return Throwable stack trace as an array of bytes.
705        *
706        * @throws IOException
707        */

708       protected byte[] readBytes(final Throwable JavaDoc detail) throws IOException JavaDoc {
709          ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
710          PrintStream JavaDoc ps = new PrintStream JavaDoc(baos);
711
712          try {
713             detail.printStackTrace(ps);
714          }
715          finally {
716             ps.close();
717          }
718
719          return baos.toByteArray();
720       }
721
722       /**
723        * Create a reader for the trace of the given Throwable.
724        *
725        * @param detail Thorwable to get trace from.
726        * @return Reader for the throwable stack trace.
727        *
728        * @throws IOException
729        */

730       protected BufferedReader JavaDoc createReader(final Throwable JavaDoc detail)
731          throws IOException JavaDoc
732       {
733          byte bytes[] = readBytes(detail);
734          ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(bytes);
735          InputStreamReader JavaDoc reader = new InputStreamReader JavaDoc(bais);
736
737          return new BufferedReader JavaDoc(reader);
738       }
739
740       /**
741        * Parse a Throwable stack trace.
742        *
743        * @param detail Throwable to get trace from.
744        * @param level Number of levels down to begin parsing.
745        * @param limit The maximum number of entries to parse (does not
746        * include skipped levels or the description).
747        * A value <= zero results in all entries being parsed.
748        * @return List of {@link StackTrace.Entry} objects.
749        *
750        * @throws IOException
751        */

752       public List JavaDoc parse(final Throwable JavaDoc detail,
753                         final int level,
754                         final int limit)
755          throws IOException JavaDoc
756       {
757          // create an reader the throwable
758
BufferedReader JavaDoc reader = createReader(detail);
759
760          // ignore throwable description
761
skipDescription(reader);
762
763          // set the stack level
764
setLevel(reader, level);
765
766          // read in the stack entrys
767
List JavaDoc list = new ArrayList JavaDoc();
768          
769          String JavaDoc raw;
770          int count = 0;
771          while ((raw = reader.readLine()) != null) {
772             Entry entry = createEntry(raw);
773             list.add(entry);
774
775             // if we have reached the limit, then stop parsing
776
if (limit > UNLIMITED && ++count >= limit) break;
777          }
778          
779          return list;
780       }
781
782       /**
783        * Create a stack trace entry for the given raw trace entry.
784        *
785        * @param raw Raw stack trace entry.
786        * @return Stack trace entry.
787        *
788        * @throws IOException
789        */

790       protected Entry createEntry(final String JavaDoc raw) throws IOException JavaDoc {
791          return new Entry(raw);
792       }
793       
794       //////////////////////////////////////////////////////////////////////
795
// Singleton Access //
796
//////////////////////////////////////////////////////////////////////
797

798       /** The single instance of StackTrace.Parser */
799       private static Parser instance = null;
800
801       /**
802        * Get the stack trace parser for this virtual machine.
803        *
804        * @return Stack trace parser
805        *
806        * @throws InstantiationException
807        */

808       public static final synchronized Parser getInstance()
809          throws InstantiationException JavaDoc
810       {
811          if (instance == null) {
812             // change to read parser class from a property
813
instance = new Parser();
814          }
815
816          return instance;
817       }
818    }
819 }
820
Popular Tags