1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 }