PageRenderTime 212ms CodeModel.GetById 139ms app.highlight 61ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/model/Queue.java

http://github.com/hudson/hudson
Java | 1664 lines | 891 code | 199 blank | 574 comment | 193 complexity | 89d12a0e8d743f859f98a8dcde859878 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * The MIT License
   3 * 
   4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
   5 * Stephen Connolly, Tom Huybrechts, InfraDNA, Inc.
   6 * 
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 * 
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 * 
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25package hudson.model;
  26
  27import hudson.AbortException;
  28import hudson.BulkChange;
  29import hudson.ExtensionList;
  30import hudson.ExtensionPoint;
  31import hudson.Util;
  32import hudson.XmlFile;
  33import hudson.init.Initializer;
  34import static hudson.init.InitMilestone.JOB_LOADED;
  35import static hudson.util.Iterators.reverse;
  36
  37import hudson.cli.declarative.CLIMethod;
  38import hudson.cli.declarative.CLIResolver;
  39import hudson.model.queue.AbstractQueueTask;
  40import hudson.model.queue.Executables;
  41import hudson.model.queue.SubTask;
  42import hudson.model.queue.FutureImpl;
  43import hudson.model.queue.MappingWorksheet;
  44import hudson.model.queue.MappingWorksheet.Mapping;
  45import hudson.model.queue.QueueSorter;
  46import hudson.model.queue.QueueTaskDispatcher;
  47import hudson.model.queue.Tasks;
  48import hudson.model.queue.WorkUnit;
  49import hudson.model.Node.Mode;
  50import hudson.model.listeners.SaveableListener;
  51import hudson.model.queue.CauseOfBlockage;
  52import hudson.model.queue.FoldableAction;
  53import hudson.model.queue.CauseOfBlockage.BecauseLabelIsBusy;
  54import hudson.model.queue.CauseOfBlockage.BecauseNodeIsOffline;
  55import hudson.model.queue.CauseOfBlockage.BecauseLabelIsOffline;
  56import hudson.model.queue.CauseOfBlockage.BecauseNodeIsBusy;
  57import hudson.model.queue.WorkUnitContext;
  58import hudson.triggers.SafeTimerTask;
  59import hudson.triggers.Trigger;
  60import hudson.util.OneShotEvent;
  61import hudson.util.TimeUnit2;
  62import hudson.util.XStream2;
  63import hudson.util.ConsistentHash;
  64import hudson.util.ConsistentHash.Hash;
  65
  66import java.io.BufferedReader;
  67import java.io.File;
  68import java.io.FileInputStream;
  69import java.io.IOException;
  70import java.io.InputStreamReader;
  71import java.lang.ref.WeakReference;
  72import java.util.ArrayList;
  73import java.util.Arrays;
  74import java.util.Calendar;
  75import java.util.Collection;
  76import java.util.Collections;
  77import java.util.GregorianCalendar;
  78import java.util.HashMap;
  79import java.util.Iterator;
  80import java.util.List;
  81import java.util.Map;
  82import java.util.NoSuchElementException;
  83import java.util.Set;
  84import java.util.TreeSet;
  85import java.util.Map.Entry;
  86import java.util.concurrent.atomic.AtomicInteger;
  87import java.util.concurrent.Future;
  88import java.util.logging.Level;
  89import java.util.logging.Logger;
  90
  91import javax.management.timer.Timer;
  92import javax.servlet.ServletException;
  93
  94import org.kohsuke.stapler.HttpResponse;
  95import org.kohsuke.stapler.HttpResponses;
  96import org.kohsuke.stapler.export.Exported;
  97import org.kohsuke.stapler.export.ExportedBean;
  98
  99import com.thoughtworks.xstream.XStream;
 100import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
 101import java.util.HashSet;
 102
 103/**
 104 * Build queue.
 105 *
 106 * <p>
 107 * This class implements the core scheduling logic. {@link Task} represents the executable
 108 * task that are placed in the queue. While in the queue, it's wrapped into {@link Item}
 109 * so that we can keep track of additional data used for deciding what to exeucte when.
 110 *
 111 * <p>
 112 * Items in queue goes through several stages, as depicted below:
 113 * <pre>
 114 * (enter) --> waitingList --+--> blockedProjects
 115 *                           |        ^
 116 *                           |        |
 117 *                           |        v
 118 *                           +--> buildables ---> pending ---> (executed)
 119 * </pre>
 120 *
 121 * <p>
 122 * In addition, at any stage, an item can be removed from the queue (for example, when the user
 123 * cancels a job in the queue.) See the corresponding field for their exact meanings.
 124 *
 125 * @author Kohsuke Kawaguchi
 126 */
 127@ExportedBean
 128public class Queue extends ResourceController implements Saveable {
 129    /**
 130     * Items that are waiting for its quiet period to pass.
 131     *
 132     * <p>
 133     * This consists of {@link Item}s that cannot be run yet
 134     * because its time has not yet come.
 135     */
 136    private final Set<WaitingItem> waitingList = new TreeSet<WaitingItem>();
 137
 138    /**
 139     * {@link Task}s that can be built immediately
 140     * but blocked because another build is in progress,
 141     * required {@link Resource}s are not available, or otherwise blocked
 142     * by {@link Task#isBuildBlocked()}.
 143     */
 144    private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
 145
 146    /**
 147     * {@link Task}s that can be built immediately
 148     * that are waiting for available {@link Executor}.
 149     * This list is sorted in such a way that earlier items are built earlier.
 150     */
 151    private final ItemList<BuildableItem> buildables = new ItemList<BuildableItem>();
 152
 153    /**
 154     * {@link Task}s that are being handed over to the executor, but execution
 155     * has not started yet.
 156     */
 157    private final ItemList<BuildableItem> pendings = new ItemList<BuildableItem>();
 158
 159    /**
 160     * Data structure created for each idle {@link Executor}.
 161     * This is a job offer from the queue to an executor.
 162     *
 163     * <p>
 164     * An idle executor (that calls {@link Queue#pop()} creates
 165     * a new {@link JobOffer} and gets itself {@linkplain Queue#parked parked},
 166     * and we'll eventually hand out an {@link #workUnit} to build.
 167     */
 168    public class JobOffer extends MappingWorksheet.ExecutorSlot {
 169        public final Executor executor;
 170
 171        /**
 172         * Used to wake up an executor, when it has an offered
 173         * {@link Project} to build.
 174         */
 175        private final OneShotEvent event = new OneShotEvent(Queue.this);
 176
 177        /**
 178         * The work unit that this {@link Executor} is going to handle.
 179         * (Or null, in which case event is used to trigger a queue maintenance.)
 180         */
 181        private WorkUnit workUnit;
 182
 183        private JobOffer(Executor executor) {
 184            this.executor = executor;
 185        }
 186
 187        @Override
 188        protected void set(WorkUnit p) {
 189            assert this.workUnit == null;
 190            this.workUnit = p;
 191            event.signal();
 192        }
 193
 194        @Override
 195        public Executor getExecutor() {
 196            return executor;
 197        }
 198
 199        /**
 200         * Verifies that the {@link Executor} represented by this object is capable of executing the given task.
 201         */
 202        public boolean canTake(Task task) {
 203            Node node = getNode();
 204            if (node==null)     return false;   // this executor is about to die
 205
 206            if(node.canTake(task)!=null)
 207                return false;   // this node is not able to take the task
 208
 209            for (QueueTaskDispatcher d : QueueTaskDispatcher.all())
 210                if (d.canTake(node,task)!=null)
 211                    return false;
 212
 213            return isAvailable();
 214        }
 215
 216        /**
 217         * Is this executor ready to accept some tasks?
 218         */
 219        public boolean isAvailable() {
 220            return workUnit == null && !executor.getOwner().isOffline() && executor.getOwner().isAcceptingTasks();
 221        }
 222
 223        public Node getNode() {
 224            return executor.getOwner().getNode();
 225        }
 226
 227        public boolean isNotExclusive() {
 228            return getNode().getMode() == Mode.NORMAL;
 229        }
 230    }
 231
 232    /**
 233     * The executors that are currently waiting for a job to run.
 234     */
 235    private final Map<Executor,JobOffer> parked = new HashMap<Executor,JobOffer>();
 236
 237    private volatile transient LoadBalancer loadBalancer;
 238
 239    private volatile transient QueueSorter sorter;
 240
 241    public Queue(LoadBalancer loadBalancer) {
 242        this.loadBalancer =  loadBalancer.sanitize();
 243        // if all the executors are busy doing something, then the queue won't be maintained in
 244        // timely fashion, so use another thread to make sure it happens.
 245        new MaintainTask(this);
 246    }
 247
 248    public LoadBalancer getLoadBalancer() {
 249        return loadBalancer;
 250    }
 251
 252    public void setLoadBalancer(LoadBalancer loadBalancer) {
 253        if(loadBalancer==null)  throw new IllegalArgumentException();
 254        this.loadBalancer = loadBalancer;
 255    }
 256
 257    public QueueSorter getSorter() {
 258        return sorter;
 259    }
 260
 261    public void setSorter(QueueSorter sorter) {
 262        this.sorter = sorter;
 263    }
 264
 265    /**
 266     * Loads the queue contents that was {@link #save() saved}.
 267     */
 268    public synchronized void load() {
 269        try {
 270            // first try the old format
 271            File queueFile = getQueueFile();
 272            if (queueFile.exists()) {
 273                BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(queueFile)));
 274                String line;
 275                while ((line = in.readLine()) != null) {
 276                    AbstractProject j = Hudson.getInstance().getItemByFullName(line, AbstractProject.class);
 277                    if (j != null)
 278                        j.scheduleBuild();
 279                }
 280                in.close();
 281                // discard the queue file now that we are done
 282                queueFile.delete();
 283            } else {
 284                queueFile = getXMLQueueFile();
 285                if (queueFile.exists()) {
 286                    List list = (List) new XmlFile(XSTREAM, queueFile).read();
 287                    int maxId = 0;
 288                    for (Object o : list) {
 289                        if (o instanceof Task) {
 290                            // backward compatibility
 291                            schedule((Task)o, 0);
 292                        } else if (o instanceof Item) {
 293                            Item item = (Item)o;
 294                            if(item.task==null)
 295                                continue;   // botched persistence. throw this one away
 296
 297                            maxId = Math.max(maxId, item.id);
 298                            if (item instanceof WaitingItem) {
 299                                waitingList.add((WaitingItem) item);
 300                            } else if (item instanceof BlockedItem) {
 301                                blockedProjects.put(item.task, (BlockedItem) item);
 302                            } else if (item instanceof BuildableItem) {
 303                                buildables.add((BuildableItem) item);
 304                            } else {
 305                                throw new IllegalStateException("Unknown item type! " + item);
 306                            }
 307                        } // this conveniently ignores null
 308                    }
 309                    WaitingItem.COUNTER.set(maxId);
 310
 311                    // I just had an incident where all the executors are dead at AbstractProject._getRuns()
 312                    // because runs is null. Debugger revealed that this is caused by a MatrixConfiguration
 313                    // object that doesn't appear to be de-serialized properly.
 314                    // I don't know how this problem happened, but to diagnose this problem better
 315                    // when it happens again, save the old queue file for introspection.
 316                    File bk = new File(queueFile.getPath() + ".bak");
 317                    bk.delete();
 318                    queueFile.renameTo(bk);
 319                    queueFile.delete();
 320                }
 321            }
 322        } catch (IOException e) {
 323            LOGGER.log(Level.WARNING, "Failed to load the queue file " + getXMLQueueFile(), e);
 324        }
 325    }
 326
 327    /**
 328     * Persists the queue contents to the disk.
 329     */
 330    public synchronized void save() {
 331        if(BulkChange.contains(this))  return;
 332        
 333        // write out the tasks on the queue
 334    	ArrayList<Queue.Item> items = new ArrayList<Queue.Item>();
 335    	for (Item item: getItems()) {
 336            if(item.task instanceof TransientTask)  continue;
 337    	    items.add(item);
 338    	}
 339
 340        try {
 341            XmlFile queueFile = new XmlFile(XSTREAM, getXMLQueueFile());
 342            queueFile.write(items);
 343            SaveableListener.fireOnChange(this, queueFile);
 344        } catch (IOException e) {
 345            LOGGER.log(Level.WARNING, "Failed to write out the queue file " + getXMLQueueFile(), e);
 346        }
 347    }
 348
 349    /**
 350     * Wipes out all the items currently in the queue, as if all of them are cancelled at once.
 351     */
 352
 353    @CLIMethod(name="clear-queue")
 354    public synchronized void clear() {
 355        Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
 356        for (WaitingItem i : waitingList)
 357            i.onCancelled();
 358        waitingList.clear();
 359        blockedProjects.cancelAll();
 360        buildables.cancelAll();
 361        scheduleMaintenance();
 362    }
 363
 364    /**
 365     * Called from queue.jelly.
 366     */
 367    public HttpResponse doClearQueue() throws IOException, ServletException {
 368        Hudson.getInstance().getQueue().clear();
 369        return HttpResponses.forwardToPreviousPage();
 370    }
 371
 372    private File getQueueFile() {
 373        return new File(Hudson.getInstance().getRootDir(), "queue.txt");
 374    }
 375
 376    /*package*/ File getXMLQueueFile() {
 377        return new File(Hudson.getInstance().getRootDir(), "queue.xml");
 378    }
 379
 380    /**
 381     * @deprecated as of 1.311
 382     *      Use {@link #schedule(AbstractProject)}
 383     */
 384    public boolean add(AbstractProject p) {
 385        return schedule(p)!=null;
 386    }
 387
 388    /**
 389     * Schedule a new build for this project.
 390     *
 391     * @return true if the project is actually added to the queue.
 392     *         false if the queue contained it and therefore the add()
 393     *         was noop
 394     */
 395    public WaitingItem schedule(AbstractProject p) {
 396        return schedule(p, p.getQuietPeriod());
 397    }
 398
 399    /**
 400     * Schedules a new build with a custom quiet period.
 401     *
 402     * <p>
 403     * Left for backward compatibility with &lt;1.114.
 404     *
 405     * @since 1.105
 406     * @deprecated as of 1.311
 407     *      Use {@link #schedule(Task, int)}
 408     */
 409    public boolean add(AbstractProject p, int quietPeriod) {
 410        return schedule(p, quietPeriod)!=null;
 411    }
 412
 413    /**
 414     * Schedules an execution of a task.
 415     *
 416     * @param actions
 417     *      These actions can be used for associating information scoped to a particular build, to
 418     *      the task being queued. Upon the start of the build, these {@link Action}s will be automatically
 419     *      added to the {@link Run} object, and hence avaialable to everyone.
 420     *      For the convenience of the caller, this list can contain null, and those will be silently ignored.
 421     * @since 1.311
 422     * @return
 423     *      null if this task is already in the queue and therefore the add operation was no-op.
 424     *      Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
 425     *      is that such {@link Item} only captures the state of the item at a particular moment,
 426     *      and by the time you inspect the object, some of its information can be already stale.
 427     *
 428     *      That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
 429     */
 430    public synchronized WaitingItem schedule(Task p, int quietPeriod, List<Action> actions) {
 431        // remove nulls
 432        actions = new ArrayList<Action>(actions);
 433        for (Iterator<Action> itr = actions.iterator(); itr.hasNext();) {
 434            Action a =  itr.next();
 435            if (a==null)    itr.remove();
 436        }
 437
 438    	for(QueueDecisionHandler h : QueueDecisionHandler.all())
 439    		if (!h.shouldSchedule(p, actions))
 440                return null;    // veto
 441
 442        return scheduleInternal(p, quietPeriod, actions);
 443    }
 444
 445    /**
 446     * Schedules an execution of a task.
 447     *
 448     * @since 1.311
 449     * @return
 450     *      null if this task is already in the queue and therefore the add operation was no-op.
 451     *      Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
 452     *      is that such {@link Item} only captures the state of the item at a particular moment,
 453     *      and by the time you inspect the object, some of its information can be already stale.
 454     *
 455     *      That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
 456     */
 457    private synchronized WaitingItem scheduleInternal(Task p, int quietPeriod, List<Action> actions) {
 458        Calendar due = new GregorianCalendar();
 459    	due.add(Calendar.SECOND, quietPeriod);
 460
 461        // Do we already have this task in the queue? Because if so, we won't schedule a new one.
 462    	List<Item> duplicatesInQueue = new ArrayList<Item>();
 463    	for(Item item : getItems(p)) {
 464    		boolean shouldScheduleItem = false;
 465    		for (QueueAction action: item.getActions(QueueAction.class)) {
 466                shouldScheduleItem |= action.shouldSchedule(actions);
 467    		}
 468    		for (QueueAction action: Util.filter(actions,QueueAction.class)) {
 469                shouldScheduleItem |= action.shouldSchedule(item.getActions());
 470    		}
 471    		if(!shouldScheduleItem) {
 472    			duplicatesInQueue.add(item);
 473    		}
 474    	}
 475    	if (duplicatesInQueue.isEmpty()) {
 476    		LOGGER.fine(p.getFullDisplayName() + " added to queue");
 477
 478    		// put the item in the queue
 479            WaitingItem added = new WaitingItem(due,p,actions);
 480    		waitingList.add(added);
 481            scheduleMaintenance();   // let an executor know that a new item is in the queue.
 482            return added;
 483    	}
 484
 485        LOGGER.fine(p.getFullDisplayName() + " is already in the queue");
 486
 487        // but let the actions affect the existing stuff.
 488        for(Item item : duplicatesInQueue) {
 489            for(FoldableAction a : Util.filter(actions,FoldableAction.class)) {
 490                a.foldIntoExisting(item, p, actions);
 491            }
 492        }
 493
 494        boolean queueUpdated = false;
 495        for(WaitingItem wi : Util.filter(duplicatesInQueue,WaitingItem.class)) {
 496            if(quietPeriod<=0) {
 497                // the user really wants to build now, and they mean NOW.
 498                // so let's pull in the timestamp if we can.
 499                if (wi.timestamp.before(due))
 500                    continue;
 501            } else {
 502                // otherwise we do the normal quiet period implementation
 503                if (wi.timestamp.after(due))
 504                    continue;
 505                // quiet period timer reset. start the period over again
 506            }
 507
 508            // waitingList is sorted, so when we change a timestamp we need to maintain order
 509            waitingList.remove(wi);
 510            wi.timestamp = due;
 511            waitingList.add(wi);
 512            queueUpdated=true;
 513        }
 514
 515        if (queueUpdated)   scheduleMaintenance();
 516        return null;
 517    }
 518    
 519    /**
 520     * @deprecated as of 1.311
 521     *      Use {@link #schedule(Task, int)} 
 522     */
 523    public synchronized boolean add(Task p, int quietPeriod) {
 524    	return schedule(p, quietPeriod)!=null;
 525    }
 526
 527    public synchronized WaitingItem schedule(Task p, int quietPeriod) {
 528    	return schedule(p, quietPeriod, new Action[0]);
 529    }
 530
 531    /**
 532     * @deprecated as of 1.311
 533     *      Use {@link #schedule(Task, int, Action...)} 
 534     */
 535    public synchronized boolean add(Task p, int quietPeriod, Action... actions) {
 536    	return schedule(p, quietPeriod, actions)!=null;
 537    }
 538
 539    /**
 540     * Convenience wrapper method around {@link #schedule(Task, int, List)}
 541     */
 542    public synchronized WaitingItem schedule(Task p, int quietPeriod, Action... actions) {
 543    	return schedule(p, quietPeriod, Arrays.asList(actions));
 544    }
 545
 546    /**
 547     * Cancels the item in the queue. If the item is scheduled more than once, cancels the first occurrence.
 548     *
 549     * @return true if the project was indeed in the queue and was removed.
 550     *         false if this was no-op.
 551     */
 552    public synchronized boolean cancel(Task p) {
 553        LOGGER.fine("Cancelling " + p.getFullDisplayName());
 554        for (Iterator<WaitingItem> itr = waitingList.iterator(); itr.hasNext();) {
 555            Item item = itr.next();
 556            if (item.task.equals(p)) {
 557                itr.remove();
 558                item.onCancelled();
 559                return true;
 560            }
 561        }
 562        // use bitwise-OR to make sure that both branches get evaluated all the time
 563        return blockedProjects.cancel(p)!=null | buildables.cancel(p)!=null;
 564    }
 565    
 566    public synchronized boolean cancel(Item item) {
 567        LOGGER.fine("Cancelling " + item.task.getFullDisplayName() + " item#" + item.id);
 568        // use bitwise-OR to make sure that all the branches get evaluated all the time
 569        boolean r = (item instanceof WaitingItem && waitingList.remove(item)) | blockedProjects.remove(item) | buildables.remove(item);
 570        if(r)
 571            item.onCancelled();
 572        return r;
 573    }
 574
 575    public synchronized boolean isEmpty() {
 576        return waitingList.isEmpty() && blockedProjects.isEmpty() && buildables.isEmpty() && pendings.isEmpty();
 577    }
 578
 579    private synchronized WaitingItem peek() {
 580        return waitingList.iterator().next();
 581    }
 582
 583    /**
 584     * Gets a snapshot of items in the queue.
 585     *
 586     * Generally speaking the array is sorted such that the items that are most likely built sooner are
 587     * at the end.
 588     */
 589    @Exported(inline=true)
 590    public synchronized Item[] getItems() {
 591        Item[] r = new Item[waitingList.size() + blockedProjects.size() + buildables.size() + pendings.size()];
 592        waitingList.toArray(r);
 593        int idx = waitingList.size();
 594        for (BlockedItem p : blockedProjects.values())
 595            r[idx++] = p;
 596        for (BuildableItem p : reverse(buildables.values()))
 597            r[idx++] = p;
 598        for (BuildableItem p : reverse(pendings.values()))
 599            r[idx++] = p;
 600        return r;
 601    }
 602    
 603    public synchronized Item getItem(int id) {
 604    	for (Item item: waitingList) if (item.id == id) return item;
 605    	for (Item item: blockedProjects) if (item.id == id) return item;
 606    	for (Item item: buildables) if (item.id == id) return item;
 607        for (Item item: pendings) if (item.id == id) return item;
 608    	return null;
 609    }
 610
 611    /**
 612     * Gets all the {@link BuildableItem}s that are waiting for an executor in the given {@link Computer}.
 613     */
 614    public synchronized List<BuildableItem> getBuildableItems(Computer c) {
 615        List<BuildableItem> result = new ArrayList<BuildableItem>();
 616        _getBuildableItems(c, buildables, result);
 617        _getBuildableItems(c, pendings, result);
 618        return result;
 619    }
 620
 621    private void _getBuildableItems(Computer c, ItemList<BuildableItem> col, List<BuildableItem> result) {
 622        Node node = c.getNode();
 623        for (BuildableItem p : col.values()) {
 624            if (node.canTake(p.task) == null)
 625                result.add(p);
 626        }
 627    }
 628
 629    /**
 630     * Gets the snapshot of all {@link BuildableItem}s.
 631     */
 632    public synchronized List<BuildableItem> getBuildableItems() {
 633        ArrayList<BuildableItem> r = new ArrayList<BuildableItem>(buildables.values());
 634        r.addAll(pendings.values());
 635        return r;
 636    }
 637
 638    /**
 639     * Gets the snapshot of all {@link BuildableItem}s.
 640     */
 641    public synchronized List<BuildableItem> getPendingItems() {
 642        return new ArrayList<BuildableItem>(pendings.values());
 643    }
 644    
 645    synchronized Set<Task> getUnblockedQueuedTasks() {
 646        Set<Task> unblockedQueuedTasks = new HashSet<Task>();
 647        for (Item item: waitingList) {
 648            unblockedQueuedTasks.add(item.task);
 649        }
 650        for (Item item: pendings) {
 651            unblockedQueuedTasks.add(item.task);
 652        }
 653        for (Item item: buildables) {
 654            unblockedQueuedTasks.add(item.task);
 655        }
 656        return unblockedQueuedTasks;
 657    }
 658
 659    /**
 660     * Is the given task currently pending execution?
 661     */
 662    public synchronized boolean isPending(Task t) {
 663        for (BuildableItem i : pendings)
 664            if (i.task.equals(t))
 665                return true;
 666        return false;
 667    }
 668
 669    /**
 670     * How many {@link BuildableItem}s are assigned for the given label?
 671     */
 672    public synchronized int countBuildableItemsFor(Label l) {
 673        int r = 0;
 674        for (BuildableItem bi : buildables.values())
 675            if(bi.task.getAssignedLabel()==l)
 676                r++;
 677        for (BuildableItem bi : pendings.values())
 678            if(bi.task.getAssignedLabel()==l)
 679                r++;
 680        return r;
 681    }
 682
 683    /**
 684     * Gets the information about the queue item for the given project.
 685     *
 686     * @return null if the project is not in the queue.
 687     */
 688    public synchronized Item getItem(Task t) {
 689        BlockedItem bp = blockedProjects.get(t);
 690        if (bp!=null)
 691            return bp;
 692        BuildableItem bi = buildables.get(t);
 693        if(bi!=null)
 694            return bi;
 695        bi = pendings.get(t);
 696        if(bi!=null)
 697            return bi;
 698
 699        for (Item item : waitingList) {
 700            if (item.task == t)
 701                return item;
 702        }
 703        return null;
 704    }
 705
 706    /**
 707     * Gets the information about the queue item for the given project.
 708     *
 709     * @return null if the project is not in the queue.
 710     */
 711    public synchronized List<Item> getItems(Task t) {
 712    	List<Item> result =new ArrayList<Item>();
 713    	result.addAll(blockedProjects.getAll(t));
 714    	result.addAll(buildables.getAll(t));
 715        result.addAll(pendings.getAll(t));
 716        for (Item item : waitingList) {
 717            if (item.task == t)
 718                result.add(item);
 719        }
 720        return result;
 721    }
 722
 723    /**
 724     * Left for backward compatibility.
 725     *
 726     * @see #getItem(Task)
 727    public synchronized Item getItem(AbstractProject p) {
 728        return getItem((Task) p);
 729    }
 730     */
 731
 732    /**
 733     * Returns true if this queue contains the said project.
 734     */
 735    public synchronized boolean contains(Task t) {
 736        if (blockedProjects.containsKey(t) || buildables.containsKey(t) || pendings.containsKey(t))
 737            return true;
 738        for (Item item : waitingList) {
 739            if (item.task == t)
 740                return true;
 741        }
 742        return false;
 743    }
 744
 745    /**
 746     * Called by the executor to fetch something to build next.
 747     * <p>
 748     * This method blocks until a next project becomes buildable.
 749     */
 750    public synchronized WorkUnit pop() throws InterruptedException {
 751        final Executor exec = Executor.currentExecutor();
 752
 753        if (exec instanceof OneOffExecutor) {
 754            OneOffExecutor ooe = (OneOffExecutor) exec;
 755            final WorkUnit wu = ooe.getWorkUnit();
 756            pendings.remove(wu.context.item);
 757            return wu;
 758        }
 759
 760        try {
 761            while (true) {
 762                final JobOffer offer = new JobOffer(exec);
 763                long sleep = -1;
 764
 765                // consider myself parked
 766                assert !parked.containsKey(exec);
 767                parked.put(exec, offer);
 768
 769                // reuse executor thread to do a queue maintenance.
 770                // at the end of this we get all the buildable jobs
 771                // in the buildables field.
 772                maintain();
 773
 774                // allocate buildable jobs to executors
 775                Iterator<BuildableItem> itr = buildables.iterator();
 776                while (itr.hasNext()) {
 777                    BuildableItem p = itr.next();
 778
 779                    // one last check to make sure this build is not blocked.
 780                    if (isBuildBlocked(p.task)) {
 781                        itr.remove();
 782                        blockedProjects.put(p.task,new BlockedItem(p));
 783                        continue;
 784                    }
 785
 786                    List<JobOffer> candidates = new ArrayList<JobOffer>(parked.size());
 787                    for (JobOffer j : parked.values())
 788                        if(j.canTake(p.task))
 789                            candidates.add(j);
 790
 791                    MappingWorksheet ws = new MappingWorksheet(p, candidates);
 792                    Mapping m = loadBalancer.map(p.task, ws);
 793                    if (m == null)
 794                        // if we couldn't find the executor that fits,
 795                        // just leave it in the buildables list and
 796                        // check if we can execute other projects
 797                        continue;
 798
 799                    // found a matching executor. use it.
 800                    WorkUnitContext wuc = new WorkUnitContext(p);
 801                    m.execute(wuc);
 802
 803                    itr.remove();
 804                    if (!wuc.getWorkUnits().isEmpty())
 805                        pendings.add(p);
 806                }
 807
 808                // we went over all the buildable projects and awaken
 809                // all the executors that got work to do. now, go to sleep
 810                // until this thread is awakened. If this executor assigned a job to
 811                // itself above, the block method will return immediately.
 812
 813                if (!waitingList.isEmpty()) {
 814                    // wait until the first item in the queue is due
 815                    sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis();
 816                    if (sleep < 100) sleep = 100;    // avoid wait(0)
 817                }
 818
 819                if (sleep == -1)
 820                    offer.event.block();
 821                else
 822                    offer.event.block(sleep);
 823
 824                // retract the offer object
 825                assert parked.get(exec) == offer;
 826                parked.remove(exec);
 827
 828                // am I woken up because I have a project to build?
 829                if (offer.workUnit != null) {
 830                    // if so, just build it
 831                    LOGGER.fine("Pop returning " + offer.workUnit + " for " + exec.getName());
 832
 833                    // TODO: I think this has to be done by the last executor that leaves the pop(), not by main executor
 834                    if (offer.workUnit.isMainWork())
 835                        pendings.remove(offer.workUnit.context.item);
 836
 837                    return offer.workUnit;
 838                }
 839                // otherwise run a queue maintenance
 840            }
 841        } finally {
 842            // remove myself from the parked list
 843            JobOffer offer = parked.remove(exec);
 844            if (offer != null && offer.workUnit != null) {
 845                // we are already assigned a project, but now we can't handle it.
 846                offer.workUnit.context.abort(new AbortException());
 847            }
 848
 849            // since this executor might have been chosen for
 850            // maintenance, schedule another one. Worst case
 851            // we'll just run a pointless maintenance, and that's
 852            // fine.
 853            scheduleMaintenance();
 854        }
 855    }
 856
 857    /**
 858     * Checks the queue and runs anything that can be run.
 859     *
 860     * <p>
 861     * When conditions are changed, this method should be invoked.
 862     * <p>
 863     * This wakes up one {@link Executor} so that it will maintain a queue.
 864     */
 865    public synchronized void scheduleMaintenance() {
 866        // this code assumes that after this method is called
 867        // no more executors will be offered job except by
 868        // the pop() code.
 869        for (Entry<Executor, JobOffer> av : parked.entrySet()) {
 870            if (av.getValue().workUnit == null) {
 871                av.getValue().event.signal();
 872                return;
 873            }
 874        }
 875    }
 876
 877    /**
 878     * Checks if the given task is blocked.
 879     */
 880    private boolean isBuildBlocked(Task t) {
 881        return t.isBuildBlocked() || !canRun(t.getResourceList());
 882    }
 883
 884    /**
 885     * Make sure we don't queue two tasks of the same project to be built
 886     * unless that project allows concurrent builds.
 887     */
 888    private boolean allowNewBuildableTask(Task t) {
 889        try {
 890            if (t.isConcurrentBuild())
 891                return true;
 892        } catch (AbstractMethodError e) {
 893            // earlier versions don't have the "isConcurrentBuild" method, so fall back gracefully
 894        }
 895        return !buildables.containsKey(t) && !pendings.containsKey(t);
 896    }
 897
 898    /**
 899     * Queue maintenance.
 900     * <p>
 901     * Move projects between {@link #waitingList}, {@link #blockedProjects}, and {@link #buildables}
 902     * appropriately.
 903     */
 904    public synchronized void maintain() {
 905        if (LOGGER.isLoggable(Level.FINE))
 906            LOGGER.fine("Queue maintenance started " + this);
 907
 908        // blocked -> buildable
 909        Iterator<BlockedItem> itr = blockedProjects.values().iterator();
 910        while (itr.hasNext()) {
 911            BlockedItem p = itr.next();
 912            if (!isBuildBlocked(p.task) && allowNewBuildableTask(p.task)) {
 913                // ready to be executed
 914                LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked");
 915                itr.remove();
 916                makeBuildable(new BuildableItem(p));
 917            }
 918        }
 919
 920        while (!waitingList.isEmpty()) {
 921            WaitingItem top = peek();
 922
 923            if (!top.timestamp.before(new GregorianCalendar()))
 924                return; // finished moving all ready items from queue
 925
 926            waitingList.remove(top);
 927            Task p = top.task;
 928            if (!isBuildBlocked(p) && allowNewBuildableTask(p)) {
 929                // ready to be executed immediately
 930                LOGGER.fine(p.getFullDisplayName() + " ready to build");
 931                makeBuildable(new BuildableItem(top));
 932            } else {
 933                // this can't be built now because another build is in progress
 934                // set this project aside.
 935                LOGGER.fine(p.getFullDisplayName() + " is blocked");
 936                blockedProjects.put(p,new BlockedItem(top));
 937            }
 938        }
 939
 940        final QueueSorter s = sorter;
 941        if (s != null)
 942        	s.sortBuildableItems(buildables);
 943    }
 944
 945    private void makeBuildable(BuildableItem p) {
 946        if(Hudson.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) {
 947            ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() {
 948                public String hash(Node node) {
 949                    return node.getNodeName();
 950                }
 951            });
 952            Hudson h = Hudson.getInstance();
 953            hash.add(h, h.getNumExecutors()*100);
 954            for (Node n : h.getNodes())
 955                hash.add(n,n.getNumExecutors()*100);
 956
 957            Label lbl = p.task.getAssignedLabel();
 958            for (Node n : hash.list(p.task.getFullDisplayName())) {
 959                Computer c = n.toComputer();
 960                if (c==null || c.isOffline())    continue;
 961                if (lbl!=null && !lbl.contains(n))  continue;
 962                c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
 963                pendings.add(p);
 964                return;
 965            }
 966            // if the execution get here, it means we couldn't schedule it anywhere.
 967            // so do the scheduling like other normal jobs.
 968        }
 969        
 970        buildables.put(p.task,p);
 971    }
 972
 973    public static boolean ifBlockedByHudsonShutdown(Task task) {
 974        return Hudson.getInstance().isQuietingDown() && !(task instanceof NonBlockingTask);
 975    }
 976
 977    public Api getApi() {
 978        return new Api(this);
 979    }
 980
 981    /**
 982     * Marks {@link Task}s that are not persisted.
 983     * @since 1.311
 984     */
 985    public interface TransientTask extends Task {}
 986
 987    /**
 988     * Marks {@link Task}s that do not consume {@link Executor}.
 989     * @see OneOffExecutor
 990     * @since 1.318
 991     */
 992    public interface FlyweightTask extends Task {}
 993
 994    /**
 995     * Marks {@link Task}s that are not affected by the {@linkplain Hudson#isQuietingDown()}  quieting down},
 996     * because these tasks keep other tasks executing.
 997     *
 998     * @since 1.336 
 999     */
