1
2 package org.naftulin.classpathexplorer.stacktrace.impl;
3
4 import java.io.File;
5 import java.io.PrintStream;
6 import java.io.PrintWriter;
7 import java.io.Serializable;
8
9 import org.naftulin.classpathexplorer.resourceinfo.impl.ResourceInfo;
10
11
12 /***
13 * Combines stack element with resource information. The class is useful when
14 * it is needed to know which resource generates an exception. {@link #getExceptionDecorator} should
15 * be used to wrap the exception. When the wrapped exception is printed out,
16 * (for example to log file), each stack trace element will have resource information
17 * available in addition to the stack trace information.
18 * @author henry naftulin
19 * @version 1.0
20 */
21 public class StackTraceElementExtension implements Serializable {
22 private static final long serialVersionUID = 1L;
23 private static final StackTraceElementExtension[] EMPTY_TRACE = new StackTraceElementExtension[0];
24 private final StackTraceElement stackTraceElement;
25 private final ResourceInfo resourceInfo;
26
27 /***
28 * Creates a stack trace extension based on the stack trace element.
29 * @param element
30 */
31 public StackTraceElementExtension(StackTraceElement element) {
32 this.stackTraceElement = element;
33 Class clazz = null;
34 ResourceInfo re = null;
35 if (element.getClassName() != null) {
36 try {
37 clazz = Class.forName(element.getClassName());
38 re = ResourceInfo.getResourceInfo(clazz);
39 } catch (ClassNotFoundException e) {
40 re = ResourceInfo.NOT_FOUND_RESOURCE_INFO;
41 }
42 }
43 resourceInfo = re;
44 }
45
46 /***
47 * Returns the file for archive for the class that this instance of the stack trace is describing.
48 * The file could be null, for example, if resource is not found.
49 * @return the file for archive for the class that this instance of the stack trace is describing.
50 */
51 public File getClassArchiveSource() {
52 return resourceInfo.getClassArchiveSource();
53 }
54
55 /***
56 * Returns the stack trace element.
57 * @return the stack trace element
58 */
59 public StackTraceElement getStackTraceElement() {
60 return stackTraceElement;
61 }
62
63 /***
64 * Returns true if the class described in this stack trace is loaded from directory.
65 * Otherwise, the class described in this stack trace could not be found, or is loaded
66 * from and archive (e.g. zip or jar).
67 * @return true if the class described in this stack trace is loaded from directory.
68 */
69 public boolean isDirectory() {
70 return getClassArchiveSource() != null && getClassArchiveSource().isDirectory();
71 }
72
73 /***
74 * Returns the name of the archive the class resides in, or not found if class was not found.
75 * @return the name of the archive the class resides in, or not found if class was not found.
76 */
77 public String getArchiveName() {
78 return getClassArchiveSource() != null ? getClassArchiveSource().getAbsolutePath() : "Not found";
79 }
80
81 public String toString() {
82 StringBuffer sb = new StringBuffer(20);
83 sb.append(stackTraceElement.toString());
84 if (this.resourceInfo != ResourceInfo.NOT_FOUND_RESOURCE_INFO) {
85 sb.append(" [ from ");
86 sb.append(getArchiveName());
87 sb.append(isDirectory() ? " directory" : " archive");
88 sb.append("] ");
89 }
90
91 return sb.toString();
92 }
93
94 private static StackTraceElementExtension[] covnverStackTrace(StackTraceElement[] elements) {
95 if (elements == null) return EMPTY_TRACE;
96 StackTraceElementExtension[] extensions = new StackTraceElementExtension[elements.length];
97 for(int i=0; i < extensions.length; ++i) {
98 extensions[i] = new StackTraceElementExtension(elements[i]);
99 }
100 return extensions;
101 }
102
103 /***
104 * Returns exception that the stack trace with additional resource information.
105 * @exception exception that will be decorated, resorce infomation will be added.
106 * @return exception with additional stack trace information.
107 */
108 public static Exception getExceptionDecorator(Exception e) {
109 return new StackTraceElementExtension.ExceptionDecorator(e);
110 }
111
112 /***
113 * Decorates exception with resource information for each of the stack traces.
114 * Follows GOF decorator pattern.
115 * @author henry naftulin
116 * @version 1.0
117 */
118 static class ExceptionDecorator extends Exception implements Serializable {
119 private static final long serialVersionUID = 1L;
120 final Exception e;
121 final StackTraceElementExtension[] elements;
122 public ExceptionDecorator(Exception e) {
123 super(e.getLocalizedMessage());
124 this.e = e;
125 elements = covnverStackTrace(e.getStackTrace());
126 }
127
128 public String getMessage() {return e.getMessage(); }
129 public Throwable getCause() { return e.getCause(); }
130 public String getLocalizedMessage() { return e.getLocalizedMessage(); }
131 public Throwable initCause(Throwable cause) { return e.initCause(cause); }
132 public void printStackTrace() { printStackTrace(System.err); }
133 public void printStackTrace(PrintStream stream) {
134 stream.print(e.getClass().getName());
135 stream.print(" ");
136 if (e.getLocalizedMessage() != null) {
137 stream.print(e.getLocalizedMessage());
138 }
139 stream.println();
140 for(int i=0; i < elements.length; ++i) {
141 stream.print("\tat ");
142 stream.println(elements[i].toString());
143 }
144 if (e.getCause() != null && e.getCause() != e ) {
145 stream.print("Caused by: ");
146 e.getCause().printStackTrace(stream);
147 }
148 }
149
150 public void printStackTrace(PrintWriter writer) {
151 writer.print(e.getClass().getName());
152 writer.print(" ");
153 if (e.getLocalizedMessage() != null) {
154 writer.print(e.getLocalizedMessage());
155 }
156 writer.println();
157 for(int i=0; i < elements.length; ++i) {
158 writer.print("\tat ");
159 writer.println(elements[i].toString());
160 }
161 if (e.getCause() != null && e.getCause() != e ) {
162 writer.print("Caused by: ");
163 e.getCause().printStackTrace(writer);
164 }
165
166 }
167 public void setStackTrace(StackTraceElement[] trace) { e.setStackTrace(trace); }
168 public Throwable fillInStackTrace() { return e != null ? e.fillInStackTrace() : null; }
169 }
170 }