I am developing a Swing application with a component performing custom painting. When I make some mistake in the painting code and an exception is thrown, the situation is hard to debug. Instead of being caught by the debugger, a popup shows with the exception information. Moreover, the thread seems to be restarted, and as the exception is a result of coding error, it is shown again and again.
When I am lucky enough to switch into the debugger (which is difficult, because more and more popups keep coming as the application gets paint requests), the debugging console shows me an exception information like:
SEVERE: Uncaught exception thrown in Thread[AWT-EventQueue-0,6,main]
.... stack follows
My application is written in Scala and I am using IntelliJ IDEA 14. My uncaught main thread exceptions are handled fine by the debugger (I have Uncaught exception
enabled for Any exception
breakpoint enabled in the Java Exception Breakpoints
), but exceptions in AWT threads are not.
I have tried installing a handler as described in this How can I detect when an Exception's been thrown globally in Java? answer, but my handler does not seem to be triggered.
I would like to achieve following (in order of importance):
- avoid the AWT thread restarting on exception, or at least prevent the popup from showing
- handle uncaught exceptions in the debugger instead of being printed in the console
(Note: while this is Scala application, I assume the behaviour would be the same for Java, hence the Java tag).
According to this link, you have to handle both regular Exception
and EDT Exception
without using the old sun.awt.exception.handler
hack (which does not work anymore since Java 7)
Here is your ExceptionHandler
public static class ExceptionHandler implements Thread.UncaughtExceptionHandler
{
public void uncaughtException(Thread thread, Throwable thrown)
{
// TODO handle your Exception here
}
}
Usage :
// Regular Exception
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
// EDT Exception
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
// We are in the event dispatching thread
Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler());
}
});
In Java
Your problem is that the exception is being thrown in another thread, the event dispatch thread. A couple of solutions:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
logger.error("Uncaught exception in thread: " + t.getName, e);
}
});
In any case you should in principle but your UI startup code in a
EventQueue.invokeLater();
or SwingUtilities.invokeLater() that directly call this.
In Scala
Your problem is that the exception is being thrown in another thread, the event dispatch thread. A couple of solutions:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
def uncaughtException(t: Thread, e: Throwable) {
logger.error("Uncaught exception in thread: " + t.getName, e)
}
})
In any case you should in principle but your UI startup code in a
EventQueue.invokeLater()
Looks like your only solution might be switching to Eclipse. :-) The other solutions require coding effort and stopping in the exception handler is not the same as stopping in the exact place where the exception is thrown.
With the following program I have no problems listening to caught/uncaught instances of RuntimeException
in Eclipse.
package lambda;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class AWTExceptionTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Test");
button.addActionListener(e -> { throw new RuntimeException(); });
frame.add(button);
frame.setSize(new Dimension(50, 50));
SwingUtilities.invokeLater(() -> frame.setVisible(true));
}
}
Here is how it looks in debug mode in Eclipse.