diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index f31316c31656..38900ddb61f6 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -129,6 +129,7 @@ protected AbstractItem(ItemGroup parent, String name) { doSetName(name); } + @NonNull @Override @Exported(visibility = 999) public String getName() { @@ -470,6 +471,7 @@ public void movedTo(DirectlyModifiableTopLevelItemGroup destination, AbstractIte @Override public abstract Collection getAllJobs(); + @NonNull @Override @Exported public final String getFullName() { diff --git a/core/src/main/java/hudson/model/Item.java b/core/src/main/java/hudson/model/Item.java index dd2fd993cb9e..daed4a1babc0 100644 --- a/core/src/main/java/hudson/model/Item.java +++ b/core/src/main/java/hudson/model/Item.java @@ -38,7 +38,10 @@ import hudson.util.Secret; import java.io.IOException; import java.util.Collection; +import jenkins.model.FullyNamed; +import jenkins.model.FullyNamedModelObject; import jenkins.model.Jenkins; +import jenkins.model.Named; import jenkins.search.SearchGroup; import jenkins.util.SystemProperties; import jenkins.util.io.OnMaster; @@ -73,7 +76,7 @@ * @see Items * @see ItemVisitor */ -public interface Item extends PersistenceRoot, SearchableModelObject, AccessControlled, OnMaster { +public interface Item extends PersistenceRoot, FullyNamedModelObject, SearchableModelObject, FullyNamed, Named, AccessControlled, OnMaster { /** * Gets the parent that contains this item. */ @@ -97,6 +100,8 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont * * @see #getFullName() */ + @NonNull + @Override String getName(); /** @@ -110,6 +115,8 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont * * @see jenkins.model.Jenkins#getItemByFullName(String,Class) */ + @NonNull + @Override String getFullName(); /** @@ -127,13 +134,6 @@ public interface Item extends PersistenceRoot, SearchableModelObject, AccessCont @Override String getDisplayName(); - /** - * Works like {@link #getDisplayName()} but return - * the full path that includes all the display names - * of the ancestors. - */ - String getFullDisplayName(); - /** * Gets the relative name to this item from the specified group. * diff --git a/core/src/main/java/hudson/model/ItemGroup.java b/core/src/main/java/hudson/model/ItemGroup.java index 27d6ebc35be1..99e0fc31d209 100644 --- a/core/src/main/java/hudson/model/ItemGroup.java +++ b/core/src/main/java/hudson/model/ItemGroup.java @@ -33,6 +33,8 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import jenkins.model.FullyNamed; +import jenkins.model.FullyNamedModelObject; import org.springframework.security.access.AccessDeniedException; /** @@ -41,19 +43,7 @@ * @author Kohsuke Kawaguchi * @see ItemGroupMixIn */ -public interface ItemGroup extends PersistenceRoot, ModelObject { - /** - * Gets the full name of this {@link ItemGroup}. - * - * @see Item#getFullName() - */ - String getFullName(); - - /** - * @see Item#getFullDisplayName() - */ - String getFullDisplayName(); - +public interface ItemGroup extends FullyNamed, FullyNamedModelObject, PersistenceRoot { /** * Gets all the items in this collection in a read-only view. */ diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index fc70d593f68f..5e1af9a5eefd 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -109,6 +109,8 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import jenkins.console.WithConsoleUrl; +import jenkins.model.FullyNamed; +import jenkins.model.FullyNamedModelObject; import jenkins.model.Jenkins; import jenkins.model.queue.AsynchronousExecution; import jenkins.model.queue.CompositeCauseOfBlockage; @@ -1882,7 +1884,7 @@ public interface NonBlockingTask extends Task {} * design, a {@link Task} must have at least one sub-task.) * Most of the time, the primary subtask is the only sub task. */ - public interface Task extends ModelObject, SubTask { + public interface Task extends FullyNamedModelObject, SubTask { /** * Returns true if the execution should be blocked * for temporary reasons. @@ -1928,21 +1930,22 @@ default CauseOfBlockage getCauseOfBlockage() { */ String getName(); - /** - * @see hudson.model.Item#getFullDisplayName() - */ - String getFullDisplayName(); - /** * Returns task-specific key which is used by the {@link LoadBalancer} to choose one particular executor * amongst all the free executors on all possibly suitable nodes. * NOTE: To be able to re-use the same node during the next run this key should not change from one run to * another. You probably want to compute that key based on the job's name. * - * @return by default: {@link #getFullDisplayName()} + * @return by default: {@link FullyNamed#getFullName()} if implements {@link FullyNamed} or {@link #getFullDisplayName()} otherwise. * @see hudson.model.LoadBalancer */ - default String getAffinityKey() { return getFullDisplayName(); } + default String getAffinityKey() { + if (this instanceof FullyNamed fullyNamed) { + return fullyNamed.getFullName(); + } else { + return getFullDisplayName(); + } + } /** * Checks the permission to see if the current user can abort this executable. diff --git a/core/src/main/java/hudson/model/ResourceActivity.java b/core/src/main/java/hudson/model/ResourceActivity.java index 838b5b34a5b1..e5c3a0a5b5c6 100644 --- a/core/src/main/java/hudson/model/ResourceActivity.java +++ b/core/src/main/java/hudson/model/ResourceActivity.java @@ -29,7 +29,7 @@ * * @author Kohsuke Kawaguchi */ -public interface ResourceActivity { +public interface ResourceActivity extends ModelObject { /** * Gets the list of {@link Resource}s that this task requires. * Used to make sure no two conflicting tasks run concurrently. @@ -47,9 +47,4 @@ public interface ResourceActivity { default ResourceList getResourceList() { return ResourceList.EMPTY; } - - /** - * Used for rendering HTML. - */ - String getDisplayName(); } diff --git a/core/src/main/java/hudson/model/queue/MappingWorksheet.java b/core/src/main/java/hudson/model/queue/MappingWorksheet.java index 8150dd1bc132..76777d3dd232 100644 --- a/core/src/main/java/hudson/model/queue/MappingWorksheet.java +++ b/core/src/main/java/hudson/model/queue/MappingWorksheet.java @@ -27,6 +27,7 @@ import static java.lang.Math.max; import com.google.common.collect.Iterables; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Computer; import hudson.model.Executor; import hudson.model.Label; @@ -48,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.logging.Logger; +import jenkins.model.Named; /** * Defines a mapping problem for answering "where do we execute this task?" @@ -114,7 +116,7 @@ public int size() { } } - public final class ExecutorChunk extends ReadOnlyList { + public final class ExecutorChunk extends ReadOnlyList implements Named { public final int index; public final Computer computer; public final Node node; @@ -150,6 +152,8 @@ public boolean canAccept(WorkChunk c) { /** * Node name. */ + @NonNull + @Override public String getName() { return node.getNodeName(); } diff --git a/core/src/main/java/jenkins/model/FullyNamed.java b/core/src/main/java/jenkins/model/FullyNamed.java new file mode 100644 index 000000000000..e3575e49aecb --- /dev/null +++ b/core/src/main/java/jenkins/model/FullyNamed.java @@ -0,0 +1,17 @@ +package jenkins.model; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * An interface for objects that have a name and a parent, so exposing a full name. + */ +public interface FullyNamed { + /** + * Returns the full name of this object, which is a qualified name + * that includes the names of all its ancestors, separated by '/'. + * + * @return the full name of this object. + */ + @NonNull + String getFullName(); +} diff --git a/core/src/main/java/jenkins/model/FullyNamedModelObject.java b/core/src/main/java/jenkins/model/FullyNamedModelObject.java new file mode 100644 index 000000000000..e713150b01a4 --- /dev/null +++ b/core/src/main/java/jenkins/model/FullyNamedModelObject.java @@ -0,0 +1,23 @@ +package jenkins.model; + +import hudson.model.ModelObject; +import jenkins.security.stapler.StaplerAccessibleType; + +/** + * A model object that has a human-readable full name. This is usually valid when nested as part of an object hierarchy. + * + *

+ * This interface is used to mark objects that can be qualified in the context of a Jenkins instance. + * It is typically used for objects that are part of the Jenkins model and can be referenced by their names. + * + * @see ModelObject + */ +@StaplerAccessibleType +public interface FullyNamedModelObject extends ModelObject { + /** + * Works like {@link #getDisplayName()} but return + * the full path that includes all the display names + * of the ancestors in an unspecified format. + */ + String getFullDisplayName(); +} diff --git a/core/src/main/java/jenkins/model/HistoricalBuild.java b/core/src/main/java/jenkins/model/HistoricalBuild.java index 480eb9935654..827849c0439b 100644 --- a/core/src/main/java/jenkins/model/HistoricalBuild.java +++ b/core/src/main/java/jenkins/model/HistoricalBuild.java @@ -29,7 +29,6 @@ import hudson.markup.MarkupFormatter; import hudson.model.BallColor; import hudson.model.BuildBadgeAction; -import hudson.model.ModelObject; import hudson.model.ParameterValue; import hudson.model.ParametersAction; import hudson.model.Queue; @@ -48,7 +47,7 @@ * @since 2.477 */ @Restricted(Beta.class) -public interface HistoricalBuild extends ModelObject { +public interface HistoricalBuild extends FullyNamedModelObject { /** * @return A build number @@ -74,12 +73,6 @@ public interface HistoricalBuild extends ModelObject { @CheckForNull String getDescription(); - /** - * @return a human-readable full display name of this build. - */ - @NonNull - String getFullDisplayName(); - /** * Get the {@link Queue.Item#getId()} of the original queue item from where this {@link HistoricalBuild} instance * originated. diff --git a/core/src/main/java/jenkins/model/IComputer.java b/core/src/main/java/jenkins/model/IComputer.java index ad4c5aa8ca8b..70fe4b16dcd9 100644 --- a/core/src/main/java/jenkins/model/IComputer.java +++ b/core/src/main/java/jenkins/model/IComputer.java @@ -27,7 +27,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Util; import hudson.model.Computer; -import hudson.model.Node; +import hudson.model.ModelObject; import hudson.security.ACL; import hudson.security.AccessControlled; import java.util.List; @@ -45,13 +45,7 @@ * @since 2.480 */ @Restricted(Beta.class) -public interface IComputer extends AccessControlled, IconSpec { - /** - * Returns {@link Node#getNodeName() the name of the node}. - */ - @NonNull - String getName(); - +public interface IComputer extends AccessControlled, IconSpec, ModelObject, Named { /** * Used to render the list of executors. * @return a snapshot of the executor display information @@ -64,12 +58,6 @@ public interface IComputer extends AccessControlled, IconSpec { */ boolean isOffline(); - /** - * @return the node name for UI purposes. - */ - @NonNull - String getDisplayName(); - /** * Returns {@code true} if the computer is accepting tasks. Needed to allow agents programmatic suspension of task * scheduling that does not overlap with being offline. diff --git a/core/src/main/java/jenkins/model/IDisplayExecutor.java b/core/src/main/java/jenkins/model/IDisplayExecutor.java index 5f959a158faa..9ac5674b8199 100644 --- a/core/src/main/java/jenkins/model/IDisplayExecutor.java +++ b/core/src/main/java/jenkins/model/IDisplayExecutor.java @@ -25,6 +25,7 @@ package jenkins.model; import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.ModelObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -34,13 +35,7 @@ * @since 2.480 */ @Restricted(Beta.class) -public interface IDisplayExecutor { - /** - * @return The UI label for this executor. - */ - @NonNull - String getDisplayName(); - +public interface IDisplayExecutor extends ModelObject { /** * @return the URL where to reach specifically this executor, relative to Jenkins URL. */ diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 2230cac08bf8..eb25d5906c4c 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -1731,6 +1731,7 @@ public Launcher createLauncher(TaskListener listener) { } + @NonNull @Override public String getFullName() { return ""; diff --git a/core/src/main/java/jenkins/model/Named.java b/core/src/main/java/jenkins/model/Named.java new file mode 100644 index 000000000000..751bc16b70a7 --- /dev/null +++ b/core/src/main/java/jenkins/model/Named.java @@ -0,0 +1,19 @@ +package jenkins.model; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * An object that has a name. + *

+ * This interface is used to provide a consistent way to retrieve the name of an object in Jenkins. + * It is typically implemented by objects that need to be identified by a name, such as tasks, nodes, or other model objects. + */ +public interface Named { + /** + * Returns the name of this object. + * + * @return the name of this object, never null. + */ + @NonNull + String getName(); +} diff --git a/core/src/main/java/jenkins/model/queue/ITask.java b/core/src/main/java/jenkins/model/queue/ITask.java index 37b0e62150de..f0c6e0d51912 100644 --- a/core/src/main/java/jenkins/model/queue/ITask.java +++ b/core/src/main/java/jenkins/model/queue/ITask.java @@ -26,15 +26,15 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.model.Item; -import hudson.model.ModelObject; import hudson.security.AccessControlled; +import jenkins.model.FullyNamedModelObject; /** * A task that can be displayed in the executors widget. * * @since 2.480 */ -public interface ITask extends ModelObject { +public interface ITask extends FullyNamedModelObject { /** * @return {@code true} if the current user can cancel the current task. * diff --git a/core/src/main/java/jenkins/model/queue/QueueItem.java b/core/src/main/java/jenkins/model/queue/QueueItem.java index 7ee696b13e91..a01c345bb128 100644 --- a/core/src/main/java/jenkins/model/queue/QueueItem.java +++ b/core/src/main/java/jenkins/model/queue/QueueItem.java @@ -3,9 +3,9 @@ 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 jenkins.model.FullyNamedModelObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -14,7 +14,7 @@ * @since 2.405 */ @Restricted(Beta.class) -public interface QueueItem extends ModelObject { +public interface QueueItem extends FullyNamedModelObject { /** * @return true if the item is starving for an executor for too long. */ @@ -73,6 +73,15 @@ default boolean hasCancelPermission() { @CheckForNull @Override default String getDisplayName() { + // TODO review usage of this method and replace with getFullDisplayName() where appropriate + return getTask().getFullDisplayName(); + } + + /** + * @return the full display name for this queue item; by default, {@link Queue.Task#getFullDisplayName()} + */ + @Override + default String getFullDisplayName() { return getTask().getFullDisplayName(); } } diff --git a/test/src/test/java/hudson/model/CauseTest.java b/test/src/test/java/hudson/model/CauseTest.java index b555ed4e558e..0382bc24a6ec 100644 --- a/test/src/test/java/hudson/model/CauseTest.java +++ b/test/src/test/java/hudson/model/CauseTest.java @@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.XmlFile; import hudson.model.queue.QueueTaskFuture; import hudson.tasks.BuildTrigger; @@ -300,6 +301,7 @@ public void setVirtualName(String virtualName) { this.virtualName = virtualName; } + @NonNull @Override public String getName() { if (virtualName != null) { diff --git a/test/src/test/java/hudson/model/RunTest.java b/test/src/test/java/hudson/model/RunTest.java index 7e52408dbfab..883a02ddb21f 100644 --- a/test/src/test/java/hudson/model/RunTest.java +++ b/test/src/test/java/hudson/model/RunTest.java @@ -34,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionList; import hudson.FilePath; import hudson.Launcher; @@ -244,6 +245,7 @@ public void setVirtualName(String virtualName) { this.virtualName = virtualName; } + @NonNull @Override public String getName() { if (virtualName != null) {