001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 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 org.jppf.JPPFRuntimeException;
022import org.jppf.management.*;
023import org.jppf.utils.*;
024
025/**
026 * An execution policy rule that checks whether a specified number of nodes match a node execution policy.
027 * The number of nodes is expressed as a condtion of type <tt>actualNbNodes <i>comp</i> expectedNbNodes</tt>, where <tt><i>comp</i></tt>
028 * is a {@link Operator comparison operator} among <tt>==, !=, &lt;, &gt;, &lt;=, &gt;=</tt>.
029 * <p>Instances of this class can only apply to, and be used in, a server.
030 * <p>As an example, the condition "if there are more than 4 nodes idle and with at least 2 processors" can be expressed as follows:
031 * <pre>
032 * ExecutionPolicy nodePolicy = new Equal("jppf.node.idle", true).and(new AtLeast("availableProcessors", 2));
033 * ExecutionPolicy globalPolicy = new NodesMatching(Operator.GREATER, 4, nodePolicy);
034 * </pre>
035 * <p>Alternatively, it can also be written as an XML document:
036 * <pre>
037 * &lt;NodesMatching operator="GREATER" expected="4"&gt;
038 *   &lt;AND&gt;
039 *     &lt;Equal valueType="boolean"&gt;
040 *       &lt;Property&gt;jppf.node.idle&lt;/Property&gt;
041 *       &lt;Value&gt;true&lt;/Value&gt;
042 *     &lt;/Equal&gt;
043 *     &lt;AtLeast&gt;
044 *       &lt;Property&gt;availableProcessors&lt;/Property&gt;
045 *       &lt;Value&gt;2&lt;/Value&gt;
046 *     &lt;/AtLeast&gt;
047 *   &lt;/AND&gt;
048 * &lt;/NodesMatching&gt;
049 * </pre>
050 * @author Laurent Cohen
051 * @since 5.2
052 */
053public class NodesMatching extends ExecutionPolicy {
054  /**
055   * Explicit serialVersionUID.
056   */
057  private static final long serialVersionUID = 1L;
058  /**
059   * Name of the corresponding XML element.
060   */
061  public static final String XML_TAG = NodesMatching.class.getSimpleName();
062  /**
063   * The comparison operator to use for the number of nodes.
064   */
065  private final Operator operator;
066  /**
067   * The expected number of nodes to use in the comparison.
068   */
069  private final Expression<Double> expectedNodes;
070  /**
071   * The execution policy to match the nodes against.
072   */
073  private final ExecutionPolicy nodePolicy;
074
075  /**
076   * Initialize this execution policy.
077   * @param operator the comparison operator to use for the number of nodes.
078   * @param expectedNodes the expected number of nodes to use in the comparison.
079   * @param nodePolicy the execution policy to match the nodes against. If {@code null}, then all the nodes will match.
080   */
081  public NodesMatching(final Operator operator, final long expectedNodes, final ExecutionPolicy nodePolicy) {
082    if (expectedNodes < 0L) throw new JPPFRuntimeException("the number of expected nodes cannot be less than 0 in a '" + getClass().getSimpleName() + "' policy rule");
083    if (nodePolicy == null) throw new JPPFRuntimeException("the node policy cannot be null in a '" + getClass().getSimpleName() + "' policy rule");
084    this.operator = operator == null ? Operator.EQUAL : operator;
085    this.expectedNodes = new NumericExpression((double) expectedNodes);
086    this.nodePolicy = nodePolicy;
087  }
088
089  /**
090   * Initialize this execution policy.
091   * @param operator the comparison operator to use for the number of nodes.
092   * @param expectedNodes an expression that evaluates to the number of expected nodes.
093   * @param nodePolicy the execution policy to match the nodes against. If {@code null}, then all the nodes will match.
094   */
095  public NodesMatching(final Operator operator, final String expectedNodes, final ExecutionPolicy nodePolicy) {
096    if (expectedNodes == null) throw new JPPFRuntimeException("the number of expected nodes expression cannot be null in a '" + getClass().getSimpleName() + "' policy rule");
097    if (nodePolicy == null) throw new JPPFRuntimeException("the node policy cannot be null");
098    this.operator = operator == null ? Operator.EQUAL : operator;
099    this.expectedNodes = new NumericExpression(expectedNodes);
100    this.nodePolicy = nodePolicy;
101  }
102
103  @Override
104  public boolean accepts(final PropertiesCollection<String> info) {
105    int nbNodes = 0;
106    try (JMXDriverConnectionWrapper jmx = new JMXDriverConnectionWrapper()) {
107      jmx.connect();
108      nbNodes = jmx.nbNodes((nodePolicy == null) ? NodeSelector.ALL_NODES : new ExecutionPolicySelector(nodePolicy));
109      return operator.evaluate(nbNodes, expectedNodes.evaluate(info).longValue());
110    } catch (final RuntimeException e) {
111      throw e;
112    } catch (final Exception e) {
113      throw new JPPFRuntimeException("error evaluating global policy", e);
114    }
115  }
116
117  @Override
118  public String toString(final int n) {
119    final StringBuilder sb = new StringBuilder();
120    sb.append(indent(n)).append('<').append(XML_TAG);
121    sb.append(" operator=\"").append(operator.name()).append("\" expected=\"").append(expectedNodes.getExpression().replace("\"", "&quot;")).append("\">\n");
122    if (nodePolicy != null) sb.append(nodePolicy.toString(n + 1));
123    sb.append(indent(n)).append("</").append(XML_TAG).append(">\n");
124    return sb.toString();
125  }
126
127  @Override
128  void initializeRoot() {
129    super.initializeRoot();
130    if (nodePolicy != null) nodePolicy.setContext(context);
131  }
132
133  @Override
134  void initializeRoot(final ExecutionPolicy root) {
135    super.initializeRoot(root);
136    if (nodePolicy != null) nodePolicy.setContext(getContext());
137  }
138}