diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 38f24332ad981b74d2c0a4c0e54b08bd722ef511..f2863b590569d172af6932f2e759cc8fbfd958eb 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -156,6 +156,8 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import hudson.model.PasswordParameterDefinition; import hudson.util.RunList; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; @@ -1453,10 +1455,14 @@ public class Functions { return Messages.Functions_NoExceptionDetails(); } StringBuilder s = new StringBuilder(); - doPrintStackTrace(s, t, null, ""); + doPrintStackTrace(s, t, null, "", new HashSet()); return s.toString(); } - private static void doPrintStackTrace(@Nonnull StringBuilder s, @Nonnull Throwable t, @CheckForNull Throwable higher, @Nonnull String prefix) { + private static void doPrintStackTrace(@Nonnull StringBuilder s, @Nonnull Throwable t, @CheckForNull Throwable higher, @Nonnull String prefix, @Nonnull Set encountered) { + if (!encountered.add(t)) { + s.append("\n"); + return; + } if (Util.isOverridden(Throwable.class, t.getClass(), "printStackTrace", PrintWriter.class)) { StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); @@ -1465,11 +1471,11 @@ public class Functions { } Throwable lower = t.getCause(); if (lower != null) { - doPrintStackTrace(s, lower, t, prefix); + doPrintStackTrace(s, lower, t, prefix, encountered); } for (Throwable suppressed : t.getSuppressed()) { s.append(prefix).append("Also: "); - doPrintStackTrace(s, suppressed, t, prefix + "\t"); + doPrintStackTrace(s, suppressed, t, prefix + "\t", encountered); } if (lower != null) { s.append(prefix).append("Caused: "); diff --git a/core/src/test/java/hudson/FunctionsTest.java b/core/src/test/java/hudson/FunctionsTest.java index a242e83e738f0f03792b5e9e72a34b7973f91085..7a1b72293b1a96415ba9b129d27a7fdea42b1a0a 100644 --- a/core/src/test/java/hudson/FunctionsTest.java +++ b/core/src/test/java/hudson/FunctionsTest.java @@ -501,6 +501,22 @@ public class FunctionsTest { s.println("Some custom exception"); } }, "Some custom exception\n", "Some custom exception\n"); + // Circular references: + Stack stack1 = new Stack("p.Exc1", "p.C.method1:17"); + Stack stack2 = new Stack("p.Exc2", "p.C.method2:27"); + stack1.cause(stack2); + stack2.cause(stack1); + assertPrintThrowable(stack1, + "p.Exc1\n" + + "\tat p.C.method1(C.java:17)\n" + + "Caused by: p.Exc2\n" + + "\tat p.C.method2(C.java:27)\n" + + "\t[CIRCULAR REFERENCE:p.Exc1]\n", + "\n" + + "Caused: p.Exc2\n" + + "\tat p.C.method2(C.java:27)\n" + + "Caused: p.Exc1\n" + + "\tat p.C.method1(C.java:17)\n"); } private static void assertPrintThrowable(Throwable t, String traditional, String custom) { StringWriter sw = new StringWriter();