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 }