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 */
018package org.jppf.node.policy;
019
020import java.net.*;
021import java.util.*;
022
023import org.jppf.net.IPv4AddressNetmask;
024import org.jppf.utils.*;
025import org.slf4j.*;
026
027/**
028 * An execution policy rule that encapsulates a test of type <i>IPv4 is in
029 * Subnet string</i>.
030 * <p>This policy has the following XML representation:
031 * <pre>&lt;IsInIPv4Subnet&gt;
032 *   &lt;Subnet&gt;subnet-1&lt;/Subnet&gt;
033 *   ...
034 *   &lt;Subnet&gt;subnet-N&lt;/Subnet&gt;
035 * &lt;/IsInIPv4Subnet&gt;</pre>
036 * where each <i>subnet-i</i> is a subnet expressed either in <a href="http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing">CIDR</a> notation
037 * or in the representation described in {@link org.jppf.net.IPv4AddressPattern IPv4AddressPattern}.
038 * 
039 * @author Daniel Widdis
040 * @since 4.2
041 */
042public class IsInIPv4Subnet extends ExecutionPolicy {
043
044  /**
045   * Explicit serialVersionUID.
046   */
047  private static final long serialVersionUID = 1L;
048  /**
049   * Logger for this class.
050   */
051  private static Logger log = LoggerFactory.getLogger(IsInIPv6Subnet.class);
052  /**
053   * Determines whether the trace level is enabled in the log configuration, without the cost of a method call.
054   */
055  private static boolean traceEnabled = log.isTraceEnabled();
056  /**
057   * Name of the tag used in the XML representation of this policy.
058   */
059  private static final String TAG = IsInIPv4Subnet.class.getSimpleName();
060  /**
061   * Name of the nested subnet mask elements used in the XML representation of this policy.
062   */
063  private static final String SUBNET = "Subnet";
064  /**
065   * String value(s) to test for subnet membership
066   */
067  private final String[] subnets;
068  /**
069   * Cached list of netmasks, lazily computed.
070   */
071  private transient List<IPv4AddressNetmask> netmasks;
072
073  /**
074   * Define a membership test using ipv4.addresses property to determine if a
075   * node or driver is in one of the specified subnets.
076   *
077   * @param subnets
078   *        One or more strings representing either a valid IPv4 subnet in CIDR
079   *        notation or IP address range pattern as defined in
080   *        {@link org.jppf.net.IPv4AddressPattern IPv4AddressPattern}
081   */
082  public IsInIPv4Subnet(final String... subnets) {
083    if ((subnets == null) || (subnets.length <= 0)) throw new IllegalArgumentException("at least one IPv4 subnet must be specified");
084    this.subnets = subnets;
085  }
086
087  /**
088   * Define a membership test using ipv6.addresses property to determine if a
089   * node is in one of the specified subnets.
090   *
091   * @param subnets
092   *        One or more strings representing either a valid IPv4 subnet in CIDR
093   *        notation or IP address range pattern as defined in
094   *        {@link org.jppf.net.IPv4AddressPattern IPv4AddressPattern}
095   */
096  public IsInIPv4Subnet(final Collection<String> subnets) {
097    if ((subnets == null) || (subnets.size() <= 0)) throw new IllegalArgumentException("at least one IPv4 subnet must be specified");
098    this.subnets = subnets.toArray(new String[subnets.size()]);
099  }
100
101  /**
102   * Determines whether this policy accepts the specified node.
103   *
104   * @param info
105   *        system information for the node on which the tasks will run if
106   *        accepted.
107   * @return true if the node is accepted, false otherwise.
108   */
109  @Override
110  public boolean accepts(final PropertiesCollection info) {
111    // Build list of subnet netmasks
112    synchronized(this) {
113      if (netmasks == null) {
114        netmasks = new ArrayList<>(subnets.length);
115        for (String subnet : subnets) {
116          netmasks.add(new IPv4AddressNetmask(subnet));
117        }
118      }
119    }
120    // Get IP strings from properties
121    // Returns as "foo.com|1.2.3.4 bar.org|5.6.7.8"
122    String ipv4 = getProperty(info, "ipv4.addresses");
123
124    for (HostIP hip: NetworkUtils.parseAddresses(ipv4)) {
125      // Check IP against each subnet
126      for (IPv4AddressNetmask netmask : netmasks) {
127        try {
128          if (netmask.matches(InetAddress.getByName(hip.ipAddress()))) {
129            return true;
130          }
131        } catch (UnknownHostException e) {
132          String message = "Unknown host '{}' : {}";
133          if (traceEnabled) log.trace(message, hip.ipAddress(), ExceptionUtils.getStackTrace(e));
134          else log.warn(message, hip.ipAddress(), ExceptionUtils.getMessage(e));
135        }
136      }
137    }
138    return false;
139  }
140
141  /**
142   * Print this object to a string.
143   * @return an XML string representation of this object
144   */
145  @Override
146  public String toString() {
147    if (computedToString == null) {
148      synchronized(ExecutionPolicy.class) {
149        StringBuilder sb = new StringBuilder();
150        sb.append(indent()).append(tagStart(TAG)).append('\n');
151        toStringIndent++;
152        for (String subnet: subnets) sb.append(indent()).append(xmlElement(SUBNET, subnet)).append('\n');
153        toStringIndent--;
154        sb.append(indent()).append(tagEnd(TAG)).append('\n');
155        computedToString = sb.toString();
156      }
157    }
158    return computedToString;
159  }
160}