mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-13 20:12:14 +00:00
Introduce jenkins.model.queue.QueueItem interface (#7926)
* Introduce jenkins.model.queue.QueueItem interface This formalizes methods used by <t:queue> taglib to do the rendering of queue items. * Apply suggestions from code review Co-authored-by: Jesse Glick <jglick@cloudbees.com> * Implement default Queue.Task methods to simplify Mock item * Do the right thing automatically if the Queue.Task implements AccessControlled --------- Co-authored-by: Jesse Glick <jglick@cloudbees.com>
This commit is contained in:
1 parent
2a4795690c
commit
cce017cdec
8 files changed
+170
-57
No files matched your search
@@ -26,6 +26,7 @@
|
||||
package hudson.model;
|
||||
|
||||
import static hudson.init.InitMilestone.JOB_CONFIG_ADAPTED;
|
||||
import static hudson.model.Item.CANCEL;
|
||||
import static hudson.util.Iterators.reverse;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -112,6 +113,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.queue.AsynchronousExecution;
|
||||
import jenkins.model.queue.CompositeCauseOfBlockage;
|
||||
import jenkins.model.queue.QueueItem;
|
||||
import jenkins.security.QueueItemAuthenticator;
|
||||
import jenkins.security.QueueItemAuthenticatorProvider;
|
||||
import jenkins.security.stapler.StaplerAccessibleType;
|
||||
@@ -1929,25 +1931,34 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* Checks the permission to see if the current user can abort this executable.
|
||||
* Returns normally from this method if it's OK.
|
||||
* <p>
|
||||
* NOTE: If you have implemented {@link AccessControlled} this should just be
|
||||
* NOTE: If you have implemented {@link AccessControlled} this defaults to
|
||||
* {@code checkPermission(hudson.model.Item.CANCEL);}
|
||||
*
|
||||
* @throws AccessDeniedException if the permission is not granted.
|
||||
*/
|
||||
void checkAbortPermission();
|
||||
default void checkAbortPermission() {
|
||||
if (this instanceof AccessControlled) {
|
||||
((AccessControlled) this).checkPermission(CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Works just like {@link #checkAbortPermission()} except it indicates the status by a return value,
|
||||
* instead of exception.
|
||||
* Also used by default for {@link hudson.model.Queue.Item#hasCancelPermission}.
|
||||
* <p>
|
||||
* NOTE: If you have implemented {@link AccessControlled} this should just be
|
||||
* NOTE: If you have implemented {@link AccessControlled} this returns by default
|
||||
* {@code return hasPermission(hudson.model.Item.CANCEL);}
|
||||
*
|
||||
* @return false
|
||||
* if the user doesn't have the permission.
|
||||
*/
|
||||
boolean hasAbortPermission();
|
||||
default boolean hasAbortPermission() {
|
||||
if (this instanceof AccessControlled) {
|
||||
return ((AccessControlled) this).hasPermission(CANCEL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of this task relative to the context root of the application.
|
||||
@@ -2117,7 +2128,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* Item in a queue.
|
||||
*/
|
||||
@ExportedBean(defaultVisibility = 999)
|
||||
public abstract static class Item extends Actionable {
|
||||
public abstract static class Item extends Actionable implements QueueItem {
|
||||
|
||||
private final long id;
|
||||
|
||||
@@ -2128,6 +2139,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* @since 1.601
|
||||
*/
|
||||
@Exported
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -2147,12 +2159,19 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* Project to be built.
|
||||
*/
|
||||
@Exported
|
||||
@NonNull
|
||||
public final Task task;
|
||||
|
||||
private /*almost final*/ transient FutureImpl future;
|
||||
|
||||
private final long inQueueSince;
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Task getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build is blocked because another build is in progress,
|
||||
* required {@link Resource}s are not available, or otherwise blocked
|
||||
@@ -2173,6 +2192,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* True if the item is starving for an executor for too long.
|
||||
*/
|
||||
@Exported
|
||||
@Override
|
||||
public boolean isStuck() { return false; }
|
||||
|
||||
/**
|
||||
@@ -2188,6 +2208,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* Returns a human readable presentation of how long this item is already in the queue.
|
||||
* E.g. something like '3 minutes 40 seconds'
|
||||
*/
|
||||
@Override
|
||||
public String getInQueueForString() {
|
||||
long duration = System.currentTimeMillis() - this.inQueueSince;
|
||||
return Util.getTimeSpanString(duration);
|
||||
@@ -2250,6 +2271,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class) // used from Jelly
|
||||
@Override
|
||||
public String getCausesDescription() {
|
||||
List<Cause> causes = getCauses();
|
||||
StringBuilder s = new StringBuilder();
|
||||
@@ -2259,15 +2281,11 @@ public class Queue extends ResourceController implements Saveable {
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
protected Item(Task task, List<Action> actions, long id, FutureImpl future) {
|
||||
this.task = task;
|
||||
this.id = id;
|
||||
this.future = future;
|
||||
this.inQueueSince = System.currentTimeMillis();
|
||||
for (Action action : actions) addAction(action);
|
||||
protected Item(@NonNull Task task, @NonNull List<Action> actions, long id, FutureImpl future) {
|
||||
this(task, actions, id, future, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
protected Item(Task task, List<Action> actions, long id, FutureImpl future, long inQueueSince) {
|
||||
protected Item(@NonNull Task task, @NonNull List<Action> actions, long id, FutureImpl future, long inQueueSince) {
|
||||
this.task = task;
|
||||
this.id = id;
|
||||
this.future = future;
|
||||
@@ -2297,6 +2315,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* Gets a human-readable status message describing why it's in the queue.
|
||||
*/
|
||||
@Exported
|
||||
@Override
|
||||
public final String getWhy() {
|
||||
CauseOfBlockage cob = getCauseOfBlockage();
|
||||
return cob != null ? cob.getShortDescription() : null;
|
||||
@@ -2312,6 +2331,7 @@ public class Queue extends ResourceController implements Saveable {
|
||||
* @return String
|
||||
*/
|
||||
@Exported
|
||||
@Override
|
||||
public String getParams() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (ParametersAction pa : getActions(ParametersAction.class)) {
|
||||
@@ -2322,19 +2342,6 @@ public class Queue extends ResourceController implements Saveable {
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a scheduled item may be canceled.
|
||||
* @return by default, the same as {@link hudson.model.Queue.Task#hasAbortPermission}
|
||||
*/
|
||||
public boolean hasCancelPermission() {
|
||||
return task.hasAbortPermission();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSearchUrl() {
|
||||
return null;
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
package hudson.widgets;
|
||||
|
||||
import hudson.model.Queue.Item;
|
||||
import hudson.model.Queue.Task;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.queue.QueueItem;
|
||||
import jenkins.widgets.HistoryPageFilter;
|
||||
|
||||
/**
|
||||
@@ -52,17 +52,17 @@ public class BuildHistoryWidget<T> extends HistoryWidget<Task, T> {
|
||||
/**
|
||||
* Returns the first queue item if the owner is scheduled for execution in the queue.
|
||||
*/
|
||||
public Item getQueuedItem() {
|
||||
public QueueItem getQueuedItem() {
|
||||
return Jenkins.get().getQueue().getItem(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the queue item if the owner is scheduled for execution in the queue, in REVERSE ORDER
|
||||
*/
|
||||
public List<Item> getQueuedItems() {
|
||||
LinkedList<Item> list = new LinkedList<>();
|
||||
for (Item item : Jenkins.get().getQueue().getItems()) {
|
||||
if (item.task == owner) {
|
||||
public List<QueueItem> getQueuedItems() {
|
||||
LinkedList<QueueItem> list = new LinkedList<>();
|
||||
for (QueueItem item : Jenkins.get().getQueue().getItems()) {
|
||||
if (item.getTask() == owner) {
|
||||
list.addFirst(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package jenkins.model.queue;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.model.Cause;
|
||||
import hudson.model.ModelObject;
|
||||
import hudson.model.Queue;
|
||||
import hudson.model.Run;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.Beta;
|
||||
|
||||
/**
|
||||
* Interface used by Jelly views to render a queue item through {@code <t:queue>}.
|
||||
* @since TODO
|
||||
*/
|
||||
@Restricted(Beta.class)
|
||||
public interface QueueItem extends ModelObject {
|
||||
/**
|
||||
* @return true if the item is starving for an executor for too long.
|
||||
*/
|
||||
boolean isStuck();
|
||||
|
||||
/**
|
||||
* @return The underlying {@link Queue.Task} currently in queue.
|
||||
*/
|
||||
@NonNull
|
||||
Queue.Task getTask();
|
||||
|
||||
/**
|
||||
* Checks whether a scheduled item may be canceled.
|
||||
* @return by default, the same as {@link Queue.Task#hasAbortPermission}
|
||||
*/
|
||||
default boolean hasCancelPermission() {
|
||||
return getTask().hasAbortPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique ID (per controller) that tracks the {@link Queue.Task} as it moves through different stages
|
||||
* in the queue (each represented by different implementations of {@link QueueItem} and into any subsequent
|
||||
* {@link Run} instance (see {@link Run#getQueueId()}).
|
||||
*/
|
||||
long getId();
|
||||
|
||||
/**
|
||||
* Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue as a single string.
|
||||
*/
|
||||
@NonNull
|
||||
String getCausesDescription();
|
||||
|
||||
/**
|
||||
* Gets a human-readable status message describing why it's in the queue.
|
||||
* May return null if there is no cause of blockage.
|
||||
*/
|
||||
@CheckForNull
|
||||
String getWhy();
|
||||
|
||||
/**
|
||||
* Gets a human-readable message about the parameters of this item
|
||||
*/
|
||||
@NonNull
|
||||
String getParams();
|
||||
|
||||
/**
|
||||
* Returns a human readable presentation of how long this item is already in the queue.
|
||||
* E.g. something like '3 minutes 40 seconds'
|
||||
*/
|
||||
@NonNull
|
||||
String getInQueueForString();
|
||||
|
||||
/**
|
||||
* @return a display name for this queue item; by default, {@link Queue.Task#getFullDisplayName()}
|
||||
*/
|
||||
@CheckForNull
|
||||
@Override
|
||||
default String getDisplayName() {
|
||||
return getTask().getFullDisplayName();
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,14 @@
|
||||
package jenkins.widgets;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.model.Queue;
|
||||
import hudson.model.Run;
|
||||
import jenkins.model.queue.QueueItem;
|
||||
|
||||
/**
|
||||
* Represents an entry used by the {@link HistoryPageFilter}.
|
||||
*
|
||||
* <p>
|
||||
* Wraps {@link Queue.Item} and {@link Run} instances from the build queue, normalizing
|
||||
* Wraps {@link QueueItem} and {@link Run} instances from the build queue, normalizing
|
||||
* access to the info required for pagination.
|
||||
*
|
||||
*
|
||||
@@ -55,8 +55,8 @@ public class HistoryPageEntry<T> {
|
||||
}
|
||||
|
||||
protected static long getEntryId(@NonNull Object entry) {
|
||||
if (entry instanceof Queue.Item) {
|
||||
return ((Queue.Item) entry).getId();
|
||||
if (entry instanceof QueueItem) {
|
||||
return ((QueueItem) entry).getId();
|
||||
} else if (entry instanceof Run) {
|
||||
Run run = (Run) entry;
|
||||
return Long.MIN_VALUE + run.getNumber();
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jenkins.model.queue.QueueItem;
|
||||
|
||||
/**
|
||||
* History page filter.
|
||||
@@ -55,9 +56,9 @@ public class HistoryPageFilter<T> {
|
||||
private Long olderThan;
|
||||
private String searchString;
|
||||
|
||||
// Need to use different Lists for Queue.Items and Runs because
|
||||
// Need to use different Lists for QueueItem and Runs because
|
||||
// we need access to them separately in the jelly files for rendering.
|
||||
public final List<HistoryPageEntry<Queue.Item>> queueItems = new ArrayList<>();
|
||||
public final List<HistoryPageEntry<QueueItem>> queueItems = new ArrayList<>();
|
||||
public final List<HistoryPageEntry<Run>> runs = new ArrayList<>();
|
||||
|
||||
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler")
|
||||
@@ -128,7 +129,7 @@ public class HistoryPageFilter<T> {
|
||||
* @param queueItems The queue items to be added. Queue items do not need to be sorted.
|
||||
* @since 2.17
|
||||
*/
|
||||
public void add(@NonNull Iterable<T> runItems, @NonNull List<Queue.Item> queueItems) {
|
||||
public void add(@NonNull Iterable<T> runItems, @NonNull List<QueueItem> queueItems) {
|
||||
sort(queueItems);
|
||||
addInternal(Iterables.concat(queueItems, runItems));
|
||||
}
|
||||
@@ -136,7 +137,7 @@ public class HistoryPageFilter<T> {
|
||||
/**
|
||||
* Add items to the History page, internal implementation.
|
||||
* @param items The items to be added.
|
||||
* @param <ItemT> The type of items should either be T or Queue.Item.
|
||||
* @param <ItemT> The type of items should either be T or {@link QueueItem}.
|
||||
*/
|
||||
private <ItemT> void addInternal(@NonNull Iterable<ItemT> items) {
|
||||
// Note that items can be a large lazily evaluated collection,
|
||||
@@ -240,8 +241,8 @@ public class HistoryPageFilter<T> {
|
||||
}
|
||||
|
||||
private long getNextBuildNumber(@NonNull Object entry) {
|
||||
if (entry instanceof Queue.Item) {
|
||||
Queue.Task task = ((Queue.Item) entry).task;
|
||||
if (entry instanceof QueueItem) {
|
||||
Queue.Task task = ((QueueItem) entry).getTask();
|
||||
if (task instanceof Job) {
|
||||
return ((Job) task).getNextBuildNumber();
|
||||
}
|
||||
@@ -253,8 +254,8 @@ public class HistoryPageFilter<T> {
|
||||
return HistoryPageEntry.getEntryId(entry) + 1;
|
||||
}
|
||||
|
||||
private void addQueueItem(Queue.Item item) {
|
||||
HistoryPageEntry<Queue.Item> entry = new HistoryPageEntry<>(item);
|
||||
private void addQueueItem(QueueItem item) {
|
||||
HistoryPageEntry<QueueItem> entry = new HistoryPageEntry<>(item);
|
||||
queueItems.add(entry);
|
||||
updateNewestOldest(entry.getEntryId());
|
||||
}
|
||||
@@ -279,8 +280,8 @@ public class HistoryPageFilter<T> {
|
||||
private boolean add(Object entry) {
|
||||
// Purposely not calling isFull(). May need to add a greater number of entries
|
||||
// to the page initially, newerThan then cutting it back down to size using cutLeading()
|
||||
if (entry instanceof Queue.Item) {
|
||||
Queue.Item item = (Queue.Item) entry;
|
||||
if (entry instanceof QueueItem) {
|
||||
QueueItem item = (QueueItem) entry;
|
||||
if (searchString != null && !fitsSearchParams(item)) {
|
||||
return false;
|
||||
}
|
||||
@@ -310,7 +311,7 @@ public class HistoryPageFilter<T> {
|
||||
return Math.max(0, maxEntries - size());
|
||||
}
|
||||
|
||||
private boolean fitsSearchParams(@NonNull Queue.Item item) {
|
||||
private boolean fitsSearchParams(@NonNull QueueItem item) {
|
||||
if (fitsSearchString(item.getDisplayName())) {
|
||||
return true;
|
||||
} else if (fitsSearchString(item.getId())) {
|
||||
|
||||
Reference in new issue
Block a user