Running on Java 24-ea+21-2447 (Preview)
Home of The JavaSpecialists' Newsletter

196Uncaught AWT Exceptions in Java 7

Author: Dr. Heinz M. KabutzDate: 2011-11-29Java Version: 7Category: Concurrency
 

Abstract: 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 :-)

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

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:

  1. 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 7. In 1.1 up to 1.3, we could catch uncaught exceptions this way, but not those thrown by AWT.
  2. 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 5 onwards.
  3. We can register an uncaught exception handler just for the EDT. This will also work from Java 5 onwards.
  4. 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.

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...