001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.system.server;
023:
024: import java.lang.reflect.Method;
025: import java.text.DecimalFormat;
026: import java.util.Enumeration;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Set;
030: import java.util.TreeSet;
031:
032: import javax.management.MBeanRegistration;
033: import javax.management.MBeanServer;
034: import javax.management.ObjectName;
035:
036: import org.jboss.logging.Logger;
037: import org.jboss.util.platform.Java;
038:
039: /**
040: * An MBean that provides a rich view of system information for the JBoss
041: * server in which it is deployed.
042: *
043: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
044: * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
045: * @author <a href="mailto:hiram.chirino@jboss.org">Hiram Chirino</a>
046: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
047: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
048: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
049: * @version $Revision: 60537 $
050: */
051: public class ServerInfo implements ServerInfoMBean, MBeanRegistration {
052: /** Class logger. */
053: private static final Logger log = Logger
054: .getLogger(ServerInfo.class);
055:
056: /** Zero */
057: private static final Integer ZERO = new Integer(0);
058:
059: /** Empty parameter signature for reflective calls */
060: private static final Class[] NO_PARAMS_SIG = new Class[0];
061:
062: /** Empty paramater list for reflective calls */
063: private static final Object[] NO_PARAMS = new Object[0];
064:
065: /** The cached host name for the server. */
066: private String hostName;
067:
068: /** The cached host address for the server. */
069: private String hostAddress;
070:
071: /** The cached jdk5+ ThreadMXBean instance */
072: private Object threadMXBean;
073:
074: /** The cached jdk5+ ManagementFactory.getMemoryPoolMXBeans() method */
075: private Method getMemoryPoolMXBeans;
076:
077: /** The cached jdk5+ MemoryPoolMXBean methods */
078: private Method getName;
079: private Method getType;
080: private Method getUsage;
081: private Method getPeakUsage;
082:
083: /** The cached jdk5+ MemoryUsage methods */
084: private Method getInit;
085: private Method getUsed;
086: private Method getCommitted;
087: private Method getMax;
088:
089: /** The cached jdk5+ ThreadMXBean.getThreadInfo() method */
090: private Method getThreadInfo;
091: private Method getAllThreadIds;
092: private Method getThreadCpuTime;
093:
094: /** The cached jdk5+ ThreadInfo methods */
095: private Method getThreadName;
096: private Method getThreadState;
097: private Method getLockName;
098: //private Method getLockOwnerId;
099: //private Method getLockOwnerName;
100: private Method getStackTrace;
101:
102: /** The cached jdk5+ Thread.getId() method */
103: private Method getThreadId;
104:
105: ///////////////////////////////////////////////////////////////////////////
106: // JMX Hooks //
107: ///////////////////////////////////////////////////////////////////////////
108:
109: public ObjectName preRegister(MBeanServer server, ObjectName name)
110: throws Exception {
111: // Dump out basic JVM & OS info as INFO priority msgs
112: log.info("Java version: " + System.getProperty("java.version")
113: + "," + System.getProperty("java.vendor"));
114:
115: log.info("Java VM: " + System.getProperty("java.vm.name") + " "
116: + System.getProperty("java.vm.version") + ","
117: + System.getProperty("java.vm.vendor"));
118:
119: log.info("OS-System: " + System.getProperty("os.name") + " "
120: + System.getProperty("os.version") + ","
121: + System.getProperty("os.arch"));
122:
123: // Dump out the entire system properties
124: log.debug("Full System Properties Dump");
125: Enumeration names = System.getProperties().propertyNames();
126: while (names.hasMoreElements()) {
127: String pname = (String) names.nextElement();
128: log
129: .debug(" " + pname + ": "
130: + System.getProperty(pname));
131: }
132:
133: // cache a reference to the platform ThreadMXBean
134: // and related Thread/ThreadInfo methods, if available
135: if (Java.isCompatible(Java.VERSION_1_5)) {
136: try {
137: ClassLoader cl = Thread.currentThread()
138: .getContextClassLoader();
139: Class clazz = cl
140: .loadClass("java.lang.management.ManagementFactory");
141:
142: // cache ThreadMXBean instance
143: Method method = clazz.getMethod("getThreadMXBean",
144: NO_PARAMS_SIG);
145: this .threadMXBean = method.invoke(null, NO_PARAMS);
146:
147: // cache ManagementFactory.getMemoryPoolMXBeans() method
148: this .getMemoryPoolMXBeans = clazz.getMethod(
149: "getMemoryPoolMXBeans", NO_PARAMS_SIG);
150:
151: // cache MemoryPoolMXBean methods
152: clazz = cl
153: .loadClass("java.lang.management.MemoryPoolMXBean");
154: this .getName = clazz
155: .getMethod("getName", NO_PARAMS_SIG);
156: this .getType = clazz
157: .getMethod("getType", NO_PARAMS_SIG);
158: this .getUsage = clazz.getMethod("getUsage",
159: NO_PARAMS_SIG);
160: this .getPeakUsage = clazz.getMethod("getPeakUsage",
161: NO_PARAMS_SIG);
162:
163: // cache MemoryUsage methods
164: clazz = cl
165: .loadClass("java.lang.management.MemoryUsage");
166: this .getInit = clazz
167: .getMethod("getInit", NO_PARAMS_SIG);
168: this .getUsed = clazz
169: .getMethod("getUsed", NO_PARAMS_SIG);
170: this .getCommitted = clazz.getMethod("getCommitted",
171: NO_PARAMS_SIG);
172: this .getMax = clazz.getMethod("getMax", NO_PARAMS_SIG);
173:
174: // cache ThreadMXBean.getThreadInfo() method
175: clazz = cl
176: .loadClass("java.lang.management.ThreadMXBean");
177: this .getThreadInfo = clazz.getMethod("getThreadInfo",
178: new Class[] { Long.TYPE, Integer.TYPE });
179: this .getAllThreadIds = clazz.getMethod(
180: "getAllThreadIds", NO_PARAMS_SIG);
181: this .getThreadCpuTime = clazz.getMethod(
182: "getThreadCpuTime", new Class[] { Long.TYPE });
183:
184: // cache ThreadInfo methods
185: clazz = cl.loadClass("java.lang.management.ThreadInfo");
186: this .getThreadName = clazz.getMethod("getThreadName",
187: NO_PARAMS_SIG);
188: this .getThreadState = clazz.getMethod("getThreadState",
189: NO_PARAMS_SIG);
190: this .getLockName = clazz.getMethod("getLockName",
191: NO_PARAMS_SIG);
192: //this.getLockOwnerId = clazz.getMethod("getLockOwnerId", NO_PARAMS_SIG);
193: //this.getLockOwnerName = clazz.getMethod("getLockOwnerName", NO_PARAMS_SIG);
194: this .getStackTrace = clazz.getMethod("getStackTrace",
195: NO_PARAMS_SIG);
196:
197: // cache Thread.getId() method
198: clazz = Thread.class;
199: this .getThreadId = clazz.getMethod("getId",
200: NO_PARAMS_SIG);
201: } catch (Exception e) {
202: log.debug("Cannot access platform ThreadMXBean", e);
203: }
204: }
205:
206: return name == null ? OBJECT_NAME : name;
207: }
208:
209: public void postRegister(Boolean registrationDone) {
210: // empty
211: }
212:
213: public void preDeregister() throws Exception {
214: // empty
215: }
216:
217: public void postDeregister() {
218: // empty
219: }
220:
221: ///////////////////////////////////////////////////////////////////////////
222: // Server Information //
223: ///////////////////////////////////////////////////////////////////////////
224:
225: /**
226: * @jmx:managed-attribute
227: */
228: public String getJavaVersion() {
229: return System.getProperty("java.version");
230: }
231:
232: /**
233: * @jmx:managed-attribute
234: */
235: public String getJavaVendor() {
236: return System.getProperty("java.vendor");
237: }
238:
239: /**
240: * @jmx:managed-attribute
241: */
242: public String getJavaVMName() {
243: return System.getProperty("java.vm.name");
244: }
245:
246: /**
247: * @jmx:managed-attribute
248: */
249: public String getJavaVMVersion() {
250: return System.getProperty("java.vm.version");
251: }
252:
253: /**
254: * @jmx:managed-attribute
255: */
256: public String getJavaVMVendor() {
257: return System.getProperty("java.vm.vendor");
258: }
259:
260: /**
261: * @jmx:managed-attribute
262: */
263: public String getOSName() {
264: return System.getProperty("os.name");
265: }
266:
267: /**
268: * @jmx:managed-attribute
269: */
270: public String getOSVersion() {
271: return System.getProperty("os.version");
272: }
273:
274: /**
275: * @jmx:managed-attribute
276: */
277: public String getOSArch() {
278: return System.getProperty("os.arch");
279: }
280:
281: /**
282: * @jmx:managed-attribute
283: */
284: public Long getTotalMemory() {
285: return new Long(Runtime.getRuntime().totalMemory());
286: }
287:
288: /**
289: * @jmx:managed-attribute
290: */
291: public Long getFreeMemory() {
292: return new Long(Runtime.getRuntime().freeMemory());
293: }
294:
295: /**
296: * Returns <tt>Runtime.getRuntime().maxMemory()<tt> on
297: * JDK 1.4 vms or -1 on previous versions.
298: *
299: * @jmx:managed-attribute
300: */
301: public Long getMaxMemory() {
302: if (Java.isCompatible(Java.VERSION_1_4)) {
303: // Uncomment when JDK 1.4 is the base JVM
304: // return new Long(Runtime.getRuntime().maxMemory());
305:
306: // until then use reflection to do the job
307: try {
308: Runtime rt = Runtime.getRuntime();
309: Method m = rt.getClass().getMethod("maxMemory",
310: NO_PARAMS_SIG);
311: return (Long) m.invoke(rt, NO_PARAMS);
312: } catch (Exception e) {
313: log.error("Operation failed", e);
314: }
315: }
316:
317: return new Long(-1);
318: }
319:
320: /**
321: * Returns <tt>Runtime.getRuntime().availableProcessors()</tt> on
322: * JDK 1.4 vms or -1 on previous versions.
323: *
324: * @jmx:managed-attribute
325: */
326: public Integer getAvailableProcessors() {
327: if (Java.isCompatible(Java.VERSION_1_4)) {
328: // Uncomment when JDK 1.4 is the base JVM
329: // return new Integer(Runtime.getRuntime().availableProcessors());
330:
331: // until then use reflection to do the job
332: try {
333: Runtime rt = Runtime.getRuntime();
334: Method m = rt.getClass().getMethod(
335: "availableProcessors", NO_PARAMS_SIG);
336: return (Integer) m.invoke(rt, NO_PARAMS);
337: } catch (Exception e) {
338: log.error("Operation failed", e);
339: }
340: }
341:
342: return new Integer(-1);
343: }
344:
345: /**
346: * Returns InetAddress.getLocalHost().getHostName();
347: *
348: * @jmx:managed-attribute
349: */
350: public String getHostName() {
351: if (hostName == null) {
352: try {
353: hostName = java.net.InetAddress.getLocalHost()
354: .getHostName();
355: } catch (java.net.UnknownHostException e) {
356: log.error("Error looking up local hostname", e);
357: hostName = "<unknown>";
358: }
359: }
360:
361: return hostName;
362: }
363:
364: /**
365: * Returns InetAddress.getLocalHost().getHostAddress();
366: *
367: * @jmx:managed-attribute
368: */
369: public String getHostAddress() {
370: if (hostAddress == null) {
371: try {
372: hostAddress = java.net.InetAddress.getLocalHost()
373: .getHostAddress();
374: } catch (java.net.UnknownHostException e) {
375: log.error("Error looking up local address", e);
376: hostAddress = "<unknown>";
377: }
378: }
379:
380: return hostAddress;
381: }
382:
383: /**
384: * Return a listing of the thread pools on jdk5+.
385: *
386: * @jmx:managed-operation
387: *
388: * @param fancy produce a text-based graph when true
389: */
390: public String listMemoryPools(boolean fancy) {
391: if (getMemoryPoolMXBeans != null) {
392: // running under jdk5+
393: StringBuffer sbuf = new StringBuffer(4196);
394: try {
395: // get the pools
396: List poolList = (List) getMemoryPoolMXBeans.invoke(
397: null, NO_PARAMS);
398: sbuf.append("<b>Total Memory Pools:</b> ").append(
399: poolList.size());
400: sbuf.append("<blockquote>");
401: for (Iterator i = poolList.iterator(); i.hasNext();) {
402: // MemoryPoolMXBean instance
403: Object pool = i.next();
404: String name = (String) getName.invoke(pool,
405: NO_PARAMS);
406: // enum MemoryType
407: Object type = getType.invoke(pool, NO_PARAMS);
408: sbuf.append("<b>Pool: ").append(name);
409: sbuf.append("</b> (").append(type).append(")");
410:
411: // PeakUsage/CurrentUsage
412: Object peakUsage = getPeakUsage.invoke(pool,
413: NO_PARAMS);
414: Object usage = getUsage.invoke(pool, NO_PARAMS);
415:
416: sbuf.append("<blockquote>");
417: if (usage != null && peakUsage != null) {
418: Long init = (Long) getInit.invoke(peakUsage,
419: NO_PARAMS);
420: Long used = (Long) getUsed.invoke(peakUsage,
421: NO_PARAMS);
422: Long committed = (Long) getCommitted.invoke(
423: peakUsage, NO_PARAMS);
424: Long max = (Long) getMax.invoke(peakUsage,
425: NO_PARAMS);
426:
427: sbuf.append("Peak Usage : ");
428: sbuf.append("init:").append(init);
429: sbuf.append(", used:").append(used);
430: sbuf.append(", committed:").append(committed);
431: sbuf.append(", max:").append(max);
432: sbuf.append("<br>");
433:
434: init = (Long) getInit.invoke(usage, NO_PARAMS);
435: used = (Long) getUsed.invoke(usage, NO_PARAMS);
436: committed = (Long) getCommitted.invoke(usage,
437: NO_PARAMS);
438: max = (Long) getMax.invoke(usage, NO_PARAMS);
439:
440: sbuf.append("Current Usage : ");
441: sbuf.append("init:").append(init);
442: sbuf.append(", used:").append(used);
443: sbuf.append(", committed:").append(committed);
444: sbuf.append(", max:").append(max);
445:
446: if (fancy) {
447: TextGraphHelper.poolUsage(sbuf, used
448: .longValue(),
449: committed.longValue(), max
450: .longValue());
451: }
452: } else {
453: sbuf.append("Memory pool NOT valid!");
454: }
455: sbuf.append("</blockquote><br>");
456: }
457: sbuf.append("</blockquote>");
458: } catch (Exception e) {
459: // ignore
460: }
461: return sbuf.toString();
462: } else {
463: return "<b>Memory pool information available only under a JDK5+ compatible JVM!</b>";
464: }
465: }
466:
467: /**
468: * @jmx:managed-operation
469: */
470: public Integer getActiveThreadCount() {
471: return new Integer(getRootThreadGroup().activeCount());
472: }
473:
474: /**
475: * @jmx:managed-operation
476: */
477: public Integer getActiveThreadGroupCount() {
478: return new Integer(getRootThreadGroup().activeGroupCount());
479: }
480:
481: /**
482: * Return a listing of the active threads and thread groups.
483: *
484: * @jmx:managed-operation
485: */
486: public String listThreadDump() {
487: ThreadGroup root = getRootThreadGroup();
488:
489: // Count the threads/groups during our traversal
490: // rather than use the often inaccurate ThreadGroup
491: // activeCount() and activeGroupCount()
492: ThreadGroupCount count = new ThreadGroupCount();
493:
494: // traverse
495: String threadGroupInfo = getThreadGroupInfo(root, count);
496: // attach counters
497: String threadDump = "<b>Total Threads:</b> " + count.threads
498: + "<br>" + "<b>Total Thread Groups:</b> "
499: + count.groups + "<br>" + threadGroupInfo;
500:
501: return threadDump;
502: }
503:
504: /**
505: * Return a listing of the active threads and thread groups.
506: *
507: * @jmx:managed-operation
508: */
509: public String listThreadCpuUtilization() {
510: Set threads = getThreadCpuUtilization();
511:
512: if (threads == null) {
513: return ("Thread cpu utilization requires J2SE5+");
514: } else {
515: long totalCPU = 0;
516: StringBuffer buffer = new StringBuffer();
517: buffer
518: .append("<table><tr><th>Thread Name</th><th>CPU (milliseconds)</th></tr>");
519: for (Iterator i = threads.iterator(); i.hasNext();) {
520: ThreadCPU thread = (ThreadCPU) i.next();
521: buffer.append("<tr><td>").append(thread.name).append(
522: "</td><td>");
523: buffer.append(thread.cpuTime).append("</td></tr>");
524: totalCPU += thread.cpuTime;
525: }
526: buffer
527: .append("<tr><td> </td><td> </td></tr><tr><td>Total</td><td>");
528: buffer.append(totalCPU).append("</td></tr></table>");
529: return buffer.toString();
530: }
531: }
532:
533: ///////////////////////////////////////////////////////////////////////////
534: // Private //
535: ///////////////////////////////////////////////////////////////////////////
536:
537: /**
538: * Get the Thread cpu utilization
539: *
540: * @return an ordered
541: */
542: private Set getThreadCpuUtilization() {
543: if (threadMXBean == null)
544: return null;
545:
546: try {
547: TreeSet result = new TreeSet();
548: long[] threads = (long[]) getAllThreadIds.invoke(
549: threadMXBean, NO_PARAMS);
550: for (int i = 0; i < threads.length; ++i) {
551: Long id = new Long(threads[i]);
552: Long cpuTime = (Long) getThreadCpuTime.invoke(
553: threadMXBean, new Object[] { id });
554: Object threadInfo = getThreadInfo.invoke(threadMXBean,
555: new Object[] { id, ZERO });
556: if (threadInfo != null) {
557: String name = (String) getThreadName.invoke(
558: threadInfo, NO_PARAMS);
559: result
560: .add(new ThreadCPU(name, cpuTime
561: .longValue()));
562: }
563: }
564: return result;
565: } catch (Exception e) {
566: log.warn("Error retrieving thread cpu utiliation", e);
567: return null;
568: }
569: }
570:
571: /*
572: * Traverse to the root thread group
573: */
574: private ThreadGroup getRootThreadGroup() {
575: ThreadGroup group = Thread.currentThread().getThreadGroup();
576: while (group.getParent() != null) {
577: group = group.getParent();
578: }
579:
580: return group;
581: }
582:
583: /*
584: * Recurse inside ThreadGroups to create the thread dump
585: */
586: private String getThreadGroupInfo(ThreadGroup group,
587: ThreadGroupCount count) {
588: StringBuffer rc = new StringBuffer();
589:
590: // Visit one more group
591: count.groups++;
592:
593: rc.append("<br><b>");
594: rc.append("Thread Group: " + group.getName());
595: rc.append("</b> : ");
596: rc.append("max priority:" + group.getMaxPriority() + ", demon:"
597: + group.isDaemon());
598:
599: rc.append("<blockquote>");
600: Thread threads[] = new Thread[group.activeCount()];
601: group.enumerate(threads, false);
602: for (int i = 0; i < threads.length && threads[i] != null; i++) {
603: // Visit one more thread
604: count.threads++;
605:
606: rc.append("<b>");
607: rc.append("Thread: " + threads[i].getName());
608: rc.append("</b> : ");
609: rc.append("priority:" + threads[i].getPriority()
610: + ", demon:" + threads[i].isDaemon());
611: // Output extra info with jdk5+, or just <br>
612: outputJdk5ThreadMXBeanInfo(rc, threads[i]);
613: }
614:
615: ThreadGroup groups[] = new ThreadGroup[group.activeGroupCount()];
616: group.enumerate(groups, false);
617: for (int i = 0; i < groups.length && groups[i] != null; i++) {
618: rc.append(getThreadGroupInfo(groups[i], count));
619: }
620: rc.append("</blockquote>");
621:
622: return rc.toString();
623: }
624:
625: /*
626: * Complete the output of thread info, with optional stuff
627: * when running under jdk5+, or just change line.
628: */
629: private void outputJdk5ThreadMXBeanInfo(StringBuffer sbuf,
630: Thread thread) {
631: // if ThreadMXBean has been found, we run under jdk5+
632: if (threadMXBean != null) {
633: // use reflection all the way, until we base on jdk5
634: try {
635: // get the threadId
636: Long threadId = (Long) getThreadId.invoke(thread,
637: NO_PARAMS);
638: sbuf.append(", threadId:").append(threadId);
639:
640: // get the ThreadInfo object for that threadId, max StackTraceElement depth
641: Object threadInfo = getThreadInfo.invoke(threadMXBean,
642: new Object[] { threadId,
643: new Integer(Integer.MAX_VALUE) });
644: // JBAS-3838, thread might not be alive
645: if (threadInfo != null) {
646: // get misc info from ThreadInfo
647: Object threadState = getThreadState.invoke(
648: threadInfo, NO_PARAMS); // enum
649: String lockName = (String) getLockName.invoke(
650: threadInfo, NO_PARAMS);
651: //Long lockOwnerId = (Long)getLockOwnerId.invoke(threadInfo, NO_PARAMS);
652: //String lockOwnerName = (String)getLockOwnerName.invoke(threadInfo, NO_PARAMS);
653: Object[] stackTrace = (Object[]) getStackTrace
654: .invoke(threadInfo, NO_PARAMS);
655:
656: sbuf.append(", threadState:").append(threadState);
657: sbuf.append(", lockName:").append(lockName);
658: //sbuf.append(", lockOwnerId:").append(lockOwnerId);
659: //sbuf.append(", lockOwnerName:").append(lockOwnerName);
660: sbuf.append("<br>");
661: if (stackTrace.length > 0) {
662: sbuf.append("<blockquote>");
663: for (int i = 0; i < stackTrace.length; i++) {
664: sbuf.append(stackTrace[i]).append("<br>");
665: }
666: sbuf.append("</blockquote>");
667: }
668: } else {
669: sbuf.append("<br>");
670: }
671: } catch (Exception ignore) {
672: // empty
673: }
674: } else {
675: // no jdk5+ info to add, just change line
676: sbuf.append("<br>");
677: }
678: }
679:
680: /**
681: * Display the java.lang.Package info for the pkgName
682: *
683: * @jmx:managed-operation
684: */
685: public String displayPackageInfo(String pkgName) {
686: Package pkg = Package.getPackage(pkgName);
687: if (pkg == null)
688: return "<h2>Package:" + pkgName + " Not Found!</h2>";
689:
690: StringBuffer info = new StringBuffer("<h2>Package: " + pkgName
691: + "</h2>");
692: displayPackageInfo(pkg, info);
693: return info.toString();
694: }
695:
696: private void displayPackageInfo(Package pkg, StringBuffer info) {
697: info.append("<pre>\n");
698: info.append("SpecificationTitle: "
699: + pkg.getSpecificationTitle());
700: info.append("\nSpecificationVersion: "
701: + pkg.getSpecificationVersion());
702: info.append("\nSpecificationVendor: "
703: + pkg.getSpecificationVendor());
704: info.append("\nImplementationTitle: "
705: + pkg.getImplementationTitle());
706: info.append("\nImplementationVersion: "
707: + pkg.getImplementationVersion());
708: info.append("\nImplementationVendor: "
709: + pkg.getImplementationVendor());
710: info.append("\nisSealed: " + pkg.isSealed());
711: info.append("</pre>\n");
712: }
713:
714: ///////////////////////////////////////////////////////////////////////////
715: // Inner //
716: ///////////////////////////////////////////////////////////////////////////
717:
718: /*
719: * Inner Helper class for fancy text graphs
720: *
721: * @author dimitris@jboss.org
722: */
723: private static class TextGraphHelper {
724: // number conversions
725: static final DecimalFormat formatter = new DecimalFormat("#.##");
726: static final long KILO = 1024;
727: static final long MEGA = 1024 * 1024;
728: static final long GIGA = 1024 * 1024 * 1024;
729:
730: // how many dashes+pipe is 100%
731: static final int factor = 70;
732: static char[] fixedline;
733: static char[] baseline;
734: static char[] barline;
735: static char[] spaces;
736: static {
737: // cache a couple of Strings
738: StringBuffer sbuf0 = new StringBuffer();
739: StringBuffer sbuf1 = new StringBuffer();
740: StringBuffer sbuf2 = new StringBuffer();
741: StringBuffer sbuf3 = new StringBuffer();
742: sbuf0.append('+');
743: sbuf1.append('|');
744: sbuf2.append('|');
745: for (int i = 1; i < factor; i++) {
746: sbuf0.append('-');
747: sbuf1.append('-');
748: sbuf2.append('/');
749: sbuf3.append(' ');
750: }
751: sbuf0.append('+');
752: fixedline = sbuf0.toString().toCharArray();
753: baseline = sbuf1.toString().toCharArray();
754: barline = sbuf2.toString().toCharArray();
755: spaces = sbuf3.toString().toCharArray();
756: }
757:
758: private TextGraphHelper() {
759: // do not instantiate
760: }
761:
762: /*
763: * Make a text graph of a memory pool usage:
764: *
765: * +---------------------------| committed:10Mb
766: * +-------------------------------------------------+
767: * |//////////////// | | max:20Mb
768: * +-------------------------------------------------+
769: * +---------------| used:3Mb
770: *
771: * When max is unknown assume max == committed
772: *
773: * |-------------------------------------------------| committed:10Mb
774: * +-------------------------------------------------+
775: * |//////////////// | max:-1
776: * +-------------------------------------------------+
777: * |---------------| used:3Mb
778: */
779: public static void poolUsage(StringBuffer sbuf, long used,
780: long committed, long max) {
781: // there is a chance that max is not provided (-1)
782: long assumedMax = (max == -1) ? committed : max;
783: // find out bar lengths
784: int localUsed = (int) (factor * used / assumedMax);
785: int localCommitted = (int) (factor * committed / assumedMax);
786: int localMax = factor;
787:
788: sbuf.append("<blockquote><br>");
789: sbuf.append(baseline, 0, localCommitted).append(
790: "| committed:").append(outputNumber(committed))
791: .append("<br>");
792: sbuf.append(fixedline).append("<br>");
793:
794: // the difficult part
795: sbuf.append(barline, 0, localUsed);
796: if (localUsed < localCommitted) {
797: sbuf.append(localUsed > 0 ? '/' : '|');
798: sbuf.append(spaces, 0, localCommitted - localUsed - 1);
799: }
800: sbuf.append('|');
801: if (localCommitted < localMax) {
802: sbuf.append(spaces, 0, localMax - localCommitted - 1);
803: sbuf.append('|');
804: }
805: sbuf.append(" max:").append(outputNumber(max)).append(
806: "<br>");
807:
808: sbuf.append(fixedline).append("<br>");
809: sbuf.append(baseline, 0, localUsed).append("| used:")
810: .append(outputNumber(used));
811: sbuf.append("</blockquote>");
812: }
813:
814: private static String outputNumber(long value) {
815: if (value >= GIGA) {
816: return formatter.format((double) value / GIGA) + "Gb";
817: } else if (value >= MEGA) {
818: return formatter.format((double) value / MEGA) + "Mb";
819: } else if (value >= KILO) {
820: return formatter.format((double) value / KILO) + "Kb";
821: } else if (value >= 0) {
822: return value + "b";
823: } else {
824: return Long.toString(value);
825: }
826: }
827: }
828:
829: private static class ThreadCPU implements Comparable {
830: public String name;
831: public long cpuTime;
832:
833: public ThreadCPU(String name, long cpuTime) {
834: this .name = name;
835: this .cpuTime = cpuTime / 1000000; // convert to millis
836: }
837:
838: public int compareTo(Object o) {
839: ThreadCPU other = (ThreadCPU) o;
840: long value = cpuTime - other.cpuTime;
841: if (value > 0)
842: return -1;
843: else if (value < 0)
844: return +1;
845: else
846: return name.compareTo(other.name);
847: }
848: }
849:
850: /*
851: * Simple data holder
852: */
853: private static class ThreadGroupCount {
854: public int threads;
855: public int groups;
856: }
857: }
|