Sunday, 3 November 2013

Using the try-with-resources Statement to avoid information hiding by closeQuietly implementations.

The problem with using resources in Java

Using resources in Java is often the cause of problems whenever the close() Operation is not granted to be run in all cases. The only solution to this behaviour is to always  close any used resource in the finally block of a surrounding try block.

To make this problem clear I'm going to do different implementations of a FirstLineReader interface.
public interface FirstLineReader {

    /**
     * Read the first line of the given {@link File} and close any open Stream
     * on that file after reading.
     * @param inputFile the {@link File} to read from
     * @return the first line of the {@link File}
     * @throws IOException If an I/O error occurs
     */
    String readFirstLineOfFile(File inputFile) throws IOException;
}

How this was done in a pre Java-7 environment.

The long form doing it all manually

public class PurePreJava7FirstLineReader implements FirstLineReader {
    private  final BufferedReaderFactory readerFactory;

    public PurePreJava7FirstLineReader(BufferedReaderFactory readerFactory) {
        this.readerFactory = readerFactory;
    }

    @Override
    public String readFirstLineOfFile(File inputFile) throws IOException {
        BufferedReader in = null;
        try {
            in = readerFactory.createBufferedReader(inputFile);
            return in.readLine();
        } finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e){
                    // Do handle the Exception from close Operation
                }
            }
        }
    }
}
This way to implement a reading method was hard to read since it is that chatty. It was also always hard to figure out what to do with the IOException from the close() method. You don't want to throw it since it may overwrite an previous thrown Exception from the readLine() method. So most implementations have silently swallowed the Exception.
An additional shortcoming of this solution is the need to have the "in" field declared outside the try block.

Using IOUtils from commons-io to close the Stream quietly

public class IoUtilsUsingFirstLineReader implements FirstLineReader {
    private final BufferedReaderFactory readerFactory;

    public IoUtilsUsingFirstLineReader(BufferedReaderFactory readerFactory) {
        this.readerFactory = readerFactory;
    }

    @Override
    public String readFirstLineOfFile(File inputFile) throws IOException {
        BufferedReader in = null;
        try {
            in = readerFactory.createBufferedReader(inputFile);
            return in.readLine();
        } finally {
            IOUtils.closeQuietly(in);
        }
    }
}
 This solution is a bit more readable by hiding the chatty close() implementation into the closeQuietly() service-method. But now it is hard coded to have no Exception handling on the close() method and no informations if there was an Exception.
You still need to declare the "in" field outside the try block.

The Java-7 solution by using the try-with-resources Statement

Oracle has introduced a new form of the try statement with Java-7 http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
Now it is possible to declare resources in the header of the try block. This resources must implement java.lang.AutoCloseable . On runtime the close() method of any resource is called as if it is implemented in the finally block.
There are two major differences:
  1. You do not need to declare the field of the resource outside the try block.
  2. You can access any thrown Exception from the try block.
The second difference makes this statement that useful. Starting with Java-7 the java.lang.Throwable class can now have suppressed Exceptions to handle situations that may have more than one Exception to be thrown. You can access this suppressed Exceptions by the java.lang.Throwable.html#getSuppressed() method.

public class UsingTryWithResourcesFirstLineReader implements FirstLineReader {

    private final BufferedReaderFactory readerFactory;

    public UsingTryWithResourcesFirstLineReader(BufferedReaderFactory readerFactory) {
        this.readerFactory = readerFactory;
    }

    public String readFirstLineOfFile(File inputFile) throws IOException {
        try (BufferedReader lineReader = readerFactory.createBufferedReader(inputFile)){
            return lineReader.readLine();
        }
    }
}

Possible Exception cases by using the try-with-resouces Statement

Exception from inside the try block but no Exception from the close() method

 The Exception from inside the try block is thrown to be handled outside the try block and provides an empty Throwable[] from getSuppressed().

Exception from the close() method but no Exception from inside the try block

 The Exception from the close() method is thrown to be handled outside the try block and provides an empty Throwable[] from getSuppressed().

Exception from inside the try block and another Exception from the close() method

 The Exception from inside the try block is thrown to be handled outside the try block and provides a not empty Throwable[] from getSuppressed(). The Throwable[] from getSuppressed() contains the Exception thrown by the close() method.

closeQuietly() is now a Anti-Pattern

In the past I've seen many discussions about using closeQuietly() but all the pros and cons where about readability of the code vs. traceability of runtime behaviour.
I really hope this try-with-resources statement will bring an end to all this discussions. The code using this statement is clean, readable and easy to implement. The traceability of runtime behaviour is given for all Exception cases.

I would like to hear your opinion if there are any reasons to still use closeQuietly() methods.

No comments:

Post a Comment