|
The Java Specialists' Newsletter
Issue 196 2011-11-29
Category:
Concurrency
Java version: OpenJDK 7 Uncaught AWT Exceptions in Java 7by Dr. Heinz M. KabutzAbstract: Java 7 removes the Swing Event Dispatch Thread (EDT) hack that allowed us to specify an uncaught exception handler for the EDT using a system property sun.awt.exception.handler.
Welcome to the 196th issue of The Java(tm) Specialists' Newsletter, sent to you from the
beautiful Island of Crete in Greece. We have now lived on
this rock in the middle of the Mediterranean for the last
five years. They have been good years, for which we are
grateful. A lot of the material possessions that we had to
leave behind in South Africa when we moved here, have been
miraculously replaced with equivalent or even better goodies.
Those of you who have moved to a new continent will
understand. Emigration is not for whimps! What we miss most
are our parents, siblings and friends. Those cannot be
replaced.
Tomorrow is the 11th anniversary since I sent out my first
The Java(tm) Specialists' Newsletter. Over the years, we have had some wonderful feedback
from Java enthusiasts around the world. Think of this: There
are apparently ten million Java programmers out there. Of
those, only 50.000 are reading this publication. That is
only 0.5%. However, I think this is the TOP 0.5% of Java
developers, the creme-de-la-creme, the elite! I am honoured
that you take the time to read my writing :-)
Our New Java Design Patterns Self-Study Course is Now Available
Uncaught AWT Exceptions in Java 7
As you probably know, in Swing we have the Event Dispatch
Thread (EDT) that processes all of the GUI events. If an
exception occurs, the default behaviour is to write the stack
trace to the console.
There are several ways that we can catch the exception and
do something else instead:
-
We can create a custom ThreadGroup to manage the uncaught
exception. To make sure the thread is constructed in that
thread group, we need to bootstrap the GUI system with a
thread that already belongs that our exception managing
thread group. I described this technique in my newsletter
from 2003,Catching Exceptions in
GUI Code. This will work in versions of Java
from 1.4 up to 1.7. In 1.1 up to 1.3, we could catch
uncaught exceptions this way, but not those thrown by AWT.
-
We can register a default uncaught exception handler. This
will catch all the uncaught exceptions that happen, also in
other non-EDT threads. This will work from Java 1.5
onwards.
-
We can register an uncaught exception handler just for the
EDT. This will also work from Java 1.5 onwards.
-
The last technique was a hack that was introduced into Sun
Java in 1.2. You could specify a special class on the
command line with the property -Dsun.awt.exception.handler.
This class had to have a method called handle taking as
parameter a single Throwable. This
was removed in Java 7, as it caused issues with
modal dialogs. In the versions where this is enabled, it
takes preference over other exception handlers.
Thus, if you want to show an error dialog for any
throwable that happens in the AWT event dispatch thread or
that causes a thread to terminate, then simply set a
default uncaught exception handler.
Here is an example of a SwingExceptionHandler, that opens up
a dialog showing the entire stack trace. Most users would
probably get scared if they see all those details, so you
should probably show a human-friendly message instead.
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.lang.reflect.*;
public class SwingExceptionHandler implements
Thread.UncaughtExceptionHandler {
public void uncaughtException(final Thread t,
final Throwable e) {
if (SwingUtilities.isEventDispatchThread()) {
showMessage(t, e);
} else {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
showMessage(t, e);
}
});
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
} catch (InvocationTargetException ite) {
// not much more we can do here except log the exception
ite.getCause().printStackTrace();
}
}
}
private String generateStackTrace(Throwable e) {
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
e.printStackTrace(pw);
pw.close();
return writer.toString();
}
private void showMessage(Thread t, Throwable e) {
String stackTrace = generateStackTrace(e);
// show an error dialog
JOptionPane.showMessageDialog(findActiveOrVisibleFrame(),
stackTrace, "Exception Occurred in " + t,
JOptionPane.ERROR_MESSAGE);
}
/**
* We look for an active frame and attach ourselves to that.
*/
private Frame findActiveOrVisibleFrame() {
Frame[] frames = JFrame.getFrames();
for (Frame frame : frames) {
if (frame.isActive()) {
return frame;
}
}
for (Frame frame : frames) {
if (frame.isVisible()) {
return frame;
}
}
return null;
}
}
We can demonstrate this with a DemoFrame that has two timers
and a button. If you press the button, it causes a
NullPointerException. The first timer to fire is a Swing
timer, meaning it will run in the EDT. This timer causes an
IllegalStateException after three seconds. The second timer
runs in an ordinary thread after six seconds and causes an
IllegalArgumentException.
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
public class DemoFrame extends JFrame {
public DemoFrame() {
super("DemoFrame");
JButton button = new JButton("Cause Exception");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
throw new NullPointerException("brain is null");
}
});
add(button);
// This timer will run in the EDT
javax.swing.Timer timer1 = new javax.swing.Timer(3000,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
throw new IllegalStateException("forgotten name");
}
});
timer1.start();
// This timer will run in a normal thread
java.util.Timer timer2 = new java.util.Timer();
timer2.schedule(new TimerTask() {
public void run() {
throw new IllegalArgumentException("stop arguing!");
}
}, 6000);
}
public static void create() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
DemoFrame frame = new DemoFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(500, 200);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
One way to set up the Swing based exception handler is to
make it the default uncaught exception handler, like this:
public class SwingExceptionFrameWithDefaultHandlerTest {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(
new SwingExceptionHandler());
DemoFrame.create();
}
}
In this case, even the timer task that causes the timer
thread to die will appear as a popup dialog. If you want
only the events from the Swing thread to appear, prior
to Java7 you could use the sun.awt.exception.handler "hack",
which took preference over the other exception handling
mechanisms. Since Java7, they have disabled that "hack", so
you could register an uncaught exception handler for the
EDT specifically:
import javax.swing.*;
public class SwingExceptionFrameWithEDTHandlerTest {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Thread.currentThread().setUncaughtExceptionHandler(
new SwingExceptionHandler()
);
}
});
DemoFrame.create();
}
}
However, you need to be careful that the Event Dispatch
Thread does not get replaced with a new thread when an
exception occurs. Some older versions of Java created a
new EDT every time an exception occurred. (Update 30th
Dec 2011: Gerard Davison sent me some code to demonstrate
that on some versions of Java, the EDT is replaced when
an exception occurs. However, since Java 7, it seems that
my trick works on all the platforms that use OpenJDK.)
The safest approach would be to create a default uncaught
exception handler that filters the thread on which the
exception happened with
SwingUtilities.isEventDispatchThread().
Again, thank you very much for reading this newsletter. I
hope you enjoy it as much as I enjoy writing it :-) Kind
regards from Crete
Heinz
We now have a Facebook
page for the Java Specialists. If you've enjoyed
reading this newsletter, please take a moment to "like" our
group.
Concurrency Articles
Related Java Course
Discuss at The Java Specialist Club
|