// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: AbstractLockupHandler.java,v 1.16 2007/03/23 19:42:47 spyromus Exp $ // package com.salas.bb.core.actions; import com.salas.bb.core.ApplicationLauncher; import com.salas.bb.core.GlobalController; import com.salas.bb.utils.BrowserLauncher; import javax.swing.*; import java.awt.*; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.logging.Level; import java.util.logging.Logger; /** * Abstract action for handling of lockups. Has tools for gathering information and * sending it to the service. */ public abstract class AbstractLockupHandler extends AbstractAction { private static final int STACK_DUMP_SIZE_BLOCKED = 50; private static final int STACK_DUMP_SIZE_NORMAL = 3; /** * Creates lockup handler. */ protected AbstractLockupHandler() { } /** * Sends message and details to log and service. * * @param aMessage message. * @param aDetails details. */ protected void report(String aMessage, String aDetails) { if (getLogger().isLoggable(Level.INFO)) getLogger().info("Reporting..."); getLogger().severe(aMessage + "\n" + aDetails); } /** * Terminates application. * * @param aDoNormalExit TRUE to try follow correct sequence with saving model and preferences. */ protected void terminate(boolean aDoNormalExit) { if (getLogger().isLoggable(Level.INFO)) getLogger().info("Terminating..."); if (aDoNormalExit) { GlobalController controller = GlobalController.SINGLETON; if (controller != null) { controller.prepareToClose(true); } } System.exit(1); } /** * Returns logger to user for logging. * * @return logger to user for logging. */ protected abstract Logger getLogger(); /** * Collects details: version of VM, locker event, threads dump. * * @param event locker. * * @return details. */ protected String collectDetails(AWTEvent event) { StringBuffer buf = new StringBuffer(); buf.append(getVMVersion()); buf.append("\n"); if (event != null) { buf.append(event.toString()); buf.append("\n\n"); } buf.append(getProperties()); buf.append(getThreadsDump(getLogger())); return buf.toString(); } /** * Returns application properties. * * @return application properties. */ private String getProperties() { StringBuffer buf = new StringBuffer(); buf.append("Properties:\n"); buf.append("Installation ID: "); buf.append(ApplicationLauncher.getInstallationId()).append("\n"); buf.append("Installation Runs: "); buf.append(ApplicationLauncher.getInstallationRuns()).append("\n"); buf.append("JGoodies - System Exit Allowed: "); buf.append(System.getProperty("jgoodies.SystemExitAllowed")).append("\n"); buf.append("Context Path: "); buf.append(ApplicationLauncher.getContextPath()).append("\n"); buf.append("Running under JWS: "); buf.append(BrowserLauncher.isRunningUnderJWS()).append("\n"); buf.append("OS: "); buf.append(System.getProperty("os.name")).append("\n"); buf.append("\n"); buf.append("\n"); return buf.toString(); } /** * Returns VM version information. * * @return VM information. */ private String getVMVersion() { OutputStream os = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(os); sun.misc.Version.print(ps); return os.toString(); } /** * Prints information about all threads. * * @param aLogger logger to use for error reporting. * * @return threads dump. */ public static String getThreadsDump(Logger aLogger) { ThreadGroup rootTG = findRootTG(Thread.currentThread().getThreadGroup()); int activeThreads = rootTG.activeCount(); Thread[] threads = new Thread[activeThreads]; int actualThreads = rootTG.enumerate(threads, true); StringBuffer buf = new StringBuffer(); for (int i = 0; i < actualThreads; i++) { Thread thread = threads[i]; buf.append(getThreadInfo(thread)); } buf.append(getDeadlockedThreads()); return buf.toString(); } /** * Gathers info about deadlocked threads. * * @return threads summary. */ private static String getDeadlockedThreads() { StringBuffer buf = new StringBuffer(); // Get ID's of all threads ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); long[] ids = mbean.getAllThreadIds(); if (ids != null) { for (long id : ids) { ThreadInfo threadInfo = mbean.getThreadInfo(id, STACK_DUMP_SIZE_BLOCKED); buf.append("\n").append(threadInfo).append("\n"); // Dump the monitor blocks String lockName = threadInfo.getLockName(); if (lockName != null) { buf.append(" waiting for "); buf.append(lockName); buf.append(" blocked by "); buf.append(threadInfo.getLockOwnerName()); buf.append("@").append(threadInfo.getLockOwnerId()).append("\n"); } // Write the stack trace of thread StackTraceElement[] traces = threadInfo.getStackTrace(); int steps = requiresDetails(threadInfo) ? traces.length : Math.min(STACK_DUMP_SIZE_NORMAL, traces.length); for (int j = 0; j < steps; j++) { buf.append("\t").append(traces[j]).append("\n"); } } } return buf.toString(); } /** * Returns <code>TRUE</code> if the thread is reported as BLOCKED by someone or it is EDT * thread. * * @param aThreadInfo thread info to analyze. * * @return thread info. */ private static boolean requiresDetails(Object aThreadInfo) { String threadInfo = aThreadInfo.toString(); return threadInfo.indexOf("BLOCKED") != -1 || threadInfo.indexOf("AWT-EventQueue") != -1; } /** * Prints information about single thread. * * @param thread thread. * * @return thread info. */ private static String getThreadInfo(Thread thread) { return "Thread: " + thread.getName() + " Running=" + thread.isAlive() + " Daemon=" + thread.isDaemon() + " Priority=" + thread.getPriority() + "\n"; } /** * Finds root group of threads. * * @param threadGroup group of threads to find parents for. * * @return root group. */ private static ThreadGroup findRootTG(ThreadGroup threadGroup) { return (threadGroup.getParent() == null) ? threadGroup : threadGroup.getParent(); } }