diff --git a/core/src/main/java/hudson/model/Label.java b/core/src/main/java/hudson/model/Label.java index 43604d408aac..b0c32f212a09 100644 --- a/core/src/main/java/hudson/model/Label.java +++ b/core/src/main/java/hudson/model/Label.java @@ -592,10 +592,16 @@ public static Set parse(@CheckForNull String labels) { final Set r = new TreeSet<>(); labels = fixNull(labels); if (labels.length() > 0) { - final QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(labels); - while (tokenizer.hasMoreTokens()) - r.add(Jenkins.get().getLabelAtom(tokenizer.nextToken())); + Jenkins j = Jenkins.get(); + LabelAtom labelAtom = j.tryGetLabelAtom(labels); + if (labelAtom == null) { + final QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(labels); + while (tokenizer.hasMoreTokens()) + r.add(j.getLabelAtom(tokenizer.nextToken())); + } else { + r.add(labelAtom); } + } return r; } diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java index 519d89c10970..2d34e6e90ade 100644 --- a/core/src/main/java/hudson/model/Node.java +++ b/core/src/main/java/hudson/model/Node.java @@ -68,6 +68,7 @@ import net.sf.json.JSONObject; import org.jvnet.localizer.Localizable; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.accmod.restrictions.ProtectedExternally; import org.kohsuke.stapler.BindInterceptor; import org.kohsuke.stapler.Stapler; @@ -298,6 +299,17 @@ public OfflineCause getTemporaryOfflineCause() { public TagCloud getLabelCloud() { return new TagCloud<>(getAssignedLabels(), Label::getTiedJobCount); } + + /** + * @return An immutable set of LabelAtom associated with the current node label. + */ + @NonNull + @Restricted(NoExternalUse.class) + protected Set getLabelAtomSet() { + // Default implementation doesn't cache, since we can't hook on label updates. + return Collections.unmodifiableSet(Label.parse(getLabelString())); + } + /** * Returns the possibly empty set of labels that are assigned to this node, * including the automatic {@link #getSelfLabel() self label}, manually @@ -305,13 +317,13 @@ public TagCloud getLabelCloud() { * {@link LabelFinder} extension point. * * This method has a side effect of updating the hudson-wide set of labels - * and should be called after events that will change that - e.g. a agent + * and should be called after events that will change that - e.g. an agent * connecting. */ @Exported public Set getAssignedLabels() { - Set r = Label.parse(getLabelString()); + Set r = new HashSet<>(getLabelAtomSet()); r.add(getSelfLabel()); r.addAll(getDynamicLabels()); return Collections.unmodifiableSet(r); diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 22eb63cd2fc6..da5195a963cf 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -36,6 +36,7 @@ import hudson.Util; import hudson.cli.CLI; import hudson.model.Descriptor.FormException; +import hudson.model.labels.LabelAtom; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.Which; @@ -60,6 +61,7 @@ import java.net.URLConnection; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.jar.JarFile; @@ -179,6 +181,7 @@ protected Slave(@NonNull String name, String remoteFS, ComputerLauncher launcher this.name = name; this.remoteFS = remoteFS; this.launcher = launcher; + this.labelAtomSet = Collections.unmodifiableSet(Label.parse(label)); } /** @@ -193,7 +196,7 @@ protected Slave(@NonNull String name, String nodeDescription, String remoteFS, i this.numExecutors = numExecutors; this.mode = mode; this.remoteFS = Util.fixNull(remoteFS).trim(); - this.label = Util.fixNull(labelString).trim(); + this.labelAtomSet = Collections.unmodifiableSet(Label.parse(labelString)); this.launcher = launcher; this.retentionStrategy = retentionStrategy; getAssignedLabels(); // compute labels now @@ -328,11 +331,24 @@ public String getLabelString() { @Override @DataBoundSetter public void setLabelString(String labelString) throws IOException { - this.label = Util.fixNull(labelString).trim(); + _setLabelString(labelString); // Compute labels now. getAssignedLabels(); } + private void _setLabelString(String labelString) { + this.label = Util.fixNull(labelString).trim(); + this.labelAtomSet = Collections.unmodifiableSet(Label.parse(label)); + } + + @NonNull + private transient Set labelAtomSet; + + @Override + protected Set getLabelAtomSet() { + return labelAtomSet; + } + @Override public Callable getClockDifferenceCallable() { return new GetClockDifference1(); @@ -574,6 +590,7 @@ public int hashCode() { protected Object readResolve() { if (nodeProperties == null) nodeProperties = new DescribableList<>(this); + _setLabelString(label); return this; } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 9bf94cf9389a..501abc905777 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -930,7 +930,7 @@ protected Jenkins(File root, ServletContext context, PluginManager pluginManager Trigger.timer = new java.util.Timer("Jenkins cron thread"); queue = new Queue(LoadBalancer.CONSISTENT_HASH); - + labelAtomSet = Collections.unmodifiableSet(Label.parse(label)); try { dependencyGraph = DependencyGraph.EMPTY; } catch (InternalError e) { @@ -1088,6 +1088,7 @@ protected Object readResolve() { /* deserializing without a value set means we need to migrate */ nodeRenameMigrationNeeded = true; } + _setLabelString(label); return this; } @@ -2114,6 +2115,20 @@ public Label getLabel(String expr) { } } + /** + * Returns the label atom of the given name, only if it already exists. + * @return non-null if the label atom already exists. + */ + @Restricted(NoExternalUse.class) + public @Nullable LabelAtom tryGetLabelAtom(@NonNull String name) { + Label label = labels.get(name); + if (label instanceof LabelAtom) { + return (LabelAtom) label; + } + return null; + } + + /** * Gets all the active labels in the current system. */ @@ -2126,6 +2141,14 @@ public Set