提交 03b39802 编写于 作者: M mdonohue

When a build is scheduled via Queue.add, but is determined to be a duplicate

of an already queued job, Hudson will now add an additional Cause to the 
already queued job, taken from the job that is determined to be a duplicate.


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@15894 71c3de6d-444a-0410-be80-ed276b4c234a
上级 b0d63631
......@@ -23,6 +23,10 @@
*/
package hudson.model;
import java.util.ArrayList;
import java.util.List;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
......@@ -53,14 +57,16 @@ public abstract class Cause {
public static class UpstreamCause extends Cause {
private String upstreamProject, upstreamUrl;
private int upstreamBuild;
private Cause upstreamCause;
@Deprecated
private transient Cause upstreamCause;
private List<Cause> upstreamCauses = new ArrayList<Cause>();
public UpstreamCause(AbstractBuild<?, ?> up) {
upstreamBuild = up.getNumber();
upstreamProject = up.getProject().getName();
upstreamUrl = up.getProject().getUrl();
CauseAction ca = up.getAction(CauseAction.class);
upstreamCause = ca == null ? null : ca.getCause();
upstreamCauses = ca == null ? null : ca.getCauses();
}
public String getUpstreamProject() {
......@@ -79,6 +85,15 @@ public abstract class Cause {
public String getShortDescription() {
return Messages.Cause_UpstreamCause_ShortDescription(upstreamProject, upstreamBuild);
}
private Object readResolve() {
if(upstreamCause != null) {
if(upstreamCauses == null) upstreamCauses = new ArrayList<Cause>();
upstreamCauses.add(upstreamCause);
upstreamCause=null;
}
return this;
}
}
public static class UserCause extends Cause {
......
......@@ -23,24 +23,33 @@
*/
package hudson.model;
import hudson.model.Queue.Task;
import java.util.ArrayList;
import java.util.List;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
@ExportedBean
public class CauseAction implements Action {
private Cause cause;
public class CauseAction implements FoldableAction {
@Deprecated
// there can be multiple causes, so this is deprecated
private transient Cause cause;
private List<Cause> causes = new ArrayList<Cause>();
@Exported(visibility=2)
public Cause getCause() {
return cause;
public List<Cause> getCauses() {
return causes;
}
public String getShortDescription() {
return cause.getShortDescription();
public CauseAction(Cause c) {
this.causes.add(c);
}
public CauseAction(Cause c) {
this.cause = c;
public CauseAction(CauseAction ca) {
this.causes.addAll(ca.causes);
}
public String getDisplayName() {
......@@ -55,4 +64,24 @@ public class CauseAction implements Action {
public String getUrlName() {
return "cause";
}
public void foldIntoExisting(Task t, List<Action> actions) {
for(Action action : actions) {
if(action instanceof CauseAction) {
this.causes.addAll(((CauseAction)action).causes);
return;
}
}
// no CauseAction found, so add a copy of this one
actions.add(new CauseAction(this));
}
private Object readResolve() {
// if we are being read in from an older version
if(cause != null) {
if(causes == null) causes=new ArrayList<Cause>();
causes.add(cause);
}
return this;
}
}
package hudson.model;
import hudson.model.Queue.Task;
import java.util.List;
/**
* @author mdonohue
* An action interface that allows action data to be folded together.
* This is useful for combining any distinct values from a build determined to
* be a duplicate of a build already in the build queue.
*/
public interface FoldableAction extends Action {
public void foldIntoExisting(Task t, List<Action> actions);
}
......@@ -292,71 +292,87 @@ public class Queue extends ResourceController implements Saveable {
public synchronized boolean add(AbstractProject p, int quietPeriod) {
return add((Task) p, quietPeriod);
}
/**
* Schedules an execution of a task.
*
* @param quietPeriod Number of seconds that the task will be placed in queue.
* Useful when the same task is likely scheduled for multiple
* times.
* @return true if the project 'p' is actually added to the queue.
* false if the queue contained it and therefore the add()
* was noop, or just changed the due date of the task.
* @since 1.114
*/
public synchronized boolean add(Task p, int quietPeriod, List<Action> actions) {
Item item = getItem(p);
Calendar due = new GregorianCalendar();
due.add(Calendar.SECOND, quietPeriod);
if (item != null) {
boolean shouldSchedule = false;
for (Action action: item.getActions()) {
if (action instanceof QueueAction)
shouldSchedule |= ((QueueAction) action).shouldSchedule(actions);
}
for (Action action: actions) {
if (action instanceof QueueAction) {
shouldSchedule |= ((QueueAction) action).shouldSchedule(item.getActions());
}
}
if (shouldSchedule) {
//TODO rework the if-s to only have this once
LOGGER.fine(p.getFullDisplayName() + " added to queue");
// put the item in the queue
waitingList.add(new WaitingItem(due,p,actions));
} else {
if (!(item instanceof WaitingItem))
// already in the blocked or buildable stage
// no need to requeue
return false;
WaitingItem wi = (WaitingItem) item;
if(quietPeriod<=0) {
// the user really wants to build now, and they mean NOW.
// so let's pull in the timestamp if we can.
if (wi.timestamp.before(due))
return false;
} else {
// otherwise we do the normal quiet period implementation
if (wi.timestamp.after(due))
return false;
// quiet period timer reset. start the period over again
}
// waitingList is sorted, so when we change a timestamp we need to maintain order
waitingList.remove(wi);
wi.timestamp = due;
waitingList.add(wi);
}
} else {
LOGGER.fine(p.getFullDisplayName() + " added to queue");
// put the item in the queue
waitingList.add(new WaitingItem(due,p,actions));
private synchronized boolean add(Task p, int quietPeriod, List<Action> actions) {
boolean taskConsumed=false;
List<Item> items = getItems(p);
Calendar due = new GregorianCalendar();
due.add(Calendar.SECOND, quietPeriod);
List<Item> duplicatesInQueue = new ArrayList<Item>();
for(Item item : items) {
boolean shouldScheduleItem = false;
for (Action action: item.getActions()) {
if (action instanceof QueueAction)
shouldScheduleItem |= ((QueueAction) action).shouldSchedule(actions);
}
for (Action action: actions) {
if (action instanceof QueueAction) {
shouldScheduleItem |= ((QueueAction) action).shouldSchedule(item.getActions());
}
}
if(!shouldScheduleItem) {
duplicatesInQueue.add(item);
}
}
if (duplicatesInQueue.size() == 0) {
LOGGER.fine(p.getFullDisplayName() + " added to queue");
// put the item in the queue
waitingList.add(new WaitingItem(due,p,actions));
taskConsumed=true;
} else {
// the requested build is already queued, so will not be added
List<WaitingItem> waitingDuplicates = new ArrayList<WaitingItem>();
for(Item item : duplicatesInQueue) {
for(Action a : actions) {
if(a instanceof FoldableAction) {
((FoldableAction)a).foldIntoExisting(item.task, item.getActions());
}
}
if ((item instanceof WaitingItem))
waitingDuplicates.add((WaitingItem)item);
}
if(duplicatesInQueue.size() == 0) {
// all duplicates in the queue are already in the blocked or
// buildable stage no need to requeue
return false;
}
// TODO: avoid calling scheduleMaintenance() if none of the waiting items
// actually change
for(WaitingItem wi : waitingDuplicates) {
if(quietPeriod<=0) {
// the user really wants to build now, and they mean NOW.
// so let's pull in the timestamp if we can.
if (wi.timestamp.before(due))
continue;
} else {
// otherwise we do the normal quiet period implementation
if (wi.timestamp.after(due))
continue;
// quiet period timer reset. start the period over again
}
}
scheduleMaintenance(); // let an executor know that a new item is in the queue.
return true;
// waitingList is sorted, so when we change a timestamp we need to maintain order
waitingList.remove(wi);
wi.timestamp = due;
waitingList.add(wi);
}
}
scheduleMaintenance(); // let an executor know that a new item is in the queue.
return taskConsumed;
}
public synchronized boolean add(Task p, int quietPeriod) {
......
......@@ -23,7 +23,9 @@ THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<t:summary icon="orange-square.gif">
<st:include page="description.jelly" it="${it.cause}" />
</t:summary>
<t:summary icon="orange-square.gif">
<j:forEach var="cause" items="${it.causes}">
<p><st:include page="description.jelly" it="${cause}"/></p>
</j:forEach>
</t:summary>
</j:jelly>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册