Tuesday 13 November 2012

Automatic Resource Management - how Java 7 makes coding neater

This is my second post on Automatic Resource Management. Actually that's not quite true - in the first post, I talked through an example of how to do "manual" resource management, as you would in Java 6. In that post I built up to a reasonable, but unpleasant, piece of code which used a nested try...finally block within a try...catch block to ensure that any resources we had used within our code where correctly closed, even if an exception occurred.

In this follow-up post, we'll look at how Java 7's Automatic Resource Management features simplify our code, and then we'll understand how it works, and when you can use it.

So let's first look at the code. The syntax is quite straight forward - we can remove our nested try blocks, and instead place the lines of code that open resources within parenthesis on the try line. This structure is known as "try with resources". The code will look something like this:


public void InsertRowWithARM()
{
     try (//code to open the resource goes here)
     {
           //code to run if the resource was opened
     }
     catch (ExceptionType1 e)
     {
           //handle the exceptions
     }
     catch (ExceptionType2 e)
     {
           //handle the exceptions
     }
}

So the code that we wrote in the last post now looks like this:

 public static void InsertRowWithARM()
{
     String sql = "INSERT INTO testTable (id,title) values(?,?)";
 
     try
     {
          Class.forName("org.apache.derby.jdbc.ClientDriver");
     }
     catch (ClassNotFoundException e)
     {
          System.out.println("Could not load database driver");
          return;
     }
  
     try (Connection conn = DriverManager.getConnection("jdbc:derby://localhost/testDB");
    PreparedStatement stm = conn.prepareStatement(sql);)
     {
     stm.setInt(1, 1);
     stm.setString(2, "The Best Java Course Ever");
     int results = stm.executeUpdate(); 
     System.out.println("Records added: " + results);
     }
     catch (SQLException e)
     {
        System.out.println("Something went wrong with the database");
     }
}

Note here that I have moved the code to load the database driver into its' own try...catch block - This is not just my preference... we can't put that line in the try with resources brackets (we'll see why shortly).  However I think it's neater this way as if this line fails we will want to exit the whole method, and this line doesn't actually open any resources so it sits more nicely by itself.

More importantly, we no longer have the finally block or nesting of try blocks, and we also have lost the stm.close() and conn.close() commands - the point of automatic resource management is that any resources which are opened in the try with resources statement are closed automatically so we don't need to call their close methods.

This is much neater and easy to write.

So how does it work? Firstly, any object which is created within the try with resources brackets must implement the java.lang.AutoCloseable interface. You can't instantiate any other kind of object, such as a string, within the brackets - if you try to do so, the code won't compile and the error will be "The resource type YourObjectType does not implement java.lang.AutoCloseable". This is the reason why we can't load the database driver in our example above within the try with resources brackets - it needs its own separate, standard, try catch block, as there are no resources here to worry about.

This need for objects to implement the AutoCloseable interface gives us some sense of security when we are writing our code - if a resource object hasn't been updated to make use of the facility within Java 7, we won't find ourselves misguidingly instantiating it within the try with resources brackets, thinking that it will get closed neatly.

For an object to implement the AutoCloseable interface it must implement a single method with method signature

public void close() throws Exception;

When our method with the try with resources brackets is exited, whether that is gracefully upon expected completion, or as a result of hitting a catch block because an exception occurred  the close() method of each and every object that was instantiated will be called.

If you are going to write your own code that will implement the AutoCloseable Interface then I'd suggest you take a look at the second part of this post which talks about the order in which things work, and what to bear in mind when writing your own resource components:

http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

1 comment: