Programmer’s Notebook: Uncaught Exception Handlers

Summary: learn how to use Thread.UncaughtExceptionHandler effectively in rich client (a.k.a. Swing) applications.

UPDATE: See One More Note on Uncaught Exception Handlers for important information about exceptions thrown from modal dialogs.

Quick Review

Java 5 introduced a new way to catch unexpected exceptions: Thread.UncaughtExceptionHandler. This is an interface with the following API:

public interface UncaughtExceptionHandler {
  void uncaughtException(Thread t, Throwable e);
}

Your job is to write a class that implements this interface. You then register your implementation on a per-thread basis, or globally for every thread. To register your handler for a single thread you do something like this:

Thread.currentThread().setUncaughtExceptionHandler(new MyExceptionHandler());

To register your handler for every thread, do this:

Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());

Notice that setDefaultUncaughtExceptionHandler(...) is a static method in the Thread class, while setUncaughtExceptionHandler(...) is a non-static method.

A Sample Swing App

Here is a very simple Swing application that shows two buttons:

Demonstration App

If you click the “EDT Exception” button, it throws an exception on the event dispatch thread (EDT). If you click the “Thread Exception” button, it starts a background thread that throws an exception.

In both cases, a stack trace appears on the console. But the EDT exception messes up repainting, so the button does not paint properly until you move your mouse around a bit. Fortunately, Swing recovers and the app continues. Here is the complete source code:

public class Main extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Main().setVisible(true);
            }
        });
    }

    // throws an exception on the event dispatch thread
    private Action edtAction = new AbstractAction("EDT Exception") {
        public void actionPerformed(ActionEvent e) {
            throw new RuntimeException("Exception generated on the EDT");
        }
    };

    // throws an exception on another thread
    private Action threadAction = new AbstractAction("Thread Exception") {
        public void actionPerformed(ActionEvent e) {
            new Thread("Sample Thread") {
                public void run() {
                    throw new RuntimeException("Exception thrown on a thread");
                }
            }.start();
        }
    };

    public Main() {
        super("Uncaught Exception Handler Demo");

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        buttonPanel.add(new JButton(edtAction));
        buttonPanel.add(new JButton(threadAction));
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Simple enough. Let’s add an uncaught exception handler.

MyExceptionHandler

Here is a simple uncaught exception handler that shows the exception message in a dialog box.

public class MyExceptionHandler implements Thread.UncaughtExceptionHandler {

    public void uncaughtException(final Thread t, final Throwable e) {
        if (SwingUtilities.isEventDispatchThread()) {
            showException(t, e);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    showException(t, e);
                }
            });
        }
    }

    private void showException(Thread t, Throwable e) {
        String msg = String.format("Unexpected problem on thread %s: %s",
                t.getName(), e.getMessage());

        logException(t, e);

        // note: in a real app, you should locate the currently focused frame
        // or dialog and use it as the parent. In this example, I'm just passing
        // a null owner, which means this dialog may get buried behind
        // some other screen.
        JOptionPane.showMessageDialog(null, msg);
    }

    private void logException(Thread t, Throwable e) {
        // todo: start a thread that sends an email, or write to a log file, or
        // send a JMS message...whatever
    }
}

To use this handler, you must register it. Simply modify the code as follows:

public static void main(String[] args) {
    Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());

    ...

Now, whenever an “unexpected” exception occurs, MyExceptionHandler kicks in and shows this lovely dialog box:

Unexpected System Problem

As an added bonus, the GUI repaints properly when this dialog displays.

This is Not Production Quality!

This is not a production-quality example! Let’s run down a list of features you will need to consider in a “real” application.

  • Event Dispatch Thread (EDT) confinement. As our UncaughtExceptionHandler shows, your uncaughtException method must ensure it is on the EDT before showing an error dialog or interacting with other Swing GUI components.
  • Dialog ownership. If you use a dialog box, your app will need to use a proper owner Window. My example simply passes null as the owner, which is unacceptable in a real app. Failure to specify an owning window causes dialog boxes to get buried, which (as we found) causes many users to simply reboot their computers. Yes, the typical computer user does not know how to Alt-Tab through Windows, nor do they know how to use task manager to kill a process. Make sure you never bury a modal dialog!
  • Consider a non-modal dialog. Many modern apps show unexpected problems in a flashing indicator in the status bar.
  • If you use a modal dialog, include an exit button. Sometimes when you hit OK, the exception occurs again, which triggers a new dialog. If modal, users are locked in and really need an exit button to abort the app.
  • If you report errors back to “home base” via email or some other mechanism, do not do it on the event dispatch thread. Spawn a background thread.
  • Make damn sure your unexpected exception hander includes try/catch blocks so you NEVER trigger yet another exception. If you do, you can get caught in infinite recursion as you display more and more errors.
  • If your dialog is already visible, do not create another one. Consider the case when a table cell renderer throws an exception. Your dialog displays, which triggers a repaint event, which causes another exception, etc. If your dialog is already visible, ignore the next exception. Otherwise you’ll get caught in an infinite loop.
  • If you are really sneaky, you can use Robot.createScreenCapture() to capture a picture of your app. Be very, very careful about privacy concerns. This is only acceptable in an internal corporate environment…even then, there are significant ethical considerations. :-)
  • Consider the possibility of an unexpected exception before your main window appears. If your window is not even visible yet and you try to create a modal dialog using that window as its parent, your dialog won’t appear.
  • If your customers are not comfortable with automated error reporting, consider adding a button that lets them choose to optionally send an email along with a screen shot.
  • See also One More Note on Uncaught Exception Handlers for important information about exceptions thrown from modal dialogs.

As you can see, there are many details to consider. Real apps are just so darn messy…


9 Responses to “Programmer’s Notebook: Uncaught Exception Handlers”

afsina Says:

very nice entry. Just last week i was almost exactly he same thing, but your tips at the end are very useful, i see that i have to add some more details. Our implementation has a bonus tough, it has “show error details” for developers to see whats going on in client test systems easily..

Matt Taylor Says:

I can see this being useful in many ways outside of Swing as well. Something to keep in the old toolbox. Thanks!

Dan Lewis Says:

Nice post. How did you find out about this feature? I consider myself pretty well informed on Java but never came across it. They should have advertised this more.

Peter Says:

This is the first I hear of this. Can be useful.
Thank you.

[...] Thread.setUncaughtExceptionHandler(UncaughtExceptionHandler). This option is also mentioned here. [...]

[...] I wrote about uncaught exception handlers on Oct 7, I omitted one important detail. If your Swing app throws an exception while displaying a modal [...]

Lucise Says:

Thank you for the tips and example!
Much appreciated indeed!

L.

This is a nice feature, but I’d like to point out on more “gotcha” that caught me out…

I had a RuntimeException that was being thrown and never caught by any of my default handlers… it just sort of disappeared. Turns out that the RuntimeException was being raised thru our code and then passed into the hands of a third party library which had an empty try-catch block. So I /thought/ my default handler would catch it, but it turned out that the 3rd party library was just failing silently.

So we had to recant on the idea that the default handlers could be used as an all-the-time replacement for try/catch blocks. Don’t go ripping out all your try/catch code just yet.

Juriy Says:

Thanks for the tips. We were thinking of a good exception handling/error reporting for our rich application. That’s really great I’ve found your entry before making production code. I guess you’ve saved me couple of hours.

Leave a Reply