001/*
002 * JPPF.
003 * Copyright (C) 2005-2019 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.*;
026
027/**
028 * This class encapsulates the system information for a node.<br>
029 * It includes:
030 * <ul>
031 * <li>System properties, including -D flags</li>
032 * <li>Runtime information such as available processors and memory usage</li>
033 * <li>Environment variables</li>
034 * <li>JPPF configuration properties</li>
035 * <li>IPV4 and IPV6 addresses assigned to the JVM host</li>
036 * <li>Disk space information</li>
037 * <li>Server statistics (server-side only)</li>
038 * </ul>
039 * @author Laurent Cohen
040 */
041public class JPPFSystemInformation implements PropertiesCollection<String> {
042  /**
043   * Explicit serialVersionUID.
044   */
045  private static final long serialVersionUID = 1L;
046  /**
047   * Mapping of all properties containers.
048   */
049  private Map<String, TypedProperties> map = new HashMap<>();
050  /**
051   * {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
052   */
053  private boolean local;
054  /**
055   * If {@code true}, then the name resolution for {@code InetAddress}es should occur immediately,
056   */
057  private boolean resolveInetAddressesNow;
058  /**
059   *
060   */
061  private transient TypedProperties[] propertiesArray;
062  /**
063   * An optional statistics object from which events can be received so the corresponding properties can be kept up to date.
064   */
065  private transient JPPFStatistics stats;
066  /**
067   * The JPPF configuration.
068   */
069  private transient TypedProperties jppfConfig;
070
071  /**
072   * Initialize this system information object with the specified uuid.
073   * @param jppfConfig the JPPF configuration.
074   * @param uuid the uuid of the corresponding JPPF component.
075   * @param local {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
076   * @param resolveInetAddressesNow if {@code true}, then name resolution for {@code InetAddress}es should occur immediately,
077   * otherwise it is different and executed in a separate thread.
078   * @exclude
079   */
080  public JPPFSystemInformation(final TypedProperties jppfConfig, final String uuid, final boolean local, final boolean resolveInetAddressesNow) {
081    this(jppfConfig, uuid, local, resolveInetAddressesNow, null);
082  }
083
084  /**
085   * Initialize this system information object with the specified uuid.
086   * @param jppfConfig the JPPF configuration.
087   * @param uuid the uuid of the corresponding JPPF component.
088   * @param local {@code true} if the JPPF component is local (local node or local client executor), {@code false} otherwise.
089   * @param resolveInetAddressesNow if {@code true}, then name resolution for {@code InetAddress}es should occur immediately,
090   * otherwise it is different and executed in a separate thread.
091   * @param stats an optional statistics object from which events can be received so the corresponding properties can be kept up to date.
092   * @exclude
093   */
094  public JPPFSystemInformation(final TypedProperties jppfConfig, final String uuid, final boolean local, final boolean resolveInetAddressesNow, final JPPFStatistics stats) {
095    this.local = local;
096    this.resolveInetAddressesNow = resolveInetAddressesNow;
097    this.stats = stats;
098    this.jppfConfig = jppfConfig;
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    addProperties("system", SystemUtils.getSystemProperties());
278    addProperties("runtime", SystemUtils.getRuntimeInformation());
279    addProperties("env", SystemUtils.getEnvironment());
280    addProperties("jppf", new TypedProperties((jppfConfig == null) ? JPPFConfiguration.getProperties() : jppfConfig));
281    //addProperties("jppf", (jppfConfig == null) ? JPPFConfiguration.getProperties() : jppfConfig);
282    getJppf().setProperty("jppf.channel.local", String.valueOf(local));
283    final Runnable r = new Runnable() {
284      @Override
285      public void run() {
286        addProperties("network", SystemUtils.getNetwork());
287      }
288    };
289    if (resolveInetAddressesNow) r.run();
290    else ThreadUtils.startThread(r, "InetAddressResolver");
291    addProperties("storage", SystemUtils.getStorageInformation());
292    if (getProperties("uuid") == null) addProperties("uuid", new TypedProperties());
293    addProperties("os", SystemUtils.getOS());
294    final TypedProperties statsProperties = new TypedProperties();
295    addProperties("stats", statsProperties);
296    if (stats != null) {
297      for (JPPFSnapshot snapshot: stats) JPPFStatisticsHelper.toProperties(statsProperties, snapshot);
298      stats = null;
299    }
300    return this;
301  }
302
303  /**
304   * Parse the list of IP v4 addresses contained in this JPPFSystemInformation instance.<br>
305   * This method is provided as a convenience so developers don't have to do the parsing themselves.
306   * @return an array of {@code HostIP} instances.
307   * @exclude
308   */
309  public HostIP[] parseIPV4Addresses() {
310    return NetworkUtils.parseAddresses(getNetwork().getString("ipv4.addresses"));
311  }
312
313  /**
314   * Parse the list of IP v6 addresses contained in this JPPFSystemInformation instance.<br>
315   * This method is provided as a convenience so developers don't have to do the parsing themselves.
316   * @return an array of {@code HostIP} instances.
317   * @exclude
318   */
319  public HostIP[] parseIPV6Addresses() {
320    return NetworkUtils.parseAddresses(getNetwork().getString("ipv6.addresses"));
321  }
322
323  @Override
324  public String toString() {
325    final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
326    sb.append("uuid=").append(getUuid().getString("jppf.uuid"));
327    sb.append(", local=").append(local);
328    sb.append(", resolveInetAddressesNow=").append(resolveInetAddressesNow);
329    //sb.append(", map=").append(map);
330    return sb.append(']').toString();
331  }
332
333  @Override
334  public String getProperty(final String name) {
335    for (TypedProperties props: getPropertiesArray()) {
336      if (props == null) continue;
337      if (props.containsKey(name)) return props.getProperty(name);
338    }
339    return null;
340  }
341
342  @Override
343  public boolean containsKey(final String name) {
344    for (TypedProperties props: getPropertiesArray()) {
345      if (props == null) continue;
346      if (props.containsKey(name)) return true;
347    }
348    return false;
349  }
350}