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

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.model;
25
26 import antlr.ANTLRException;
27 import static hudson.Util.fixNull;
28
29 import hudson.model.labels.LabelAtom;
30 import hudson.model.labels.LabelExpression;
31 import hudson.model.labels.LabelExpression.And;
32 import hudson.model.labels.LabelExpression.Binary;
33 import hudson.model.labels.LabelExpression.Iff;
34 import hudson.model.labels.LabelExpression.Implies;
35 import hudson.model.labels.LabelExpression.Not;
36 import hudson.model.labels.LabelExpression.Or;
37 import hudson.model.labels.LabelExpression.Paren;
38 import hudson.model.labels.LabelExpressionLexer;
39 import hudson.model.labels.LabelExpressionParser;
40 import hudson.model.labels.LabelOperatorPrecedence;
41 import hudson.model.labels.LabelVisitor;
42 import hudson.slaves.NodeProvisioner;
43 import hudson.slaves.Cloud;
44 import hudson.util.QuotedStringTokenizer;
45 import hudson.util.VariableResolver;
46 import jenkins.model.Jenkins;
47 import org.kohsuke.stapler.export.Exported;
48 import org.kohsuke.stapler.export.ExportedBean;
49
50 import java.io.StringReader;
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Set;
56 import java.util.Collection;
57 import java.util.TreeSet;
58
59 import com.thoughtworks.xstream.converters.Converter;
60 import com.thoughtworks.xstream.converters.MarshallingContext;
61 import com.thoughtworks.xstream.converters.UnmarshallingContext;
62 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
63 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
64
65 /**
66 * Group of {@link Node}s.
67 *
68 * @author Kohsuke Kawaguchi
69 * @see Jenkins#getLabels()
70 * @see Jenkins#getLabel(String)
71 */
72 @ExportedBean
73 public abstract class Label extends Actionable implements Comparable<Label>, ModelObject {
74 /**
75 * Display name of this label.
76 */
77 protected transient final String name;
78 private transient volatile Set<Node> nodes;
79 private transient volatile Set<Cloud> clouds;
80
81 @Exported
82 public transient final LoadStatistics loadStatistics;
83 public transient final NodeProvisioner nodeProvisioner;
84
85 public Label(String name) {
86 this.name = name;
87 // passing these causes an infinite loop - getTotalExecutors(),getBusyExecutors());
88 this.loadStatistics = new LoadStatistics(0,0) {
89 @Override
90 public int computeIdleExecutors() {
91 return Label.this.getIdleExecutors();
92 }
93
94 @Override
95 public int computeTotalExecutors() {
96 return Label.this.getTotalExecutors();
97 }
98
99 @Override
100 public int computeQueueLength() {
101 return Jenkins.getInstance().getQueue().countBuildableItemsFor(Label.this);
102 }
103 };
104 this.nodeProvisioner = new NodeProvisioner(this, loadStatistics);
105 }
106
107 /**
108 * Alias for {@link #getDisplayName()}.
109 */
110 @Exported
111 public final String getName() {
112 return getDisplayName();
113 }
114
115 /**
116 * Returns a human-readable text that represents this label.
117 */
118 public String getDisplayName() {
119 return name;
120 }
121
122 /**
123 * Returns a label expression that represents this label.
124 */
125 public abstract String getExpression();
126
127 /**
128 * Relative URL from the context path, that ends with '/'.
129 */
130 public String getUrl() {
131 return "label/"+name+'/';
132 }
133
134 public String getSearchUrl() {
135 return getUrl();
136 }
137
138 /**
139 * Evaluates whether the label expression is true given the specified value assignment.
140 * IOW, returns true if the assignment provided by the resolver matches this label expression.
141 */
142 public abstract boolean matches(VariableResolver<Boolean> resolver);
143
144 /**
145 * Evaluates whether the label expression is true when an entity owns the given set of
146 * {@link LabelAtom}s.
147 */
148 public final boolean matches(final Collection<LabelAtom> labels) {
149 return matches(new VariableResolver<Boolean>() {
150 public Boolean resolve(String name) {
151 for (LabelAtom a : labels)
152 if (a.getName().equals(name))
153 return true;
154 return false;
155 }
156 });
157 }
158
159 public final boolean matches(Node n) {
160 return matches(n.getAssignedLabels());
161 }
162
163 /**
164 * Returns true if this label is a "self label",
165 * which means the label is the name of a {@link Node}.
166 */
167 public boolean isSelfLabel() {
168 Set<Node> nodes = getNodes();
169 return nodes.size() == 1 && nodes.iterator().next().getSelfLabel() == this;
170 }
171
172 /**
173 * Gets all {@link Node}s that belong to this label.
174 */
175 @Exported
176 public Set<Node> getNodes() {
177 Set<Node> nodes = this.nodes;
178 if(nodes!=null) return nodes;
179
180 Set<Node> r = new HashSet<Node>();
181 Jenkins h = Jenkins.getInstance();
182 if(this.matches(h))
183 r.add(h);
184 for (Node n : h.getNodes()) {
185 if(this.matches(n))
186 r.add(n);
187 }
188 return this.nodes = Collections.unmodifiableSet(r);
189 }
190
191 /**
192 * Gets all {@link Cloud}s that can launch for this label.
193 */
194 @Exported
195 public Set<Cloud> getClouds() {
196 if(clouds==null) {
197 Set<Cloud> r = new HashSet<Cloud>();
198 Jenkins h = Jenkins.getInstance();
199 for (Cloud c : h.clouds) {
200 if(c.canProvision(this))
201 r.add(c);
202 }
203 clouds = Collections.unmodifiableSet(r);
204 }
205 return clouds;
206 }
207
208 /**
209 * Can jobs be assigned to this label?
210 * <p>
211 * The answer is yes if there is a reasonable basis to believe that Hudson can have
212 * an executor under this label, given the current configuration. This includes
213 * situations such as (1) there are offline slaves that have this label (2) clouds exist
214 * that can provision slaves that have this label.
215 */
216 public boolean isAssignable() {
217 for (Node n : getNodes())
218 if(n.getNumExecutors()>0)
219 return true;
220 return !getClouds().isEmpty();
221 }
222
223 /**
224 * Number of total {@link Executor}s that belong to this label.
225 * <p>
226 * This includes executors that belong to offline nodes, so the result
227 * can be thought of as a potential capacity, whereas {@link #getTotalExecutors()}
228 * is the currently functioning total number of executors.
229 * <p>
230 * This method doesn't take the dynamically allocatable nodes (via {@link Cloud})
231 * into account. If you just want to test if there's some executors, use {@link #isAssignable()}.
232 */
233 public int getTotalConfiguredExecutors() {
234 int r=0;
235 for (Node n : getNodes())
236 r += n.getNumExecutors();
237 return r;
238 }
239
240 /**
241 * Number of total {@link Executor}s that belong to this label that are functioning.
242 * <p>
243 * This excludes executors that belong to offline nodes.
244 */
245 @Exported
246 public int getTotalExecutors() {
247 int r=0;
248 for (Node n : getNodes()) {
249 Computer c = n.toComputer();
250 if(c!=null && c.isOnline())
251 r += c.countExecutors();
252 }
253 return r;
254 }
255
256 /**
257 * Number of busy {@link Executor}s that are carrying out some work right now.
258 */
259 @Exported
260 public int getBusyExecutors() {
261 int r=0;
262 for (Node n : getNodes()) {
263 Computer c = n.toComputer();
264 if(c!=null && c.isOnline())
265 r += c.countBusy();
266 }
267 return r;
268 }
269
270 /**
271 * Number of idle {@link Executor}s that can start working immediately.
272 */
273 @Exported
274 public int getIdleExecutors() {
275 int r=0;
276 for (Node n : getNodes()) {
277 Computer c = n.toComputer();
278 if(c!=null && (c.isOnline() || c.isConnecting()) && c.isAcceptingTasks())
279 r += c.countIdle();
280 }
281 return r;
282 }
283
284 /**
285 * Returns true if all the nodes of this label is offline.
286 */
287 @Exported
288 public boolean isOffline() {
289 for (Node n : getNodes()) {
290 Computer c = n.toComputer();
291 if(c != null && !c.isOffline())
292 return false;
293 }
294 return true;
295 }
296
297 /**
298 * Returns a human readable text that explains this label.
299 */
300 @Exported
301 public String getDescription() {
302 Set<Node> nodes = getNodes();
303 if(nodes.isEmpty()) {
304 Set<Cloud> clouds = getClouds();
305 if(clouds.isEmpty())
306 return Messages.Label_InvalidLabel();
307
308 return Messages.Label_ProvisionedFrom(toString(clouds));
309 }
310
311 if(nodes.size()==1)
312 return nodes.iterator().next().getNodeDescription();
313
314 return Messages.Label_GroupOf(toString(nodes));
315 }
316
317 private String toString(Collection<? extends ModelObject> model) {
318 boolean first=true;
319 StringBuilder buf = new StringBuilder();
320 for (ModelObject c : model) {
321 if(buf.length()>80) {
322 buf.append(",...");
323 break;
324 }
325 if(!first) buf.append(',');
326 else first=false;
327 buf.append(c.getDisplayName());
328 }
329 return buf.toString();
330 }
331
332 /**
333 * Returns projects that are tied on this node.
334 */
335 @Exported
336 public List<AbstractProject> getTiedJobs() {
337 List<AbstractProject> r = new ArrayList<AbstractProject>();
338 for (AbstractProject<?,?> p : Jenkins.getInstance().getAllItems(AbstractProject.class)) {
339 if(this.equals(p.getAssignedLabel()))
340 r.add(p);
341 }
342 return r;
343 }
344
345 public boolean contains(Node node) {
346 return getNodes().contains(node);
347 }
348
349 /**
350 * If there's no such label defined in {@link Node} or {@link Cloud}.
351 * This is usually used as a signal that this label is invalid.
352 */
353 public boolean isEmpty() {
354 return getNodes().isEmpty() && getClouds().isEmpty();
355 }
356
357 /*package*/ void reset() {
358 nodes = null;
359 clouds = null;
360 }
361
362 /**
363 * Expose this object to the remote API.
364 */
365 public Api getApi() {
366 return new Api(this);
367 }
368
369 /**
370 * Accepts a visitor and call its respective "onXYZ" method based no the actual type of 'this'.
371 */
372 public abstract <V,P> V accept(LabelVisitor<V,P> visitor, P param);
373
374 /**
375 * Lists up all the atoms contained in in this label.
376 *
377 * @since 1.420
378 */
379 public Set<LabelAtom> listAtoms() {
380 Set<LabelAtom> r = new HashSet<LabelAtom>();
381 accept(ATOM_COLLECTOR,r);
382 return r;
383 }
384
385 /**
386 * Returns the label that represents "this&amp;rhs"
387 */
388 public Label and(Label rhs) {
389 return new LabelExpression.And(this,rhs);
390 }
391
392 /**
393 * Returns the label that represents "this|rhs"
394 */
395 public Label or(Label rhs) {
396 return new LabelExpression.Or(this,rhs);
397 }
398
399 /**
400 * Returns the label that represents "this&lt;->rhs"
401 */
402 public Label iff(Label rhs) {
403 return new LabelExpression.Iff(this,rhs);
404 }
405
406 /**
407 * Returns the label that represents "this->rhs"
408 */
409 public Label implies(Label rhs) {
410 return new LabelExpression.Implies(this,rhs);
411 }
412
413 /**
414 * Returns the label that represents "!this"
415 */
416 public Label not() {
417 return new LabelExpression.Not(this);
418 }
419
420 /**
421 * Returns the label that represents "(this)"
422 * This is a pointless operation for machines, but useful
423 * for humans who find the additional parenthesis often useful
424 */
425 public Label paren() {
426 return new LabelExpression.Paren(this);
427 }
428
429 /**
430 * Precedence of the top most operator.
431 */
432 public abstract LabelOperatorPrecedence precedence();
433
434
435 @Override
436 public boolean equals(Object that) {
437 if (this == that) return true;
438 if (that == null || getClass() != that.getClass()) return false;
439
440 return name.equals(((Label)that).name);
441
442 }
443
444 @Override
445 public int hashCode() {
446 return name.hashCode();
447 }
448
449 public int compareTo(Label that) {
450 return this.name.compareTo(that.name);
451 }
452
453 @Override
454 public String toString() {
455 return name;
456 }
457
458 public static final class ConverterImpl implements Converter {
459 public ConverterImpl() {
460 }
461
462 public boolean canConvert(Class type) {
463 return Label.class.isAssignableFrom(type);
464 }
465
466 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
467 Label src = (Label) source;
468 writer.setValue(src.getExpression());
469 }
470
471 public Object unmarshal(HierarchicalStreamReader reader, final UnmarshallingContext context) {
472 return Jenkins.getInstance().getLabel(reader.getValue());
473 }
474 }
475
476 /**
477 * Convers a whitespace-separate list of tokens into a set of {@link Label}s.
478 *
479 * @param labels
480 * Strings like "abc def ghi". Can be empty or null.
481 * @return
482 * Can be empty but never null. A new writable set is always returned,
483 * so that the caller can add more to the set.
484 * @since 1.308
485 */
486 public static Set<LabelAtom> parse(String labels) {
487 Set<LabelAtom> r = new TreeSet<LabelAtom>();
488 labels = fixNull(labels);
489 if(labels.length()>0)
490 for( String l : new QuotedStringTokenizer(labels).toArray())
491 r.add(Jenkins.getInstance().getLabelAtom(l));
492 return r;
493 }
494
495 /**
496 * Obtains a label by its {@linkplain #getName() name}.
497 */
498 public static Label get(String l) {
499 return Jenkins.getInstance().getLabel(l);
500 }
501
502 /**
503 * Parses the expression into a label expression tree.
504 *
505 * TODO: replace this with a real parser later
506 */
507 public static Label parseExpression(String labelExpression) throws ANTLRException {
508 LabelExpressionLexer lexer = new LabelExpressionLexer(new StringReader(labelExpression));
509 return new LabelExpressionParser(lexer).expr();
510 }
511
512 /**
513 * Collects all the atoms in the expression.
514 */
515 private static final LabelVisitor<Void,Set<LabelAtom>> ATOM_COLLECTOR = new LabelVisitor<Void,Set<LabelAtom>>() {
516 @Override
517 public Void onAtom(LabelAtom a, Set<LabelAtom> param) {
518 param.add(a);
519 return null;
520 }
521
522 @Override
523 public Void onParen(Paren p, Set<LabelAtom> param) {
524 return p.base.accept(this,param);
525 }
526
527 @Override
528 public Void onNot(Not p, Set<LabelAtom> param) {
529 return p.base.accept(this,param);
530 }
531
532 @Override
533 public Void onAnd(And p, Set<LabelAtom> param) {
534 return onBinary(p,param);
535 }
536
537 @Override
538 public Void onOr(Or p, Set<LabelAtom> param) {
539 return onBinary(p,param);
540 }
541
542 @Override
543 public Void onIff(Iff p, Set<LabelAtom> param) {
544 return onBinary(p,param);
545 }
546
547 @Override
548 public Void onImplies(Implies p, Set<LabelAtom> param) {
549 return onBinary(p,param);
550 }
551
552 private Void onBinary(Binary b, Set<LabelAtom> param) {
553 b.lhs.accept(this,param);
554 b.rhs.accept(this,param);
555 return null;
556 }
557 };
558 }
559