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 }