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

089Catching Uncaught Exceptions in Java 5

Author: Dr. Heinz M. KabutzDate: 2004-05-26Java Version: 5Category: Exceptions
 

Abstract: Java 5 introduced the UncaughtExceptionHandler. We can set it globally or per thread. This helps us to manage uncaught exceptions better.

 

Welcome to the 89th edition of The Java(tm) Specialists' Newsletter. Ours is probably the most elite Java newsletter in the world, so if you are a member, you are a part of that elite! If you know of people who are really good at Java, please let them know about this newsletter.

Today was an exceptionally beautiful day in Cape Town. No wind, nice warm weather, as if it were summer. My friend Herman Lintvelt (who has authored a few newsletters) and I had a great lunch dining on huge slabs of meat and fine South African red wine, to celebrate the "good life". I am sitting outside on my balcony at 23:00 enjoying a mild evening at 25 degrees celsius :-) It will probably rain tomorrow.

A small change in my newsletter structure is that from now on, the heading will show which version of Java I was working with when I wrote the newsletter. Since The Java(tm) Specialists' Newsletter explores interesting features, we sometimes stumble across "features" that are actually bugs and that are removed in the next release. This has caused confusion in the past, especially when readers look at older newsletters.

I would like to thank all those who sent me their quotes of what they thought about the newsletter. I was touched, and have new motivation and energy to write these newsletters :-)

Last week I presented a Java Course at a South African company. During the course, one of the C++ programmers questioned me about generics in Java. I try to stay away from beta versions for production code, but curiosity got the better of me, so I tried playing with it. IntelliJ IDEA 4.0 was not too happy with the new for construct, so I tried IDEA 4.1, which worked fine. I must admit that generics take some getting used to, and changing your code is not always straightforward. For example, I could not find a way of using generics in a static context. In a future newsletter, I will write about some of the experiences of migrating my existing code to generics. A Google on "generics java" gave me approximately 50'000 hits, so I won't bore you with "yet another how-to-do Java Generics newsletter" until I have something interesting to write about them :-)

Instead, like most topics in this newsletter series, I will write about something that I discovered by chance, whilst I was glancing at the source code of Sun JDK 5 beta. Google did not reveal any newsletters about this topic, so here goes...

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

Catching Uncaught Exceptions in Java 5

In my experience, all Java projects contain poorly written exception handling code. Let's take a simple example, and make it complicated:

import java.sql.*;
import java.util.List;

/**
 * You'll have to compile with JDK 5 and use the switch
 * javac -source 1.5
 */
public class DatabaseQueryCode {
  private final Connection con;
  public DatabaseQueryCode(Connection con) {
    this.con = con;
  }
  /**
   * Take a list of Strings and execute all the queries in one
   * transaction.
   */
  public void executeQueries(List<String> queries) throws SQLException {
    con.setAutoCommit(false);
    Statement st = con.createStatement();
    for(String s : queries) { // I love this construct :-)
      st.execute(s);
    }
    con.commit();
    st.close();
    con.setAutoCommit(true);
  }
}

That code is obviously not as correct as it could have been. If we fail halfway through the method, we won't set the auto-commit to be true, so let's change that:

  public void executeQueries(List<String> queries) throws SQLException {
    con.setAutoCommit(false);
    Statement st = con.createStatement();
    try {
      for(String s : queries) {
        st.execute(s);
      }
      con.commit();
      st.close();
    } finally {
      con.setAutoCommit(true);
    }
  }

This is better, but also not ideal. If any of the queries fail, we want to roll back and still close the statement, and we want to make sure that we do not close the statement if it was not open, so let's change it again.

  public void executeQueries(List<String> queries) throws SQLException {
    con.setAutoCommit(false);
    Statement st = con.createStatement();
    try {
      for(String s : queries) {
        st.execute(s);
      }
      con.commit();
    } catch(SQLException ex) {
      con.rollback();
    } finally {
      st.close();
      con.setAutoCommit(true);
    }
  }

Good, this is better, but what happens if one of the Strings is null and we get a NullPointerException? What happens if we run out of memory and get an OutOfMemoryError? What happens if we get an OOME and at the same time the connection does not work anymore? Then the finally would cause an exception, which would mask the OOME, and make it disappear. There are lots of possibilities, and if we try to cater for all eventualities (excuse the pun) then we will go crazy trying and our code will look rather complicated.

