Monitors the AWT event dispatch thread for events that take longer than a certain time to be dispatched : General Event « Event « Java

Java
1. 2D Graphics GUI
2. 3D
3. Advanced Graphics
4. Ant
5. Apache Common
6. Chart
7. Class
8. Collections Data Structure
9. Data Type
10. Database SQL JDBC
11. Design Pattern
12. Development Class
13. EJB3
14. Email
15. Event
16. File Input Output
17. Game
18. Generics
19. GWT
20. Hibernate
21. I18N
22. J2EE
23. J2ME
24. JDK 6
25. JNDI LDAP
26. JPA
27. JSP
28. JSTL
29. Language Basics
30. Network Protocol
31. PDF RTF
32. Reflection
33. Regular Expressions
34. Scripting
35. Security
36. Servlets
37. Spring
38. Swing Components
39. Swing JFC
40. SWT JFace Eclipse
41. Threads
42. Tiny Application
43. Velocity
44. Web Services SOA
45. XML
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java » Event » General EventScreenshots 
Monitors the AWT event dispatch thread for events that take longer than a certain time to be dispatched
  

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */


import java.awt.*;
import java.awt.event.*;
import java.lang.management.*;
import java.util.*;
import java.util.Timer;

import javax.swing.*;

/**
 * Monitors the AWT event dispatch thread for events that take longer than
 * a certain time to be dispatched.
 * <p/>
 * The principle is to record the time at which we start processing an event,
 * and have another thread check frequently to see if we're still processing.
 * If the other thread notices that we've been processing a single event for
 * too long, it prints a stack trace showing what the event dispatch thread
 * is doing, and continues to time it until it finally finishes.
 * <p/>
 * This is useful in determining what code is causing your Java application's
 * GUI to be unresponsive.
 
 * <p>The original blog can be found here<br>  
 * <a href="http://elliotth.blogspot.com/2005/05/automatically-detecting-awt-event.html">
 * Automatically detecting AWT event dispatch thread hangs</a>
 * </p>
 *
 @author Elliott Hughes <enh@jessies.org>
 
 * Advice, bug fixes, and test cases from
 * Alexander Potochkin and Oleg Sukhodolsky.
 
 * https://swinghelper.dev.java.net/
 */
public final class EventDispatchThreadHangMonitor extends EventQueue {
    private static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor();

    // Time to wait between checks that the event dispatch thread isn't hung.
    private static final long CHECK_INTERVAL_MS = 100;

    // Maximum time we won't warn about. This used to be 500 ms, but 1.5 on
    // late-2004 hardware isn't really up to it; there are too many parts of
    // the JDK that can go away for that long (often code that has to be
    // called on the event dispatch thread, like font loading).
    private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1000;

    // Help distinguish multiple hangs in the log, and match start and end too.
    // Only access this via getNewHangNumber.
    private static int hangCount = 0;

    // Prevents us complaining about hangs during start-up, which are probably
    // the JVM vendor's fault.
    private boolean haveShownSomeComponent = false;

    // The currently outstanding event dispatches. The implementation of
    // modal dialogs is a common cause for multiple outstanding dispatches.
    private final LinkedList<DispatchInfo> dispatches = new LinkedList<DispatchInfo>();

    private static class DispatchInfo {
        // The last-dumped hung stack trace for this dispatch.
        private StackTraceElement[] lastReportedStack;
        // If so; what was the identifying hang number?
        private int hangNumber;

        // The EDT for this dispatch (for the purpose of getting stack traces).
        // I don't know of any API for getting the event dispatch thread,
        // but we can assume that it's the current thread if we're in the
        // middle of dispatching an AWT event...
        // We can't cache this because the EDT can die and be replaced by a
        // new EDT if there's an uncaught exception.
        private final Thread eventDispatchThread = Thread.currentThread();

        // The last time in milliseconds at which we saw a dispatch on the above thread.
        private long lastDispatchTimeMillis = System.currentTimeMillis();

        public DispatchInfo() {
            // All initialization is done by the field initializers.
        }

        public void checkForHang() {
            if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) {
                examineHang();
            }
        }

