diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java index 61385b09458b37f565c8194a06dfa583d91af6d0..01149f7fa9b328025c15e852ff30cb24dd12efa5 100644 --- a/core/src/main/java/hudson/model/Cause.java +++ b/core/src/main/java/hudson/model/Cause.java @@ -34,6 +34,7 @@ import jenkins.model.Jenkins; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import javax.annotation.Nonnull; /** * Cause object base class. This class hierarchy is used to keep track of why @@ -100,6 +101,11 @@ public abstract class Cause { * A build is triggered by the completion of another build (AKA upstream build.) */ public static class UpstreamCause extends Cause { + + /** + * Maximum depth of transitive upstream causes we want to record. + */ + private static final int MAX_DEPTH = 10; private String upstreamProject, upstreamUrl; private int upstreamBuild; /** @@ -107,7 +113,7 @@ public abstract class Cause { */ @Deprecated private transient Cause upstreamCause; - private List upstreamCauses; + private @Nonnull List upstreamCauses; /** * @deprecated since 2009-02-28 @@ -121,7 +127,33 @@ public abstract class Cause { upstreamBuild = up.getNumber(); upstreamProject = up.getParent().getFullName(); upstreamUrl = up.getParent().getUrl(); - upstreamCauses = new ArrayList(up.getCauses()); + upstreamCauses = new ArrayList(); + for (Cause c : up.getCauses()) { + upstreamCauses.add(trim(c, MAX_DEPTH)); + } + } + + private UpstreamCause(String upstreamProject, int upstreamBuild, String upstreamUrl, @Nonnull List upstreamCauses) { + this.upstreamProject = upstreamProject; + this.upstreamBuild = upstreamBuild; + this.upstreamUrl = upstreamUrl; + this.upstreamCauses = upstreamCauses; + } + + private @Nonnull Cause trim(@Nonnull Cause c, int depth) { + if (!(c instanceof UpstreamCause)) { + return c; + } + UpstreamCause uc = (UpstreamCause) c; + List cs = new ArrayList(); + if (depth > 0) { + for (Cause c2 : uc.upstreamCauses) { + cs.add(trim(c2, depth - 1)); + } + } else { + cs.add(new DeeplyNestedUpstreamCause()); + } + return new UpstreamCause(uc.upstreamProject, uc.upstreamBuild, uc.upstreamUrl, cs); } /** @@ -167,6 +199,10 @@ public abstract class Cause { ); } + @Override public String toString() { + return upstreamUrl + upstreamBuild + upstreamCauses; + } + public static class ConverterImpl extends XStream2.PassthruConverter { public ConverterImpl(XStream2 xstream) { super(xstream); } @Override protected void callback(UpstreamCause uc, UnmarshallingContext context) { @@ -178,6 +214,16 @@ public abstract class Cause { } } } + + public static class DeeplyNestedUpstreamCause extends Cause { + @Override public String getShortDescription() { + return "(deeply nested causes)"; + } + @Override public String toString() { + return "JENKINS-14814"; + } + } + } /**