View Javadoc

1   /*
2   This file is part of the xframe software package
3   hosted at http://xframe.sourceforge.net
4   
5   Copyright (c) 2003 Kurt Riede.
6   
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public
9   License as published by the Free Software Foundation; either
10  version 2.1 of the License, or (at your option) any later version.
11  
12  This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16  
17  You should have received a copy of the GNU Lesser General Public
18  License along with this library; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21  package net.sf.xframe.ex;
22  
23  import java.io.PrintWriter;
24  import java.io.StringWriter;
25  import java.lang.reflect.Method;
26  import java.util.StringTokenizer;
27  
28  /***
29   * This class provides basic facilities for manipulating exceptions.
30   *
31   * @author <a href="mailto:kriede@users.sourceforge.net">Kurt Riede</a>
32   */
33  public final class ExceptionUtil {
34  
35      /*** line seperator. */
36      private static final String LINE_SEPARATOR =
37          System.getProperty("line.separator");
38  
39      /*** name of method getCause. */
40      private static final String GET_CAUSE_NAME = "getCause";
41  
42      /*** parameters to getCause methods. */
43      private static final Class[] GET_CAUSE_PARAMTYPES = new Class[0];
44  
45      /***
46       * Private constructor to prevent instantiation.
47       */
48      private ExceptionUtil() {
49      }
50  
51      /***
52       * Generate string for specified exception and the cause of
53       * this exception (if any).
54       * @param throwable a <code>Throwable</code>
55       * @return the stack trace as a <code>String</code>
56       */
57      public static String printStackTrace(final Throwable throwable) {
58          return printStackTrace(throwable, 0, true);
59      }
60  
61      /***
62       * Generate string for specified exception and if printCascading
63       * is true will print all cascading exceptions.
64       * @param throwable a <code>Throwable</code>
65       * @param printCascading if <code>true</code> will print all cascading
66       *                       exceptions
67       * @return the stack trace as a <code>String</code>
68       */
69      public static String printStackTrace(final Throwable throwable,
70                                           final boolean printCascading) {
71          return printStackTrace(throwable, 0, printCascading);
72      }
73  
74      /***
75       * Serialize the specified <code>Throwable</code> to a string.
76       * Restrict the number of frames printed out to the specified depth.
77       * If the depth specified is <code>0</code> then all the frames are
78       * converted into a string.
79       * @param throwable a <code>Throwable</code>
80       * @param depth number of stack trace frames to show
81       * @return the stack trace as a <code>String</code>
82       */
83      public static String printStackTrace(final Throwable throwable,
84                                           final int depth) {
85          int dp = depth;
86          final String[] lines = captureStackTrace(throwable);
87          if (0 == dp || dp > lines.length) {
88              dp = lines.length;
89          }
90          final StringBuffer sb = new StringBuffer();
91          for (int i = 0; i < dp; i++) {
92              sb.append(lines[i]);
93              sb.append(LINE_SEPARATOR);
94          }
95          return sb.toString();
96      }
97  
98      /***
99       * Generate exception string for specified exception to specified depth
100      * and all Cascading exceptions if printCascading is true.
101      * @param throwable a <code>Throwable</code>
102      * @param depth number of stack trace frames to show
103      * @param printCascading if <code>true</code> will print all cascading
104      *                        exceptions
105      * @return the stack trace as a <code>String</code>
106      */
107     public static String printStackTrace(final Throwable throwable,
108                                          final int depth,
109                                          final boolean printCascading) {
110         return printStackTrace(throwable, depth, printCascading, true);
111     }
112 
113     /***
114      * Generate exception string for specified exception to specified depth
115      * and all Cascading exceptions if printCascading is true. If useReflection
116      * is true then the method will also attempt to use reflection to find a
117      * method with signature <code>Throwable getCause()</code>. This makes
118      * it compatible with JDK1.4 mechanisms for nesting exceptions.
119      * @param throwable a <code>Throwable</code>
120      * @param depth number of stack trace frames to show
121      * @param printCascading if <code>true</code> will print all cascading
122      *                       exceptions
123      * @param useReflection if <code>true</code> will use reflection to handle
124      *                      JDK1.4 nested exceptions
125      * @return the stack trace as a <code>String</code>
126      */
127     public static String printStackTrace(final Throwable throwable,
128                                          final int depth,
129                                          final boolean printCascading,
130                                          final boolean useReflection) {
131         final String result = printStackTrace(throwable, depth);
132         if (!printCascading) {
133             return result;
134         }
135         final StringBuffer sb = new StringBuffer();
136         sb.append(result);
137         Throwable cause = getCause(throwable, useReflection);
138         while (null != cause) {
139             sb.append("rethrown from");
140             sb.append(LINE_SEPARATOR);
141             sb.append(printStackTrace(cause, depth));
142             cause = getCause(cause, useReflection);
143         }
144         return sb.toString();
145     }
146 
147     /***
148      * Utility method to get cause of exception.
149      * @param throwable a <code>Throwable</code>
150      * @param useReflection if <code>true</code> will use reflection to handle
151      *                      JDK1.4 nested exceptions
152      * @return cause of specified exception
153      */
154     public static Throwable getCause(final Throwable throwable, final boolean useReflection) {
155         if (throwable instanceof CascadingThrowable) {
156             return ((CascadingThrowable) throwable).getCause();
157         } else if (useReflection) {
158             try {
159                 final Class clazz = throwable.getClass();
160                 final Method method =
161                     clazz.getMethod(GET_CAUSE_NAME, GET_CAUSE_PARAMTYPES);
162                 return (Throwable) method.invoke(throwable, null);
163             } catch (final Throwable t) {
164                 return null;
165             }
166         } else {
167             return null;
168         }
169     }
170 
171     /***
172      * Captures the stack trace associated with this exception.
173      *
174      * @param throwable a <code>Throwable</code>
175      * @return an array of Strings describing stack frames.
176      */
177     public static String[] captureStackTrace(final Throwable throwable) {
178         final StringWriter sw = new StringWriter();
179         throwable.printStackTrace(new PrintWriter(sw, true));
180         return splitString(sw.toString(), LINE_SEPARATOR);
181     }
182 
183     /***
184      * Splits the string on every token into an array of stack frames.
185      *
186      * @param string the string to split
187      * @param onToken the token to split on
188      * @return the resultant array
189      */
190     private static String[] splitString(final String string,
191                                         final String onToken) {
192         final StringTokenizer tokenizer = new StringTokenizer(string, onToken);
193         final String[] result = new String[tokenizer.countTokens()];
194         for (int i = 0; i < result.length; i++) {
195             result[i] = tokenizer.nextToken();
196         }
197         return result;
198     }
199 }