1000    public interface NonBlockingTask extends Task {}
1001
1002    /**
1003     * Task whose execution is controlled by the queue.
1004     *
1005     * <p>
1006     * {@link #equals(Object) Value equality} of {@link Task}s is used
1007     * to collapse two tasks into one. This is used to avoid infinite
1008     * queue backlog.
1009     *
1010     * <p>
1011     * Pending {@link Task}s are persisted when Hudson shuts down, so
1012     * it needs to be persistable via XStream. To create a non-persisted
1013     * transient Task, extend {@link TransientTask} marker interface.
1014     *
1015     * <p>
1016     * Plugins are encouraged to extend from {@link AbstractQueueTask}
1017     * instead of implementing this interface directly, to maintain
1018     * compatibility with future changes to this interface.
1019     *
1020     * <p>
1021     * For historical reasons, {@link Task} object by itself
1022     * also represents the "primary" sub-task (and as implied by this
1023     * design, a {@link Task} must have at least one sub-task.)
1024     * Most of the time, the primary subtask is the only sub task.
1025     */
1026    public interface Task extends ModelObject, SubTask {
1027        /**
1028         * Returns true if the execution should be blocked
1029         * for temporary reasons.
1030         *
1031         * <p>
1032         * Short-hand for {@code getCauseOfBlockage()!=null}.
1033         */
1034        boolean isBuildBlocked();
1035
1036        /**
1037         * @deprecated as of 1.330
1038         *      Use {@link CauseOfBlockage#getShortDescription()} instead.
1039         */
1040        String getWhyBlocked();
1041
1042        /**
1043         * If the execution of this task should be blocked for temporary reasons,
1044         * this method returns a non-null object explaining why.
1045         *
1046         * <p>
1047         * Otherwise this method returns null, indicating that the build can proceed right away.
1048         *
1049         * <p>
1050         * This can be used to define mutual exclusion that goes beyond
1051         * {@link #getResourceList()}.
1052         */
1053        CauseOfBlockage getCauseOfBlockage();
1054
1055        /**
1056         * Unique name of this task.
1057         *
1058         * <p>
1059         * This method is no longer used, left here for compatibility. Just return {@link #getDisplayName()}.
1060         */
1061        String getName();
1062
1063        /**
1064         * @see hudson.model.Item#getFullDisplayName()
1065         */
1066        String getFullDisplayName();
1067
1068        /**
1069         * Checks the permission to see if the current user can abort this executable.
1070         * Returns normally from this method if it's OK.
1071         *
1072         * @throws org.acegisecurity.AccessDeniedException if the permission is not granted.
1073         */
1074        void checkAbortPermission();
1075
1076        /**
1077         * Works just like {@link #checkAbortPermission()} except it indicates the status by a return value,
1078         * instead of exception.
1079         */
1080        boolean hasAbortPermission();
1081        
1082        /**
1083         * Returns the URL of this task relative to the context root of the application.
1084         *
1085         * <p>
1086         * When the user clicks an item in the queue, this is the page where the user is taken to.
1087         * Hudson expects the current instance to be bound to the URL returned by this method.
1088         *
1089         * @return
1090         *      URL that ends with '/'.
1091         */
1092        String getUrl();
1093        
1094        /**
1095         * True if the task allows concurrent builds
1096         *
1097         * @since 1.338
1098         */
1099        boolean isConcurrentBuild();
1100
1101        /**
1102         * Obtains the {@link SubTask}s that constitute this task.
1103         *
1104         * <p>
1105         * The collection returned by this method must also contain the primary {@link SubTask}
1106         * represented by this {@link Task} object itself as the first element.
1107         * The returned value is read-only.
1108         *
1109         * <p>
1110         * At least size 1.
1111         *
1112         * <p>
1113         * Since this is a newly added method, the invocation may results in {@link AbstractMethodError}.
1114         * Use {@link Tasks#getSubTasksOf(Task)} that avoids this.
1115         *
1116         * @since 1.377
1117         */
1118        Collection<? extends SubTask> getSubTasks();
1119    }
1120
1121    /**
1122     * Represents the real meat of the computation run by {@link Executor}.
1123     *
1124     * <h2>Views</h2>
1125     * <p>
1126     * Implementation must have <tt>executorCell.jelly</tt>, which is
1127     * used to render the HTML that indicates this executable is executing.
1128     */
1129    public interface Executable extends Runnable {
1130        /**
1131         * Task from which this executable was created.
1132         * Never null.
1133         *
1134         * <p>
1135         * Since this method went through a signature change in 1.377, the invocation may results in
1136         * {@link AbstractMethodError}.
1137         * Use {@link Executables#getParentOf(Executable)} that avoids this.
1138         */
1139        SubTask getParent();
1140
1141        /**
1142         * Called by {@link Executor} to perform the task
1143         */
1144        void run();
1145        
1146        /**
1147         * Estimate of how long will it take to execute this executable.
1148         * Measured in milliseconds.
1149         * 
1150         * Please, consider using {@link Executables#getEstimatedDurationFor(Executable)}
1151         * to protected against AbstractMethodErrors!
1152         *
1153         * @return -1 if it's impossible to estimate.
1154         * @since 1.383
1155         */
1156        long getEstimatedDuration();
1157
1158        /**
1159         * Used to render the HTML. Should be a human readable text of what this executable is.
1160         */
1161        @Override String toString();
1162    }
1163
1164    /**
1165     * Item in a queue.
1166     */
1167    @ExportedBean(defaultVisibility = 999)
1168    public static abstract class Item extends Actionable {
1169        /**
1170         * VM-wide unique ID that tracks the {@link Task} as it moves through different stages
1171         * in the queue (each represented by different subtypes of {@link Item}.
1172         */
1173        //TODO: review and check whether we can do it private
1174    	public final int id;
1175    	
1176		/**
1177         * Project to be built.
1178         */
1179        //TODO: review and check whether we can do it private
1180        @Exported
1181        public final Task task;
1182
1183        private /*almost final*/ transient FutureImpl future;
1184
1185        /**
1186         * Build is blocked because another build is in progress,
1187         * required {@link Resource}s are not available, or otherwise blocked
1188         * by {@link Task#isBuildBlocked()}.
1189         */
1190        @Exported
1191        public boolean isBlocked() { return this instanceof BlockedItem; }
1192
1193        /**
1194         * Build is waiting the executor to become available.
1195         * This flag is only used in {@link Queue#getItems()} for
1196         * 'pseudo' items that are actually not really in the queue.
1197         */
1198        @Exported
1199        public boolean isBuildable() { return this instanceof BuildableItem; }
1200
1201        /**
1202         * True if the item is starving for an executor for too long.
1203         */
1204        @Exported
1205        public boolean isStuck() { return false; }
1206
1207        /**
1208         * Can be used to wait for the completion (either normal, abnormal, or cancellation) of the {@link Task}.
1209         * <p>
1210         * Just like {@link #id}, the same object tracks various stages of the queue.
1211         */
1212        public Future<Executable> getFuture() { return future; }
1213
1214        /**
1215         * Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue.
1216         *
1217         * @return can be empty but never null
1218         * @since 1.343
1219         */
1220        public final List<Cause> getCauses() {
1221            CauseAction ca = getAction(CauseAction.class);
1222            if (ca!=null)
1223                return Collections.unmodifiableList(ca.getCauses());
1224            return Collections.emptyList();
1225        }
1226
1227        protected Item(Task task, List<Action> actions, int id, FutureImpl future) {
1228            this.task = task;
1229            this.id = id;
1230            this.future = future;
1231            for (Action action: actions) addAction(action);
1232        }
1233        
1234        protected Item(Item item) {
1235        	this(item.task, item.getActions(), item.id, item.future);
1236        }
1237
1238        public int getId() {
1239            return id;
1240        }
1241
1242        public Task getTask() {
1243            return task;
1244        }
1245
1246        /**
1247         * Gets a human-readable status message describing why it's in the queue.
1248         */
1249        @Exported
1250        public final String getWhy() {
1251            CauseOfBlockage cob = getCauseOfBlockage();
1252            return cob!=null ? cob.getShortDescription() : null;
1253        }
1254
1255        /**
1256         * Gets an object that describes why this item is in the queue.
1257         */
1258        //TODO: review and check whether we can do it private
1259        public abstract CauseOfBlockage getCauseOfBlockage();
1260
1261        /**
1262         * Gets a human-readable message about the parameters of this item
1263         * @return String
1264         */
1265        @Exported
1266        public String getParams() {
1267        	StringBuilder s = new StringBuilder();
1268        	for(Action action : getActions()) {
1269        		if(action instanceof ParametersAction) {
1270        			ParametersAction pa = (ParametersAction)action;
1271        			for (ParameterValue p : pa.getParameters()) {
1272        				s.append('\n').append(p.getShortDescription());
1273        			}
1274        		}
1275        	}
1276        	return s.toString();
1277        }
1278        
1279        public boolean hasCancelPermission() {
1280            return task.hasAbortPermission();
1281        }
1282        
1283        public String getDisplayName() {
1284			// TODO Auto-generated method stub
1285			return null;
1286		}
1287
1288		public String getSearchUrl() {
1289			// TODO Auto-generated method stub
1290			return null;
1291		}
1292
1293        /**
1294         * Called from queue.jelly.
1295         */
1296        public HttpResponse doCancelQueue() throws IOException, ServletException {
1297        	Hudson.getInstance().getQueue().cancel(this);
1298            return HttpResponses.forwardToPreviousPage();
1299        }
1300
1301        /**
1302         * Participates in the cancellation logic to set the {@link #future} accordingly.
1303         */
1304        /*package*/ void onCancelled() {
1305            future.setAsCancelled();
1306        }
1307
1308        private Object readResolve() {
1309            this.future = new FutureImpl(task);
1310            return this;
1311        }
1312
1313        @Override
1314        public String toString() {
1315            return getClass().getName()+':'+task.toString();
1316        }
1317    }
1318    
1319    /**
1320     * An optional interface for actions on Queue.Item.
1321     * Lets the action cooperate in queue management.
1322     * 
1323     * @since 1.300-ish.
1324     */
1325    public interface QueueAction extends Action {
1326    	/**
1327    	 * Returns whether the new item should be scheduled. 
1328    	 * An action should return true if the associated task is 'different enough' to warrant a separate execution.
1329    	 */
1330    	public boolean shouldSchedule(List<Action> actions);
1331    }
1332
1333    /**
1334     * Extension point for deciding if particular job should be scheduled or not.
1335     *
1336     * <p>
1337     * This handler is consulted every time someone tries to submit a task to the queue.
1338     * If any of the registered handlers returns false, the task will not be added
1339     * to the queue, and the task will never get executed. 
1340     *
1341     * <p>
1342     * This extension point is still a subject to change, as we are seeking more
1343     * comprehensive Queue pluggability. See HUDSON-2072.
1344     *
1345     * @since 1.316
1346     */
1347    public static abstract class QueueDecisionHandler implements ExtensionPoint {
1348    	/**
1349    	 * Returns whether the new item should be scheduled.
1350    	 */
1351    	public abstract boolean shouldSchedule(Task p, List<Action> actions);
1352    	    	
1353    	/**
1354    	 * All registered {@link QueueDecisionHandler}s
1355    	 * @return
1356    	 */
1357    	public static ExtensionList<QueueDecisionHandler> all() {
1358    		return Hudson.getInstance().getExtensionList(QueueDecisionHandler.class);
1359    	}
1360    }
1361    
1362    /**
1363     * {@link Item} in the {@link Queue#waitingList} stage.
1364     */
1365    public static final class WaitingItem extends Item implements Comparable<WaitingItem> {
1366    	private static final AtomicInteger COUNTER = new AtomicInteger(0);
1367    	
1368        /**
1369         * This item can be run after this time.
1370         */
1371        //TODO: review and check whether we can do it private
1372        @Exported
1373        public Calendar timestamp;
1374
1375        public Calendar getTimestamp() {
1376            return timestamp;
1377        }
1378
1379        public WaitingItem(Calendar timestamp, Task project, List<Action> actions) {
1380            super(project, actions, COUNTER.incrementAndGet(), new FutureImpl(project));
1381            this.timestamp = timestamp;
1382        }
1383        
1384        public int compareTo(WaitingItem that) {
1385            int r = this.timestamp.getTime().compareTo(that.timestamp.getTime());
1386            if (r != 0) return r;
1387
1388            return this.id - that.id;
1389        }
1390
1391        public CauseOfBlockage getCauseOfBlockage() {
1392            long diff = timestamp.getTimeInMillis() - System.currentTimeMillis();
1393            if (diff > 0)
1394                return CauseOfBlockage.fromMessage(Messages._Queue_InQuietPeriod(Util.getTimeSpanString(diff)));
1395            else
1396                return CauseOfBlockage.fromMessage(Messages._Queue_Unknown());
1397        }
1398    }
1399
1400    /**
1401     * Common part between {@link BlockedItem} and {@link BuildableItem}.
1402     */
1403    public static abstract class NotWaitingItem extends Item {
1404        /**
1405         * When did this job exit the {@link Qu

Large files files are truncated, but you can click here to view the full file