Johannes Thönes

Software-Developer, ThoughtWorker, Permanent Journeyman, Ruby Enthusiast, Java and Devils Advocate.

JDK 7 Features: Project Coin Exception Improvements

| Comments

In this post I’ll present the new features in JDK7/Java 7 regarding exceptions, namely “multi-catch and final rethrow” and the “try-with-resources” of Project Coin. The other four Project Coin improvements were already discussed in an post a few days ago.

So, let’s dive into the topic:

Multi-Catch and Final Rethrow

Sometimes in Java, you face the problem that a certain set of method calls may throw more than one checked exceptions. This is especially true, if you are working with reflections.

You often want to do the same thing with those exceptions. You might, for example, box them into a RuntimeException because you and your caller methods will not be able to do anything, if the method toString() on the String class is suddenly missing.

The problem might be, that the only common parent of all those checked exceptions is java.lang.Exception. But you cannot really use java.lang.Exception in a catch block, because you are garanteed to catch exceptions you don’t want to catch as well. One of those examples might be the famous java.lang.NullPointerException.

So you have to dublicate your code. This might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
  callWithReflection(arg);
} catch (NoSuchMethodException e) {
  throw new RuntimeException(e);
} catch (IllegalAccessException e) {
  throw new RuntimeException(e);
} catch (InvocationTargetException e) {
  throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
  throw new RuntimeException(e);
} catch (IOException e) {
  throw new RuntimeException(e);
}

JDK 7 will improve the situation for the specific case of reflections, by offering a common parent for all reflection related operations: ReflectiveOperationException. So the following code looks a little better - but we still have two catch blocks, so we are dublicating our code still:

1
2
3
4
5
6
7
try {
  callWithReflection(arg);
} catch (ReflectiveOperationException e) {
  throw new RuntimeException(e);
} catch (IOException e) {
  throw new RuntimeException(e);
}

This is were multi-catch comes to help. You will be allowed to catch any type of exception - you just have to declare the parameter in the catch block final and seperate the types by a pipe |.

1
2
3
4
5
try {
  callWithReflection(arg);
} catch (final ReflectiveOperationException | IOException e) {
  throw new RuntimeException(e);
}

In the exception logic, you can still use only methods on which are available on the first common parent of all the exception types you specified. But if you need more specific methods, you better want to use different catch blocks anyway.

The next features is related. Because you declare the method parameter final, the compiler knows, which checked exceptions might occur in a catch block. So if you are catching IOException and ReflectiveOperationException with a final parameter and rethrow them, you only need to declare those two on the method. So it can look like this:

1
2
3
4
5
6
7
8
public void call() throws ReflectiveOperationException, IOException {
    try {
      callWithReflection(arg);
    } catch (final Exception e) {
      logger.trace("Exception in reflection", e);
      throw e;
    }
}

You can - and should - of course combine multi-catch and final rethrow.

try-with-resources

There is a second place were exception handling can be nasty: If you have a resource, which needs to be closed. It becomes especially unhealthy if you have more than one resource to close.

Here I want to read the data from a database. The example is the same I used in the first post as an example for the diamond operator. We  have a table, were we have a patient, a medication date and a medication description.

For this we need the following resources: a database connection, a jdbc statement and a jdbc result set. So let’s try the code with JDBC 4.1 (which is new in JDK 7 as well, by the way):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void readData() {
  Connection conn = null;
  Statement statement = null;
  ResultSet resultSet = null;
  try {
    conn = DriverManager.getConnection(CONNECTION_STRING);
    statement = conn.createStatement();
    resultSet = statement.executeQuery("SELECT patient, medication_day, medication_desc FROM medications");
    while (resultSet.next()) {
      String patient = resultSet.getString("patient");
      LocalDate medication_day = LocalDate.fromDateFields(resultSet.getDate("medication_day"));
      String medication_desc = resultSet.getString("medication_desc");</p>
      System.out.println(String.format("'%s','%s','%s'", patient, medication_day, medication_desc));
    }
  } catch (SQLException ex) {
    logger.error("Error in Database", ex);
  } finally {
    try {
      resultSet.close();
      statement.close();
      conn.close();
    } catch (SQLException ex) {
      logger.error("Error in Database", ex);
    }
  }
}

The problem is, that this code does not account for all possible error which might occur. Here are some of those:

  • connection could not be opened (NullPointerException line 22)

  • statement could not be opened (NullPointerException line 22)

  • sql exception when creating the result set (NullPointer Exception in line 22)

  • exception in one of the close statements (unclosed exceptions)

So I did an - probably not perfectly correct - attempt to do a more sophisticated error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
finally {
  try {
    if (resultSet != null) {
      resultSet.close();
    }
  } catch (SQLException ex) {
    logger.error("Error in Database", ex);
  } finally {
    try {
      if (statement != null) {
        statement.close();
      }</p>
    } catch (SQLException ex) {
      logger.error("Error in Database", ex);
    } finally {
      try {
       if (conn != null) {
         conn.close();
       }
     } catch (SQLException ex) {
      logger.error("Error in Database", ex);
    }
  }
}

This is a lot of code. And the problem with it is: It might even be still wrong. You can do exceptions handling with resources correct, but  you cannot do innovation. It is a stultifying work.

And stultifying work can be done by compilers! So - with Project Coin - the compiler can do the work for you.

You have to allocate you resources inside of parentheses after the try statement. Then the compiler will care for the resources.

This made my example so easy that I could add another resource (using NIO.2 which is also new with JDK 7) to write the data into a CSV file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try (Connection conn = DriverManager.getConnection(CONNECTION_STRING);
        Statement statement = conn.createStatement();
        ResultSet resultSet = statement.executeQuery("SELECT patient, medication_day, medication_desc FROM medications");
        BufferedWriter output = Files.newBufferedWriter(target, Charset.forName("utf-8"))) {
  while (resultSet.next()) {
    String patient = resultSet.getString("patient");
    LocalDate medication_day = LocalDate.fromDateFields(resultSet.getDate("medication_day"));
    String medication_desc = resultSet.getString("medication_desc");</p>
    output.write(String.format("'%s','%s','%s'\n", patient, medication_day, medication_desc));</p>
  }
} catch (SQLException ex) {
  logger.error("Error in Database", ex);
} catch (IOException ex) {
  logger.error("Error in File Writing", ex);
}

You still need to handle the exception with the try-with-resources concept. You can either rethrow or catch them - but the system will care to close the resources you have.

One thing, which requires mentioning, is that in a try-with-resources statement it might happen, that more than one exception occur in the handling of closes. You can find those exceptions as “supressed” in the new method on Throwable getSuppressed()).

Comments