Squeezing max from the ‘try/finally’ blocks

on Aug 23, 11 • by Mikhail Ksenzov • with 3 Comments

I often hear that closing resources properly is way too verbose in Java, especially considering that resource freeing methods such as ‘close()’ are often throwing some type of an exception. However, if you handle resources properly it might turn out to be less of a burden than one might think. Let’s...

Home » General Coding » Squeezing max from the ‘try/finally’ blocks

I often hear that closing resources properly is way too verbose in Java, especially considering that resource freeing methods such as ‘close()’ are often throwing some type of an exception. However, if you handle resources properly it might turn out to be less of a burden than one might think. Let’s start with the following snippet, where I use an SQL driver to retrieve the list of “codes” matching the given “id”:

09 List<String> requestCodes(String dbUrl, String id) {
10   List<String> result = new ArrayList<String>();
11   try {
12     Connection conn = DriverManager.getConnection(dbUrl);
13     PreparedStatement stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
14     stmt.setString(1, id);
15     ResultSet rs = stmt.executeQuery();
16     while (rs.next()) {
17       result.add(rs.getString("code"));
18     }
19   } catch (SQLException e) {
20      e.printStackTrace();
21   }
22   return result;
23 }

The problem with the code above is that it allocates SQL server resources but fails to properly release them. More specifically:

  • Line 12: SQL connection ‘conn’ is not closed on exit.
  • Line 13: SQL object ‘stmt’ is not closed on exit.
  • Line 16: SQL object ‘rs’ is not closed on exit.

The next snippet illustrates how one can fix the defects listed above:

09 List<String> requestCodes(String dbUrl, String id) {
10   List<String> result = new ArrayList<String>();
11   Connection conn = null;
12   PreparedStatement stmt = null;
13   ResultSet rs = null;
14   try {
15     conn = DriverManager.getConnection(dbUrl);
16     stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
17     stmt.setString(1, id);
18     rs = stmt.executeQuery();
19     while (rs.next()) {
20       result.add(rs.getString("code"));
21     }
22   } catch (SQLException e) {
23     e.printStackTrace();
24   } finally {
25     if (rs != null) {
26       try {
27         rs.close();          // close() throws an exception...
28       } catch (SQLException e) {
29         e.printStackTrace(); // ...have to catch it to free 'stmt'
30       }
31     }
32     if (stmt != null) {
33       try {
34         stmt.close();        // again, close() throws an exception...
35       } catch (SQLException ignore) {
36         e.printStackTrace(); // ...have to catch it to free 'conn'
37       }
38     }
39     if (conn != null) {
30       try {
41         conn.close();
42       } catch (SQLException ignore) {
43         e.printStackTrace();
44       }
45     }
46   }
47   return result;
48 }

The code above is correct but extremely verbose. However, it can be improved without sacrifices in semantics…

Tip #1: It is better to allocate a resource before the ‘try/finally’ block, not inside it. Let’s start with the following code:

Connection conn = null;
try {
  conn = DriverManager.getConnection(dbUrl);
  // use conn
} finally {
  if (conn != null) {
    conn.close();
  }
}

can be rewritten as:

Connection conn = DriverManager.getConnection(dbUrl);
try {
  // use conn
} finally {
  conn.close();
}

Tip #2: Use nested ‘try/finally’ blocks if you allocate a sequence of resources. Let’s start with a snippet:

