java - ¿Cómo puedo capturar excepciones de subprocesos de despacho de eventos(EDT)?
exception try-catch (3)
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 de EDT, así que intenté usar esto en el método main()
para manejar las excepciones de 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 etiquetas de un archivo de paquete en el constructor como este:
setTitle( bundle.getString( "MyJFrame.title" ) );
MyJFrame.title
la clave MyJFrame.title
del archivo de paquete para probar el controlador de excepciones, ¡pero no funcionó! La excepción normalmente se imprimía en el registro.
¿Estoy haciendo algo mal aquí?
El controlador de excepciones EDT no usa Thread.UncaughtExceptionHandler
. En su lugar, llama a un método con la siguiente firma:
public void handle(Throwable thrown);
Agregue eso a MyExceptionHandler
, y debería funcionar.
La "documentación" para esto se encuentra en EventDispatchThread
, que es una clase de paquete privado en java.awt
. Citando desde el javadoc para handleException()
allí:
/**
* 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.
*/
Cómo exactamente Sun esperaba que encontraras esto, no tengo idea.
Aquí hay un ejemplo completo que detecta excepciones tanto dentro como fuera de la 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.
Resumiendo lo anterior ... con Java más reciente, simplemente 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)));
Solo para obtener información adicional, en muchos casos, Throwables puede ser atrapado por UncaughtExceptionHandler de la EDT incluso en 1.5 y 1.6. Mirando el código fuente de 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 un Throwable no será capturado por la Excepción de Excepciones de la Llamada del Hilo EDT:
- El Throwable se maneja con éxito por sun.awt.exception.handler (se encontró la clase, se crearon instancias y se llamó al método de su tirador (Throwable) sin lanzar nada)
- El EDT está en un diálogo modal.
- El Throwable no es una excepción RuntimeException ni un error