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

3

20

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?

Annabellannabella answered 15/12, 2010 at 9:40 Comment(1)
possible duplicate of How can I catch AWT thread exceptions in Java?Tami
M
32

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.

Mikiso answered 15/12, 2010 at 9:47 Comment(9)
@Mikiso ... I did what you mentioned, but the exception was not caught ! ... public void handle( Throwable thrown ) { System.out.println( "Exception is Catched" ); // Did bot print ! }Annabellannabella
@Brad: handle() won't be run in the example you give above because no exception should happen on the EDT there. Try JFrame frame = null; to force an exception.Mikiso
@Mikiso ... If i follow the example you mentioned, then the exception will be handled by handle(...) as you mentioned. And even if handle(...) doesn't exist, then the exception will be handled by the implemented method uncaughtException(...), then there is no need for handle(...) ! But the case that completely fails is if i initialize a JFrame, then open another JFrame from it which its title is missed in the bundle file as mentioned in my question. This exception is not handled by handle(...) nor ncaughtException(...) but only printed to the log.Annabellannabella
This is the right way to do it - unfortunately, as this depends on undocumented implementation details: there's no "official" way to set an exception handler to the EDT.Rondelet
@Brad: "And even if handle(...) doesn't exist, then the exception will be handled by the implemented method uncaughtException(...), then there is no need for handle(...) !" This is false. Uncaught exceptions on the EDT never go to a Thread.UncaughtException handler, because the EDT is not a regular thread. If your exception is taking neither path through the exception handler in the example, then you don't have an uncaught exception---it's being caught somewhere.Mikiso
@Mikiso ... After investigating my code, you are right uckelman. A part in my code caught the EDT exceptions. But for the handle(...) method, it is not required. Just setting the system property is enough : System.setProperty( "sun.awt.exception.handler",MyExceptionHandler.class.getName()); ... I'm sure of it. I've created a complete sample to test it. You can try this too. Thanks a lot for your help.Annabellannabella
@Brad: I think something changed in java.awt.EventDispatchThread since the last time I looked at it---namely, processException() rethrows exceptions if handleException() returns false, which it will if you have no handle() method in the handler you specify with the sun.awt.exception.handler property. So, in current versions of Java 6, at least, I guess you can use just a Thread.UncaughtExceptionHandler. That's good to know, but it might not work properly in older versions of Java.Mikiso
I came across this very interesting subject and the solution works fine. I can confirm in Java 7 there is no need for the handle() method, nor we can find any trace of it in the Javadoc anymore. I am now able to log the uncaught exception correctly, thanks to you both! :) However, the Exception trace is still being printed on stderr. Is there a way to avoid that?Decennary
You'd need to have a look at the Java 7 source to see what it is that's printing traces to stderr.Mikiso
R
3

Summarizing the above... with newer Java you can just do this:

// Log exceptions thrown on the event dispatcher thread
SwingUtilities.invokeLater(()
  -> Thread.currentThread().setUncaughtExceptionHandler((thread, t)
  -> this.log.error("exception in event dispatcher thread", t)));
Resentful answered 13/4, 2018 at 15:56 Comment(0)
J
0

Just for some extra info, in many cases Throwables can get caught by the EDT's UncaughtExceptionHandler even in 1.5 and 1.6. Looking at the source code for EventDispatchThread in 1.5.0_22:

private void processException(Throwable e, boolean isModal) {
    if (!handleException(e)) {
        // See bug ID 4499199.
        // If we are in a modal dialog, we cannot throw
        // an exception for the ThreadGroup to handle (as added
        // in RFE 4063022).  If we did, the message pump of
        // the modal dialog would be interrupted.
        // We instead choose to handle the exception ourselves.
        // It may be useful to add either a runtime flag or API
        // later if someone would like to instead dispose the
        // dialog and allow the thread group to handle it.
        if (isModal) {
            System.err.println(
                "Exception occurred during event dispatching:");
            e.printStackTrace();
        } else if (e instanceof RuntimeException) {
            throw (RuntimeException)e;
        } else if (e instanceof Error) {
            throw (Error)e;
        }
    }
}

private boolean handleException(Throwable thrown) {

    try {

        if (handlerClassName == NO_HANDLER) {
            return false;   /* Already tried, and failed */
        }

        /* Look up the class name */
        if (handlerClassName == null) {
            handlerClassName = ((String) AccessController.doPrivileged(
                new GetPropertyAction(handlerPropName)));
            if (handlerClassName == null) {
                handlerClassName = NO_HANDLER; /* Do not try this again */
                return false;
            }
        }

        /* Load the class, instantiate it, and find its handle method */
        Method m;
        Object h;
        try {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Class c = Class.forName(handlerClassName, true, cl);
            m = c.getMethod("handle", new Class[] { Throwable.class });
            h = c.newInstance();
        } catch (Throwable x) {
            handlerClassName = NO_HANDLER; /* Do not try this again */
            return false;
        }

        /* Finally, invoke the handler */
        m.invoke(h, new Object[] { thrown });

    } catch (Throwable x) {
        return false;
    }

    return true;
}

According to this code, there are only 3 ways a Throwable will not be caught by the EDT Thread's UncaughtExceptionHandler:

  1. The Throwable is handled by the sun.awt.exception.handler successfully (the class was found, instantiated and its handle(Throwable) method called without throwing anything)
  2. The EDT is in a modal dialog
  3. The Throwable is neither a RuntimeException nor an Error
Jumbuck answered 3/2, 2014 at 19:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.