¿Cómo puedo detectar excepciones de subprocesos de envío de eventos (EDT)?

Estoy usando una clase llamada MyExceptionHandler que implementa Thread.UncaughtExceptionHandler para manejar las excepciones normales en mi proyecto.

Como entiendo, esta clase no puede detectar las excepciones EDT, así que traté de usar esto en el método main() para manejar las excepciones EDT:

 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 ); } }); } 

Pero hasta ahora no está funcionando. Por ejemplo, al inicializar un JFrame, cargo sus tags desde un archivo de paquete en el constructor de la siguiente manera:

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

MyJFrame.title la clave MyJFrame.title del archivo del paquete para probar el manejador de excepciones, ¡pero no funcionó! La excepción normalmente se imprimió en el registro.

¿Estoy haciendo algo mal aquí?

El controlador de excepciones EDT no utiliza Thread.UncaughtExceptionHandler . En cambio, llama a un método con la siguiente firma:

 public void handle(Throwable thrown); 

MyExceptionHandler a MyExceptionHandler , y debería funcionar.

La “documentación” para esto se encuentra en EventDispatchThread , que es una clase privada de paquete en java.awt . Citando de javadoc para handleException() allí:

 /** * Handles an exception thrown in the event-dispatch thread. * * 

If the system property "sun.awt.exception.handler" is defined, then * when this method is invoked it will attempt to do the following: * *

    *
  1. Load the class named by the value of that property, using the * current thread's context class loader, *
  2. Instantiate that class using its zero-argument constructor, *
  3. Find the resulting handler object's public void handle * method, which should take a single argument of type * Throwable, and *
  4. Invoke the handler's handle method, passing it the * thrown argument that was passed to this method. *
* * If any of the first three steps fail then this method will return * false and all following invocations of this method will return * false immediately. An exception thrown by the handler object's * handle will be caught, and will cause this method to return * false. If the handler's handle method is successfully * invoked, then this method will return true. This method will * never throw any sort of exception. * *

Note: 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 * will be removed in a future release. */

Cómo exactamente Sun espera que encuentres esto, no tengo ni idea.

Aquí hay un ejemplo completo que capta excepciones tanto dentro como fuera del 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(); } } 

Deberias hacer eso.

Solo para obtener información adicional, en muchos casos Throwables puede quedar atrapado por el UncaughtExceptionHandler de EDT incluso en 1.5 y 1.6. Mirando el código fuente para EventDispatchThread en 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; } 

De acuerdo con este código, solo hay 3 formas en que Throwable no será capturado por el UncaughtExceptionHandler del subproceso EDT:

  1. The sunrowable es manejado por sun.awt.exception.handler con éxito (la clase fue encontrada, instanciada y su método de manejo (Throwable) llamado sin tirar nada)
  2. El EDT está en un diálogo modal
  3. The Throwable no es ni RuntimeException ni Error

Resumiendo lo anterior … con Java más nuevo, puede hacer esto:

 // Log exceptions thrown on the event dispatcher thread SwingUtilities.invokeLater(() -> Thread.currentThread().setUncaughtExceptionHandler((thread, t) -> this.log.error("exception in event dispatcher thread", t))); 
    Intereting Posts