FELIX-3339 New consolidated Thread Dumper tool contributed by Simo Tripodi (thanks alot)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1494960 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/threaddump/pom.xml b/threaddump/pom.xml
new file mode 100644
index 0000000..3a8901d
--- /dev/null
+++ b/threaddump/pom.xml
@@ -0,0 +1,146 @@
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
+>
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>2.1</version>
+ <relativePath></relativePath>
+ </parent>
+
+ <artifactId>org.apache.felix.threaddump</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>Apache Felix Thread Dump</name>
+ <description>Thread Dump classes for OSGi.</description>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/threaddump</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/threaddump</developerConnection>
+ <url>http://svn.apache.org/repos/asf/felix/threaddump</url>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.inventory</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.3.7</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>
+ org.apache.felix.threaddump.internal.ThreadDumpActivator
+ </Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <!--
+ Check API compliance for Java 1.4, 5, and 6.
+ Unfortunately the <ignores> sections do not seem to
+ properly work, so the sniffer is disabled by default.
+
+ It is recommended to regularly and manually run the
+ sniffer and check for the following rules:
+
+ * Classes in the ..internal package must be compliant
+ with Java 1.4
+ * Classes in the ..internal.jdk5 package must be compliant
+ with Java 5
+ * Classes in the ..internal.jdk6 package must be compliant
+ with Java 6
+ -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>animal-sniffer-maven-plugin</artifactId>
+ <version>1.8</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <id>test-1.4</id>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <configuration>
+ <signature>
+ <groupId>org.codehaus.mojo.signature</groupId>
+ <artifactId>java14</artifactId>
+ <version>1.0</version>
+ </signature>
+ <ignores>
+ <ignore>org.apache.felix.threaddump.internal.jdk5.*</ignore>
+ <ignore>org.apache.felix.threaddump.internal.jdk6.*</ignore>
+ </ignores>
+ </configuration>
+ </execution>
+ <execution>
+ <id>test-1.5</id>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <configuration>
+ <signature>
+ <groupId>org.codehaus.mojo.signature</groupId>
+ <artifactId>java15</artifactId>
+ <version>1.0</version>
+ </signature>
+ <skip>true</skip>
+ <ignores>
+ <ignore>org.apache.felix.threaddump.internal.jdk6.*</ignore>
+ </ignores>
+ </configuration>
+ </execution>
+ <execution>
+ <id>test-1.6</id>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <configuration>
+ <signature>
+ <groupId>org.codehaus.mojo.signature</groupId>
+ <artifactId>java16</artifactId>
+ <version>1.0</version>
+ </signature>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+
+</project>
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/Jdk14ThreadDumper.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/Jdk14ThreadDumper.java
new file mode 100644
index 0000000..30b0e76
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/Jdk14ThreadDumper.java
@@ -0,0 +1,90 @@
+/*
+ * 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.threaddump.internal;
+
+/**
+ * {@link ThreadDumper} implementation which relies on regular Java API.
+ */
+final class Jdk14ThreadDumper implements ThreadDumper
+{
+
+ /**
+ * {@inheritDoc}
+ */
+ public void printThreads(ThreadWriter threadWriter)
+ {
+ // first get the root thread group
+ ThreadGroup rootGroup = getRootThreadGroup();
+
+ printThreadGroup(threadWriter, rootGroup);
+
+ int numGroups = rootGroup.activeGroupCount();
+ ThreadGroup[] groups = new ThreadGroup[2 * numGroups];
+ rootGroup.enumerate(groups);
+ for (int i = 0; i < groups.length; i++)
+ {
+ printThreadGroup(threadWriter, groups[i]);
+ }
+ }
+
+ private static ThreadGroup getRootThreadGroup()
+ {
+ ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
+ while (rootGroup.getParent() != null)
+ {
+ rootGroup = rootGroup.getParent();
+ }
+ return rootGroup;
+ }
+
+ private static void printThreadGroup(ThreadWriter threadWriter, ThreadGroup group)
+ {
+ if (group != null)
+ {
+ int numThreads = group.activeCount();
+ Thread[] threads = new Thread[numThreads * 2];
+ group.enumerate(threads, false);
+ for (int i = 0; i < threads.length; i++)
+ {
+ printThread(threadWriter, threads[i]);
+ }
+ }
+ }
+
+ private static void printThread(ThreadWriter threadWriter, Thread thread)
+ {
+ if (thread != null)
+ {
+ short status = ThreadWriter.NEW;
+ if (thread.isAlive())
+ {
+ status = ThreadWriter.RUNNABLE;
+ }
+ else if (thread.isInterrupted())
+ {
+ status = ThreadWriter.TERMINATED;
+ }
+ // TODO there are missing cases here!
+
+ threadWriter.printThread(thread.getName(), thread.isDaemon(), thread.getPriority(), -1, status);
+ threadWriter.printEmptyLine();
+ }
+ }
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpActivator.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpActivator.java
new file mode 100644
index 0000000..60ef92b
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpActivator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.threaddump.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.inventory.InventoryPrinter;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * OSGi bundle activator which takes care of (un)binding the
+ * Thread Dump Inventory Printer service.
+ */
+public final class ThreadDumpActivator implements BundleActivator
+{
+
+ /**
+ * The service description.
+ */
+ private static final String SERVICE_TITLE = "Apache Felix Thread Dump";
+
+ /**
+ * The service identifier.
+ */
+ private static final String SERVICE_NAME = "threaddump";
+
+ /**
+ * The ThreadDumper ServiceRegistration reference.
+ */
+ private ServiceRegistration threadDumperRegistration;
+
+ public void start(BundleContext context)
+ {
+ final Dictionary props = new Hashtable();
+ props.put(Constants.SERVICE_VENDOR, context.getBundle().getHeaders(Constants.BUNDLE_VENDOR));
+ props.put(Constants.SERVICE_DESCRIPTION, SERVICE_TITLE);
+ props.put(InventoryPrinter.NAME, SERVICE_NAME);
+ props.put(InventoryPrinter.TITLE, SERVICE_TITLE);
+
+ threadDumperRegistration = context.registerService(InventoryPrinter.SERVICE, new ThreadDumpInventoryPrinter(),
+ props);
+ }
+
+ public void stop(BundleContext context)
+ {
+ threadDumperRegistration.unregister();
+ }
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpInventoryPrinter.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpInventoryPrinter.java
new file mode 100644
index 0000000..ec734f4
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumpInventoryPrinter.java
@@ -0,0 +1,85 @@
+/*
+ * 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.threaddump.internal;
+
+import java.io.PrintWriter;
+
+import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.InventoryPrinter;
+import org.apache.felix.threaddump.internal.jdk5.Jdk15ThreadDumper;
+import org.apache.felix.threaddump.internal.jdk6.Jdk16ThreadDumper;
+
+/**
+ * A composite {@link ThreadDumper} implementation that:
+ * <ul>
+ * <li>uses Java 6 JMX API if available;
+ * <li>
+ * <li>falls back to Java 5 JMX API if not Java 6;</li>
+ * <li>falls back to regular Java API as a last step.</li>
+ * </ul>
+ */
+final class ThreadDumpInventoryPrinter implements InventoryPrinter
+{
+
+ /**
+ * The <code>java.specification.version</code> string constant.
+ */
+ private static final String JAVA_SPECIFICATION_VERSION = "java.specification.version";
+
+ /**
+ * The <code>1.6</code> string constant.
+ */
+ private static final String JDK16_SPECIFICATION_VERSION = "1.6";
+
+ /**
+ * The <code>1.5</code> string constant.
+ */
+ private static final String JDK15_SPECIFICATION_VERSION = "1.5";
+
+ /**
+ * {@inheritDoc}
+ */
+ public void print(PrintWriter printWriter, Format format, boolean isZip)
+ {
+ ThreadDumper delegated;
+
+ final String javaSpecificationVersion = System.getProperty(JAVA_SPECIFICATION_VERSION);
+
+ // JDK 1.6, 1.7 and 1.8 have same APIs
+ if (JDK16_SPECIFICATION_VERSION.compareToIgnoreCase(javaSpecificationVersion) <= 0)
+ {
+ delegated = new Jdk16ThreadDumper();
+ }
+ else if (JDK15_SPECIFICATION_VERSION.equalsIgnoreCase(javaSpecificationVersion))
+ {
+ delegated = new Jdk15ThreadDumper();
+ }
+ else
+ {
+ // Falls back to regular Java API as a last step
+ delegated = new Jdk14ThreadDumper();
+ }
+
+ ThreadWriter threadWriter = new ThreadWriter(printWriter);
+
+ threadWriter.printHeader();
+ delegated.printThreads(threadWriter);
+ }
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumper.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumper.java
new file mode 100644
index 0000000..b0cac4d
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadDumper.java
@@ -0,0 +1,34 @@
+/*
+ * 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.threaddump.internal;
+
+/**
+ * Service that dumps current running threads in the JVM.
+ */
+public interface ThreadDumper
+{
+
+ /**
+ * Dumps current running threads in the JVM.
+ *
+ * @param writer the target writer where dumping the threads.
+ */
+ void printThreads(ThreadWriter writer);
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadWriter.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadWriter.java
new file mode 100644
index 0000000..cc4e41a
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/ThreadWriter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.threaddump.internal;
+
+import java.io.PrintWriter;
+import java.text.MessageFormat;
+import java.util.Date;
+
+/**
+ *
+ */
+public final class ThreadWriter
+{
+
+ public static final short NEW = 0;
+
+ public static final short RUNNABLE = 1;
+
+ public static final short BLOCKED = 2;
+
+ public static final short WAITING = 3;
+
+ public static final short TIMED_WAITING = 4;
+
+ public static final short TERMINATED = 5;
+
+ private static final String DATE = "{0,date,yyyy-MM-dd HH:mm:ss}";
+
+ private static final String HEADER = "Full thread dump {0} ({1} {2}):";
+
+ // nid is unknown
+ private static final String THREAD = "\"{0}\" {1}prio={2} tid=0x{3} nid=0x{4} {5,choice,0#new|1#runnable|2#waiting for monitor entry|3#in Object.wait()|4#timed_waiting|5#terminated}";
+
+ private static final String THREAD_STATUS = " java.lang.Thread.State: {0,choice,0#NEW|1#RUNNABLE|2#BLOCKED|3#WAITING (on object monitor)|4#TIMED_WAITING|5#TERMINATED}";
+
+ private static final String STACKTRACE_ELEMENT = "\tat {0}";
+
+ private final PrintWriter writer;
+
+ public ThreadWriter(PrintWriter writer)
+ {
+ this.writer = writer;
+ }
+
+ /**
+ * Full thread dump identifier
+ */
+ public void printHeader()
+ {
+ println(DATE, new Object[]
+ { new Date() });
+ println(HEADER, getSystemProperties(new String[]
+ { "java.vm.name", "java.runtime.version", "java.vm.info" }));
+ printEmptyLine();
+ }
+
+ public void printThread(String name, boolean isDaemon, long priority, long id, short status)
+ {
+ String daemon = isDaemon ? "daemon " : "";
+ println(THREAD, new Object[]
+ { name, daemon, String.valueOf(priority), Long.toHexString(id), Integer.toHexString(-1), // nid
+ new Short(status) });
+ println(THREAD_STATUS, new Object[]
+ { new Short(status) });
+ }
+
+ public void printStackTrace(StackTraceElement[] stackTrace)
+ {
+ if (stackTrace != null)
+ {
+ for (int i = 0; i < stackTrace.length; i++)
+ {
+ printStackTraceElement(stackTrace[i]);
+ }
+ }
+ }
+
+ public void printStackTraceElement(StackTraceElement element)
+ {
+ println(STACKTRACE_ELEMENT, new Object[]
+ { element });
+ }
+
+ public void printEmptyLine()
+ {
+ writer.println();
+ }
+
+ public void println(String message)
+ {
+ writer.println(message);
+ }
+
+ public void println(String pattern, Object[] arguments)
+ {
+ String result = MessageFormat.format(pattern, arguments);
+ writer.println(result);
+ }
+
+ private static Object[] getSystemProperties(String[] keys)
+ {
+ Object[] values = new Object[keys.length];
+
+ for (int i = 0; i < keys.length; i++)
+ {
+ values[i] = System.getProperty(keys[i]);
+ }
+
+ return values;
+ }
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/Jdk15ThreadDumper.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/Jdk15ThreadDumper.java
new file mode 100644
index 0000000..eb0afec
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/Jdk15ThreadDumper.java
@@ -0,0 +1,202 @@
+/*
+ * 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.threaddump.internal.jdk5;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.threaddump.internal.ThreadDumper;
+import org.apache.felix.threaddump.internal.ThreadWriter;
+
+/**
+ * {@link ThreadDumper} implementation which relies on JMX APIs in JDK1.5.
+ */
+public class Jdk15ThreadDumper implements ThreadDumper
+{
+
+ private static final String DEADLOCK = "Found {0} {0,choice,1#deadlock|1<deadlocks}.";
+
+ public void printThreads(ThreadWriter threadWriter)
+ {
+ final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+
+ // thread Infos
+ final ThreadInfo[] infos = getThreadInfo(threadMXBean);
+
+ // map thread ids to infos idx
+ final Map/* <long, int> */id2idx = new HashMap();
+ for (int i = 0; i < infos.length; i++)
+ {
+ id2idx.put(Long.valueOf(infos[i].getThreadId()), Integer.valueOf(i));
+ }
+
+ // create an array of all Thread objects indexed equivalent to Infos
+ final Thread[] threads = getThreads(id2idx);
+
+ // print the thread information
+ for (int i = 0; i < infos.length; i++)
+ {
+ printThreadInfo(threadWriter, threads[i], infos[i]);
+ threadWriter.printEmptyLine();
+ }
+
+ // dupm deadlock information
+ long[] deadlockedThreadsIds = findDeadlockedThreads(threadMXBean);
+ if (deadlockedThreadsIds != null)
+ {
+
+ List/* <List<int>> */deadlocks = new ArrayList();
+ for (int i = 0; i < deadlockedThreadsIds.length; i++)
+ {
+ Long l = Long.valueOf(deadlockedThreadsIds[i]);
+ Integer idx = (Integer) id2idx.remove(l);
+ if (idx != null)
+ {
+ List/* <int> */idxs = new ArrayList();
+ deadlocks.add(idxs);
+
+ do
+ {
+ idxs.add(idx);
+ ThreadInfo info = infos[idx.intValue()];
+ if (info != null)
+ {
+ idx = (Integer) id2idx.remove(Long.valueOf(info.getLockOwnerId()));
+ }
+ else
+ {
+ idx = null;
+ }
+ }
+ while (idx != null);
+ }
+ }
+
+ for (Iterator di = deadlocks.iterator(); di.hasNext();)
+ {
+ List idxs = (List) di.next();
+
+ threadWriter.printEmptyLine();
+ threadWriter.println("Found one Java-level deadlock:");
+ threadWriter.println("=============================");
+
+ for (Iterator ii = idxs.iterator(); ii.hasNext();)
+ {
+ Integer idx = (Integer) ii.next();
+ ThreadInfo info = infos[idx.intValue()];
+
+ printDeadlockedThreadInfo(threadWriter, info);
+ }
+
+ threadWriter.printEmptyLine();
+ threadWriter.println("Java stack information for the threads listed above:");
+ threadWriter.println("===================================================");
+
+ for (Iterator ii = idxs.iterator(); ii.hasNext();)
+ {
+ int idx = ((Integer) ii.next()).intValue();
+ printThreadInfo(threadWriter, threads[idx], infos[idx]);
+ }
+ }
+
+ threadWriter.printEmptyLine();
+ threadWriter.println(DEADLOCK, new Object[]
+ { Integer.valueOf(deadlocks.size()) });
+ threadWriter.printEmptyLine();
+
+ }
+ }
+
+ private void printThreadInfo(ThreadWriter threadWriter, Thread t, ThreadInfo info)
+ {
+ if (t == null)
+ {
+ return;
+ }
+
+ short status = ThreadStateConverter.toStatus(t.getState());
+ threadWriter.printThread(t.getName(), t.isDaemon(), t.getPriority(), t.getId(), status);
+
+ printStackTrace(threadWriter, info);
+ }
+
+ protected ThreadInfo[] getThreadInfo(ThreadMXBean threadMXBean)
+ {
+ long[] threadIds = threadMXBean.getAllThreadIds();
+ return threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
+ }
+
+ protected long[] findDeadlockedThreads(ThreadMXBean threadMXBean)
+ {
+ return threadMXBean.findMonitorDeadlockedThreads();
+ }
+
+ protected void printStackTrace(ThreadWriter threadWriter, ThreadInfo info)
+ {
+ threadWriter.printStackTrace(info.getStackTrace());
+ }
+
+ protected void printDeadlockedThreadInfo(ThreadWriter threadWriter, ThreadInfo info)
+ {
+ threadWriter.println("\"{0}\":", new Object[]
+ { info.getThreadName() });
+ threadWriter.println(" waiting to lock monitor,");
+ threadWriter.println(" which is held by \"{0}\"", new Object[]
+ { info.getLockOwnerName() });
+ }
+
+ private static Thread[] getThreads(final Map/* <long, int> */id2idx)
+ {
+ // find root thread group
+ ThreadGroup g = Thread.currentThread().getThreadGroup();
+ while (g.getParent() != null)
+ {
+ g = g.getParent();
+ }
+ int numThreads = g.activeCount();
+ Thread[] threads = new Thread[numThreads * 2];
+ int actualThreads = g.enumerate(threads);
+ if (threads.length == actualThreads)
+ {
+ // some threads have been missed !!
+ }
+
+ Thread[] result = new Thread[id2idx.size()];
+ for (int i = 0; i < threads.length; i++)
+ {
+ Thread t = threads[i];
+ if (t != null)
+ {
+ Integer idx = (Integer) id2idx.get(Long.valueOf(t.getId()));
+ if (idx != null)
+ {
+ result[idx.intValue()] = t;
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/ThreadStateConverter.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/ThreadStateConverter.java
new file mode 100644
index 0000000..99fffbf
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk5/ThreadStateConverter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.threaddump.internal.jdk5;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.threaddump.internal.ThreadWriter;
+
+/**
+ *
+ */
+public final class ThreadStateConverter
+{
+
+ private static final ThreadStateConverter INSTANCE = new ThreadStateConverter();
+
+ private final Map statuses = new HashMap();
+
+ private ThreadStateConverter()
+ {
+ register(Thread.State.NEW, ThreadWriter.NEW);
+ register(Thread.State.RUNNABLE, ThreadWriter.RUNNABLE);
+ register(Thread.State.BLOCKED, ThreadWriter.BLOCKED);
+ register(Thread.State.WAITING, ThreadWriter.WAITING);
+ register(Thread.State.TIMED_WAITING, ThreadWriter.TIMED_WAITING);
+ register(Thread.State.TERMINATED, ThreadWriter.TERMINATED);
+ }
+
+ private void register(Thread.State state, short status)
+ {
+ statuses.put(state, Short.valueOf(status));
+ }
+
+ private short getStatus(Thread.State state)
+ {
+ return ((Short) statuses.get(state)).shortValue();
+ }
+
+ public static short toStatus(Thread.State state)
+ {
+ return INSTANCE.getStatus(state);
+ }
+
+}
diff --git a/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk6/Jdk16ThreadDumper.java b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk6/Jdk16ThreadDumper.java
new file mode 100644
index 0000000..f55f460
--- /dev/null
+++ b/threaddump/src/main/java/org/apache/felix/threaddump/internal/jdk6/Jdk16ThreadDumper.java
@@ -0,0 +1,118 @@
+/*
+ * 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.threaddump.internal.jdk6;
+
+import java.lang.management.LockInfo;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+
+import org.apache.felix.threaddump.internal.ThreadDumper;
+import org.apache.felix.threaddump.internal.ThreadWriter;
+import org.apache.felix.threaddump.internal.jdk5.Jdk15ThreadDumper;
+
+/**
+ * {@link ThreadDumper} implementation which relies on JMX APIs in JDK1.5.
+ */
+public final class Jdk16ThreadDumper extends Jdk15ThreadDumper
+{
+
+ private static final String LOCKED = "\t- locked <0x{0}> (a {1})";
+
+ private static final String BLOCKED = "\t- waiting to lock <0x{0}> (a {1}) owned by \"{2}\" tid=0x{3}";
+
+ protected ThreadInfo[] getThreadInfo(ThreadMXBean threadMXBean)
+ {
+ return threadMXBean.dumpAllThreads(true, true);
+ }
+
+ protected long[] findDeadlockedThreads(ThreadMXBean threadMXBean)
+ {
+ return threadMXBean.findDeadlockedThreads();
+ }
+
+ protected void printStackTrace(ThreadWriter threadWriter, ThreadInfo info)
+ {
+ StackTraceElement[] trace = info.getStackTrace();
+ if (trace.length > 0)
+ {
+ MonitorInfo[] locks = info.getLockedMonitors();
+ int currentLock = 0;
+
+ for (int idx = 0; idx < trace.length; idx++)
+ {
+ threadWriter.printStackTraceElement(trace[idx]);
+
+ if (idx == 0)
+ {
+ LockInfo locked = info.getLockInfo();
+ if (locked != null)
+ {
+ printLockInfo(threadWriter, BLOCKED, locked, info.getLockOwnerName(), info.getLockOwnerId());
+ }
+ }
+
+ if (currentLock < locks.length && locks[currentLock].getLockedStackDepth() == idx)
+ {
+ printLockInfo(threadWriter, LOCKED, locks[currentLock]);
+ currentLock++;
+ }
+ }
+
+ // print synchronizers ...
+ LockInfo[] syncs = info.getLockedSynchronizers();
+ if (syncs != null && syncs.length > 0)
+ {
+ threadWriter.printEmptyLine();
+ threadWriter.println(" Locked ownable synchronizers:");
+ for (int j = 0; j < syncs.length; j++)
+ {
+ LockInfo sync = syncs[j];
+ printLockInfo(threadWriter, LOCKED, sync);
+ }
+ }
+ }
+ }
+
+ protected void printDeadlockedThreadInfo(ThreadWriter threadWriter, ThreadInfo info)
+ {
+ threadWriter.println("\"{0}\":", new Object[]
+ { info.getThreadName() });
+ threadWriter.println(" waiting to lock monitor {0} (object {1}, a {2}),", new Object[]
+ { "7f8a5595d180" /* ? */, Integer.toHexString(info.getLockInfo().getIdentityHashCode()),
+ info.getLockInfo().getClassName() });
+ threadWriter.println(" which is held by \"{0}\"", new Object[]
+ { info.getLockOwnerName() });
+ }
+
+ private static void printLockInfo(ThreadWriter threadWriter, String pattern, LockInfo lockInfo)
+ {
+ printLockInfo(threadWriter, pattern, lockInfo, null, -1);
+ }
+
+ private static void printLockInfo(ThreadWriter threadWriter, String pattern, LockInfo lockInfo,
+ String lockOwnerName, long lockOwnerId)
+ {
+ threadWriter.println(
+ pattern,
+ new Object[]
+ { Integer.toHexString(lockInfo.getIdentityHashCode()), lockInfo.getClassName(), lockOwnerName,
+ Long.valueOf(lockOwnerId) });
+ }
+}