package com.jasonclawson.dropwizardry.jersey.errors; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; import com.google.common.base.Preconditions; import lombok.Getter; @Getter public class DebugErrorMessage { private final long errorId; private final ExceptionBean exception; public DebugErrorMessage(long errorId, Throwable t) { Preconditions.checkNotNull(t); this.errorId = errorId; exception = new ExceptionBean(t); } @Override public String toString() { StringWriter str = new StringWriter(); try { exception.printStackTrace(str); } catch (IOException e) { throw new RuntimeException("Unable to write StackTrace for errorId '"+errorId+"'", e); } return LoggingDebugExceptionMapper.formatErrorMessage(errorId)+" "+str.toString(); } // private String formatErrorMessage(long id) { // return String.format("There was an error processing your request. It has been logged (ID %016x).", id); // } @Getter protected static class ExceptionBean { private final String clazzName; private final String message; private final ExceptionBean cause; private final StackTraceElement[] stack; public ExceptionBean(Throwable t) { clazzName = t.getClass().getSimpleName(); message = t.getMessage(); Throwable cause = t.getCause(); if(cause != null) { this.cause = new ExceptionBean(cause); } else { this.cause = null; } stack = t.getStackTrace(); } private static final String CAUSE_CAPTION = "Caused by: "; @Override public String toString() { String s = clazzName; return (message != null) ? (s + ": " + message) : s; } protected void printStackTrace(Writer s) throws IOException { // Guard against malicious overrides of Throwable.equals by // using a Set with identity equality semantics. Set<ExceptionBean> dejaVu = Collections.newSetFromMap(new IdentityHashMap<ExceptionBean, Boolean>()); dejaVu.add(this); // Print our stack trace s.write(this.toString()); s.write("\n"); StackTraceElement[] trace = getStack(); for (StackTraceElement traceElement : trace) { s.write("\tat " + traceElement); s.write("\n"); } // Print cause, if any ExceptionBean ourCause = getCause(); if (ourCause != null) ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu); } /** * Print our stack trace as an enclosed exception for the specified * stack trace. * @throws IOException */ private void printEnclosedStackTrace(Writer s, StackTraceElement[] enclosingTrace, String caption, String prefix, Set<ExceptionBean> dejaVu) throws IOException { if (dejaVu.contains(this)) { s.write("\t[CIRCULAR REFERENCE:" + this + "]"); s.write("\n"); } else { dejaVu.add(this); // Compute number of frames in common between this and enclosing trace StackTraceElement[] trace = getStack(); int m = trace.length - 1; int n = enclosingTrace.length - 1; while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) { m--; n--; } int framesInCommon = trace.length - 1 - m; // Print our stack trace s.write(prefix + caption + this); s.write("\n"); for (int i = 0; i <= m; i++) { s.write(prefix + "\tat " + trace[i]); s.write("\n"); } if (framesInCommon != 0) { s.write(prefix + "\t... " + framesInCommon + " more"); s.write("\n"); } // Print cause, if any ExceptionBean ourCause = getCause(); if (ourCause != null) ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu); } } } }