try {
  Connection conn = DriverManager.getConnection(dbUrl);
  PreparedStatement stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
  try {
    // use conn
    // use stmt
  } finally {
    try {
      conn.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
    try {
       stmt.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
} catch (SQLException e) {
  e.printStackTrace();
}

How many problems have you noticed in the snippet above? I found three:

  • Allocation of resource ‘stmt’ can throw an exception before we enter the outer ‘try/catch/finally’. If happens ‘conn’ will never be freed.
  • We duplicate code for the SQLException handling. We were lucky to have only one line of code replicated, but it in general cases exception handling can be a bit more involved that we see here…
  • The order of resource allocation does not match the order of deallocation: here the order of deallocation should be reversed to be correct.

The only robust way to handle resource allocation/deallocation and to address the issues listed above is to use nested try/finally blocks:

try {
  Connection conn = DriverManager.getConnection(dbUrl);
  try {
    // use conn
    PreparedStatement stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
    try {
      // use stmt
    } finally {
      stmt.close();
    }
  } finally {
    conn.close();
  }
} catch (SQLException e) {
  e.printStackTrace();
}

Let’s apply tips #1 and #2 to our original method and fix the resource leaks on lines 12, 13, 16:

09 List<String> requestCodes(String dbUrl, String id) {
10   List<String> result = new ArrayList<String>();
11   try {
12     Connection conn = DriverManager.getConnection(dbUrl);
13     try {
14       PreparedStatement stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
15       try {
16         stmt.setString(1, id);
17         ResultSet rs = stmt.executeQuery();
18         try {
19           while (rs.next()) {
20             result.add(rs.getString("code"));
21           }
22         } finally {
23           rs.close();
24         }
25       } finally {
26          stmt.close();
27       }
28     } finally {
29       conn.close();
30     }
31   } catch (SQLException e) {
32     e.printStackTrace();
33   }
34   return result;
35 }

This is way shorter than the original solution!

Tip #3: If after applying tip #2 you feel that all your code drifted way too close to the right page margin it means that you probably have too much nested ‘try/finally’ blocks and that is time to check if you actually want to have all the resources allocated at the same time. Chances are that you do not need them all; otherwise use the Extract Method refactoring pattern to move out some of the resource access logic.

Tip #4: Know specific behavior of resources you are dealing with. While tips #1 – #3 provide a robust and compact approach for dealing with resource allocation/deallocation in general, in certain cases you can make code even more compact. In our example: Statement.close() closes its current ResultSet object if one exists. Likewise Connections.close() releases JDBC resources. It means that if you deal specifically with JDBC it would be sufficient to close the ‘parent’ resource to be sure that all ‘subresources’ will be properly released:

09 List<String> requestCodes(String dbUrl, String id) {
10   List<String> result = new ArrayList<String>();
11   try {
12     Connection conn = DriverManager.getConnection(dbUrl);
13     try {
14       PreparedStatement stmt = conn.prepareStatement("SELECT * FROM customers WHERE id = ?");
15       stmt.setString(1, id);
16       ResultSet rs = stmt.executeQuery();
17       while (rs.next()) {
18         result.add(rs.getString("code"));
19       }
20     } finally {
21       conn.close();
22     }
23   } catch (SQLException e) {
24     e.printStackTrace();
25   }
26   return result;
27 }

Related Posts

3 Responses to Squeezing max from the ‘try/finally’ blocks

  1. Johnie, I agree, if your resources implement interface Closeable you can use one of those utility methods you have mentioned. However sometime you have to deal with some resources that are not instances of Closeable. Pretty much all of non SDK resources would fit the description:

    * DirContexts in JNDI
    * IMAP and Pop3Store in Java Mail API
    * etc… (as of now our tool detects 19 different RLK types)

    In this case one can create a similar ‘closeQuetly’ method. However, in my view the whole ‘closeQuietly’ approach has a few drawbacks:

    * It does not enforce closing of resources in the correct order (i.e reverse of allocation order)
    * have seen a lot of home grown ‘closeQuietly’ methods (create for non-Closeable resources) that were plainly wrong in the sense that they did not guarantee closing .

    Interesting enough that Java 7 will introduce a new try-with-resource block which will make life so much easier, but it will take some time before Java 7 will become a new standard.

  2. Johnie says:

    Why not use one of the numerous utilities that handle Java closeable elegantly?

    For example:
    http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/io/Closeables.html
    http://commons.apache.org/io/apidocs/org/apache/commons/io/IOUtils.html#closeQuietly(java.io.Closeable)

    Change your example to:

    Connection conn = DriverManager.getConnection(dbUrl);
    try {
    // use conn
    } finally {
    Closeables.closeQuietly(conn);
    }

    This is a lot less work than all the try/catch

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Scroll to top