Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 0892004-05-26 Category: Exceptions Java version: Sun JDK 1.5.0-beta

GitHub Subscribe Free RSS Feed

Catching Uncaught Exceptions in JDK 1.5

by Dr. Heinz M. Kabutz

Welcome to the 89th edition of The Java(tm) Specialists' Newsletter. We have 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 1.5 beta. Google did not reveal any newsletters about this topic, so here goes...

NEW: We have revised our "Advanced Topics" course, covering Reflection, Java NIO, Data Structures, Memory Management and several other useful topics for Java experts to master. 2 days of extreme fun and learning. Extreme Java - Advanced Topics.

Catching Uncaught Exceptions in JDK 1.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 1.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 JDK 1.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 JDK 1.5 permanently. A nice resource for finding differences between JDK 1.4.2 and JDK 1.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 JDK 1.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

Exceptions Articles Related Java Course

Extreme Java - Concurrency and Performance for Java 8
Extreme Java - Advanced Topics for Java 8
Design Patterns
In-House Courses

© 2010-2016 Heinz Kabutz - All Rights Reserved Sitemap
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.