File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\model\Queue.java.html

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 */
25 package hudson.model;
26
27 import com.google.common.collect.ImmutableList;
28 import hudson.AbortException;
29 import hudson.BulkChange;
30 import hudson.CopyOnWrite;
31 import hudson.ExtensionList;
32 import hudson.ExtensionPoint;
33 import hudson.Util;
34 import hudson.XmlFile;
35 import hudson.init.Initializer;
36 import static hudson.init.InitMilestone.JOB_LOADED;
37 import static hudson.util.Iterators.reverse;
38
39 import hudson.cli.declarative.CLIMethod;
40 import hudson.cli.declarative.CLIResolver;
41 import hudson.model.labels.LabelAssignmentAction;
42 import hudson.model.queue.AbstractQueueTask;
43 import hudson.model.queue.Executables;
44 import hudson.model.queue.SubTask;
45 import hudson.model.queue.FutureImpl;
46 import hudson.model.queue.MappingWorksheet;
47 import hudson.model.queue.MappingWorksheet.Mapping;
48 import hudson.model.queue.QueueSorter;
49 import hudson.model.queue.QueueTaskDispatcher;
50 import hudson.model.queue.Tasks;
51 import hudson.model.queue.WorkUnit;
52 import hudson.model.Node.Mode;
53 import hudson.model.listeners.SaveableListener;
54 import hudson.model.queue.CauseOfBlockage;
55 import hudson.model.queue.FoldableAction;
56 import hudson.model.queue.CauseOfBlockage.BecauseLabelIsBusy;
57 import hudson.model.queue.CauseOfBlockage.BecauseNodeIsOffline;
58 import hudson.model.queue.CauseOfBlockage.BecauseLabelIsOffline;
59 import hudson.model.queue.CauseOfBlockage.BecauseNodeIsBusy;
60 import hudson.model.queue.WorkUnitContext;
61 import hudson.triggers.SafeTimerTask;
62 import hudson.triggers.Trigger;
63 import hudson.util.OneShotEvent;
64 import hudson.util.TimeUnit2;
65 import hudson.util.XStream2;
66 import hudson.util.ConsistentHash;
67 import hudson.util.ConsistentHash.Hash;
68
69 import java.io.BufferedReader;
70 import java.io.File;
71 import java.io.FileInputStream;
72 import java.io.IOException;
73 import java.io.InputStreamReader;
74 import java.lang.ref.WeakReference;
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Calendar;
78 import java.util.Collection;
79 import java.util.Collections;
80 import java.util.GregorianCalendar;
81 import java.util.HashMap;
82 import java.util.HashSet;
83 import java.util.Iterator;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.NoSuchElementException;
87 import java.util.Set;
88 import java.util.Timer;
89 import java.util.TreeSet;
90 import java.util.Map.Entry;
91 import java.util.concurrent.atomic.AtomicInteger;
92 import java.util.concurrent.Future;
93 import java.util.concurrent.atomic.AtomicLong;
94 import java.util.logging.Level;
95 import java.util.logging.Logger;
96
97 import javax.servlet.ServletException;
98
99 import jenkins.model.Jenkins;
100 import org.acegisecurity.AccessDeniedException;
101 import org.kohsuke.stapler.HttpResponse;
102 import org.kohsuke.stapler.HttpResponses;
103 import org.kohsuke.stapler.export.Exported;
104 import org.kohsuke.stapler.export.ExportedBean;
105
106 import com.thoughtworks.xstream.XStream;
107 import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
108 import org.kohsuke.stapler.QueryParameter;
109 import org.kohsuke.stapler.interceptor.RequirePOST;
110
111 /**
112 * Build queue.
113 *
114 * <p>
115 * This class implements the core scheduling logic. {@link Task} represents the executable
116 * task that are placed in the queue. While in the queue, it's wrapped into {@link Item}
117 * so that we can keep track of additional data used for deciding what to exeucte when.
118 *
119 * <p>
120 * Items in queue goes through several stages, as depicted below:
121 * <pre>
122 * (enter) --> waitingList --+--> blockedProjects
123 * | ^
124 * | |
125 * | v
126 * +--> buildables ---> pending ---> (executed)
127 * </pre>
128 *
129 * <p>
130 * In addition, at any stage, an item can be removed from the queue (for example, when the user
131 * cancels a job in the queue.) See the corresponding field for their exact meanings.
132 *
133 * @author Kohsuke Kawaguchi
134 */
135 @ExportedBean
136 public class Queue extends ResourceController implements Saveable {
137 /**
138 * Items that are waiting for its quiet period to pass.
139 *
140 * <p>
141 * This consists of {@link Item}s that cannot be run yet
142 * because its time has not yet come.
143 */
144 private final Set<WaitingItem> waitingList = new TreeSet<WaitingItem>();
145
146 /**
147 * {@link Task}s that can be built immediately
148 * but blocked because another build is in progress,
149 * required {@link Resource}s are not available,
150 * blocked via {@link QueueTaskDispatcher#canRun(Item)},
151 * or otherwise blocked by {@link Task#isBuildBlocked()}.
152 */
153 private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
154
155 /**
156 * {@link Task}s that can be built immediately
157 * that are waiting for available {@link Executor}.
158 * This list is sorted in such a way that earlier items are built earlier.
159 */
160 private final ItemList<BuildableItem> buildables = new ItemList<BuildableItem>();
161
162 /**
163 * {@link Task}s that are being handed over to the executor, but execution
164 * has not started yet.
165 */
166 private final ItemList<BuildableItem> pendings = new ItemList<BuildableItem>();
167
168 private final CachedItemList itemsView = new CachedItemList();
169
170 /**
171 * Maintains a copy of {@link Queue#getItems()}
172 *
173 * @see Queue#getApproximateItemsQuickly()
174 */
175 private class CachedItemList {
176 /**
177 * The current cached value.
178 */
179 @CopyOnWrite
180 private volatile List<Item> itemsView = Collections.emptyList();
181 /**
182 * When does the cache info expire?
183 */
184 private final AtomicLong expires = new AtomicLong();
185
186 List<Item> get() {
187 long t = System.currentTimeMillis();
188 long d = expires.get();
189 if (t>d) {// need to refresh the cache
190 long next = t+1000;
191 if (expires.compareAndSet(d,next)) {
192 // avoid concurrent cache update via CAS.
193 // if the getItems() lock is contended,
194 // some threads will end up serving stale data,
195 // but that's OK.
196 itemsView = ImmutableList.copyOf(getItems());
197 }
198 }
199 return itemsView;
200 }
201 }
202
203 /**
204 * Data structure created for each idle {@link Executor}.
205 * This is a job offer from the queue to an executor.
206 *
207 * <p>
208 * An idle executor (that calls {@link Queue#pop()} creates
209 * a new {@link JobOffer} and gets itself {@linkplain Queue#parked parked},
210 * and we'll eventually hand out an {@link #workUnit} to build.
211 */
212 public class JobOffer extends MappingWorksheet.ExecutorSlot {
213 public final Executor executor;
214
215 /**
216 * Used to wake up an executor, when it has an offered
217 * {@link Project} to build.
218 */
219 private final OneShotEvent event = new OneShotEvent(Queue.this);
220
221 /**
222 * The work unit that this {@link Executor} is going to handle.
223 * (Or null, in which case event is used to trigger a queue maintenance.)
224 */
225 private WorkUnit workUnit;
226
227 private JobOffer(Executor executor) {
228 this.executor = executor;
229 }
230
231 @Override
232 protected void set(WorkUnit p) {
233 assert this.workUnit == null;
234 this.workUnit = p;
235 event.signal();
236 }
237
238 @Override
239 public Executor getExecutor() {
240 return executor;
241 }
242
243 /**
244 * Verifies that the {@link Executor} represented by this object is capable of executing the given task.
245 */
246 public boolean canTake(BuildableItem item) {
247 Node node = getNode();
248 if (node==null) return false; // this executor is about to die
249
250 if(node.canTake(item)!=null)
251 return false; // this node is not able to take the task
252
253 for (QueueTaskDispatcher d : QueueTaskDispatcher.all())
254 if (d.canTake(node,item)!=null)
255 return false;
256
257 return isAvailable();
258 }
259
260 /**
261 * Is this executor ready to accept some tasks?
262 */
263 public boolean isAvailable() {
264 return workUnit == null && !executor.getOwner().isOffline() && executor.getOwner().isAcceptingTasks();
265 }
266
267 public Node getNode() {
268 return executor.getOwner().getNode();
269 }
270
271 public boolean isNotExclusive() {
272 return getNode().getMode() == Mode.NORMAL;
273 }
274 }
275
276 /**
277 * The executors that are currently waiting for a job to run.
278 */
279 private final Map<Executor,JobOffer> parked = new HashMap<Executor,JobOffer>();
280
281 private volatile transient LoadBalancer loadBalancer;
282
283 private volatile transient QueueSorter sorter;
284
285 public Queue(LoadBalancer loadBalancer) {
286 this.loadBalancer = loadBalancer.sanitize();
287 // if all the executors are busy doing something, then the queue won't be maintained in
288 // timely fashion, so use another thread to make sure it happens.
289 new MaintainTask(this);
290 }
291
292 public LoadBalancer getLoadBalancer() {
293 return loadBalancer;
294 }
295
296 public void setLoadBalancer(LoadBalancer loadBalancer) {
297 if(loadBalancer==null) throw new IllegalArgumentException();
298 this.loadBalancer = loadBalancer.sanitize();
299 }
300
301 public QueueSorter getSorter() {
302 return sorter;
303 }
304
305 public void setSorter(QueueSorter sorter) {
306 this.sorter = sorter;
307 }
308
309 /**
310 * Loads the queue contents that was {@link #save() saved}.
311 */
312 public synchronized void load() {
313 try {
314 // first try the old format
315 File queueFile = getQueueFile();
316 if (queueFile.exists()) {
317 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(queueFile)));
318 try {
319 String line;
320 while ((line = in.readLine()) != null) {
321 AbstractProject j = Jenkins.getInstance().getItemByFullName(line, AbstractProject.class);
322 if (j != null)
323 j.scheduleBuild();
324 }
325 } finally {
326 in.close();
327 }
328 // discard the queue file now that we are done
329 queueFile.delete();
330 } else {
331 queueFile = getXMLQueueFile();
332 if (queueFile.exists()) {
333 List list = (List) new XmlFile(XSTREAM, queueFile).read();
334 int maxId = 0;
335 for (Object o : list) {
336 if (o instanceof Task) {
337 // backward compatibility
338 schedule((Task)o, 0);
339 } else if (o instanceof Item) {
340 Item item = (Item)o;
341 if(item.task==null)
342 continue; // botched persistence. throw this one away
343
344 maxId = Math.max(maxId, item.id);
345 if (item instanceof WaitingItem) {
346 waitingList.add((WaitingItem) item);
347 } else if (item instanceof BlockedItem) {
348 blockedProjects.put(item.task, (BlockedItem) item);
349 } else if (item instanceof BuildableItem) {
350 buildables.add((BuildableItem) item);
351 } else {
352 throw new IllegalStateException("Unknown item type! " + item);
353 }
354 } // this conveniently ignores null
355 }
356 WaitingItem.COUNTER.set(maxId);
357
358 // I just had an incident where all the executors are dead at AbstractProject._getRuns()
359 // because runs is null. Debugger revealed that this is caused by a MatrixConfiguration
360 // object that doesn't appear to be de-serialized properly.
361 // I don't know how this problem happened, but to diagnose this problem better
362 // when it happens again, save the old queue file for introspection.
363 File bk = new File(queueFile.getPath() + ".bak");
364 bk.delete();
365 queueFile.renameTo(bk);
366 queueFile.delete();
367 }
368 }
369 } catch (IOException e) {
370 LOGGER.log(Level.WARNING, "Failed to load the queue file " + getXMLQueueFile(), e);
371 }
372 }
373
374 /**
375 * Persists the queue contents to the disk.
376 */
377 public synchronized void save() {
378 if(BulkChange.contains(this)) return;
379
380 // write out the tasks on the queue
381 ArrayList<Queue.Item> items = new ArrayList<Queue.Item>();
382 for (Item item: getItems()) {
383 if(item.task instanceof TransientTask) continue;
384 items.add(item);
385 }
386
387 try {
388 XmlFile queueFile = new XmlFile(XSTREAM, getXMLQueueFile());
389 queueFile.write(items);
390 SaveableListener.fireOnChange(this, queueFile);
391 } catch (IOException e) {
392 LOGGER.log(Level.WARNING, "Failed to write out the queue file " + getXMLQueueFile(), e);
393 }
394 }
395
396 /**
397 * Wipes out all the items currently in the queue, as if all of them are cancelled at once.
398 */
399 @CLIMethod(name="clear-queue")
400 public synchronized void clear() {
401 Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
402 for (WaitingItem i : waitingList)
403 i.onCancelled();
404 waitingList.clear();
405 blockedProjects.cancelAll();
406 buildables.cancelAll();
407 scheduleMaintenance();
408 }
409
410 private File getQueueFile() {
411 return new File(Jenkins.getInstance().getRootDir(), "queue.txt");
412 }
413
414 /*package*/ File getXMLQueueFile() {
415 return new File(Jenkins.getInstance().getRootDir(), "queue.xml");
416 }
417
418 /**
419 * @deprecated as of 1.311
420 * Use {@link #schedule(AbstractProject)}
421 */
422 public boolean add(AbstractProject p) {
423 return schedule(p)!=null;
424 }
425
426 /**
427 * Schedule a new build for this project.
428 *
429 * @return true if the project is actually added to the queue.
430 * false if the queue contained it and therefore the add()
431 * was noop
432 */
433 public WaitingItem schedule(AbstractProject p) {
434 return schedule(p, p.getQuietPeriod());
435 }
436
437 /**
438 * Schedules a new build with a custom quiet period.
439 *
440 * <p>
441 * Left for backward compatibility with &lt;1.114.
442 *
443 * @since 1.105
444 * @deprecated as of 1.311
445 * Use {@link #schedule(Task, int)}
446 */
447 public boolean add(AbstractProject p, int quietPeriod) {
448 return schedule(p, quietPeriod)!=null;
449 }
450
451 /**
452 * Schedules an execution of a task.
453 *
454 * @param actions
455 * These actions can be used for associating information scoped to a particular build, to
456 * the task being queued. Upon the start of the build, these {@link Action}s will be automatically
457 * added to the {@link Run} object, and hence avaialable to everyone.
458 * For the convenience of the caller, this list can contain null, and those will be silently ignored.
459 * @since 1.311
460 * @return
461 * null if this task is already in the queue and therefore the add operation was no-op.
462 * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
463 * is that such {@link Item} only captures the state of the item at a particular moment,
464 * and by the time you inspect the object, some of its information can be already stale.
465 *
466 * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
467 */
468 public synchronized WaitingItem schedule(Task p, int quietPeriod, List<Action> actions) {
469 // remove nulls
470 actions = new ArrayList<Action>(actions);
471 for (Iterator<Action> itr = actions.iterator(); itr.hasNext();) {
472 Action a = itr.next();
473 if (a==null) itr.remove();
474 }
475
476 for(QueueDecisionHandler h : QueueDecisionHandler.all())
477 if (!h.shouldSchedule(p, actions))
478 return null; // veto
479
480 return scheduleInternal(p, quietPeriod, actions);
481 }
482
483 /**
484 * Schedules an execution of a task.
485 *
486 * @since 1.311
487 * @return
488 * null if this task is already in the queue and therefore the add operation was no-op.
489 * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
490 * is that such {@link Item} only captures the state of the item at a particular moment,
491 * and by the time you inspect the object, some of its information can be already stale.
492 *
493 * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
494 */
495 private synchronized WaitingItem scheduleInternal(Task p, int quietPeriod, List<Action> actions) {
496 Calendar due = new GregorianCalendar();
497 due.add(Calendar.SECOND, quietPeriod);
498
499 // Do we already have this task in the queue? Because if so, we won't schedule a new one.
500 List<Item> duplicatesInQueue = new ArrayList<Item>();
501 for(Item item : getItems(p)) {
502 boolean shouldScheduleItem = false;
503 for (QueueAction action: item.getActions(QueueAction.class)) {
504 shouldScheduleItem |= action.shouldSchedule(actions);
505 }
506 for (QueueAction action: Util.filter(actions,QueueAction.class)) {
507 shouldScheduleItem |= action.shouldSchedule(item.getActions());
508 }
509 if(!shouldScheduleItem) {
510 duplicatesInQueue.add(item);
511 }
512 }
513 if (duplicatesInQueue.isEmpty()) {
514 LOGGER.fine(p.getFullDisplayName() + " added to queue");
515
516 // put the item in the queue
517 WaitingItem added = new WaitingItem(due,p,actions);
518 waitingList.add(added);
519 scheduleMaintenance(); // let an executor know that a new item is in the queue.
520 return added;
521 }
522
523 LOGGER.fine(p.getFullDisplayName() + " is already in the queue");
524
525 // but let the actions affect the existing stuff.
526 for(Item item : duplicatesInQueue) {
527 for(FoldableAction a : Util.filter(actions,FoldableAction.class)) {
528 a.foldIntoExisting(item, p, actions);
529 }
530 }
531
532 boolean queueUpdated = false;
533 for(WaitingItem wi : Util.filter(duplicatesInQueue,WaitingItem.class)) {
534 if(quietPeriod<=0) {
535 // the user really wants to build now, and they mean NOW.
536 // so let's pull in the timestamp if we can.
537 if (wi.timestamp.before(due))
538 continue;
539 } else {
540 // otherwise we do the normal quiet period implementation
541 if (wi.timestamp.after(due))
542 continue;
543 // quiet period timer reset. start the period over again
544 }
545
546 // waitingList is sorted, so when we change a timestamp we need to maintain order
547 waitingList.remove(wi);
548 wi.timestamp = due;
549 waitingList.add(wi);
550 queueUpdated=true;
551 }
552
553 if (queueUpdated) scheduleMaintenance();
554 return null;
555 }
556
557 /**
558 * @deprecated as of 1.311
559 * Use {@link #schedule(Task, int)}
560 */
561 public synchronized boolean add(Task p, int quietPeriod) {
562 return schedule(p, quietPeriod)!=null;
563 }
564
565 public synchronized WaitingItem schedule(Task p, int quietPeriod) {
566 return schedule(p, quietPeriod, new Action[0]);
567 }
568
569 /**
570 * @deprecated as of 1.311
571 * Use {@link #schedule(Task, int, Action...)}
572 */
573 public synchronized boolean add(Task p, int quietPeriod, Action... actions) {
574 return schedule(p, quietPeriod, actions)!=null;
575 }
576
577 /**
578 * Convenience wrapper method around {@link #schedule(Task, int, List)}
579 */
580 public synchronized WaitingItem schedule(Task p, int quietPeriod, Action... actions) {
581 return schedule(p, quietPeriod, Arrays.asList(actions));
582 }
583
584 /**
585 * Cancels the item in the queue. If the item is scheduled more than once, cancels the first occurrence.
586 *
587 * @return true if the project was indeed in the queue and was removed.
588 * false if this was no-op.
589 */
590 public synchronized boolean cancel(Task p) {
591 LOGGER.fine("Cancelling " + p.getFullDisplayName());
592 for (Iterator<WaitingItem> itr = waitingList.iterator(); itr.hasNext();) {
593 Item item = itr.next();
594 if (item.task.equals(p)) {
595 itr.remove();
596 item.onCancelled();
597 return true;
598 }
599 }
600 // use bitwise-OR to make sure that both branches get evaluated all the time
601 return blockedProjects.cancel(p)!=null | buildables.cancel(p)!=null;
602 }
603
604 public synchronized boolean cancel(Item item) {
605 LOGGER.fine("Cancelling " + item.task.getFullDisplayName() + " item#" + item.id);
606 // use bitwise-OR to make sure that all the branches get evaluated all the time
607 boolean r = (item instanceof WaitingItem && waitingList.remove(item)) | blockedProjects.remove(item) | buildables.remove(item);
608 if(r)
609 item.onCancelled();
610 return r;
611 }
612
613 /**
614 * Called from {@code queue.jelly} and {@code entries.jelly}.
615 */
616 @RequirePOST
617 public HttpResponse doCancelItem(@QueryParameter int id) throws IOException, ServletException {
618 Item item = getItem(id);
619 if (item != null) {
620 cancel(item);
621 } // else too late, ignore (JENKINS-14813)
622 return HttpResponses.forwardToPreviousPage();
623 }
624
625 public synchronized boolean isEmpty() {
626 return waitingList.isEmpty() && blockedProjects.isEmpty() && buildables.isEmpty() && pendings.isEmpty();
627 }
628
629 private synchronized WaitingItem peek() {
630 return waitingList.iterator().next();
631 }
632
633 /**
634 * Gets a snapshot of items in the queue.
635 *
636 * Generally speaking the array is sorted such that the items that are most likely built sooner are
637 * at the end.
638 */
639 @Exported(inline=true)
640 public synchronized Item[] getItems() {
641 Item[] r = new Item[waitingList.size() + blockedProjects.size() + buildables.size() + pendings.size()];
642 waitingList.toArray(r);
643 int idx = waitingList.size();
644 for (BlockedItem p : blockedProjects.values())
645 r[idx++] = p;
646 for (BuildableItem p : reverse(buildables.values()))
647 r[idx++] = p;
648 for (BuildableItem p : reverse(pendings.values()))
649 r[idx++] = p;
650 return r;
651 }
652
653 /**
654 * Like {@link #getItems()}, but returns an approximation that might not be completely up-to-date.
655 *
656 * <p>
657 * At the expense of accuracy, this method does not usually lock {@link Queue} and therefore is faster
658 * in a highly concurrent situation.
659 *
660 * <p>
661 * The list obtained is an accurate snapshot of the queue at some point in the past. The snapshot
662 * is updated and normally no more than one second old, but this is a soft commitment that might
663 * get violated when the lock on {@link Queue} is highly contended.
664 *
665 * <p>
666 * This method is primarily added to make UI threads run faster.
667 *
668 * @since 1.483
669 */
670 public List<Item> getApproximateItemsQuickly() {
671 return itemsView.get();
672 }
673
674 public synchronized Item getItem(int id) {
675 for (Item item: waitingList) if (item.id == id) return item;
676 for (Item item: blockedProjects) if (item.id == id) return item;
677 for (Item item: buildables) if (item.id == id) return item;
678 for (Item item: pendings) if (item.id == id) return item;
679 return null;
680 }
681
682 /**
683 * Gets all the {@link BuildableItem}s that are waiting for an executor in the given {@link Computer}.
684 */
685 public synchronized List<BuildableItem> getBuildableItems(Computer c) {
686 List<BuildableItem> result = new ArrayList<BuildableItem>();
687 _getBuildableItems(c, buildables, result);
688 _getBuildableItems(c, pendings, result);
689 return result;
690 }
691
692 private void _getBuildableItems(Computer c, ItemList<BuildableItem> col, List<BuildableItem> result) {
693 Node node = c.getNode();
694 for (BuildableItem p : col.values()) {
695 if (node.canTake(p) == null)
696 result.add(p);
697 }
698 }
699
700 /**
701 * Gets the snapshot of all {@link BuildableItem}s.
702 */
703 public synchronized List<BuildableItem> getBuildableItems() {
704 ArrayList<BuildableItem> r = new ArrayList<BuildableItem>(buildables.values());
705 r.addAll(pendings.values());
706 return r;
707 }
708
709 /**
710 * Gets the snapshot of all {@link BuildableItem}s.
711 */
712 public synchronized List<BuildableItem> getPendingItems() {
713 return new ArrayList<BuildableItem>(pendings.values());
714 }
715
716 /**
717 * Gets all items that are in the queue but not blocked
718 *
719 * @since 1.402
720 */
721 public synchronized List<Item> getUnblockedItems() {
722 List<Item> queuedNotBlocked = new ArrayList<Item>();
723 queuedNotBlocked.addAll(waitingList);
724 queuedNotBlocked.addAll(buildables);
725 queuedNotBlocked.addAll(pendings);
726 // but not 'blockedProjects'
727 return queuedNotBlocked;
728 }
729
730 /**
731 * Works just like {@link #getUnblockedItems()} but return tasks.
732 *
733 * @since 1.402
734 */
735 public synchronized Set<Task> getUnblockedTasks() {
736 List<Item> items = getUnblockedItems();
737 Set<Task> unblockedTasks = new HashSet<Task>(items.size());
738 for (Queue.Item t : items)
739 unblockedTasks.add(t.task);
740 return unblockedTasks;
741 }
742
743 /**
744 * Is the given task currently pending execution?
745 */
746 public synchronized boolean isPending(Task t) {
747 for (BuildableItem i : pendings)
748 if (i.task.equals(t))
749 return true;
750 return false;
751 }
752
753 /**
754 * How many {@link BuildableItem}s are assigned for the given label?
755 */
756 public synchronized int countBuildableItemsFor(Label l) {
757 int r = 0;
758 for (BuildableItem bi : buildables.values())
759 if(bi.getAssignedLabel()==l)
760 r++;
761 for (BuildableItem bi : pendings.values())
762 if(bi.getAssignedLabel()==l)
763 r++;
764 return r;
765 }
766
767 /**
768 * Counts all the {@link BuildableItem}s currently in the queue.
769 */
770 public synchronized int countBuildableItems() {
771 return buildables.size()+pendings.size();
772 }
773
774 /**
775 * Gets the information about the queue item for the given project.
776 *
777 * @return null if the project is not in the queue.
778 */
779 public synchronized Item getItem(Task t) {
780 BlockedItem bp = blockedProjects.get(t);
781 if (bp!=null)
782 return bp;
783 BuildableItem bi = buildables.get(t);
784 if(bi!=null)
785 return bi;
786 bi = pendings.get(t);
787 if(bi!=null)
788 return bi;
789
790 for (Item item : waitingList) {
791 if (item.task == t)
792 return item;
793 }
794 return null;
795 }
796
797 /**
798 * Gets the information about the queue item for the given project.
799 *
800 * @return null if the project is not in the queue.
801 */
802 public synchronized List<Item> getItems(Task t) {
803 List<Item> result =new ArrayList<Item>();
804 result.addAll(blockedProjects.getAll(t));
805 result.addAll(buildables.getAll(t));
806 result.addAll(pendings.getAll(t));
807 for (Item item : waitingList) {
808 if (item.task == t)
809 result.add(item);
810 }
811 return result;
812 }
813
814 /**
815 * Left for backward compatibility.
816 *
817 * @see #getItem(Task)
818 public synchronized Item getItem(AbstractProject p) {
819 return getItem((Task) p);
820 }
821 */
822
823 /**
824 * Returns true if this queue contains the said project.
825 */
826 public synchronized boolean contains(Task t) {
827 if (blockedProjects.containsKey(t) || buildables.containsKey(t) || pendings.containsKey(t))
828 return true;
829 for (Item item : waitingList) {
830 if (item.task == t)
831 return true;
832 }
833 return false;
834 }
835
836 /**
837 * Called by the executor to fetch something to build next.
838 * <p>
839 * This method blocks until a next project becomes buildable.
840 */
841 public synchronized WorkUnit pop() throws InterruptedException {
842 final Executor exec = Executor.currentExecutor();
843
844 if (exec instanceof OneOffExecutor) {
845 OneOffExecutor ooe = (OneOffExecutor) exec;
846 final WorkUnit wu = ooe.getAssignedWorkUnit();
847 pendings.remove(wu.context.item);
848 return wu;
849 }
850
851 try {
852 while (true) {
853 final JobOffer offer = new JobOffer(exec);
854 long sleep = -1;
855
856 // consider myself parked
857 assert !parked.containsKey(exec);
858 parked.put(exec, offer);
859
860 // reuse executor thread to do a queue maintenance.
861 // at the end of this we get all the buildable jobs
862 // in the buildables field.
863 maintain();
864
865 // we went over all the buildable projects and awaken
866 // all the executors that got work to do. now, go to sleep
867 // until this thread is awakened. If this executor assigned a job to
868 // itself above, the block method will return immediately.
869
870 if (!waitingList.isEmpty()) {
871 // wait until the first item in the queue is due
872 sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis();
873 if (sleep < 100) sleep = 100; // avoid wait(0)
874 }
875
876 if (sleep == -1)
877 offer.event.block();
878 else
879 offer.event.block(sleep);
880
881 // retract the offer object
882 assert parked.get(exec) == offer;
883 parked.remove(exec);
884
885 // am I woken up because I have a project to build?
886 if (offer.workUnit != null) {
887 // if so, just build it
888 LOGGER.fine("Pop returning " + offer.workUnit + " for " + exec.getName());
889
890 // TODO: I think this has to be done by the last executor that leaves the pop(), not by main executor
891 if (offer.workUnit.isMainWork())
892 pendings.remove(offer.workUnit.context.item);
893
894 return offer.workUnit;
895 }
896 // otherwise run a queue maintenance
897 }
898 } finally {
899 // remove myself from the parked list
900 JobOffer offer = parked.remove(exec);
901 if (offer != null && offer.workUnit != null) {
902 // we are already assigned a project, but now we can't handle it.
903 offer.workUnit.context.abort(new AbortException());
904 }
905
906 // since this executor might have been chosen for
907 // maintenance, schedule another one. Worst case
908 // we'll just run a pointless maintenance, and that's
909 // fine.
910 scheduleMaintenance();
911 }
912 }
913
914 /**
915 * Checks the queue and runs anything that can be run.
916 *
917 * <p>
918 * When conditions are changed, this method should be invoked.
919 * <p>
920 * This wakes up one {@link Executor} so that it will maintain a queue.
921 */
922 public synchronized void scheduleMaintenance() {
923 // this code assumes that after this method is called
924 // no more executors will be offered job except by
925 // the pop() code.
926 for (Entry<Executor, JobOffer> av : parked.entrySet()) {
927 if (av.getValue().workUnit == null) {
928 av.getValue().event.signal();
929 return;
930 }
931 }
932 }
933
934 /**
935 * Checks if the given item should be prevented from entering into the {@link #buildables} state
936 * and instead stay in the {@link #blockedProjects} state.
937 */
938 private boolean isBuildBlocked(Item i) {
939 if (i.task.isBuildBlocked() || !canRun(i.task.getResourceList()))
940 return true;
941
942 for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
943 if (d.canRun(i)!=null)
944 return true;
945 }
946
947 return false;
948 }
949
950 /**
951 * Make sure we don't queue two tasks of the same project to be built
952 * unless that project allows concurrent builds.
953 */
954 private boolean allowNewBuildableTask(Task t) {
955 try {
956 if (t.isConcurrentBuild())
957 return true;
958 } catch (AbstractMethodError e) {
959 // earlier versions don't have the "isConcurrentBuild" method, so fall back gracefully
960 }
961 return !buildables.containsKey(t) && !pendings.containsKey(t);
962 }
963
964 /**
965 * Queue maintenance.
966 *
967 * <p>
968 * Move projects between {@link #waitingList}, {@link #blockedProjects}, {@link #buildables}, and {@link #pendings}
969 * appropriately.
970 *
971 * <p>
972 * Jenkins internally invokes this method by itself whenever there's a change that can affect
973 * the scheduling (such as new node becoming online, # of executors change, a task completes execution, etc.),
974 * and it also gets invoked periodically (see {@link MaintainTask}.)
975 */
976 public synchronized void maintain() {
977 if (LOGGER.isLoggable(Level.FINE))
978 LOGGER.fine("Queue maintenance started " + this);
979
980 {// blocked -> buildable
981 Iterator<BlockedItem> itr = blockedProjects.values().iterator();
982 while (itr.hasNext()) {
983 BlockedItem p = itr.next();
984 if (!isBuildBlocked(p) && allowNewBuildableTask(p.task)) {
985 // ready to be executed
986 LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked");
987 itr.remove();
988 makeBuildable(new BuildableItem(p));
989 }
990 }
991 }
992
993 // waitingList -> buildable/blocked
994 while (!waitingList.isEmpty()) {
995 WaitingItem top = peek();
996
997 if (!top.timestamp.before(new GregorianCalendar()))
998 break; // finished moving all ready items from queue
999
1000 waitingList.remove(top);
1001 Task p = top.task;
1002 if (!isBuildBlocked(top) && allowNewBuildableTask(p)) {
1003 // ready to be executed immediately
1004 LOGGER.fine(p.getFullDisplayName() + " ready to build");
1005 makeBuildable(new BuildableItem(top));
1006 } else {
1007 // this can't be built now because another build is in progress
1008 // set this project aside.
1009 LOGGER.fine(p.getFullDisplayName() + " is blocked");
1010 blockedProjects.put(p,new BlockedItem(top));
1011 }
1012 }
1013
1014 final QueueSorter s = sorter;
1015 if (s != null)
1016 s.sortBuildableItems(buildables);
1017
1018 // allocate buildable jobs to executors
1019 Iterator<BuildableItem> itr = buildables.iterator();
1020 while (itr.hasNext()) {
1021 BuildableItem p = itr.next();
1022
1023 // one last check to make sure this build is not blocked.
1024 if (isBuildBlocked(p)) {
1025 itr.remove();
1026 blockedProjects.put(p.task,new BlockedItem(p));
1027 continue;
1028 }
1029
1030 List<JobOffer> candidates = new ArrayList<JobOffer>(parked.size());
1031 for (JobOffer j : parked.values())
1032 if(j.canTake(p))
1033 candidates.add(j);
1034
1035 MappingWorksheet ws = new MappingWorksheet(p, candidates);
1036 Mapping m = loadBalancer.map(p.task, ws);
1037 if (m == null)
1038 // if we couldn't find the executor that fits,
1039 // just leave it in the buildables list and
1040 // check if we can execute other projects
1041 continue;
1042
1043 // found a matching executor. use it.
1044 WorkUnitContext wuc = new WorkUnitContext(p);
1045 m.execute(wuc);
1046
1047 itr.remove();
1048 if (!wuc.getWorkUnits().isEmpty())
1049 pendings.add(p);
1050 }
1051 }
1052
1053 private void makeBuildable(BuildableItem p) {
1054 if(Jenkins.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) {
1055 ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() {
1056 public String hash(Node node) {
1057 return node.getNodeName();
1058 }
1059 });
1060 Jenkins h = Jenkins.getInstance();
1061 hash.add(h, h.getNumExecutors()*100);
1062 for (Node n : h.getNodes())
1063 hash.add(n,n.getNumExecutors()*100);
1064
1065 Label lbl = p.getAssignedLabel();
1066 for (Node n : hash.list(p.task.getFullDisplayName())) {
1067 Computer c = n.toComputer();
1068 if (c==null || c.isOffline()) continue;
1069 if (lbl!=null && !lbl.contains(n)) continue;
1070 if (n.canTake(p) != null) continue;
1071 c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
1072 pendings.add(p);
1073 return;
1074 }
1075 // if the execution get here, it means we couldn't schedule it anywhere.
1076 // so do the scheduling like other normal jobs.
1077 }
1078
1079 buildables.put(p.task,p);
1080 }
1081
1082 public static boolean ifBlockedByHudsonShutdown(Task task) {
1083 return Jenkins.getInstance().isQuietingDown() && !(task instanceof NonBlockingTask);
1084 }
1085
1086 public Api getApi() {
1087 return new Api(this);
1088 }
1089
1090 /**
1091 * Marks {@link Task}s that are not persisted.
1092 * @since 1.311
1093 */
1094 public interface TransientTask extends Task {}
1095
1096 /**
1097 * Marks {@link Task}s that do not consume {@link Executor}.
1098 * @see OneOffExecutor
1099 * @since 1.318
1100 */
1101 public interface FlyweightTask extends Task {}
1102
1103 /**
1104 * Marks {@link Task}s that are not affected by the {@linkplain Jenkins#isQuietingDown()} quieting down},
1105 * because these tasks keep other tasks executing.
1106 *
1107 * @since 1.336
1108 */
1109 public interface NonBlockingTask extends Task {}
1110
1111 /**
1112 * Task whose execution is controlled by the queue.
1113 *
1114 * <p>
1115 * {@link #equals(Object) Value equality} of {@link Task}s is used
1116 * to collapse two tasks into one. This is used to avoid infinite
1117 * queue backlog.
1118 *
1119 * <p>
1120 * Pending {@link Task}s are persisted when Hudson shuts down, so
1121 * it needs to be persistable via XStream. To create a non-persisted
1122 * transient Task, extend {@link TransientTask} marker interface.
1123 *
1124 * <p>
1125 * Plugins are encouraged to extend from {@link AbstractQueueTask}
1126 * instead of implementing this interface directly, to maintain
1127 * compatibility with future changes to this interface.
1128 *
1129 * <p>
1130 * For historical reasons, {@link Task} object by itself
1131 * also represents the "primary" sub-task (and as implied by this
1132 * design, a {@link Task} must have at least one sub-task.)
1133 * Most of the time, the primary subtask is the only sub task.
1134 */
1135 public interface Task extends ModelObject, SubTask {
1136 /**
1137 * Returns true if the execution should be blocked
1138 * for temporary reasons.
1139 *
1140 * <p>
1141 * Short-hand for {@code getCauseOfBlockage()!=null}.
1142 */
1143 boolean isBuildBlocked();
1144
1145 /**
1146 * @deprecated as of 1.330
1147 * Use {@link CauseOfBlockage#getShortDescription()} instead.
1148 */
1149 String getWhyBlocked();
1150
1151 /**
1152 * If the execution of this task should be blocked for temporary reasons,
1153 * this method returns a non-null object explaining why.
1154 *
1155 * <p>
1156 * Otherwise this method returns null, indicating that the build can proceed right away.
1157 *
1158 * <p>
1159 * This can be used to define mutual exclusion that goes beyond
1160 * {@link #getResourceList()}.
1161 */
1162 CauseOfBlockage getCauseOfBlockage();
1163
1164 /**
1165 * Unique name of this task.
1166 *
1167 * <p>
1168 * This method is no longer used, left here for compatibility. Just return {@link #getDisplayName()}.
1169 */
1170 String getName();
1171
1172 /**
1173 * @see hudson.model.Item#getFullDisplayName()
1174 */
1175 String getFullDisplayName();
1176
1177 /**
1178 * Checks the permission to see if the current user can abort this executable.
1179 * Returns normally from this method if it's OK.
1180 *
1181 * @throws AccessDeniedException if the permission is not granted.
1182 */
1183 void checkAbortPermission();
1184
1185 /**
1186 * Works just like {@link #checkAbortPermission()} except it indicates the status by a return value,
1187 * instead of exception.
1188 */
1189 boolean hasAbortPermission();
1190
1191 /**
1192 * Returns the URL of this task relative to the context root of the application.
1193 *
1194 * <p>
1195 * When the user clicks an item in the queue, this is the page where the user is taken to.
1196 * Hudson expects the current instance to be bound to the URL returned by this method.
1197 *
1198 * @return
1199 * URL that ends with '/'.
1200 */
1201 String getUrl();
1202
1203 /**
1204 * True if the task allows concurrent builds, where the same {@link Task} is executed
1205 * by multiple executors concurrently on the same or different nodes.
1206 *
1207 * @since 1.338
1208 */
1209 boolean isConcurrentBuild();
1210
1211 /**
1212 * Obtains the {@link SubTask}s that constitute this task.
1213 *
1214 * <p>
1215 * The collection returned by this method must also contain the primary {@link SubTask}
1216 * represented by this {@link Task} object itself as the first element.
1217 * The returned value is read-only.
1218 *
1219 * <p>
1220 * At least size 1.
1221 *
1222 * <p>
1223 * Since this is a newly added method, the invocation may results in {@link AbstractMethodError}.
1224 * Use {@link Tasks#getSubTasksOf(Task)} that avoids this.
1225 *
1226 * @since 1.377
1227 */
1228 Collection<? extends SubTask> getSubTasks();
1229 }
1230
1231 /**
1232 * Represents the real meat of the computation run by {@link Executor}.
1233 *
1234 * <h2>Views</h2>
1235 * <p>
1236 * Implementation must have <tt>executorCell.jelly</tt>, which is
1237 * used to render the HTML that indicates this executable is executing.
1238 */
1239 public interface Executable extends Runnable {
1240 /**
1241 * Task from which this executable was created.
1242 * Never null.
1243 *
1244 * <p>
1245 * Since this method went through a signature change in 1.377, the invocation may results in
1246 * {@link AbstractMethodError}.
1247 * Use {@link Executables#getParentOf(Executable)} that avoids this.
1248 */
1249 SubTask getParent();
1250
1251 /**
1252 * Called by {@link Executor} to perform the task
1253 */
1254 void run();
1255
1256 /**
1257 * Estimate of how long will it take to execute this executable.
1258 * Measured in milliseconds.
1259 *
1260 * Please, consider using {@link Executables#getEstimatedDurationFor(Executable)}
1261 * to protected against AbstractMethodErrors!
1262 *
1263 * @return -1 if it's impossible to estimate.
1264 * @since 1.383
1265 */
1266 long getEstimatedDuration();
1267
1268 /**
1269 * Used to render the HTML. Should be a human readable text of what this executable is.
1270 */
1271 @Override String toString();
1272 }
1273
1274 /**
1275 * Item in a queue.
1276 */
1277 @ExportedBean(defaultVisibility = 999)
1278 public static abstract class Item extends Actionable {
1279 /**
1280 * VM-wide unique ID that tracks the {@link Task} as it moves through different stages
1281 * in the queue (each represented by different subtypes of {@link Item}.
1282 */
1283 @Exported
1284 public final int id;
1285
1286 /**
1287 * Project to be built.
1288 */
1289 @Exported
1290 public final Task task;
1291
1292 private /*almost final*/ transient FutureImpl future;
1293
1294 private final long inQueueSince;
1295
1296 /**
1297 * Build is blocked because another build is in progress,
1298 * required {@link Resource}s are not available, or otherwise blocked
1299 * by {@link Task#isBuildBlocked()}.
1300 */
1301 @Exported
1302 public boolean isBlocked() { return this instanceof BlockedItem; }
1303
1304 /**
1305 * Build is waiting the executor to become available.
1306 * This flag is only used in {@link Queue#getItems()} for
1307 * 'pseudo' items that are actually not really in the queue.
1308 */
1309 @Exported
1310 public boolean isBuildable() { return this instanceof BuildableItem; }
1311
1312 /**
1313 * True if the item is starving for an executor for too long.
1314 */
1315 @Exported
1316 public boolean isStuck() { return false; }
1317
1318 /**
1319 * Since when is this item in the queue.
1320 * @return Unix timestamp
1321 */
1322 @Exported
1323 public long getInQueueSince() {
1324 return this.inQueueSince;
1325 }
1326
1327 /**
1328 * Returns a human readable presentation of how long this item is already in the queue.
1329 * E.g. something like '3 minutes 40 seconds'
1330 */
1331 public String getInQueueForString() {
1332 long duration = System.currentTimeMillis() - this.inQueueSince;
1333 return Util.getTimeSpanString(duration);
1334 }
1335
1336 /**
1337 * Can be used to wait for the completion (either normal, abnormal, or cancellation) of the {@link Task}.
1338 * <p>
1339 * Just like {@link #id}, the same object tracks various stages of the queue.
1340 */
1341 public Future<Executable> getFuture() { return future; }
1342
1343 /**
1344 * If this task needs to be run on a node with a particular label,
1345 * return that {@link Label}. Otherwise null, indicating
1346 * it can run on anywhere.
1347 *
1348 * <p>
1349 * This code takes {@link LabelAssignmentAction} into account, then fall back to {@link SubTask#getAssignedLabel()}
1350 */
1351 public Label getAssignedLabel() {
1352 for (LabelAssignmentAction laa : getActions(LabelAssignmentAction.class)) {
1353 Label l = laa.getAssignedLabel(task);
1354 if (l!=null) return l;
1355 }
1356 return task.getAssignedLabel();
1357 }
1358
1359 /**
1360 * Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue.
1361 *
1362 * @return can be empty but never null
1363 * @since 1.343
1364 */
1365 public final List<Cause> getCauses() {
1366 CauseAction ca = getAction(CauseAction.class);
1367 if (ca!=null)
1368 return Collections.unmodifiableList(ca.getCauses());
1369 return Collections.emptyList();
1370 }
1371
1372 protected Item(Task task, List<Action> actions, int id, FutureImpl future) {
1373 this.task = task;
1374 this.id = id;
1375 this.future = future;
1376 this.inQueueSince = System.currentTimeMillis();
1377 for (Action action: actions) addAction(action);
1378 }
1379
1380 protected Item(Task task, List<Action> actions, int id, FutureImpl future, long inQueueSince) {
1381 this.task = task;
1382 this.id = id;
1383 this.future = future;
1384 this.inQueueSince = inQueueSince;
1385 for (Action action: actions) addAction(action);
1386 }
1387
1388 protected Item(Item item) {
1389 this(item.task, item.getActions(), item.id, item.future, item.inQueueSince);
1390 }
1391
1392 /**
1393 * Gets a human-readable status message describing why it's in the queue.
1394 */
1395 @Exported
1396 public final String getWhy() {
1397 CauseOfBlockage cob = getCauseOfBlockage();
1398 return cob!=null ? cob.getShortDescription() : null;
1399 }
1400
1401 /**
1402 * Gets an object that describes why this item is in the queue.
1403 */
1404 public abstract CauseOfBlockage getCauseOfBlockage();
1405
1406 /**
1407 * Gets a human-readable message about the parameters of this item
1408 * @return String
1409 */
1410 @Exported
1411 public String getParams() {
1412 StringBuilder s = new StringBuilder();
1413 for(Action action : getActions()) {
1414 if(action instanceof ParametersAction) {
1415 ParametersAction pa = (ParametersAction)action;
1416 for (ParameterValue p : pa.getParameters()) {
1417 s.append('\n').append(p.getShortDescription());
1418 }
1419 }
1420 }
1421 return s.toString();
1422 }
1423
1424 public boolean hasCancelPermission() {
1425 return task.hasAbortPermission();
1426 }
1427
1428 public String getDisplayName() {
1429 // TODO Auto-generated method stub
1430 return null;
1431 }
1432
1433 public String getSearchUrl() {
1434 // TODO Auto-generated method stub
1435 return null;
1436 }
1437
1438 /** @deprecated Use {@link #doCancelItem} instead. */
1439 @Deprecated
1440 @RequirePOST
1441 public HttpResponse doCancelQueue() throws IOException, ServletException {
1442 Jenkins.getInstance().getQueue().cancel(this);
1443 return HttpResponses.forwardToPreviousPage();
1444 }
1445
1446 /**
1447 * Participates in the cancellation logic to set the {@link #future} accordingly.
1448 */
1449 /*package*/ void onCancelled() {
1450 future.setAsCancelled();
1451 }
1452
1453 private Object readResolve() {
1454 this.future = new FutureImpl(task);
1455 return this;
1456 }
1457
1458 @Override
1459 public String toString() {
1460 return getClass().getName()+':'+task.toString();
1461 }
1462 }
1463
1464 /**
1465 * An optional interface for actions on Queue.Item.
1466 * Lets the action cooperate in queue management.
1467 *
1468 * @since 1.300-ish.
1469 */
1470 public interface QueueAction extends Action {
1471 /**
1472 * Returns whether the new item should be scheduled.
1473 * An action should return true if the associated task is 'different enough' to warrant a separate execution.
1474 */
1475 boolean shouldSchedule(List<Action> actions);
1476 }
1477
1478 /**
1479 * Extension point for deciding if particular job should be scheduled or not.
1480 *
1481 * <p>
1482 * This handler is consulted every time someone tries to submit a task to the queue.
1483 * If any of the registered handlers returns false, the task will not be added
1484 * to the queue, and the task will never get executed.
1485 *
1486 * <p>
1487 * The other use case is to add additional {@link Action}s to the task
1488 * (for example {@link LabelAssignmentAction}) to tasks that are submitted to the queue.
1489 *
1490 * @since 1.316
1491 */
1492 public static abstract class QueueDecisionHandler implements ExtensionPoint {
1493 /**
1494 * Returns whether the new item should be scheduled.
1495 *
1496 * @param actions
1497 * List of actions that are to be made available as {@link AbstractBuild#getActions()}
1498 * upon the start of the build. This list is live, and can be mutated.
1499 */
1500 public abstract boolean shouldSchedule(Task p, List<Action> actions);
1501
1502 /**
1503 * All registered {@link QueueDecisionHandler}s
1504 * @return
1505 */
1506 public static ExtensionList<QueueDecisionHandler> all() {
1507 return Jenkins.getInstance().getExtensionList(QueueDecisionHandler.class);
1508 }
1509 }
1510
1511 /**
1512 * {@link Item} in the {@link Queue#waitingList} stage.
1513 */
1514 public static final class WaitingItem extends Item implements Comparable<WaitingItem> {
1515 private static final AtomicInteger COUNTER = new AtomicInteger(0);
1516
1517 /**
1518 * This item can be run after this time.
1519 */
1520 @Exported
1521 public Calendar timestamp;
1522
1523 public WaitingItem(Calendar timestamp, Task project, List<Action> actions) {
1524 super(project, actions, COUNTER.incrementAndGet(), new FutureImpl(project));
1525 this.timestamp = timestamp;
1526 }
1527
1528 public int compareTo(WaitingItem that) {
1529 int r = this.timestamp.getTime().compareTo(that.timestamp.getTime());
1530 if (r != 0) return r;
1531
1532 return this.id - that.id;
1533 }
1534
1535 public CauseOfBlockage getCauseOfBlockage() {
1536 long diff = timestamp.getTimeInMillis() - System.currentTimeMillis();
1537 if (diff > 0)
1538 return CauseOfBlockage.fromMessage(Messages._Queue_InQuietPeriod(Util.getTimeSpanString(diff)));
1539 else
1540 return CauseOfBlockage.fromMessage(Messages._Queue_Unknown());
1541 }
1542 }
1543
1544 /**
1545 * Common part between {@link BlockedItem} and {@link BuildableItem}.
1546 */
1547 public static abstract class NotWaitingItem extends Item {
1548 /**
1549 * When did this job exit the {@link Queue#waitingList} phase?
1550 */
1551 @Exported
1552 public final long buildableStartMilliseconds;
1553
1554 protected NotWaitingItem(WaitingItem wi) {
1555 super(wi);
1556 buildableStartMilliseconds = System.currentTimeMillis();
1557 }
1558
1559 protected NotWaitingItem(NotWaitingItem ni) {
1560 super(ni);
1561 buildableStartMilliseconds = ni.buildableStartMilliseconds;
1562 }
1563 }
1564
1565 /**
1566 * {@link Item} in the {@link Queue#blockedProjects} stage.
1567 */
1568 public final class BlockedItem extends NotWaitingItem {
1569 public BlockedItem(WaitingItem wi) {
1570 super(wi);
1571 }
1572
1573 public BlockedItem(NotWaitingItem ni) {
1574 super(ni);
1575 }
1576
1577 public CauseOfBlockage getCauseOfBlockage() {
1578 ResourceActivity r = getBlockingActivity(task);
1579 if (r != null) {
1580 if (r == task) // blocked by itself, meaning another build is in progress
1581 return CauseOfBlockage.fromMessage(Messages._Queue_InProgress());
1582 return CauseOfBlockage.fromMessage(Messages._Queue_BlockedBy(r.getDisplayName()));
1583 }
1584
1585 for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
1586 CauseOfBlockage cause = d.canRun(this);
1587 if (cause != null)
1588 return cause;
1589 }
1590
1591 return task.getCauseOfBlockage();
1592 }
1593 }
1594
1595 /**
1596 * {@link Item} in the {@link Queue#buildables} stage.
1597 */
1598 public final static class BuildableItem extends NotWaitingItem {
1599 public BuildableItem(WaitingItem wi) {
1600 super(wi);
1601 }
1602
1603 public BuildableItem(NotWaitingItem ni) {
1604 super(ni);
1605 }
1606
1607 public CauseOfBlockage getCauseOfBlockage() {
1608 Jenkins jenkins = Jenkins.getInstance();
1609 if(ifBlockedByHudsonShutdown(task))
1610 return CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown());
1611
1612 Label label = getAssignedLabel();
1613 List<Node> allNodes = jenkins.getNodes();
1614 if (allNodes.isEmpty())
1615 label = null; // no master/slave. pointless to talk about nodes
1616
1617 if (label != null) {
1618 Set<Node> nodes = label.getNodes();
1619 if (label.isOffline()) {
1620 if (nodes.size() != 1) return new BecauseLabelIsOffline(label);
1621 else return new BecauseNodeIsOffline(nodes.iterator().next());
1622 } else {
1623 if (nodes.size() != 1) return new BecauseLabelIsBusy(label);
1624 else return new BecauseNodeIsBusy(nodes.iterator().next());
1625 }
1626 } else {
1627 CauseOfBlockage c = null;
1628 for (Node node : allNodes) {
1629 if (node.toComputer().isPartiallyIdle()) {
1630 c = canTake(node);
1631 if (c==null) break;
1632 }
1633 }
1634
1635 return CauseOfBlockage.createNeedsMoreExecutor(Messages._Queue_WaitingForNextAvailableExecutor());
1636 }
1637 }
1638
1639 private CauseOfBlockage canTake(Node node) {
1640 for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
1641 CauseOfBlockage cause = d.canTake(node, this);
1642 if (cause!=null) return cause;
1643 }
1644 return null;
1645 }
1646
1647 @Override
1648 public boolean isStuck() {
1649 Label label = getAssignedLabel();
1650 if(label!=null && label.isOffline())
1651 // no executor online to process this job. definitely stuck.
1652 return true;
1653
1654 long d = task.getEstimatedDuration();
1655 long elapsed = System.currentTimeMillis()-buildableStartMilliseconds;
1656 if(d>=0) {
1657 // if we were running elsewhere, we would have done this build ten times.
1658 return elapsed > Math.max(d,60000L)*10;
1659 } else {
1660 // more than a day in the queue
1661 return TimeUnit2.MILLISECONDS.toHours(elapsed)>24;
1662 }
1663 }
1664 }
1665
1666 private static final Logger LOGGER = Logger.getLogger(Queue.class.getName());
1667
1668 /**
1669 * This {@link XStream} instance is used to persist {@link Task}s.
1670 */
1671 public static final XStream XSTREAM = new XStream2();
1672
1673 static {
1674 XSTREAM.registerConverter(new AbstractSingleValueConverter() {
1675
1676 @Override
1677 @SuppressWarnings("unchecked")
1678 public boolean canConvert(Class klazz) {
1679 return hudson.model.Item.class.isAssignableFrom(klazz);
1680 }
1681
1682 @Override
1683 public Object fromString(String string) {
1684 Object item = Jenkins.getInstance().getItemByFullName(string);
1685 if(item==null) throw new NoSuchElementException("No such job exists: "+string);
1686 return item;
1687 }
1688
1689 @Override
1690 public String toString(Object item) {
1691 return ((hudson.model.Item) item).getFullName();
1692 }
1693 });
1694 XSTREAM.registerConverter(new AbstractSingleValueConverter() {
1695
1696 @SuppressWarnings("unchecked")
1697 @Override
1698 public boolean canConvert(Class klazz) {
1699 return Run.class.isAssignableFrom(klazz);
1700 }
1701
1702 @Override
1703 public Object fromString(String string) {
1704 String[] split = string.split("#");
1705 String projectName = split[0];
1706 int buildNumber = Integer.parseInt(split[1]);
1707 Job<?,?> job = (Job<?,?>) Jenkins.getInstance().getItemByFullName(projectName);
1708 if(job==null) throw new NoSuchElementException("No such job exists: "+projectName);
1709 Run<?,?> run = job.getBuildByNumber(buildNumber);
1710 if(run==null) throw new NoSuchElementException("No such build: "+string);
1711 return run;
1712 }
1713
1714 @Override
1715 public String toString(Object object) {
1716 Run<?,?> run = (Run<?,?>) object;
1717 return run.getParent().getFullName() + "#" + run.getNumber();
1718 }
1719 });
1720
1721 /**
1722 * Reconnect every reference to {@link Queue} by the singleton.
1723 */
1724 XSTREAM.registerConverter(new AbstractSingleValueConverter() {
1725 @Override
1726 public boolean canConvert(Class klazz) {
1727 return Queue.class.isAssignableFrom(klazz);
1728 }
1729
1730 @Override
1731 public Object fromString(String string) {
1732 return Jenkins.getInstance().getQueue();
1733 }
1734
1735 @Override
1736 public String toString(Object item) {
1737 return "queue";
1738 }
1739 });
1740 }
1741
1742 /**
1743 * Regularly invokes {@link Queue#maintain()} and clean itself up when
1744 * {@link Queue} gets GC-ed.
1745 */
1746 private static class MaintainTask extends SafeTimerTask {
1747 private final WeakReference<Queue> queue;
1748
1749 MaintainTask(Queue queue) {
1750 this.queue = new WeakReference<Queue>(queue);
1751
1752 long interval = 5000;
1753 Timer timer = Trigger.timer;
1754 if (timer != null) {
1755 timer.schedule(this, interval, interval);
1756 }
1757 }
1758
1759 protected void doRun() {
1760 Queue q = queue.get();
1761 if (q != null)
1762 q.maintain();
1763 else
1764 cancel();
1765 }
1766 }
1767
1768 /**
1769 * {@link ArrayList} of {@link Item} with more convenience methods.
1770 */
1771 private static class ItemList<T extends Item> extends ArrayList<T> {
1772 public T get(Task task) {
1773 for (T item: this) {
1774 if (item.task == task) {
1775 return item;
1776 }
1777 }
1778 return null;
1779 }
1780
1781 public List<T> getAll(Task task) {
1782 List<T> result = new ArrayList<T>();
1783 for (T item: this) {
1784 if (item.task == task) {
1785 result.add(item);
1786 }
1787 }
1788 return result;
1789 }
1790
1791 public boolean containsKey(Task task) {
1792 return get(task) != null;
1793 }
1794
1795 public T remove(Task task) {
1796 Iterator<T> it = iterator();
1797 while (it.hasNext()) {
1798 T t = it.next();
1799 if (t.task == task) {
1800 it.remove();
1801 return t;
1802 }
1803 }
1804 return null;
1805 }
1806
1807 public void put(Task task, T item) {
1808 assert item.task == task;
1809 add(item);
1810 }
1811
1812 public ItemList<T> values() {
1813 return this;
1814 }
1815
1816 /**
1817 * Works like {@link #remove(Task)} but also marks the {@link Item} as cancelled.
1818 */
1819 public T cancel(Task p) {
1820 T x = remove(p);
1821 if(x!=null) x.onCancelled();
1822 return x;
1823 }
1824
1825 /**
1826 * Works like {@link #remove(Object)} but also marks the {@link Item} as cancelled.
1827 */
1828 public boolean cancel(Item t) {
1829 boolean r = remove(t);
1830 if(r) t.onCancelled();
1831 return r;
1832 }
1833
1834 public void cancelAll() {
1835 for (T t : this)
1836 t.onCancelled();
1837 clear();
1838 }
1839 }
1840
1841 @CLIResolver
1842 public static Queue getInstance() {
1843 return Jenkins.getInstance().getQueue();
1844 }
1845
1846 /**
1847 * Restores the queue content during the start up.
1848 */
1849 @Initializer(after=JOB_LOADED)
1850 public static void init(Jenkins h) {
1851 h.getQueue().load();
1852 }
1853 }
1854