001/*
002 * JPPF.
003 * Copyright (C) 2005-2016 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.node.policy;
020
021import java.io.Serializable;
022
023import org.jppf.node.protocol.*;
024import org.jppf.utils.*;
025import org.jppf.utils.stats.JPPFStatistics;
026
027/**
028 * Interface for all execution policy implementations.
029 * This API defines a DSL for predicate-oriented expressions.
030 * @author Laurent Cohen
031 */
032public abstract class ExecutionPolicy implements Serializable {
033  /**
034   * Explicit serialVersionUID.
035   */
036  private static final long serialVersionUID = 1L;
037  /**
038   * Level of indentation used in the toString() method.
039   */
040  static int toStringIndent = 0;
041  /**
042   * Stores the XML representation of this object.
043   * Used to avoid doing it more than once.
044   */
045  transient String computedToString = null;
046  /**
047   * The children of this rule.
048   * @exclude
049   */
050  protected final ExecutionPolicy[] children;
051  /**
052   * The root of an execution policy graph, set by the queue manager on the client or server side.
053   */
054  transient ExecutionPolicy root = null;
055  /**
056   * The context for this policy.
057   */
058  transient PolicyContext context = null;
059
060  /**
061   * Initialize this policy with the specified children.
062   * @param children the children of this policy.
063   */
064  protected ExecutionPolicy(final ExecutionPolicy... children) {
065    this.children = children;
066  }
067
068  /**
069   * Determines whether this policy accepts the specified node.
070   * @param info system information for the node on which the tasks will run if accepted.
071   * @return true if the node is accepted, false otherwise.
072   */
073  public abstract boolean accepts(PropertiesCollection info);
074
075  /**
076   * Create an execution policy that is a logical "AND" combination of this policy and the one specified as operand.
077   * @param rule the rule to combine this one with.
078   * @return an execution policy that combines the this policy with the operand in an "AND" operation.
079   */
080  public ExecutionPolicy and(final ExecutionPolicy rule) {
081    return and(new ExecutionPolicy[] {rule});
082  }
083
084  /**
085   * Create an execution policy that is a logical "AND" combination of this policy and those specified as operands.
086   * @param rules rules to combine this one with.
087   * @return an execution policy that combines the this policy with the operand in an "AND" operation.
088   */
089  public ExecutionPolicy and(final ExecutionPolicy...rules) {
090    return new AndRule(makeRuleArray(this, rules));
091  }
092
093  /**
094   * Create an execution policy that is a logical "NOT" combination of this policy and the negations of the one specified as argument.
095   * @param rule the rule to combine this one with.
096   * @return an execution policy that combines the this policy with the operand in an "AND" operation.
097   */
098  public ExecutionPolicy andNot(final ExecutionPolicy rule) {
099    return andNot(new ExecutionPolicy[] {rule});
100  }
101
102  /**
103   * Create an execution policy that is a logical "AND" combination of this policy and the negations of those specified as argumens.
104   * @param rules rules to combine this one with.
105   * @return an execution policy that combines the this policy with the operand in an "AND" operation.
106   */
107  public ExecutionPolicy andNot(final ExecutionPolicy...rules) {
108    return new AndRule(makeNotRuleArray(this, rules));
109  }
110
111  /**
112   * Create an execution policy that is a logical "OR" combination of this policy and the one specified as operand.
113   * @param rule the rule to combine this one with.
114   * @return an execution policy that combines the this policy with the operand in an "OR" operation.
115   */
116  public ExecutionPolicy or(final ExecutionPolicy rule) {
117    return or(new ExecutionPolicy[] {rule});
118  }
119
120  /**
121   * Create an execution policy that is a logical "OR" combination of this policy and those specified as operands.
122   * @param rules rules to combine this one with.
123   * @return an execution policy that combines the this policy with the operand in an "OR" operation.
124   */
125  public ExecutionPolicy or(final ExecutionPolicy...rules) {
126    return new OrRule(makeRuleArray(this, rules));
127  }
128
129  /**
130   * Create an execution policy that is a logical "OR" combination of this policy and the negations of the one specified as argument.
131   * @param rule rules to combine this one with.
132   * @return an execution policy that combines the this policy with the negated operand in an "OR NOT" operation.
133   */
134  public ExecutionPolicy orNot(final ExecutionPolicy rule) {
135    return orNot(new ExecutionPolicy[] {rule});
136  }
137
138  /**
139   * Create an execution policy that is a logical "OR" combination of this policy and the negations of those specified as arguments.
140   * @param rules rules to combine this one with.
141   * @return an execution policy that combines the this policy with the negated operand in an "OR NOT" operation.
142   */
143  public ExecutionPolicy orNot(final ExecutionPolicy...rules) {
144    return new OrRule(makeNotRuleArray(this, rules));
145  }
146
147  /**
148   * Create an execution policy that is a logical "XOR" combination of this policy and the one specified as operand.
149   * @param rule the rule to combine this one with.
150   * @return an execution policy that combines the this policy with the operand in an "XOR" operation.
151   */
152  public ExecutionPolicy xor(final ExecutionPolicy rule) {
153    return xor(new ExecutionPolicy[] {rule});
154  }
155
156  /**
157   * Create an execution policy that is a logical "XOR" combination of the this policy and those specified as operands.
158   * @param rules rules to combine this one with.
159   * @return an execution policy that combines the this policy with the operand in an "XOR" operation.
160   */
161  public ExecutionPolicy xor(final ExecutionPolicy...rules) {
162    return new XorRule(makeRuleArray(this, rules));
163  }
164
165  /**
166   * Create an execution policy that is a negation of this policy.
167   * @return an execution policy that negates this policy.
168   */
169  public ExecutionPolicy not() {
170    return new NotRule(this);
171  }
172
173  /**
174   * Get the children of this rule.
175   * @return an array of {@link ExecutionPolicy} instances, or {@code null} for leaf rules.
176   * @exclude
177   */
178  public ExecutionPolicy[] getChildren() {
179    return children;
180  }
181
182  /**
183   * Generate  new array with size +1 and the specified rule as first element.
184   * @param rule the rule to set as first element.
185   * @param ruleArray the array of other rules.
186   * @return an array of <code>ExecutionPolicy</code> instances.
187   */
188  private static ExecutionPolicy[] makeRuleArray(final ExecutionPolicy rule, final ExecutionPolicy[] ruleArray) {
189    ExecutionPolicy[] result = new ExecutionPolicy[ruleArray.length + 1];
190    int count = 0;
191    result[count++] = rule;
192    for (ExecutionPolicy r: ruleArray) result[count++] = r;
193    return result;
194  }
195
196  /**
197   * Generate  new array with size +1 and the specified rule as first element,
198   * while the other rules are negated before being added to the new array.
199   * @param rule the rule to set as first element.
200   * @param ruleArray the array of other rules.
201   * @return an array of <code>ExecutionPolicy</code> instances.
202   */
203  private static ExecutionPolicy[] makeNotRuleArray(final ExecutionPolicy rule, final ExecutionPolicy[] ruleArray) {
204    ExecutionPolicy[] result = new ExecutionPolicy[ruleArray.length + 1];
205    int count = 0;
206    result[count++] = rule;
207    for (ExecutionPolicy r: ruleArray) result[count++] = r.not();
208    return result;
209  }
210
211  /**
212   * Get the value of the specified property in the specified set of system information.
213   * @param info the system information in which to lookup the property.
214   * @param name the name of the property to look for.
215   * @return the value of the property, or null if it could not be found.
216   */
217  public String getProperty(final PropertiesCollection info, final String name) {
218    for (TypedProperties props: info.getPropertiesArray()) {
219      String value = props.getString(name);
220      if (value != null) return value;
221    }
222    return null;
223  }
224
225  /**
226   * Get an indented string.
227   * @return an indented string depending on the value of <code>toStringIndent</code>.
228   * @exclude
229   */
230  protected static String indent() {
231    StringBuilder sb = new StringBuilder();
232    for (int i=0; i<toStringIndent; i++) sb.append("  ");
233    return sb.toString();
234  }
235
236  /**
237   * An execution policy that realizes a binary logical combination of the policies specified as operands.
238   */
239  public abstract static class LogicalRule extends ExecutionPolicy {
240    /**
241     * Initialize this binary logical operator with the specified operands.
242     * @param rules the first operand.
243     */
244    public LogicalRule(final ExecutionPolicy...rules) {
245      super(rules);
246    }
247
248    /**
249     * Print this object to a string.
250     * @return an XML string representation of this object
251     */
252    @Override
253    public String toString() {
254      synchronized(ExecutionPolicy.class) {
255        StringBuilder sb = new StringBuilder();
256        toStringIndent++;
257        if (children == null) sb.append(indent()).append("null\n");
258        else {
259          for (ExecutionPolicy ep: children) sb.append(ep.toString());
260        }
261        toStringIndent--;
262        return sb.toString();
263      }
264    }
265  }
266
267  /**
268   * An execution policy that realizes a logical "AND" combination of multiple policies specified as operands.
269   */
270  public static class AndRule extends LogicalRule {
271    /**
272     * Initialize this AND operator with the specified operands.
273     * @param rules the rules to combine.
274     */
275    public AndRule(final ExecutionPolicy...rules) {
276      super(rules);
277    }
278
279    /**
280     * Determine if a node is acceptable for this policy.
281     * @param info system information for the node on which the tasks will run if accepted.
282     * @return true if and only if the 2 operands' accepts() method return true or an empty or null operand list was specified.
283     */
284    @Override
285    public boolean accepts(final PropertiesCollection info) {
286      if ((children == null) || (children.length <= 0)) return true;
287      boolean b = true;
288      for (ExecutionPolicy child: children) {
289        b = b && child.accepts(info);
290        if (!b) return false;
291      }
292      return b;
293    }
294
295    /**
296     * Print this object to a string.
297     * @return an XML string representation of this object
298     * @see java.lang.Object#toString()
299     */
300    @Override
301    public String toString() {
302      if (computedToString == null) {
303        synchronized(ExecutionPolicy.class) {
304          computedToString = new StringBuilder().append(indent()).append("<AND>\n").append(super.toString()).append(indent()).append("</AND>\n").toString();
305        }
306      }
307      return computedToString;
308    }
309  }
310
311  /**
312   * An execution policy that realizes a logical "OR" combination of multiple policies specified as operands.
313   */
314  public static class OrRule extends LogicalRule {
315    /**
316     * Initialize this OR operator with the specified operands.
317     * @param rules the rules to combine.
318     */
319    public OrRule(final ExecutionPolicy...rules) {
320      super(rules);
321    }
322
323    /**
324     * Determine if a node is acceptable for this policy.
325     * @param info system information for the node on which the tasks will run if accepted.
326     * @return true if at least one of the operands' accepts() method returns true.
327     */
328    @Override
329    public boolean accepts(final PropertiesCollection info) {
330      if ((children == null) || (children.length <= 0)) return true;
331      boolean b = false;
332      for (ExecutionPolicy child: children) {
333        b = b || child.accepts(info);
334        if (b) return true;
335      }
336      return b;
337    }
338
339    /**
340     * Print this object to a string.
341     * @return an XML string representation of this object
342     */
343    @Override
344    public String toString() {
345      if (computedToString == null) {
346        synchronized(ExecutionPolicy.class) {
347          computedToString = new StringBuilder().append(indent()).append("<OR>\n").append(super.toString()).append(indent()).append("</OR>\n").toString();
348        }
349      }
350      return computedToString;
351    }
352  }
353
354  /**
355   * An execution policy that realizes a logical "XOR" combination of multiple policies specified as operands.
356   */
357  public static class XorRule extends LogicalRule {
358    /**
359     * Initialize this OR operator with the specified operands.
360     * @param rules the rules to combine.
361     */
362    public XorRule(final ExecutionPolicy...rules) {
363      super(rules);
364    }
365
366    /**
367     * Determine if a node is acceptable for this policy.
368     * @param info system information for the node on which the tasks will run if accepted.
369     * @return true if and only if the operands' accepts() method return different values.
370     */
371    @Override
372    public boolean accepts(final PropertiesCollection info) {
373      if ((children == null) || (children.length <= 0)) return true;
374      boolean b = children[0].accepts(info);
375      if (children.length >= 1) for (int i=1; i<children.length; i++) b = (b != children[i].accepts(info));
376      return b;
377    }
378
379    /**
380     * Print this object to a string.
381     * @return an XML string representation of this object
382     */
383    @Override
384    public String toString() {
385      if (computedToString == null) {
386        synchronized(ExecutionPolicy.class) {
387          computedToString = new StringBuilder().append(indent()).append("<XOR>\n").append(super.toString()).append(indent()).append("</XOR>\n").toString();
388        }
389      }
390      return computedToString;
391    }
392  }
393
394  /**
395   * An execution policy that realizes the negation of a policy specified as operand.
396   */
397  public static class NotRule extends ExecutionPolicy {
398    /**
399     * Initialize this binary logical operator with the specified operands.
400     * @param rule the operand.
401     */
402    public NotRule(final ExecutionPolicy rule) {
403      super(new ExecutionPolicy[] { rule });
404      if (rule == null) throw new IllegalArgumentException("negated rule cannot be null");
405    }
406
407    /**
408     * Determine if a node is acceptable for this policy.
409     * @param info system information for the node on which the tasks will run if accepted.
410     * @return true if and only if the 2 operands' accepts() method return true.
411     */
412    @Override
413    public boolean accepts(final PropertiesCollection info) {
414      return !children[0].accepts(info);
415    }
416
417    /**
418     * Print this object to a string.
419     * @return an XML string representation of this object.
420     */
421    @Override
422    public String toString() {
423      if (computedToString == null) {
424        synchronized(ExecutionPolicy.class) {
425          StringBuilder sb = new StringBuilder();
426          sb.append(indent()).append("<NOT>\n");
427          toStringIndent++;
428          sb.append(children[0].toString());
429          toStringIndent--;
430          sb.append(indent()).append("</NOT>\n");
431          computedToString = sb.toString();
432        }
433      }
434      return computedToString;
435    }
436  }
437
438  /**
439   * A convenience method to provide a way to negate a policy that is consistent with the execution policy DSL.
440   * @param policy the policy to negate.
441   * @return An execution policy which negates the specified policy, or <code>null</code> if the specified policy was <code>null</code>.
442   */
443  public static ExecutionPolicy Not(final ExecutionPolicy policy) {
444    return policy == null ? null : new NotRule(policy);
445  }
446
447  /**
448   * Format the specified name as <i>&lt;name&gt;</i>.
449   * @param name the name of the tag.
450   * @return an XML start tag built from the name.
451   * @exclude
452   */
453  protected String tagStart(final String name) {
454    return new StringBuilder().append('<').append(name).append('>').toString();
455  }
456
457  /**
458   * Format the specified name as <i>&lt;/name&gt;</i>.
459   * @param name the name of the tag.
460   * @return an XML end tag built from the name.
461   * @exclude
462   */
463  protected String tagEnd(final String name) {
464    return new StringBuilder().append('<').append(name).append('>').toString();
465  }
466
467  /**
468   * Format an XML element as <i>&lt;tag;&gt;value&lt/tag&gt;</i>.
469   * @param tag the name of the XML tag.
470   * @param value the string value of the XML element.
471   * @return an XML element built from the tag name and value.
472   * @exclude
473   */
474  protected String xmlElement(final String tag, final String value) {
475    StringBuilder sb = new StringBuilder();
476    sb.append('<').append(tag).append('>');
477    sb.append(value);
478    sb.append("</").append(tag).append('>');
479    return sb.toString();
480  }
481
482  /**
483   * Format an XML element as <i>&lt;tag;&gt;value&lt/tag&gt;</i>.
484   * @param tag the name of the XML tag.
485   * @param value the value of the XML element as a double.
486   * @return an XML element built from the tag name and value.
487   * @exclude
488   */
489  protected String xmlElement(final String tag, final double value) {
490    return xmlElement(tag, Double.toString(value));
491  }
492
493  /**
494   * Initialize the root for all the elements in the policy graph for which this is the root.
495   * @since 5.0
496   */
497  void initializeRoot() {
498    if (children != null) {
499      for (ExecutionPolicy child: children) child.initializeRoot(this);
500    }
501  }
502
503  /**
504   * Initialize the specified root for all the elements in the policy sub-graph for which this is the root.
505   * @param root the root to set.
506   * @since 5.0
507   */
508  void initializeRoot(final ExecutionPolicy root) {
509    if (this.root != null) return;
510    this.root = root;
511    if (children != null) {
512      for (ExecutionPolicy child: children) child.initializeRoot(root);
513    }
514  }
515
516  /**
517   * Set the parameters used as bound variables in the script.
518   * @param sla the job server-side sla.
519   * @param clientSla the job client-side sla.
520   * @param metadata the job metadata.
521   * @param jobDispatches the number of nodes the job is already dispatched to.
522   * @param stats the server statistics.
523   * @since 5.0
524   * @exclude
525   */
526  public void setContext(final JobSLA sla, final JobClientSLA clientSla, final JobMetadata metadata, final int jobDispatches, final JPPFStatistics stats) {
527    setContext(new PolicyContext(sla, clientSla, metadata, jobDispatches, stats));
528  }
529
530  /**
531   * Set this policy's context.
532   * @param context the context to set.
533   * @since 5.2
534   * @exclude
535   */
536  protected void setContext(final PolicyContext context) {
537    this.context = context;
538    initializeRoot();
539  }
540
541  /**
542   * Get the context for this policy.
543   * @return a {@link PolicyContext} object.
544   * @since 5.0
545   */
546  public PolicyContext getContext() {
547    return root == null ? context : root.context;
548  }
549}