javaexceptiontry-catchevent-dispatch-thread

How can I catch Event Dispatch Thread (EDT) exceptions?


I am using a class called MyExceptionHandler that implements Thread.UncaughtExceptionHandler to handle normal exceptions in my project.

As I understand this class can't catch the EDT exceptions, so I tried to use this in the main() method to handle EDT exceptions:

public static void main( final String[] args ) {
    Thread.setDefaultUncaughtExceptionHandler( new MyExceptionHandler() );  // Handle normal exceptions
    System.setProperty( "sun.awt.exception.handler",MyExceptionHandler.class.getName());  // Handle EDT exceptions
    SwingUtilities.invokeLater(new Runnable() {  // Execute some code in the EDT. 
        public void run() {
            JFrame myFrame = new JFrame();
             myFrame.setVisible( true );
        }
    });
}

But untill now it's not working. For example while initializing a JFrame I load its labels from a bundle file in the constructor like this:

setTitle( bundle.getString( "MyJFrame.title" ) );

I deleted the key MyJFrame.title from the bundle file to test the exception handler, but it didn't work! The exception was normally printed in the log.

Am I doing something wrong here?


Solution

  • The EDT exception handler doesn't use Thread.UncaughtExceptionHandler. Instead, it calls a method with the following signature:

    public void handle(Throwable thrown);
    

    Add that to MyExceptionHandler, and it should work.

    The "documentation" for this is found in EventDispatchThread, which is a package-private class in java.awt. Quoting from the javadoc for handleException() there:

    /**
     * Handles an exception thrown in the event-dispatch thread.
     *
     * <p> If the system property "sun.awt.exception.handler" is defined, then
     * when this method is invoked it will attempt to do the following:
     *
     * <ol>
     * <li> Load the class named by the value of that property, using the
     *      current thread's context class loader,
     * <li> Instantiate that class using its zero-argument constructor,
     * <li> Find the resulting handler object's <tt>public void handle</tt>
     *      method, which should take a single argument of type
     *      <tt>Throwable</tt>, and
     * <li> Invoke the handler's <tt>handle</tt> method, passing it the
     *      <tt>thrown</tt> argument that was passed to this method.
     * </ol>
     *
     * If any of the first three steps fail then this method will return
     * <tt>false</tt> and all following invocations of this method will return
     * <tt>false</tt> immediately.  An exception thrown by the handler object's
     * <tt>handle</tt> will be caught, and will cause this method to return
     * <tt>false</tt>.  If the handler's <tt>handle</tt> method is successfully
     * invoked, then this method will return <tt>true</tt>.  This method will
     * never throw any sort of exception.
     *
     * <p> <i>Note:</i> This method is a temporary hack to work around the
     * absence of a real API that provides the ability to replace the
     * event-dispatch thread.  The magic "sun.awt.exception.handler" property
     * <i>will be removed</i> in a future release.
     */
    

    How exactly Sun expected you find this, I have no idea.

    Here's a complete example which catches exceptions both on and off the EDT:

    import javax.swing.SwingUtilities;
    
    public class Test {
      public static class ExceptionHandler
                                       implements Thread.UncaughtExceptionHandler {
    
        public void handle(Throwable thrown) {
          // for EDT exceptions
          handleException(Thread.currentThread().getName(), thrown);
        }
    
        public void uncaughtException(Thread thread, Throwable thrown) {
          // for other uncaught exceptions
          handleException(thread.getName(), thrown);
        }
    
        protected void handleException(String tname, Throwable thrown) {
          System.err.println("Exception on " + tname);
          thrown.printStackTrace();
        }
      }
    
      public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
        System.setProperty("sun.awt.exception.handler",
                           ExceptionHandler.class.getName());
    
        // cause an exception on the EDT
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            ((Object) null).toString();        
          }
        });
    
        // cause an exception off the EDT
        ((Object) null).toString();
      }
    }
    

    That should do it.