My point with this example was not to show you how to write the perfect database exception handling. Truth is, I don't know how to make it bullet proof. Maybe water balloon proof, but not bullet proof.

So in the real world, how are exceptions handled? Frequently, exceptions are stubbed out and ignored, because the writer of the code did not know how to handle the error (and was going to go back and fix it, one day, but the project manager was breathing down his neck and the release had to go out that afternoon). This is bad, since you then do not know that something has gone awry. On the other hand, if the exception bubbles up the call stack, it may kill the thread, and you may never know that there was an exception.

I have witnessed production code do things like this (I kid you not):

    try {
      // do something
    } catch(Exception ex) {
      // log to some obscure log file, maybe
      return "";
    }

The effect was that the webpage showed empty strings as values when something went wrong with the code.

My approach to exceptions is to have a central mechanism that deals with any exceptions that I am not 100% sure of how to handle. Whenever something goes wrong, this central place is notified. However, what happens when you are using someone else's code and their threads die without warning?

An amusing example was an early version of Together/J. I enjoyed using Together/J, even though it was rather memory hungry. Instead of starting with 512MB as default maximum old generation memory size, I set it to only use 92MB. This made Together work faster and save resources. However, occasionally random threads would simply die, so you could perhaps not print anymore or some other functionality would vanish.

How Does Java 5 Help?

In newsletter 81, I described a way that you could catch unhandled exceptions in your GUI code, by starting up your GUI in a special thread group. I had assumed that this was the way that uncaught exceptions should be handled in future. The old way of catching these exceptions was to set a system property, but in the JDK code comments that was described as a temporary workaround.

If you look at the java.lang.Thread JavaDocs, you will notice some new methods that can help us, specifically setDefaultUncaughtExceptionHandler() and setUncaughtExceptionHandler(). With these two methods, you can specify an exception handler for an individual thread (setUncaughtExceptionHandler()) or you can set a default handler for all threads that do not have their own UncaughtExceptionHandler (setDefaultUncaughtExceptionHandler()).

To contrast this with the earlier newsletter, please use the Gui class of newsletter 81 and compile it together with these two classes, DefaultExceptionHandler and EvenBetterGui:

import javax.swing.*;
import java.awt.*;
// did you know that you could import inner classes?
import java.lang.Thread.*;

public class DefaultExceptionHandler implements UncaughtExceptionHandler {
  public void uncaughtException(Thread t, Throwable e) {
    // Here you should have a more robust, permanent record of problems
    JOptionPane.showMessageDialog(findActiveFrame(),
        e.toString(), "Exception Occurred", JOptionPane.OK_OPTION);
    e.printStackTrace();
  }
  private Frame findActiveFrame() {
    Frame[] frames = JFrame.getFrames();
    for (int i = 0; i < frames.length; i++) {
      if (frames[i].isVisible()) {
        return frames[i];
      }
    }
    return null;
  }
}
import javax.swing.*;

public class EvenBetterGui {
  public static void main(String[] args) {
    Thread.setDefaultUncaughtExceptionHandler(
        new DefaultExceptionHandler());
    Gui gui = new Gui();
    gui.pack();
    gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    gui.setVisible(true);
  }
}

We can now catch all unhandled exceptions by calling Thread.setDefaultUncaughtExceptionHandler() and passing in our own exception handler (subject to security manager permissions of course).

In my opinion, this is a great addition to the Java Programming Language, and I am looking forward to finding more nuggets that will convince me to switch over to Java 5 permanently. A nice resource for finding differences between JDK 1.4.2 and JDK 5 is JDiff. [I discovered after sending this newsletter that the author of JDiff is on our newsletter :-]

Thread has some other rather useful methods, such as getStackTrace() and getAllStackTraces(). What else can you do in Java 5? You can measure elapsed time in nanoseconds instead of milliseconds, which should make performance calculations more accurate (or more suspect?). Have a look at System.nanoTime().

That's all for this newsletter. I have to get to sleep before I catch yet another cold from overworking...

Kind regards

Heinz

 

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...