Fixed FELIX-3502 Improve Threads web console printer
https://issues.apache.org/jira/browse/FELIX-3502
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1337062 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadDumper.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadDumper.java
new file mode 100644
index 0000000..5e3024e
--- /dev/null
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadDumper.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.webconsole.internal.misc;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This is a helper class for dumping thread stacks.
+ */
+public class ThreadDumper
+{
+
+ private final Method getStackTrace;
+ private final Method getId;
+
+ /**
+ * Base constructor.
+ */
+ public ThreadDumper()
+ {
+ Method _getStackTrace = null;
+ Method _getId = null;
+ final Class[] nullArgs = null;
+ try
+ {
+ _getStackTrace = Thread.class.getMethod("getStackTrace", nullArgs); //$NON-NLS-1$
+ _getId = Thread.class.getMethod("getId", nullArgs); //$NON-NLS-1$
+ }
+ catch (Throwable e)
+ {
+ /* ignore - stack traces will be unavailable */
+ }
+ getStackTrace = _getStackTrace;
+ getId = _getId;
+ }
+
+ /**
+ * Prints all available thread groups, threads and a summary of threads availability.
+ * The thread groups and the threads will be sorted alphabetically regardless of the case.
+ *
+ * @param pw the writer where to print the threads information
+ * @param withStackTrace to include or not the stack traces
+ */
+ public final void printThreads(PrintWriter pw, boolean withStackTrace)
+ {
+ // first get the root thread group
+ ThreadGroup rootGroup = getRootThreadGroup();
+
+ // enumerate all other threads
+ int numGroups = rootGroup.activeGroupCount();
+ final ThreadGroup[] groups = new ThreadGroup[2 * numGroups];
+ numGroups = rootGroup.enumerate(groups);
+ Arrays.sort(groups, ThreadGroupComparator.getInstance());
+
+ printSummary(pw, rootGroup, groups);
+ printThreadGroup(pw, rootGroup, withStackTrace);
+
+ for (int i = 0; i < numGroups; i++)
+ {
+ printThreadGroup(pw, groups[i], withStackTrace);
+ }
+
+ pw.println();
+ }
+
+ /**
+ * Prints information for the given thread.
+ *
+ * @param pw the writer where to print the threads information
+ * @param thread the thread for which to print the information
+ * @param withStackTrace to include or not the stack traces
+ */
+ public final void printThread(PrintWriter pw, Thread thread, boolean withStackTrace)
+ {
+ if (thread != null)
+ {
+
+ pw.print(" Thread ");
+ pw.print(getId(thread));
+ pw.print('/');
+ pw.print(thread.getName());
+ pw.print(" ["); //$NON-NLS-1$
+ pw.print("priority=");
+ pw.print(thread.getPriority());
+ pw.print(", alive=");
+ pw.print(thread.isAlive());
+ pw.print(", daemon=");
+ pw.print(thread.isDaemon());
+ pw.print(", interrupted=");
+ pw.print(thread.isInterrupted());
+ pw.print(", loader=");
+ pw.print(thread.getContextClassLoader());
+ pw.println(']');
+
+ if (withStackTrace)
+ {
+ printClassLoader(pw, thread.getContextClassLoader());
+ printStackTrace(pw, getStackTrace(thread));
+ pw.println();
+ }
+ }
+ }
+
+ private final void printThreadGroup(PrintWriter pw, ThreadGroup group,
+ boolean withStackTrace)
+ {
+ if (group != null)
+ {
+ pw.print("ThreadGroup ");
+ pw.print(group.getName());
+ pw.print(" ["); //$NON-NLS-1$
+ pw.print("maxprio=");
+ pw.print(group.getMaxPriority());
+
+ pw.print(", parent=");
+ if (group.getParent() != null)
+ {
+ pw.print(group.getParent().getName());
+ }
+ else
+ {
+ pw.print('-');
+ }
+
+ pw.print(", isDaemon=");
+ pw.print(group.isDaemon());
+ pw.print(", isDestroyed=");
+ pw.print(group.isDestroyed());
+ pw.println(']');
+
+ int numThreads = group.activeCount();
+ Thread[] threads = new Thread[numThreads * 2];
+ group.enumerate(threads, false);
+ Arrays.sort(threads, ThreadComparator.getInstance());
+ for (int i = 0; i < threads.length; i++)
+ {
+ printThread(pw, threads[i], withStackTrace);
+ }
+
+ pw.println();
+ }
+ }
+
+ private final void printClassLoader(PrintWriter pw, ClassLoader classLoader)
+ {
+ if (classLoader != null)
+ {
+ pw.print(" ClassLoader=");
+ pw.println(classLoader);
+ pw.print(" Parent=");
+ pw.println(classLoader.getParent());
+
+ if (classLoader instanceof URLClassLoader)
+ {
+ URLClassLoader loader = (URLClassLoader) classLoader;
+ URL[] urls = loader.getURLs();
+ if (urls != null && urls.length > 0)
+ {
+ for (int i = 0; i < urls.length; i++)
+ {
+ pw.print(" ");
+ pw.print(i);
+ pw.print(" - ");
+ pw.println(urls[i]);
+ }
+ }
+ }
+ }
+ }
+
+ private final void printStackTrace(PrintWriter pw, Object stackTrace)
+ {
+ pw.println(" Stacktrace");
+ if (stackTrace == null || Array.getLength(stackTrace) == 0)
+ {
+ pw.println(" -"); //$NON-NLS-1$
+ }
+ else
+ {
+ for (int i = 0, len = Array.getLength(stackTrace); i < len; i++)
+ {
+ Object/*StackTraceElement*/stackTraceElement = Array.get(stackTrace, i);
+ pw.print(" "); //$NON-NLS-1$
+ pw.println(stackTraceElement);
+ }
+ }
+ }
+
+ private final void printSummary(PrintWriter pw, ThreadGroup rootGroup,
+ ThreadGroup[] groups)
+ {
+ int alive = 0;
+ int daemon = 0;
+ int interrupted = 0;
+
+ int threadCount = 0;
+ int threadGroupCount = 0;
+ int threadGroupDestroyed = 0;
+
+ ArrayList/*<ThreadGroup>*/list = new ArrayList(groups.length + 1);
+ list.add(rootGroup);
+ list.addAll(Arrays.asList(groups));
+ for (int j = 0; j < list.size(); j++)
+ {
+ ThreadGroup group = (ThreadGroup) list.get(j);
+ if (null == group)
+ {
+ continue;
+ }
+ threadGroupCount++;
+ if (group.isDestroyed())
+ {
+ threadGroupDestroyed++;
+ }
+
+ Thread[] threads = new Thread[group.activeCount()];
+ group.enumerate(threads);
+ for (int i = 0, size = threads.length; i < size; i++)
+ {
+ Thread thread = threads[i];
+ if (null != thread)
+ {
+ if (thread.isAlive())
+ {
+ alive++;
+ }
+ if (thread.isDaemon())
+ {
+ daemon++;
+ }
+ if (thread.isInterrupted())
+ {
+ interrupted++;
+ }
+ threadCount++;
+ }
+ }
+ }
+
+ pw.print("Status: ");
+ pw.print(threadCount);
+ pw.print(" threads (");
+ pw.print(alive);
+ pw.print(" alive/");
+ pw.print(daemon);
+ pw.print(" daemon/");
+ pw.print(interrupted);
+ pw.print(" interrupted) in ");
+ pw.print(threadGroupCount);
+ pw.print(" groups (");
+ pw.print(threadGroupDestroyed);
+ pw.print(" destroyed).");
+ pw.println();
+ pw.println();
+ }
+
+ private final String getId(Thread thread)
+ {
+ String ret = ""; //$NON-NLS-1$
+ if (null != getId)
+ {
+ try
+ {
+ ret = "#" + getId.invoke(thread, null); //$NON-NLS-1$
+ }
+ catch (Throwable e)
+ {
+ /* ignore */
+ }
+ }
+ return ret;
+ }
+
+ private final Object/*StackTraceElement[]*/getStackTrace(Thread thread)
+ {
+ Object/*StackTraceElement[]*/ret = null;
+ if (null != getStackTrace)
+ {
+ try
+ {
+
+ ret = getStackTrace.invoke(thread, null);
+
+ }
+ catch (Throwable e)
+ {
+ // ignore - no traces available
+ }
+ }
+ return ret;
+ }
+
+ private static final ThreadGroup getRootThreadGroup()
+ {
+ ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
+ while (rootGroup.getParent() != null)
+ {
+ rootGroup = rootGroup.getParent();
+ }
+ return rootGroup;
+ }
+}
+
+final class ThreadComparator implements Comparator
+{
+
+ private ThreadComparator()
+ {
+ // prevent instantiation
+ }
+
+ private static final Comparator instance = new ThreadComparator();
+
+ public static final Comparator getInstance()
+ {
+ return instance;
+ }
+
+ public int compare(Object thread1, Object thread2)
+ {
+ if (null == thread1 || null == thread2)
+ return -1;
+ String t1 = ((Thread) thread1).getName();
+ String t2 = ((Thread) thread2).getName();
+ if (null == t1)
+ {
+ t1 = ""; //$NON-NLS-1$
+ }
+ if (null == t2)
+ {
+ t2 = ""; //$NON-NLS-1$
+ }
+
+ return t1.toLowerCase().compareTo(t2.toLowerCase());
+ }
+
+}
+
+final class ThreadGroupComparator implements Comparator
+{
+
+ private ThreadGroupComparator()
+ {
+ // prevent instantiation
+ }
+
+ private static final Comparator instance = new ThreadGroupComparator();
+
+ public static final Comparator getInstance()
+ {
+ return instance;
+ }
+
+ public int compare(Object thread1, Object thread2)
+ {
+ if (null == thread1 || null == thread2)
+ return -1;
+ String t1 = ((ThreadGroup) thread1).getName();
+ String t2 = ((ThreadGroup) thread2).getName();
+ if (null == t1)
+ {
+ t1 = ""; //$NON-NLS-1$
+ }
+ if (null == t2)
+ {
+ t2 = ""; //$NON-NLS-1$
+ }
+
+ return t1.toLowerCase().compareTo(t2.toLowerCase());
+ }
+
+}
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadPrinter.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadPrinter.java
index 8e8c89e..eb979f1 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadPrinter.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/misc/ThreadPrinter.java
@@ -19,228 +19,45 @@
package org.apache.felix.webconsole.internal.misc;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
+import org.apache.felix.webconsole.ModeAwareConfigurationPrinter;
import org.apache.felix.webconsole.internal.AbstractConfigurationPrinter;
-public class ThreadPrinter extends AbstractConfigurationPrinter
+/**
+ * This class provides the Threads tab in the configuration status.
+ */
+public class ThreadPrinter extends AbstractConfigurationPrinter implements ModeAwareConfigurationPrinter
{
private static final String TITLE = "Threads";
private static final String LABEL = "_threads";
-
+
+ private final ThreadDumper dumper = new ThreadDumper();
+
+ /**
+ * @see org.apache.felix.webconsole.ConfigurationPrinter#getTitle()
+ */
public String getTitle()
{
return TITLE;
}
+ /**
+ * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+ */
public void printConfiguration(PrintWriter pw)
{
- // first get the root thread group
- ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
- while (rootGroup.getParent() != null)
- {
- rootGroup = rootGroup.getParent();
- }
-
- int numGroups = rootGroup.activeGroupCount();
- ThreadGroup[] groups = new ThreadGroup[2 * numGroups];
- rootGroup.enumerate(groups);
- Arrays.sort(groups, ThreadGroupComparator.getInstance());
-
- printStatusLine(pw, rootGroup, groups);
- printThreadGroup(pw, rootGroup);
-
- for (int i = 0; i < groups.length; i++)
- {
- printThreadGroup(pw, groups[i]);
- }
+ printConfiguration(pw, MODE_TXT);
}
- private void printStatusLine(PrintWriter pw, ThreadGroup rootGroup,
- ThreadGroup[] groups)
+ /**
+ * @see ModeAwareConfigurationPrinter#printConfiguration(java.io.PrintWriter, java.lang.String)
+ */
+ public void printConfiguration(PrintWriter pw, String mode)
{
- int alive = 0;
- int daemon = 0;
- int interrupted = 0;
- int threadCount = 0;
- int threadGroupCount = 0;
- int threadGroupDestroyed = 0;
-
- ArrayList/*<ThreadGroup>*/list = new ArrayList(groups.length + 1);
- list.add(rootGroup);
- list.addAll(Arrays.asList(groups));
- for (int j = 0; j < list.size(); j++)
- {
- ThreadGroup group = (ThreadGroup) list.get(j);
- if (null == group)
- {
- continue;
- }
- threadGroupCount++;
- if (group.isDestroyed())
- {
- threadGroupDestroyed++;
- }
-
- Thread[] threads = new Thread[group.activeCount()];
- group.enumerate(threads);
- for (int i = 0, size = threads.length; i < size; i++)
- {
- Thread thread = threads[i];
- if (null != thread)
- {
- if (thread.isAlive())
- {
- alive++;
- }
- if (thread.isDaemon())
- {
- daemon++;
- }
- if (thread.isInterrupted())
- {
- interrupted++;
- }
- threadCount++;
- }
- }
- }
-
- ConfigurationRender.infoLine(pw, "", null, "Status: " + threadCount
- + " threads (" + alive + " alive/" + daemon + " daemon/" + interrupted
- + " interrupted) in " + threadGroupCount + " groups ("
- + threadGroupDestroyed + " destroyed).");
- pw.println();
- }
-
- private static final void printThreadGroup(PrintWriter pw, ThreadGroup group)
- {
- if (group != null)
- {
- StringBuffer info = new StringBuffer();
- info.append("ThreadGroup ").append(group.getName());
- info.append(" [");
- info.append("maxprio=").append(group.getMaxPriority());
-
- info.append(", parent=");
- if (group.getParent() != null)
- {
- info.append(group.getParent().getName());
- }
- else
- {
- info.append('-');
- }
-
- info.append(", isDaemon=").append(group.isDaemon());
- info.append(", isDestroyed=").append(group.isDestroyed());
- info.append(']');
-
- ConfigurationRender.infoLine(pw, null, null, info.toString());
-
- int numThreads = group.activeCount();
- Thread[] threads = new Thread[numThreads * 2];
- group.enumerate(threads, false);
- Arrays.sort(threads, ThreadComparator.getInstance());
- for (int i = 0; i < threads.length; i++)
- {
- printThread(pw, threads[i]);
- }
-
- pw.println();
- }
- }
-
- private static final void printThread(PrintWriter pw, Thread thread)
- {
- if (thread != null)
- {
- StringBuffer info = new StringBuffer();
- info.append("Thread ").append(thread.getName());
- info.append(" [");
- info.append("priority=").append(thread.getPriority());
- info.append(", alive=").append(thread.isAlive());
- info.append(", daemon=").append(thread.isDaemon());
- info.append(", interrupted=").append(thread.isInterrupted());
- info.append(", loader=").append(thread.getContextClassLoader());
- info.append(']');
-
- ConfigurationRender.infoLine(pw, " ", null, info.toString());
- }
- }
-}
-
-final class ThreadComparator implements Comparator
-{
-
- private ThreadComparator()
- {
- // prevent instantiation
- }
-
- private static final Comparator instance = new ThreadComparator();
-
- public static final Comparator getInstance()
- {
- return instance;
- }
-
- public int compare(Object thread1, Object thread2)
- {
- if (null == thread1 || null == thread2)
- return -1;
- String t1 = ((Thread) thread1).getName();
- String t2 = ((Thread) thread2).getName();
- if (null == t1)
- {
- t1 = ""; //$NON-NLS-1$
- }
- if (null == t2)
- {
- t2 = ""; //$NON-NLS-1$
- }
-
- return t1.compareTo(t2);
- }
-
-}
-
-final class ThreadGroupComparator implements Comparator
-{
-
- private ThreadGroupComparator()
- {
- // prevent instantiation
- }
-
- private static final Comparator instance = new ThreadGroupComparator();
-
- public static final Comparator getInstance()
- {
- return instance;
- }
-
- public int compare(Object thread1, Object thread2)
- {
- if (null == thread1 || null == thread2)
- return -1;
- String t1 = ((ThreadGroup) thread1).getName();
- String t2 = ((ThreadGroup) thread2).getName();
- if (null == t1)
- {
- t1 = ""; //$NON-NLS-1$
- }
- if (null == t2)
- {
- t2 = ""; //$NON-NLS-1$
- }
-
- return t1.compareTo(t2);
+ dumper.printThreads(pw, MODE_ZIP.equals(mode));
}
}