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