diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java
index 50c115a850b443e51a41e46369acbc33bc1b2174..8c9e72edd9c6ba6f5c8ff2c6bab0da4d8668f109 100644
--- a/core/src/main/java/hudson/Functions.java
+++ b/core/src/main/java/hudson/Functions.java
@@ -150,6 +150,7 @@ import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgume
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
+import hudson.util.RunList;
import java.util.concurrent.atomic.AtomicLong;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -1431,7 +1432,8 @@ public class Functions {
}
/**
- * Returns a sub-list if the given list is bigger than the specified 'maxSize'
+ * Returns a sub-list if the given list is bigger than the specified {@code maxSize}.
+ * Warning: do not call this with a {@link RunList}, or you will break lazy loading!
*/
public static List subList(List base, int maxSize) {
if(maxSize builds;
public BuildTimelineWidget(RunList> builds) {
- this.builds = builds;
+ this.builds = builds.limit(20); // TODO instead render lazily
}
+ @Deprecated
public Run, ?> getFirstBuild() {
return builds.getFirstBuild();
}
+ @Deprecated
public Run, ?> getLastBuild() {
return builds.getLastBuild();
}
diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java
index aef474184ac66d21114e69e82c51c22bac4a1069..29a7cd9425672c4daf644cc22c404efb67f6efc6 100644
--- a/core/src/main/java/hudson/model/Job.java
+++ b/core/src/main/java/hudson/model/Job.java
@@ -1269,7 +1269,7 @@ public abstract class Job, RunT extends Run data = new DataSetBuilder();
- for (Run r : getBuilds()) {
+ for (Run r : getNewBuilds()) {
if (r.isBuilding())
continue;
data.add(((double) r.getDuration()) / (1000 * 60), "min",
diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java
index 90e6f472f0fc996871152124b48a8e44416028b2..e0b3cd5e877b1e4b923571089ce6176a415c495d 100644
--- a/core/src/main/java/hudson/model/User.java
+++ b/core/src/main/java/hudson/model/User.java
@@ -24,6 +24,7 @@
*/
package hudson.model;
+import com.google.common.base.Predicate;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import hudson.*;
import hudson.model.Descriptor.FormException;
@@ -451,19 +452,14 @@ public class User extends AbstractModelObject implements AccessControlled, Descr
/**
* Gets the list of {@link Build}s that include changes by this user,
* by the timestamp order.
- *
- * TODO: do we need some index for this?
*/
@WithBridgeMethods(List.class)
public RunList getBuilds() {
- List r = new ArrayList();
- for (AbstractProject,?> p : Jenkins.getInstance().getAllItems(AbstractProject.class))
- for (AbstractBuild,?> b : p.getBuilds().newBuilds()){
- if (relatedTo(b)) {
- r.add(b);
- }
+ return new RunList>(Jenkins.getInstance().getAllItems(Job.class)).filter(new Predicate>() {
+ @Override public boolean apply(Run,?> r) {
+ return r instanceof AbstractBuild && relatedTo((AbstractBuild,?>) r);
}
- return RunList.fromRuns(r);
+ });
}
/**
diff --git a/core/src/main/java/hudson/util/RunList.java b/core/src/main/java/hudson/util/RunList.java
index 891e4ff8b58e0aa6890abf4aa9c9d7da8733231f..add82eb8907d87486793c5b1fc6ac661be86b1d2 100644
--- a/core/src/main/java/hudson/util/RunList.java
+++ b/core/src/main/java/hudson/util/RunList.java
@@ -186,9 +186,10 @@ public class RunList extends AbstractList {
/**
* Returns elements that satisfy the given predicate.
+ * Warning: this method mutates the original list and then returns it.
+ * @since TODO
*/
- // for compatibility reasons, this method doesn't create a new list but updates the current one
- private RunList filter(Predicate predicate) {
+ public RunList filter(Predicate predicate) {
size = null;
first = null;
base = Iterables.filter(base,predicate);
diff --git a/core/src/main/java/jenkins/widgets/BuildListTable.java b/core/src/main/java/jenkins/widgets/BuildListTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec8e60e368373ad98619d354729f8a6a33770984
--- /dev/null
+++ b/core/src/main/java/jenkins/widgets/BuildListTable.java
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Jesse Glick.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package jenkins.widgets;
+
+import hudson.Functions;
+import hudson.model.BallColor;
+import hudson.model.Run;
+import net.sf.json.JSONObject;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+
+@Restricted(DoNotUse.class) // only for buildListTable.jelly
+public class BuildListTable extends RunListProgressiveRendering {
+
+ @Override protected void calculate(Run,?> build, JSONObject element) {
+ BallColor iconColor = build.getIconColor();
+ element.put("iconColorOrdinal", iconColor.ordinal());
+ element.put("iconColorDescription", iconColor.getDescription());
+ element.put("url", build.getUrl());
+ element.put("buildStatusUrl", build.getBuildStatusUrl());
+ element.put("parentUrl", build.getParent().getUrl());
+ element.put("parentFullDisplayName", Functions.breakableString(Functions.escape(build.getParent().getFullDisplayName())));
+ element.put("displayName", build.getDisplayName());
+ element.put("timestampString", build.getTimestampString());
+ element.put("timestampString2", build.getTimestampString2());
+ Run.Summary buildStatusSummary = build.getBuildStatusSummary();
+ element.put("buildStatusSummaryWorse", buildStatusSummary.isWorse);
+ element.put("buildStatusSummaryMessage", buildStatusSummary.message);
+ }
+
+}
diff --git a/core/src/main/java/jenkins/widgets/BuildTimeTrend.java b/core/src/main/java/jenkins/widgets/BuildTimeTrend.java
new file mode 100644
index 0000000000000000000000000000000000000000..158ec398ea29cca22c0e2829b81c2fef394ed137
--- /dev/null
+++ b/core/src/main/java/jenkins/widgets/BuildTimeTrend.java
@@ -0,0 +1,65 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Jesse Glick.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package jenkins.widgets;
+
+import hudson.model.AbstractBuild;
+import hudson.model.BallColor;
+import hudson.model.Node;
+import hudson.model.Run;
+import jenkins.model.Jenkins;
+import net.sf.json.JSONObject;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+
+@Restricted(DoNotUse.class) // only for buildTimeTrend.jelly
+public class BuildTimeTrend extends RunListProgressiveRendering {
+
+ @Override protected void calculate(Run,?> build, JSONObject element) {
+ BallColor iconColor = build.getIconColor();
+ element.put("iconColorOrdinal", iconColor.ordinal());
+ element.put("iconColorDescription", iconColor.getDescription());
+ element.put("buildStatusUrl", build.getBuildStatusUrl());
+ element.put("number", build.getNumber());
+ element.put("displayName", build.getDisplayName());
+ element.put("duration", build.getDuration());
+ element.put("durationString", build.getDurationString());
+ if (build instanceof AbstractBuild) {
+ AbstractBuild,?> b = (AbstractBuild) build;
+ Node n = b.getBuiltOn();
+ if (n == null) {
+ String ns = b.getBuiltOnStr();
+ if (ns != null && !ns.isEmpty()) {
+ element.put("builtOnStr", ns);
+ }
+ } else if (n != Jenkins.getInstance()) {
+ element.put("builtOn", n.getNodeName());
+ element.put("builtOnStr", n.getDisplayName());
+ } else {
+ element.put("builtOnStr", hudson.model.Messages.Hudson_Computer_DisplayName());
+ }
+ }
+ }
+
+}
diff --git a/core/src/main/java/jenkins/widgets/RunListProgressiveRendering.java b/core/src/main/java/jenkins/widgets/RunListProgressiveRendering.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ab72a8a38eb2af4d3001058e443dcbdc1cd5972
--- /dev/null
+++ b/core/src/main/java/jenkins/widgets/RunListProgressiveRendering.java
@@ -0,0 +1,81 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Jesse Glick.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package jenkins.widgets;
+
+import hudson.model.Run;
+import hudson.util.RunList;
+import java.util.ArrayList;
+import java.util.List;
+import jenkins.util.ProgressiveRendering;
+import net.sf.json.JSON;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+/**
+ * Makes it possible to incrementally render some information from a {@link RunList}.
+ */
+@Restricted(NoExternalUse.class)
+public abstract class RunListProgressiveRendering extends ProgressiveRendering {
+
+ /**
+ * Since we cannot predict how many runs there will be, just show an ever-growing progress bar.
+ * The first increment will be sized as if this many runs will be in the total,
+ * but then like Zeno’s paradox we will never seem to finish until we actually do.
+ */
+ private static final double MAX_LIKELY_RUNS = 20;
+ private final List results = new ArrayList();
+ private Iterable extends Run,?>> builds;
+
+ /** Jelly cannot call a constructor with arguments. */
+ public void setBuilds(Iterable extends Run,?>> builds) {
+ this.builds = builds;
+ }
+
+ @Override protected void compute() throws Exception {
+ double decay = 1;
+ for (Run,?> build : builds) {
+ if (canceled()) {
+ return;
+ }
+ JSONObject element = new JSONObject();
+ calculate(build, element);
+ synchronized (results) {
+ results.add(element);
+ }
+ decay *= (1 - 1 / MAX_LIKELY_RUNS);
+ progress(1 - decay);
+ }
+ }
+
+ @Override protected synchronized JSON data() {
+ JSONArray d = JSONArray.fromObject(results);
+ results.clear();
+ return d;
+ }
+
+ protected abstract void calculate(Run,?> build, JSONObject element);
+}
diff --git a/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly b/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly
index 394192552612be10400215b2cee2a83f289adb08..f695275f06656ae91e35c41097263479810c7a56 100644
--- a/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly
+++ b/core/src/main/resources/hudson/model/BuildTimelineWidget/control.jelly
@@ -77,10 +77,7 @@ THE SOFTWARE.
// theme1.autoWidth = true; // Set the Timeline's "width" automatically.
// Set autoWidth on the Timeline's first band's theme,
// will affect all bands.
- theme1.timeline_start = new Date(${it.firstBuild.timeInMillis-24*60*60*1000});
- theme1.timeline_stop = new Date(${it.lastBuild.timeInMillis+24*60*60*1000});
- var d = new Date(${it.lastBuild.timeInMillis});
var bandInfos = [
// the bar that shows outline
Timeline.createBandInfo({
diff --git a/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly b/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly
index ec41aca7154d6c43bcc4ca890c331d62193aed1f..e389beed31b9ee2b53af2ed68460f43303d481f5 100644
--- a/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly
+++ b/core/src/main/resources/hudson/model/Job/buildTimeTrend.jelly
@@ -34,15 +34,39 @@ THE SOFTWARE.
${%Build Time Trend}
-
-