Author: fmeschbe Date: Tue Nov 18 02:23:37 2008 New Revision: 718548 URL: http://svn.apache.org/viewvc?rev=718548&view=rev Log: SLING-737 Provide Thread Dumper extension to web console and shell
Added: incubator/sling/trunk/extensions/threaddump/ (with props) incubator/sling/trunk/extensions/threaddump/pom.xml (with props) incubator/sling/trunk/extensions/threaddump/src/ incubator/sling/trunk/extensions/threaddump/src/main/ incubator/sling/trunk/extensions/threaddump/src/main/java/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java (with props) incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java (with props) incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java (with props) incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java (with props) Propchange: incubator/sling/trunk/extensions/threaddump/ ------------------------------------------------------------------------------ --- svn:ignore (added) +++ svn:ignore Tue Nov 18 02:23:37 2008 @@ -0,0 +1,4 @@ +.classpath +.project +target +.settings Added: incubator/sling/trunk/extensions/threaddump/pom.xml URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump/pom.xml?rev=718548&view=auto ============================================================================== --- incubator/sling/trunk/extensions/threaddump/pom.xml (added) +++ incubator/sling/trunk/extensions/threaddump/pom.xml Tue Nov 18 02:23:37 2008 @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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.sling</groupId> + <artifactId>sling</artifactId> + <version>4-incubator-SNAPSHOT</version> + <relativePath>../../parent/pom.xml</relativePath> + </parent> + + <artifactId>org.apache.sling.extensions.threaddump</artifactId> + <packaging>bundle</packaging> + <version>0.1.0-incubator-SNAPSHOT</version> + + <name>Sling Thread Dumper</name> + <description> + Plugin providing plugins to the Felix Shell and Web Console to + have the current threads with the stack traces dumped. + </description> + + <scm> + <connection> + scm:svn:http://svn.apache.org/repos/asf/incubator/sling/trunk/extensions/threaddump + </connection> + <developerConnection> + scm:svn:https://svn.apache.org/repos/asf/incubator/sling/trunk/extensions/threaddump + </developerConnection> + <url> + http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump + </url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package> + org.osgi.framework, + javax.servlet.*; + org.apache.felix.shell; + org.apache.felix.webconsole;resolution:=optional + </Import-Package> + <Private-Package> + org.apache.sling.extensions.threaddump.* + </Private-Package> + <Bundle-Activator> + org.apache.sling.extensions.threaddump.Activator + </Bundle-Activator> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <excludePackageNames> + org.apache.sling.extensions.threaddump.internal + </excludePackageNames> + </configuration> + </plugin> + </plugins> + </reporting> + <dependencies> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.shell</artifactId> + <version>1.0.0</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.webconsole</artifactId> + <version>1.2.0</version> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + </dependencies> +</project> Propchange: incubator/sling/trunk/extensions/threaddump/pom.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java?rev=718548&view=auto ============================================================================== --- incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java (added) +++ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java Tue Nov 18 02:23:37 2008 @@ -0,0 +1,71 @@ +/* + * 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.sling.extensions.threaddump; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +public class Activator implements BundleActivator { + + public void start(BundleContext bundleContext) { + try { + register(bundleContext, + new String[] { "org.apache.felix.shell.Command" }, + new ThreadDumpCommand(), null); + } catch (Throwable t) { + // shell service might not be available, don't care + } + try { + ThreadDumperPanel tdp = new ThreadDumperPanel(); + tdp.activate(bundleContext); + + Dictionary<String, Object> properties = new Hashtable<String, Object>(); + properties.put("felix.webconsole.label", tdp.getLabel()); + + register(bundleContext, new String[] { "javax.servlet.Servlet", + "org.apache.felix.webconsole.ConfigurationPrinter" }, tdp, + properties); + } catch (Throwable t) { + // shell service might not be available, don't care + } + } + + public void stop(BundleContext bundleContext) { + } + + private void register(BundleContext context, String[] serviceNames, + Object service, Dictionary<String, Object> properties) { + + // ensure properties + if (properties == null) { + properties = new Hashtable<String, Object>(); + } + + // default settings + properties.put(Constants.SERVICE_DESCRIPTION, "Thread Dumper (" + + serviceNames[0] + ")"); + properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation"); + + context.registerService(serviceNames, service, properties); + } +} Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/Activator.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java?rev=718548&view=auto ============================================================================== --- incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java (added) +++ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java Tue Nov 18 02:23:37 2008 @@ -0,0 +1,164 @@ +/* + * 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.sling.extensions.threaddump; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URL; +import java.net.URLClassLoader; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.felix.webconsole.AbstractWebConsolePlugin; +import org.apache.felix.webconsole.ConfigurationPrinter; + +class BaseThreadDumper { + + boolean printThread(PrintWriter pw, long threadId, boolean withStackTrace) { + // first get the root thread group + ThreadGroup rootGroup = getRootThreadGroup(); + int numThreads = rootGroup.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + rootGroup.enumerate(threads); + + for (Thread thread : threads) { + if (thread != null && thread.getId() == threadId) { + printThread(pw, thread, withStackTrace); + return true; + } + } + + return false; + } + + void printThreads(PrintWriter pw, boolean withStackTrace) { + // first get the root thread group + ThreadGroup rootGroup = getRootThreadGroup(); + + printThreadGroup(pw, rootGroup, withStackTrace); + + int numGroups = rootGroup.activeGroupCount(); + ThreadGroup[] groups = new ThreadGroup[2 * numGroups]; + rootGroup.enumerate(groups); + for (int i = 0; i < groups.length; i++) { + printThreadGroup(pw, groups[i], withStackTrace); + } + + pw.println(); + } + + private ThreadGroup getRootThreadGroup() { + ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + return rootGroup; + } + + private void printThreadGroup(PrintWriter pw, ThreadGroup group, boolean withStackTrace) { + 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(']'); + + pw.println(info); + + int numThreads = group.activeCount(); + Thread[] threads = new Thread[numThreads * 2]; + group.enumerate(threads, false); + for (int i = 0; i < threads.length; i++) { + printThread(pw, threads[i], withStackTrace); + } + + pw.println(); + } + } + + private void printThread(PrintWriter pw, Thread thread, boolean withStackTrace) { + if (thread != null) { + StringBuffer info = new StringBuffer(); + info.append(" Thread ").append(thread.getId()); + info.append('/').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(']'); + + pw.println(info); + + if (withStackTrace) { + printClassLoader(pw, thread.getContextClassLoader()); + printStackTrace(pw, thread.getStackTrace()); + pw.println(); + } + } + } + + private 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 void printStackTrace(PrintWriter pw, StackTraceElement[] stackTrace) { + pw.println(" Stacktrace"); + if (stackTrace == null || stackTrace.length == 0) { + pw.println(" -"); + } else { + for (StackTraceElement stackTraceElement : stackTrace) { + pw.print(" "); + pw.println(stackTraceElement); + } + } + } +} Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/BaseThreadDumper.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java?rev=718548&view=auto ============================================================================== --- incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java (added) +++ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java Tue Nov 18 02:23:37 2008 @@ -0,0 +1,92 @@ +/* + * 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.sling.extensions.threaddump; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.LinkedList; +import java.util.StringTokenizer; + +import org.apache.felix.shell.Command; + +public class ThreadDumpCommand extends BaseThreadDumper implements Command { + + private static final String CMD_NAME = "threads"; + + private static final String OPT_STACK = "-s"; + + public String getName() { + return CMD_NAME; + } + + public String getShortDescription() { + return "dumps the JVM threads"; + } + + public String getUsage() { + return CMD_NAME + " [" + OPT_STACK + "] <id> ..."; + } + + public void execute(String command, PrintStream out, PrintStream err) { + + // cut off leading command name + if (command.startsWith(CMD_NAME)) { + command = command.substring(CMD_NAME.length()); + } + + boolean longListing = false; + LinkedList<Long> threadIds = new LinkedList<Long>(); + + StringTokenizer tokener = new StringTokenizer(command, ", \t"); + while (tokener.hasMoreTokens()) { + String token = tokener.nextToken().trim(); + if (OPT_STACK.equals(token)) { + longListing = true; + } else { + try { + long threadId = Long.parseLong(token); + threadIds.add(threadId); + } catch (NumberFormatException nfe) { + noSuchThread(err, token); + } + } + } + + PrintWriter pw = new PrintWriter(out); + + if (threadIds.isEmpty()) { + printThreads(pw, longListing); + } else { + while (!threadIds.isEmpty()) { + Long threadId = threadIds.removeFirst(); + if (!printThread(pw, threadId, longListing)) { + noSuchThread(err, threadId); + } + } + } + + pw.flush(); + } + + private void noSuchThread(PrintStream err, Object threadId) { + err.println("No such Thread: " + threadId); + err.flush(); + } + +} Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumpCommand.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java?rev=718548&view=auto ============================================================================== --- incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java (added) +++ incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java Tue Nov 18 02:23:37 2008 @@ -0,0 +1,93 @@ +/* + * 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.sling.extensions.threaddump; + +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; + +import javax.servlet.Servlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.felix.webconsole.AbstractWebConsolePlugin; +import org.apache.felix.webconsole.ConfigurationPrinter; + +public class ThreadDumperPanel extends AbstractWebConsolePlugin implements + Servlet, ConfigurationPrinter { + + private static final String LABEL = "threads"; + + private static final String TITLE = "Threads"; + + private BaseThreadDumper baseThreadDumper = new BaseThreadDumper(); + + // ---------- AbstractWebConsolePlugin API + + @Override + public String getLabel() { + return LABEL; + } + + @Override + public String getTitle() { + return TITLE; + } + + @Override + protected void renderContent(HttpServletRequest request, + HttpServletResponse response) throws IOException { + PrintWriter pw = response.getWriter(); + + pw.println("<pre>"); + pw.println("</pre>"); + + pw.println( "<table class='content' cellpadding='0' cellspacing='0' width='100%'>" ); + + pw.println( "<tr class='content'>" ); + pw.println( "<th class='content container'>" + getTitle() + "</th>" ); + pw.println( "</tr>" ); + + pw.println( "<tr class='content'>" ); + pw.println( "<td class='content'>" ); + pw.println( "<pre>" ); + + pw.println( "*** Date: " + + SimpleDateFormat.getDateTimeInstance( SimpleDateFormat.LONG, SimpleDateFormat.LONG, Locale.US ).format( + new Date() ) ); + pw.println(); + + baseThreadDumper.printThreads(pw, true); + + pw.println( "</pre>" ); + pw.println( "</td>" ); + pw.println( "</tr>" ); + pw.println( "</table>" ); + } + + // ---------- ConfigurationPrinter + + public void printConfiguration(PrintWriter pw) { + pw.println("*** Threads Dumps:"); + baseThreadDumper.printThreads(pw, true); + } +} Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/sling/trunk/extensions/threaddump/src/main/java/org/apache/sling/extensions/threaddump/ThreadDumperPanel.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url