001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2017 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028package edu.wisc.ssec.mcidasv.util; 029 030import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arrList; 031import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashMap; 032 033import java.io.BufferedReader; 034import java.io.IOException; 035import java.io.InputStream; 036import java.io.InputStreamReader; 037import java.lang.management.ManagementFactory; 038import java.lang.management.OperatingSystemMXBean; 039import java.lang.reflect.Method; 040 041import java.net.URL; 042import java.text.ParseException; 043import java.text.SimpleDateFormat; 044import java.util.Date; 045import java.util.Enumeration; 046import java.util.HashMap; 047import java.util.List; 048import java.util.Map; 049import java.util.Properties; 050import java.util.TimeZone; 051import java.util.TreeSet; 052 053import java.awt.DisplayMode; 054import java.awt.GraphicsConfiguration; 055import java.awt.GraphicsDevice; 056import java.awt.GraphicsEnvironment; 057import java.awt.Rectangle; 058import java.util.jar.Attributes; 059import java.util.jar.Manifest; 060 061import javax.media.j3d.Canvas3D; 062import javax.media.j3d.VirtualUniverse; 063 064import org.python.core.Py; 065import org.python.core.PySystemState; 066 067import org.slf4j.Logger; 068import org.slf4j.LoggerFactory; 069 070import ucar.nc2.grib.GribConverterUtility; 071import ucar.unidata.idv.ArgsManager; 072import ucar.unidata.idv.IdvResourceManager.IdvResource; 073import ucar.unidata.util.ResourceCollection; 074import ucar.visad.display.DisplayUtil; 075 076import edu.wisc.ssec.mcidasv.Constants; 077import edu.wisc.ssec.mcidasv.McIDASV; 078import edu.wisc.ssec.mcidasv.StateManager; 079 080/** 081 * Utility methods for querying the state of the user's machine. 082 */ 083public class SystemState { 084 085 /** Handy logging object. */ 086 private static final Logger logger = LoggerFactory.getLogger(SystemState.class); 087 088 // Don't allow outside instantiation. 089 private SystemState() { } 090 091 public static String escapeWhitespaceChars(final CharSequence sequence) { 092 StringBuilder sb = new StringBuilder(sequence.length() * 7); 093 for (int i = 0; i < sequence.length(); i++) { 094 switch (sequence.charAt(i)) { 095 case '\t': sb.append("\\t"); break; 096 case '\n': sb.append('\\').append('n'); break; 097 case '\013': sb.append("\\013"); break; 098 case '\f': sb.append("\\f"); break; 099 case '\r': sb.append("\\r"); break; 100 case '\u0085': sb.append("\\u0085"); break; 101 case '\u1680': sb.append("\\u1680"); break; 102 case '\u2028': sb.append("\\u2028"); break; 103 case '\u2029': sb.append("\\u2029"); break; 104 case '\u205f': sb.append("\\u205f"); break; 105 case '\u3000': sb.append("\\u3000"); break; 106 } 107 } 108 logger.trace("incoming={} outgoing={}", sequence.length(), sb.length()); 109 return sb.toString(); 110 } 111 112 /** 113 * Attempt to invoke {@code OperatingSystemMXBean.methodName} via 114 * reflection. 115 * 116 * @param <T> Either {@code Long} or {@code Double}. 117 * @param methodName The method to invoke. Must belong to 118 * {@code com.sun.management.OperatingSystemMXBean}. 119 * @param defaultValue Default value to return, must be in 120 * {@literal "boxed"} form. 121 * 122 * @return Either the result of the {@code methodName} call or 123 * {@code defaultValue}. 124 */ 125 private static <T> T hackyMethodCall(final String methodName, final T defaultValue) { 126 assert methodName != null : "Cannot invoke a null method name"; 127 assert !methodName.isEmpty() : "Cannot invoke an empty method name"; 128 OperatingSystemMXBean osBean = 129 ManagementFactory.getOperatingSystemMXBean(); 130 T result = defaultValue; 131 try { 132 Method m = osBean.getClass().getMethod(methodName); 133 m.setAccessible(true); 134 // don't suppress warnings because we cannot guarantee that this 135 // cast is correct. 136 result = (T)m.invoke(osBean); 137 } catch (Exception e) { 138 logger.error("couldn't call method: " + methodName, e); 139 } 140 return result; 141 } 142 143 /** 144 * Returns the contents of Jython's registry (basically just Jython-specific 145 * properties) as well as some of the information from Python's 146 * {@literal "sys"} module. 147 * 148 * @return Jython's configuration settings. 149 */ 150 public static Map<Object, Object> queryJythonProps() { 151 Map<Object, Object> properties = newLinkedHashMap(PySystemState.registry); 152 try (PySystemState systemState = Py.getSystemState()) { 153 properties.put("sys.argv", systemState.argv.toString()); 154 properties.put("sys.path", systemState.path); 155 properties.put("sys.platform", systemState.platform.toString()); 156 } 157 properties.put("sys.builtin_module_names", PySystemState.builtin_module_names.toString()); 158 properties.put("sys.byteorder", PySystemState.byteorder); 159 properties.put("sys.isPackageCacheEnabled", PySystemState.isPackageCacheEnabled()); 160 properties.put("sys.version", PySystemState.version); 161 properties.put("sys.version_info", PySystemState.version_info); 162 return properties; 163 } 164 165 /** 166 * Attempts to call methods belonging to 167 * {@code com.sun.management.OperatingSystemMXBean}. If successful, we'll 168 * have the following information: 169 * <ul> 170 * <li>opsys.memory.virtual.committed: virtual memory that is guaranteed to be available</li> 171 * <li>opsys.memory.swap.total: total amount of swap space in bytes</li> 172 * <li>opsys.memory.swap.free: free swap space in bytes</li> 173 * <li>opsys.cpu.time: CPU time used by the process (nanoseconds)</li> 174 * <li>opsys.memory.physical.free: free physical memory in bytes</li> 175 * <li>opsys.memory.physical.total: physical memory in bytes</li> 176 * <li>opsys.load: system load average for the last minute</li> 177 * </ul> 178 * 179 * @return Map of properties that contains interesting information about 180 * the hardware McIDAS-V is using. 181 */ 182 public static Map<String, String> queryOpSysProps() { 183 Map<String, String> properties = newLinkedHashMap(10); 184 long committed = hackyMethodCall("getCommittedVirtualMemorySize", Long.MIN_VALUE); 185 long freeMemory = hackyMethodCall("getFreePhysicalMemorySize", Long.MIN_VALUE); 186 long freeSwap = hackyMethodCall("getFreeSwapSpaceSize", Long.MIN_VALUE); 187 long cpuTime = hackyMethodCall("getProcessCpuTime", Long.MIN_VALUE); 188 long totalMemory = hackyMethodCall("getTotalPhysicalMemorySize", Long.MIN_VALUE); 189 long totalSwap = hackyMethodCall("getTotalSwapSpaceSize", Long.MIN_VALUE); 190 double loadAvg = hackyMethodCall("getSystemLoadAverage", Double.NaN); 191 192 Runtime rt = Runtime.getRuntime(); 193 long currentMem = rt.totalMemory() - rt.freeMemory(); 194 195 properties.put("opsys.cpu.time", Long.toString(cpuTime)); 196 properties.put("opsys.load", Double.toString(loadAvg)); 197 properties.put("opsys.memory.jvm.current", Long.toString(currentMem)); 198 properties.put("opsys.memory.jvm.max", Long.toString(rt.maxMemory())); 199 properties.put("opsys.memory.virtual.committed", Long.toString(committed)); 200 properties.put("opsys.memory.physical.free", Long.toString(freeMemory)); 201 properties.put("opsys.memory.physical.total", Long.toString(totalMemory)); 202 properties.put("opsys.memory.swap.free", Long.toString(freeSwap)); 203 properties.put("opsys.memory.swap.total", Long.toString(totalSwap)); 204 205 return properties; 206 } 207 208 /** 209 * Polls Java for information about the user's machine. We're specifically 210 * after memory statistics, number of processors, and display information. 211 * 212 * @return {@link Map} of properties that describes the user's machine. 213 */ 214 public static Map<String, String> queryMachine() { 215 Map<String, String> props = newLinkedHashMap(); 216 217 // cpu count and whatnot 218 int processors = Runtime.getRuntime().availableProcessors(); 219 props.put("opsys.cpu.count", Integer.toString(processors)); 220 221 // memory: available, used, etc 222 props.putAll(queryOpSysProps()); 223 224 // screen: count, resolution(s) 225 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 226 int displayCount = ge.getScreenDevices().length; 227 228 for (int i = 0; i < displayCount; i++) { 229 String baseId = "opsys.display."+i+'.'; 230 GraphicsDevice dev = ge.getScreenDevices()[i]; 231 DisplayMode mode = dev.getDisplayMode(); 232 props.put(baseId+"name", dev.getIDstring()); 233 props.put(baseId+"depth", Integer.toString(mode.getBitDepth())); 234 props.put(baseId+"width", Integer.toString(mode.getWidth())); 235 props.put(baseId+"height", Integer.toString(mode.getHeight())); 236 props.put(baseId+"refresh", Integer.toString(mode.getRefreshRate())); 237 } 238 return props; 239 } 240 241 /** 242 * Returns a mapping of display number to a {@link java.awt.Rectangle} 243 * that represents the {@literal "bounds"} of the display. 244 * 245 * @return Rectangles representing the {@literal "bounds"} of the current 246 * display devices. 247 */ 248 public static Map<Integer, Rectangle> getDisplayBounds() { 249 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 250 int idx = 0; 251 Map<Integer, Rectangle> map = newLinkedHashMap(ge.getScreenDevices().length * 2); 252 for (GraphicsDevice dev : ge.getScreenDevices()) { 253 for (GraphicsConfiguration config : dev.getConfigurations()) { 254 map.put(idx++, config.getBounds()); 255 } 256 } 257 return map; 258 } 259 260 // TODO(jon): this should really be a polygon 261 public static Rectangle getVirtualDisplayBounds() { 262 Rectangle virtualBounds = new Rectangle(); 263 for (Rectangle bounds : getDisplayBounds().values()) { 264 virtualBounds = virtualBounds.union(bounds); 265 } 266 return virtualBounds; 267 } 268 269 /** 270 * Polls Java 3D for information about its environment. Specifically, we 271 * call {@link VirtualUniverse#getProperties()} and 272 * {@link Canvas3D#queryProperties()}. 273 * 274 * @return As much information as Java 3D can provide. 275 */ 276 @SuppressWarnings("unchecked") // casting to Object, so this should be fine. 277 public static Map<String, Object> queryJava3d() { 278 279 Map<String, Object> universeProps = 280 (Map<String, Object>)VirtualUniverse.getProperties(); 281 282 GraphicsConfiguration config = 283 DisplayUtil.getPreferredConfig(null, true, false); 284 Map<String, Object> c3dMap = new Canvas3D(config).queryProperties(); 285 286 Map<String, Object> props = 287 newLinkedHashMap(universeProps.size() + c3dMap.size()); 288 props.putAll(universeProps); 289 props.putAll(c3dMap); 290 return props; 291 } 292 293 /** 294 * Gets a human-friendly representation of the information embedded within 295 * IDV's {@code build.properties}. 296 * 297 * @return {@code String} that looks like {@literal "IDV version major.minor<b>revision</b> built <b>date</b>"}. 298 * For example: {@code IDV version 2.9u4 built 2011-04-13 14:01 UTC}. 299 */ 300 public static String getIdvVersionString() { 301 Map<String, String> info = queryIdvBuildProperties(); 302 return "IDV version " + info.get("idv.version.major") + '.' + 303 info.get("idv.version.minor") + info.get("idv.version.revision") + 304 " built " + info.get("idv.build.date"); 305 } 306 307 /** 308 * Gets a human-friendly representation of the information embedded within 309 * McIDAS-V's {@code build.properties}. 310 * 311 * @return {@code String} that looks like {@literal "McIDAS-V version major.minor<b>release</b> built <b>date</b>"}. 312 * For example: {@code McIDAS-V version 1.02beta1 built 2011-04-14 17:36}. 313 */ 314 public static String getMcvVersionString() { 315 Map<String, String> info = queryMcvBuildProperties(); 316 return "McIDAS-V version " + info.get(Constants.PROP_VERSION_MAJOR) + '.' + 317 info.get(Constants.PROP_VERSION_MINOR) + info.get(Constants.PROP_VERSION_RELEASE) + 318 " built " + info.get(Constants.PROP_BUILD_DATE); 319 } 320 321 /** 322 * Gets a human-friendly representation of the version information embedded 323 * within VisAD's {@literal "DATE"} file. 324 * 325 * @return {@code String} that looks 326 * {@literal "VisAD version <b>revision</b> built <b>date</b>"}. 327 * For example: {@code VisAD version 5952 built Thu Mar 22 13:01:31 CDT 2012}. 328 */ 329 public static String getVisadVersionString() { 330 Map<String, String> props = queryVisadBuildProperties(); 331 return "VisAD version " + props.get(Constants.PROP_VISAD_REVISION) + " built " + props.get(Constants.PROP_VISAD_DATE); 332 } 333 334 /** 335 * Gets a human-friendly representation of the ncIdv.jar version 336 * information. 337 * 338 * @return {@code String} that looks like 339 * {@literal "netCDF-Java version <b>revision</b> <b>built</b>"}. 340 */ 341 public static String getNcidvVersionString() { 342 Map<String, String> props = queryNcidvBuildProperties(); 343 return "netCDF-Java version " + props.get("version") + " built " + props.get("buildDate"); 344 } 345 346 /** 347 * Open a file for reading. 348 * 349 * @param name File to open. 350 * 351 * @return {@code InputStream} used to read {@code name}, or {@code null} 352 * if {@code name} could not be found. 353 */ 354 private static InputStream getResourceAsStream(final String name) { 355 return ClassLoader.getSystemResourceAsStream(name); 356 } 357 358 /** 359 * Returns a {@link Map} containing any relevant version information. 360 * 361 * <p>Currently this information consists of the date visad.jar was built, 362 * as well as the (then-current) Subversion revision number. 363 * 364 * @return {@code Map} of the contents of VisAD's DATE file. 365 */ 366 public static Map<String, String> queryVisadBuildProperties() { 367 Map<String, String> props = newLinkedHashMap(4); 368 BufferedReader input = null; 369 370 try { 371 input = new BufferedReader(new InputStreamReader(getResourceAsStream("DATE"))); 372 String contents = input.readLine(); 373 // string should look like: Thu Mar 22 13:01:31 CDT 2012 Rev:5952 374 String splitAt = " Rev:"; 375 int index = contents.indexOf(splitAt); 376 String buildDate = "ERROR"; 377 String revision = "ERROR"; 378 String parseFail = "true"; 379 if (index > 0) { 380 buildDate = contents.substring(0, index); 381 revision = contents.substring(index + splitAt.length()); 382 parseFail = "false"; 383 } 384 props.put(Constants.PROP_VISAD_ORIGINAL, contents); 385 props.put(Constants.PROP_VISAD_PARSE_FAIL, parseFail); 386 props.put(Constants.PROP_VISAD_DATE, buildDate); 387 props.put(Constants.PROP_VISAD_REVISION, revision); 388 } catch (Exception e) { 389 logger.error("could not read from VisAD DATE file", e); 390 } finally { 391 if (input != null) { 392 try { 393 input.close(); 394 } catch (Exception e) { 395 logger.error("could not close VisAD DATE file", e); 396 } 397 } 398 } 399 return props; 400 } 401 402 /** 403 * Returns a {@link Map} containing {@code version} and {@code buildDate} 404 * keys. 405 * 406 * @return {@code Map} containing netCDF-Java version and build date. 407 */ 408 public static Map<String, String> queryNcidvBuildProperties() { 409 // largely taken from LibVersionUtil's getBuildInfo method. 410 GribConverterUtility util = new GribConverterUtility(); 411 HashMap<String, String> buildInfo = new HashMap<>(4); 412 // format from ncidv.jar 413 SimpleDateFormat formatIn = 414 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 415 // format of output timestamp 416 SimpleDateFormat formatOut = 417 new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); 418 formatOut.setTimeZone(TimeZone.getTimeZone("UTC")); 419 try { 420 Enumeration<URL> resources = 421 util.getClass().getClassLoader().getResources( 422 "META-INF/MANIFEST.MF"); 423 while (resources.hasMoreElements()) { 424 Manifest manifest = 425 new Manifest(resources.nextElement().openStream()); 426 Attributes attrs = manifest.getMainAttributes(); 427 if (attrs != null) { 428 String implTitle = attrs.getValue("Implementation-Title"); 429 if ((implTitle != null) && implTitle.contains("ncIdv")) { 430 buildInfo.put( 431 "version", 432 attrs.getValue("Implementation-Version")); 433 String strDate = attrs.getValue("Built-On"); 434 Date date = formatIn.parse(strDate); 435 buildInfo.put("buildDate", formatOut.format(date)); 436 break; 437 } 438 } 439 } 440 } catch (IOException e) { 441 logger.warn("exception occurred:", e); 442 } catch (ParseException pe) { 443 logger.warn("failed to parse build date from ncIdv.jar", pe); 444 } 445 return buildInfo; 446 } 447 448 /** 449 * Returns a {@link Map} of the (currently) most useful contents of 450 * {@code ucar/unidata/idv/resources/build.properties}. 451 * 452 * <p>Consider the output of {@link #getIdvVersionString()}; it's built 453 * with the the following: 454 * <ul> 455 * <li><b>{@code idv.version.major}</b>: currently {@literal "3"}</li> 456 * <li><b>{@code idv.version.minor}</b>: currently {@literal "0"}</li> 457 * <li><b>{@code idv.version.revision}</b>: currently {@literal "u2"}}</li> 458 * <li><b>{@code idv.build.date}</b>: varies pretty frequently, 459 * as it's the build timestamp for idv.jar</li> 460 * </ul> 461 * 462 * @return A {@code Map} of at least the useful parts of build.properties. 463 */ 464 public static Map<String, String> queryIdvBuildProperties() { 465 Map<String, String> versions = newLinkedHashMap(4); 466 InputStream input = null; 467 try { 468 input = getResourceAsStream("ucar/unidata/idv/resources/build.properties"); 469 Properties props = new Properties(); 470 props.load(input); 471 String major = props.getProperty("idv.version.major", "no_major"); 472 String minor = props.getProperty("idv.version.minor", "no_minor"); 473 String revision = props.getProperty("idv.version.revision", "no_revision"); 474 String date = props.getProperty("idv.build.date", ""); 475 versions.put("idv.version.major", major); 476 versions.put("idv.version.minor", minor); 477 versions.put("idv.version.revision", revision); 478 versions.put("idv.build.date", date); 479 } catch (Exception e) { 480 logger.error("could not read from IDV build.properties", e); 481 } finally { 482 if (input != null) { 483 try { 484 input.close(); 485 } catch (Exception ex) { 486 logger.error("could not close IDV build.properties", ex); 487 } 488 } 489 } 490 return versions; 491 } 492 493 /** 494 * Returns a {@link Map} of the (currently) most useful contents of 495 * {@code edu/wisc/ssec/mcidasv/resources/build.properties}. 496 * 497 * <p>Consider the output of {@link #getMcvVersionString()}; it's built 498 * with the the following: 499 * <ul> 500 * <li><b>{@code mcidasv.version.major}</b>: 501 * currently {@literal "1"}</li> 502 * <li><b>{@code mcidasv.version.minor}</b>: 503 * currently {@literal "02"}</li> 504 * <li><b>{@code mcidasv.version.release}</b>: currently 505 * {@literal "beta1"}</li> 506 * <li><b>{@code mcidasv.build.date}</b>: varies pretty frequently, as 507 * it's the build timestamp for mcidasv.jar.</li> 508 * </ul> 509 * 510 * @return A {@code Map} of at least the useful parts of build.properties. 511 */ 512 public static Map<String, String> queryMcvBuildProperties() { 513 Map<String, String> versions = newLinkedHashMap(4); 514 InputStream input = null; 515 try { 516 input = getResourceAsStream("edu/wisc/ssec/mcidasv/resources/build.properties"); 517 Properties props = new Properties(); 518 props.load(input); 519 String major = props.getProperty(Constants.PROP_VERSION_MAJOR, "0"); 520 String minor = props.getProperty(Constants.PROP_VERSION_MINOR, "0"); 521 String release = props.getProperty(Constants.PROP_VERSION_RELEASE, ""); 522 String date = props.getProperty(Constants.PROP_BUILD_DATE, "Unknown"); 523 versions.put(Constants.PROP_VERSION_MAJOR, major); 524 versions.put(Constants.PROP_VERSION_MINOR, minor); 525 versions.put(Constants.PROP_VERSION_RELEASE, release); 526 versions.put(Constants.PROP_BUILD_DATE, date); 527 } catch (Exception e) { 528 logger.error("could not read from McIDAS-V build.properties!", e); 529 } finally { 530 if (input != null) { 531 try { 532 input.close(); 533 } catch (Exception ex) { 534 logger.error("could not close McIDAS-V build.properties!", ex); 535 } 536 } 537 } 538 return versions; 539 } 540 541 /** 542 * Queries McIDAS-V for information about its state. There's not a good way 543 * to characterize what we're interested in, so let's leave it at 544 * {@literal "whatever seems useful"}. 545 * 546 * @param mcv The McIDASV {@literal "god"} object. 547 * 548 * @return Information about the state of McIDAS-V. 549 */ 550 // need: argsmanager, resource manager 551 public static Map<String, Object> queryMcvState(final McIDASV mcv) { 552 // through some simple verification, props generally has under 250 elements 553 Map<String, Object> props = newLinkedHashMap(250); 554 555 ArgsManager args = mcv.getArgsManager(); 556 props.put("mcv.state.islinteractive", args.getIslInteractive()); 557 props.put("mcv.state.offscreen", args.getIsOffScreen()); 558 props.put("mcv.state.initcatalogs", args.getInitCatalogs()); 559 props.put("mcv.state.actions", mcv.getActionHistory()); 560 props.put("mcv.plugins.installed", args.installPlugins); 561 props.put("mcv.state.commandline", mcv.getCommandLineArgs()); 562 563 // loop through resources 564 List<IdvResource> resources = 565 (List<IdvResource>)mcv.getResourceManager().getResources(); 566 for (IdvResource resource : resources) { 567 String id = resource.getId(); 568 props.put(id+".description", resource.getDescription()); 569 if (resource.getPattern() == null) { 570 props.put(id+".pattern", "null"); 571 } else { 572 props.put(id+".pattern", resource.getPattern()); 573 } 574 575 ResourceCollection rc = mcv.getResourceManager().getResources(resource); 576 int rcSize = rc.size(); 577 List<String> specified = arrList(rcSize); 578 List<String> valid = arrList(rcSize); 579 for (int i = 0; i < rcSize; i++) { 580 String tmpResource = (String)rc.get(i); 581 specified.add(tmpResource); 582 if (rc.isValid(i)) { 583 valid.add(tmpResource); 584 } 585 } 586 587 props.put(id+".specified", specified); 588 props.put(id+".existing", valid); 589 } 590 return props; 591 } 592 593 /** 594 * Builds a (filtered) subset of the McIDAS-V system properties and returns 595 * the results as a {@code String}. 596 * 597 * @param mcv The McIDASV {@literal "god"} object. 598 * 599 * @return The McIDAS-V system properties in the following format: 600 * {@code KEY=VALUE\n}. This is so we kinda-sorta conform to the standard 601 * {@link Properties} file format. 602 * 603 * @see #getStateAsString(edu.wisc.ssec.mcidasv.McIDASV, boolean) 604 */ 605 public static String getStateAsString(final McIDASV mcv) { 606 return getStateAsString(mcv, false); 607 } 608 609 /** 610 * Builds the McIDAS-V system properties and returns the results as a 611 * {@code String}. 612 * 613 * @param mcv The McIDASV {@literal "god"} object. 614 * @param firehose If {@code true}, enables {@literal "unfiltered"} output. 615 * 616 * @return The McIDAS-V system properties in the following format: 617 * {@code KEY=VALUE\n}. This is so we kinda-sorta conform to the standard 618 * {@link Properties} file format. 619 */ 620 public static String getStateAsString(final McIDASV mcv, final boolean firehose) { 621 int builderSize = firehose ? 45000 : 1000; 622 StringBuilder buf = new StringBuilder(builderSize); 623 624 Map<String, String> versions = ((StateManager)mcv.getStateManager()).getVersionInfo(); 625 Properties sysProps = System.getProperties(); 626 Map<String, Object> j3dProps = queryJava3d(); 627 Map<String, String> machineProps = queryMachine(); 628 Map<Object, Object> jythonProps = queryJythonProps(); 629 Map<String, Object> mcvProps = queryMcvState(mcv); 630 631 if (sysProps.contains("line.separator")) { 632 sysProps.put("line.separator", escapeWhitespaceChars((String)sysProps.get("line.separator"))); 633 logger.trace("grr='{}'", sysProps.get("line.separator")); 634 } 635 636 String maxMem = Long.toString(Long.valueOf(machineProps.get("opsys.memory.jvm.max")) / 1048576L); 637 String curMem = Long.toString(Long.valueOf(machineProps.get("opsys.memory.jvm.current")) / 1048576L); 638 639 buf.append("# Software Versions:") 640 .append("\n# McIDAS-V: ").append(versions.get("mcv.version.general")).append(" (").append(versions.get("mcv.version.build")).append(')') 641 .append("\n# VisAD: ").append(versions.get("visad.version.general")).append(" (").append(versions.get("visad.version.build")).append(')') 642 .append("\n# IDV: ").append(versions.get("idv.version.general")).append(" (").append(versions.get("idv.version.build")).append(')') 643 .append("\n# netcdf-Java: ").append(versions.get("netcdf.version.general")).append(" (").append(versions.get("netcdf.version.build")).append(')') 644 .append("\n\n# Operating System:") 645 .append("\n# Name: ").append(sysProps.getProperty("os.name")) 646 .append("\n# Version: ").append(sysProps.getProperty("os.version")) 647 .append("\n# Architecture: ").append(sysProps.getProperty("os.arch")) 648 .append("\n\n# Java:") 649 .append("\n# Version: ").append(sysProps.getProperty("java.version")) 650 .append("\n# Vendor: ").append(sysProps.getProperty("java.vendor")) 651 .append("\n# Home: ").append(sysProps.getProperty("java.home")) 652 .append("\n\n# JVM Memory") 653 .append("\n# Current: ").append(curMem).append(" MB") 654 .append("\n# Maximum: ").append(maxMem).append(" MB") 655 .append("\n\n# Java 3D:") 656 .append("\n# Renderer: ").append(j3dProps.get("j3d.renderer")) 657 .append("\n# Pipeline: ").append(j3dProps.get("j3d.pipeline")) 658 .append("\n# Vendor: ").append(j3dProps.get("native.vendor")) 659 .append("\n# Version: ").append(j3dProps.get("j3d.version")) 660 .append("\n\n# Jython:") 661 .append("\n# Version: ").append(jythonProps.get("sys.version_info")) 662 .append("\n# python.home: ").append(jythonProps.get("python.home")); 663 664 if (firehose) { 665 buf.append("\n\n\n#Firehose:\n\n# SOFTWARE VERSIONS\n"); 666 for (String key : new TreeSet<>(versions.keySet())) { 667 buf.append(key).append('=').append(versions.get(key)).append('\n'); 668 } 669 670 buf.append("\n# MACHINE PROPERTIES\n"); 671 for (String key : new TreeSet<>(machineProps.keySet())) { 672 buf.append(key).append('=').append(machineProps.get(key)).append('\n'); 673 } 674 675 buf.append("\n# JAVA SYSTEM PROPERTIES\n"); 676 for (Object key : new TreeSet<>(sysProps.keySet())) { 677 buf.append(key).append('=').append(sysProps.get(key)).append('\n'); 678 } 679 680 buf.append("\n# JAVA3D/JOGL PROPERTIES\n"); 681 for (String key : new TreeSet<>(j3dProps.keySet())) { 682 buf.append(key).append('=').append(j3dProps.get(key)).append('\n'); 683 } 684 685 buf.append("\n# JYTHON PROPERTIES\n"); 686 for (Object key : new TreeSet<>(jythonProps.keySet())) { 687 buf.append(key).append('=').append(jythonProps.get(key)).append('\n'); 688 } 689 690 // get idv/mcv properties 691 buf.append("\n# IDV AND MCIDAS-V PROPERTIES\n"); 692 for (String key : new TreeSet<>(mcvProps.keySet())) { 693 buf.append(key).append('=').append(mcvProps.get(key)).append('\n'); 694 } 695 } 696 return buf.toString(); 697 } 698}