        // We can't use StackTraceElement.equals because that insists on checking the filename and line number.
        // That would be version-specific.
        private static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) {
            return e.getClassName().equals(className&& e.getMethodName().equals(methodName&& e.isNativeMethod() == isNative;
        }

        // Checks whether the given stack looks like it's waiting for another event.
        // This relies on JDK implementation details.
        private boolean isWaitingForNextEvent(StackTraceElement[] currentStack) {
            return stackTraceElementIs(currentStack[0]"java.lang.Object""wait"true&& stackTraceElementIs(currentStack[1]"java.lang.Object""wait"false&& stackTraceElementIs(currentStack[2]"java.awt.EventQueue""getNextEvent"false);
        }

        private void examineHang() {
            StackTraceElement[] currentStack = eventDispatchThread.getStackTrace();

            if (isWaitingForNextEvent(currentStack)) {
                // Don't be fooled by a modal dialog if it's waiting for its next event.
                // As long as the modal dialog's event pump doesn't get stuck, it's okay for the outer pump to be suspended.
                return;
            }

            if (stacksEqual(lastReportedStack, currentStack)) {
                // Don't keep reporting the same hang every time the timer goes off.
                return;
            }

            hangNumber = getNewHangNumber();
            String stackTrace = stackTraceToString(currentStack);
            lastReportedStack = currentStack;
            Log.warn("(hang #" + hangNumber + ") event dispatch thread stuck processing event for " + timeSoFar() " ms:" + stackTrace);
            checkForDeadlock();
        }

        private static boolean stacksEqual(StackTraceElement[] a, StackTraceElement[] b) {
            if (a == null) {
                return false;
            }
            if (a.length != b.length) {
                return false;
            }
            for (int i = 0; i < a.length; ++i) {
                if (a[i].equals(b[i]) == false) {
                    return false;
                }
            }
            return true;
        }

        /**
         * Returns how long this dispatch has been going on (in milliseconds).
         */
        private long timeSoFar() {
            return (System.currentTimeMillis() - lastDispatchTimeMillis);
        }

        public void dispose() {
            if (lastReportedStack != null) {
                Log.warn("(hang #" + hangNumber + ") event dispatch thread unstuck after " + timeSoFar() " ms.");
            }
        }
    }

    private EventDispatchThreadHangMonitor() {
        initTimer();
    }

    /**
     * Sets up a timer to check for hangs frequently.
     */
    private void initTimer() {
        final long initialDelayMs = 0;
        final boolean isDaemon = true;
        Timer timer = new Timer("EventDispatchThreadHangMonitor", isDaemon);
        timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS);
    }

    private class HangChecker extends TimerTask {
        @Override
        public void run() {
            synchronized (dispatches) {
                if (dispatches.isEmpty() || !haveShownSomeComponent) {
                    // Nothing to do.
                    // We don't destroy the timer when there's nothing happening
                    // because it would mean a lot more work on every single AWT
                    // event that gets dispatched.
                    return;
                }
                // Only the most recent dispatch can be hung; nested dispatches
                // by their nature cause the outer dispatch pump to be suspended.
                dispatches.getLast().checkForHang();
            }
        }
    }

    /**
     * Sets up hang detection for the event dispatch thread.
     */
    public static void initMonitoring() {
        Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE);
    }

    /**
     * Overrides EventQueue.dispatchEvent to call our pre and post hooks either
     * side of the system's event dispatch code.
     */
    @Override
    protected void dispatchEvent(AWTEvent event) {
        try {
            preDispatchEvent();
            super.dispatchEvent(event);
        finally {
            postDispatchEvent();
            if (!haveShownSomeComponent && 
                    event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) {
                haveShownSomeComponent = true;
            }
        }
    }

    private void debug(String which) {
        if (false) {
            for (int i = dispatches.size(); i >= 0; --i) {
                System.out.print(' ');
            }
            System.out.println(which);
        }
    }

    /**
     * Starts tracking a dispatch.
     */
    private synchronized void preDispatchEvent() {
        debug("pre");
        synchronized (dispatches) {
            dispatches.addLast(new DispatchInfo());
        }
    }

    /**
     * Stops tracking a dispatch.
     */
    private synchronized void postDispatchEvent() {
        synchronized (dispatches) {
            // We've finished the most nested dispatch, and don't need it any longer.
            DispatchInfo justFinishedDispatch = dispatches.removeLast();
            justFinishedDispatch.dispose();

            // The other dispatches, which have been waiting, need to be credited extra time.
            // We do this rather simplistically by pretending they've just been redispatched.
            Thread currentEventDispatchThread = Thread.currentThread();
            for (DispatchInfo dispatchInfo : dispatches) {
                if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) {
                    dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis();
                }
            }
        }
        debug("post");
    }

    private static void checkForDeadlock() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        long[] threadIds = threadBean.findMonitorDeadlockedThreads();
        if (threadIds == null) {
            return;
        }
        Log.warn("deadlock detected involving the following threads:");
        ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
        for (ThreadInfo info : threadInfos) {
            Log.warn("Thread #" + info.getThreadId() " " + info.getThreadName() 
                    " (" + info.getThreadState() ") waiting on " + info.getLockName() 
                    " held by " + info.getLockOwnerName() + stackTraceToString(info.getStackTrace()));
        }
    }

    private static String stackTraceToString(StackTraceElement[] stackTrace) {
        StringBuilder result = new StringBuilder();
        // We used to avoid showing any code above where this class gets
        // involved in event dispatch, but that hides potentially useful
        // information when dealing with modal dialogs. Maybe we should
        // reinstate that, but search from the other end of the stack?
        for (StackTraceElement stackTraceElement : stackTrace) {
            String indentation = "    ";
            result.append("\n" + indentation + stackTraceElement);
        }
        return result.toString();
    }

    private synchronized static int getNewHangNumber() {
        return ++hangCount;
    }

    public static void main(String[] args) {
        initMonitoring();
        //special case for deadlock test
        if (args.length > && "deadlock".equals(args[0])) {
            EventDispatchThreadHangMonitor.INSTANCE.haveShownSomeComponent = true;
            Tests.runDeadlockTest();
            return;
        }
        Tests.main(args);
    }

    private static class Tests {
        public static void main(final String[] args) {

            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    for (String arg : args) {
                        final JFrame frame = new JFrame();
                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        frame.setLocationRelativeTo(null);
                        if (arg.equals("exception")) {
                            runExceptionTest(frame);
                        else if (arg.equals("focus")) {
                            runFocusTest(frame);
                        else if (arg.equals("modal-hang")) {
                            runModalTest(frame, true);
                        else if (arg.equals("modal-no-hang")) {
                            runModalTest(frame, false);
                        else {
                            System.err.println("unknown regression test '" + arg + "'");
                            System.exit(1);
                        }
                        frame.pack();
                        frame.setVisible(true);
                    }
                }
            });
        }

        private static void runDeadlockTest() {
            class Locker {
                private Locker locker;

                public void setLocker(Locker locker) {
                    this.locker = locker;
                }

                public synchronized void tryToDeadlock() {
                    locker.toString();
                }

                public synchronized String toString() {
                    try {
                        Thread.sleep(50);
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return super.toString();
                }
            }
            final Locker one = new Locker();
            final Locker two = new Locker();
            one.setLocker(two);
            two.setLocker(one);

            //Deadlock expected here:
            for (int i = 0; i < 100; i++) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        one.tryToDeadlock();
                    }
                });
                two.tryToDeadlock();
            }
        }

        // If we don't do our post-dispatch activity in a finally block, we'll
        // report bogus hangs.
        private static void runExceptionTest(final JFrame frame) {
            JButton button = new JButton("Throw Exception");
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    // This shouldn't cause us to report a hang.
                    throw new RuntimeException("Nobody expects the Spanish Inquisition!");
                }
            });
            frame.add(button);
        }

        // A demonstration of nested calls to dispatchEvent caused by SequencedEvent.
        private static void runFocusTest(final JFrame frame) {
            final JDialog dialog = new JDialog(frame, "Non-Modal Dialog");
            dialog.add(new JLabel("Close me!"));
            dialog.pack();
            dialog.setLocationRelativeTo(frame);
            dialog.addWindowFocusListener(new WindowAdapter() {
                public void windowGainedFocus(WindowEvent e) {
                    System.out.println("FocusTest.windowGainedFocus");
                    // If you don't cope with nested calls to dispatchEvent, you won't detect this.
                    // See java.awt.SequencedEvent for an example.
                    sleep(2500);
                }
            });
            JButton button = new JButton("Show Non-Modal Dialog");
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    dialog.setVisible(true);
                }
            });
            frame.add(button);
        }

        // A demonstration of the problems of dealing with modal dialogs.
        private static void runModalTest(final JFrame frame, final boolean shouldSleep) {
            System.out.println(shouldSleep ? "Expect hangs!" "There should be no hangs...");
            JButton button = new JButton("Show Modal Dialog");
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (shouldSleep) {
                        sleep(2500)// This is easy.
                    }
                    JDialog dialog = new JDialog(frame, "Modal dialog"true);
                    dialog.setLayout(new FlowLayout());
                    dialog.add(new JLabel("Close this dialog!"));
                    final JLabel label = new JLabel(" ");
                    dialog.add(label);
                    dialog.pack();
                    dialog.setLocation(frame.getX() 100, frame.getY());

                    // Make sure the new event pump has some work to do, each unit of which is insufficient to cause a hang.
                    new Thread(new Runnable() {
                        public void run() {
                            for (int i = 0; i <= 100000; ++i) {
                                final int value = i;
                                EventQueue.invokeLater(new Runnable() {
                                    public void run() {
                                        label.setText(Integer.toString(value));
                                    }
                                });
                            }
                        }
                    }).start();

                    dialog.setVisible(true);

                    if (shouldSleep) {
                        sleep(2500)// If you don't distinguish different stack traces, you won't report this.
                    }
                }
            });
            frame.add(button);
        }

        private static void sleep(long ms) {
            try {
                System.out.println("Sleeping for " + ms + " ms on " + Thread.currentThread() "...");
                Thread.sleep(ms);
                System.out.println("Finished sleeping...");
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    private static class Log {
        public static void warn(String str) {
            System.out.println(str);
        }
    }
}

   
    
  
Related examples in the same category
1. Swing Event MultiListener Swing Event MultiListener
2. Multicast eventMulticast event
3. EventTest PaneEventTest Pane
4. Timer EventTimer Event
5. KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner()
6. Event Listener List
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.