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