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.management;
020
021import java.util.*;
022
023import org.jppf.utils.*;
024import org.jppf.utils.concurrent.ThreadUtils;
025import org.jppf.utils.stats.*;
026import org.slf4j.*;
027
028/**
029 * This class encapsulates the system information for a node.<br>
030 * It includes:
031 * <ul>
032 * <li>System properties, including -D flags</li>
033 * <li>Runtime information such as available processors and memory usage</li>
034 * <li>Environment variables</li>
035 * <li>JPPF configuration properties</li>
036 * <li>IPV4 and IPV6 addresses assigned to the JVM host</li>
037 * <li>Disk space information</li>
038 * <li>Server statistics (server-side only)</li>
039 * </ul>
040 * @author Laurent Cohen
041 */
042public class JPPFSystemInformation implements PropertiesCollection<String> {
043  /**
044   * Explicit serialVersionUID.
045   */
046  private static final long serialVersionUID = 1L;
047  /**
048   * Logger for this class.
049   */
050  private static Logger log = LoggerFactory.getLogger(JPPFSystemInformation.class);
051  /**
052   * Determines whether the trace level is enabled in the log configuration, without the cost of a method call.
053   */
054  private static boolean traceEnabled = log.isTraceEnabled();
055  /**
056   * Mapping of all properties containers.
057   */
058  private Map<String, TypedProperties> map = new HashMap<>();
059  /**
060   * {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
061   */
062  private boolean local;
063  /**
064   * If {@code true}, then the name resolution for {@code InetAddress}es should occur immediately,
065   */
066  private boolean resolveInetAddressesNow;
067  /**
068   *
069   */
070  private transient TypedProperties[] propertiesArray;
071  /**
072   * An optional statistics object from which events can be received so the corresponding properties can be kept up to date.
073   */
074  private transient JPPFStatistics stats;
075
076  /**
077   * Initialize this system information object with the specified uuid.
078   * @param uuid the uuid of the corresponding JPPF component.
079   * @param local {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
080   * @param resolveInetAddressesNow if {@code true}, then name resolution for {@code InetAddress}es should occur immediately,
081   * otherwise it is different and executed in a separate thread.
082   */
083  public JPPFSystemInformation(final String uuid, final boolean local, final boolean resolveInetAddressesNow) {
084    this(uuid, local, resolveInetAddressesNow, null);
085  }
086
087  /**
088   * Initialize this system information object with the specified uuid.
089   * @param uuid the uuid of the corresponding JPPF component.
090   * @param local {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
091   * @param resolveInetAddressesNow if {@code true}, then name resolution for {@code InetAddress}es should occur immediately,
092   * otherwise it is different and executed in a separate thread.
093   * @param stats an optional statistics object from which events can be received so the corresponding properties can be kept up to date.
094   */
095  public JPPFSystemInformation(final String uuid, final boolean local, final boolean resolveInetAddressesNow, final JPPFStatistics stats) {
096    this.local = local;
097    this.resolveInetAddressesNow = resolveInetAddressesNow;
098    this.stats = stats;
099    final TypedProperties uuidProps = new TypedProperties();
100    uuidProps.setProperty("jppf.uuid", (uuid == null) ? "" : uuid);
101    uuidProps.setInt("jppf.pid", SystemUtils.getPID());
102    final VersionUtils.Version v = VersionUtils.getVersion();
103    uuidProps.setProperty("jppf.version.number", v.getVersionNumber());
104    uuidProps.setProperty("jppf.build.number", v.getBuildNumber());
105    uuidProps.setProperty("jppf.build.date", v.getBuildDate());
106    addProperties("uuid", uuidProps);
107    populate();
108  }
109
110  /**
111   * Get the map holding the system properties.
112   * @return a {@code TypedProperties} instance.
113   * @see org.jppf.utils.SystemUtils#getSystemProperties()
114   */
115  public TypedProperties getSystem() {
116    return getProperties("system");
117  }
118
119  /**
120   * Get the map holding the runtime information.
121   * <p>The resulting map will contain the following properties:
122   * <ul>
123   * <li>availableProcessors = <i>number of processors available to the JVM</i></li>
124   * <li>freeMemory = <i>current free heap size in bytes</i></li>
125   * <li>totalMemory = <i>current total heap size in bytes</i></li>
126   * <li>maxMemory = <i>maximum heap size in bytes (i.e. as specified by -Xmx JVM option)</i></li>
127   * <li>usedMemory = <i>currently used heap memory in bytes</i></li>
128   * <li>availableMemory = <i>the currently available heap memory in bytes; equal to: </i>{@code maxMemory - usedMemory}</li>
129   * <li>startTime = <i>the approximate timestamp in millis of when the JVM started</i></li>
130   * <li>upTime = <i>how long the JVM has been up in millis</i></li>
131   * <li>inputArgs = <i>arguments given to the 'java' command, excluding those passed to the main method.
132   * Arguments are given as a list of strings separated by the ", " delimiter (a comma followed by a space)</i></li>
133   * </ul>
134   * <p>Some or all of these properties may be missing if a security manager is installed
135   * that does not grant access to the related {@link java.lang.Runtime} APIs.
136   * @return a {@code TypedProperties} instance.
137   * @see org.jppf.utils.SystemUtils#getRuntimeInformation()
138   */
139  public TypedProperties getRuntime() {
140    return getProperties("runtime");
141  }
142
143  /**
144   * Get the map holding the environment variables.
145   * @return a {@code TypedProperties} instance.
146   * @see org.jppf.utils.SystemUtils#getEnvironment()
147   */
148  public TypedProperties getEnv() {
149    return getProperties("env");
150  }
151
152  /**
153   * Get the  map of the network configuration.
154   * <p>The resulting map will contain the following properties:
155   * <ul>
156   * <li>ipv4.addresses = <i>hostname_1</i>|<i>ipv4_address_1</i> ... <i>hostname_n</i>|<i>ipv4_address_n</i></li>
157   * <li>ipv6.addresses = <i>hostname_1</i>|<i>ipv6_address_1</i> ... <i>hostname_p</i>|<i>ipv6_address_p</i></li>
158   * </ul>
159   * <p>Each property is a space-separated list of <i>hostname</i>|<i>ip_address</i> pairs,
160   * the hostname and ip address being separated by a pipe symbol &quot;|&quot;.
161   * @return a {@code TypedProperties} instance.
162   * @see org.jppf.utils.SystemUtils#getNetwork()
163   */
164  public TypedProperties getNetwork() {
165    return getProperties("network");
166  }
167
168  /**
169   * Get the map holding the JPPF configuration properties.
170   * @return a {@code TypedProperties} instance.
171   * @see org.jppf.utils.JPPFConfiguration#getProperties()
172   */
173  public TypedProperties getJppf() {
174    return getProperties("jppf");
175  }
176
177  /**
178   * Get the map holding the host storage information.
179   * <p>The map will contain the following information:
180   * <ul>
181   * <li>host.roots.names = <i>root_name_0</i> ... <i>root_name_n-1</i> : the names of all accessible file system roots</li>
182   * <li>host.roots.number = <i>n</i> : the number of accessible file system roots</li>
183   * <li><b>For each root <i>i</i>:</b></li>
184   * <li>root.<i>i</i>.name = <i>root_name</i> : for instance &quot;C:\&quot; on Windows or &quot;/&quot; on Unix</li>
185   * <li>root.<i>i</i>.space.free = <i>space_in_bytes</i> : current free space for the root</li>
186   * <li>root.<i>i</i>.space.total = <i>space_in_bytes</i> : total space for the root</li>
187   * <li>root.<i>i</i>.space.usable = <i>space_in_bytes</i> : space available to the user the JVM is running under</li>
188   * </ul>
189   * If the JVM version is prior to 1.6, the space information will not be available.
190   * @return a {@code TypedProperties} instance.
191   * @see org.jppf.utils.SystemUtils#getStorageInformation()
192   */
193  public TypedProperties getStorage() {
194    return getProperties("storage");
195  }
196
197  /**
198   * Get the properties object holding the JPPF uuid and version information.
199   * The following properties are provided:
200   * <ul>
201   * <li>"jppf.uuid" : the uuid of the node or driver</li>
202   * <li>"jppf.pid" : the process id of the JVM running the JPPF driver or node</li>
203   * <li>"jppf.version.number" : the current JPPF version number</li>
204   * <li>"jppf.build.number" : the current build number</li>
205   * <li>"jppf.build.date" : the build date, including the time zone, in the format "yyyy-MM-dd hh:mm z" </li>
206   * </ul>
207   * @return a {@code TypedProperties} wrapper for the uuid and version information of the corresponding JPPF component.
208   */
209  public TypedProperties getUuid() {
210    return getProperties("uuid");
211  }
212
213  /**
214   * Get the properties object holding the JPPF server statistics, listed as constants in {@link JPPFStatisticsHelper}.
215   * @return a {@code TypedProperties} wrapper for the server statistics; for a node this will return an empty set of properties.
216   */
217  public TypedProperties getStats() {
218    return getProperties("stats");
219  }
220
221  /**
222   * Get the properties object holding the operating system information.
223   * The following properties are provided:
224   * <ul>
225   * <li>"os.TotalPhysicalMemorySize" : total physical RAM (long value)</li>
226   * <li>"os.FreePhysicalMemorySize" : available physical RAM (long value)</li>
227   * <li>"os.TotalSwapSpaceSize" : total swap space (long value)</li>
228   * <li>"os.FreeSwapSpaceSize" : available swap space (long value)</li>
229   * <li>"os.CommittedVirtualMemorySize" : total committed virtual memory of the process (long value)</li>
230   * <li>"os.ProcessCpuLoad" : process CPU load (double value in range [0 ... 1])</li>
231   * <li>"os.ProcessCpuTime" : process CPU time (long value)</li>
232   * <li>"os.SystemCpuLoad" : system total CPU load (double value in range [0 ... 1])</li>
233   * <li>"os.Name" : the name of the OS (string value, ex: "Windows 7")</li>
234   * <li>"os.Version" : the OS version (string value, ex: "6.1")</li>
235   * <li>"os.Arch" : the OS architecture (string value, ex: "amd64")</li>
236   * <li>"os.AvailableProcessors" : number of processors available to the OS (int value)</li>
237   * <li>"os.SystemLoadAverage" : average system CPU load over the last minute (double value in the range [0 ... 1], always returns -1 on Windows)</li>
238   * </ul>
239   * @return a {@code TypedProperties} wrapper for the operating system information of the corresponding JPPF component.
240   */
241  public TypedProperties getOS() {
242    return getProperties("os");
243  }
244
245  /**
246   * Get all the properties as an array.
247   * @return an array of all the sets of properties.
248   */
249  @Override
250  public TypedProperties[] getPropertiesArray() {
251    synchronized(map) {
252      if (propertiesArray == null) propertiesArray = map.values().toArray(new TypedProperties[map.size()]);
253      return propertiesArray;
254    }
255  }
256
257  @Override
258  public void addProperties(final String key, final TypedProperties properties) {
259    synchronized(map) {
260      map.put(key, properties);
261      propertiesArray = map.values().toArray(new TypedProperties[map.size()]);
262    }
263  }
264
265  @Override
266  public TypedProperties getProperties(final String key) {
267    synchronized(map) {
268      return map.get(key);
269    }
270  }
271
272  /**
273   * Populate this system information object.
274   * @return this {@code JPPFSystemInformation} object.
275   */
276  public JPPFSystemInformation populate() {
277    if (traceEnabled) {
278      final Exception e = new Exception("call stack for JPPFSystemInformation.populate(" + resolveInetAddressesNow + ")");
279      log.trace(e.getMessage(), e);
280    }
281    addProperties("system", SystemUtils.getSystemProperties());
282    addProperties("runtime", SystemUtils.getRuntimeInformation());
283    addProperties("env", SystemUtils.getEnvironment());
284    addProperties("jppf", new TypedProperties(JPPFConfiguration.getProperties()));
285    getJppf().setProperty("jppf.channel.local", String.valueOf(local));
286    final Runnable r = new Runnable() {
287      @Override
288      public void run() {
289        addProperties("network", SystemUtils.getNetwork());
290      }
291    };
292    if (resolveInetAddressesNow) r.run();
293    else ThreadUtils.startThread(r, "InetAddressResolver");
294    addProperties("storage", SystemUtils.getStorageInformation());
295    if (getProperties("uuid") == null) addProperties("uuid", new TypedProperties());
296    addProperties("os", SystemUtils.getOS());
297    final TypedProperties statsProperties = new TypedProperties();
298    addProperties("stats", statsProperties);
299    if (stats != null) {
300      for (JPPFSnapshot snapshot: stats) JPPFStatisticsHelper.toProperties(statsProperties, snapshot);
301      stats = null;
302    }
303    return this;
304  }
305
306  /**
307   * Parse the list of IP v4 addresses contained in this JPPFSystemInformation instance.<br>
308   * This method is provided as a convenience so developers don't have to do the parsing themselves.
309   * @return an array of {@code HostIP} instances.
310   * @exclude
311   */
312  public HostIP[] parseIPV4Addresses() {
313    return NetworkUtils.parseAddresses(getNetwork().getString("ipv4.addresses"));
314  }
315
316  /**
317   * Parse the list of IP v6 addresses contained in this JPPFSystemInformation instance.<br>
318   * This method is provided as a convenience so developers don't have to do the parsing themselves.
319   * @return an array of {@code HostIP} instances.
320   * @exclude
321   */
322  public HostIP[] parseIPV6Addresses() {
323    return NetworkUtils.parseAddresses(getNetwork().getString("ipv6.addresses"));
324  }
325
326  @Override
327  public String toString() {
328    final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
329    sb.append("local=").append(local);
330    sb.append(", resolveInetAddressesNow=").append(resolveInetAddressesNow);
331    sb.append(", map=").append(map);
332    return sb.append(']').toString();
333  }
334
335  @Override
336  public String getProperty(final String name) {
337    for (TypedProperties props: getPropertiesArray()) {
338      if (props == null) continue;
339      if (props.containsKey(name)) return props.getProperty(name);
340    }
341    return null;
342  }
343
344  @Override
345  public boolean containsKey(final String name) {
346    for (TypedProperties props: getPropertiesArray()) {
347      if (props == null) continue;
348      if (props.containsKey(name)) return true;
349    }
350    return false;
351  }
352}