Thursday 14 March 2013

try-with-resources (ARM) introduced in Java 7

Leave a Comment
Close the resources in finally block is a well know rule for all java developers. As part of exception handling mechanism, we should always ensure to close and release the resources that is used inside the block. In a block of code which is enclosed in a try-catch block, if an exception is thrown the normal flow is altered.
try {
  //code statements
  //exception thrown here
  //lines not reached if exception thrown
} catch (Exception e) {
  //lines reached only when exception is thrown
} finally {
  //always executed
  //irrespective of an exception thrown or not
}
Look at the above code block. The best place to close and release all the resources is finally block. So what is the resource we are talking about here? Resources means database connection, file connections, etc.

Poor Structure

As stated above, we are going to close the resources in the finally block. Look at the following code, how ugly it looks like.
...
InputStream is = null;
try {
  is = new FileInputStream("test");
  is.read();
  ...
} catch (Exception e) {
  ...
} finally {
  try {
    is.close();
  } catch (IOException e) {
    e.printStackTrace();
    ...
  }
}
Explicitly the programmer has to close the resource and need to surround it with a try-catch block and all inside the finally. This is a standard repeated code in java projects.
We just need to put a try-catch and we don’t do anything to handle it there. Apache’s IOUtils introduced methods to use inside finally block such as above.
org.apache.commons.io.IOUtils has methods to suppress the exception thrown inside finally block when the close method is executed.

Automatic Resource Management (ARM)

In Java 7, we got a nice feature to manage these resources automatically. Manage is really a hype word here, all it does is close the resources. Automatic resource management, helps to close the resources automatically.
Resource instantiation should be done within try(). A parenthesis () is introduced after try statement and the resource instantiation should happen within that paranthesis as below,
try (InputStream is = new FileInputStream("test")) {
  is.read();
  ...
} catch(Exception e) {
  ...
} finally {
  //no need to add code to close InputStream
  //it's close method will be internally called
}

try-with-resources and AutoCloseable

  • All the classes cannot be used in try-with-resources. AutoCloseable is an interface used as a contract to implement try-with-resources. Classes that implements AutoCloseable must be used as a resource in try-with-resources, else we will get compilation error.
  • close is the only method in AutoCloseable and it gets automatically invoked at runtime.
  • Multiple classes can be declared within the same try as “try (Lion lion = new Lion(); Tiger tiger = new Tiger()) {…”
  • During initialization of multiple resources in ‘try’, if there is any issue then immediately in the reverse order those resources that are already initialized will be closed.
  • When multiple classes are used in ‘try’, then the close method is called in the reverse order. To understand this, check the below example.
  • try-with-resources, can have catch and finally. They work as usual and no change in it.
  • In try-with-resources on exception, before going to catch the close statements will be executed.
  • While implementing AutoCloseable, best practice is to throw specific exception and not the highest level ‘Exception’ itself. This needs to highlighted because the signature of ‘close’ in AutoCloseable throws Exception.
  • On implementation, AutoCloseable.close should not throw InterrupedException, because at runtime if this exception is suppressed it will cause issues in thread handling.
  • By specification this close method is not required to be idempotent. But still, it is best practice to implement it as idempotent. That is, even if the close method is invoked multiple times, it should act as same.
  • Closeable is an interface that extends AutoCloseable and its close method must be idempotent.
  • Resource declared in try gets instantiated just before the start of the try-block.
  • Resource is implicitly declared as final.

Custom Implementation of AutoCloseable

Let us create a custom class that implement AutoCloseable. It is not rocket science, there is only one method named ‘close’ in the interface and implementing that is sufficient.

Lion.java

package com.javapapers.exceptionhandling;
 
public class Lion implements AutoCloseable {
  public Lion() {
    System.out.println("LION is OPEN in the wild.");
  };
 
  public void hunt() throws Exception {
    throw new Exception("DeerNotFound says Lion!");
  }
 
  public void close() throws Exception {
    System.out.println("LION is CLOSED in the cage.");
    throw new Exception("Unable to close the cage!");
  }
}

Tiger.java

package com.javapapers.exceptionhandling;
 
public class Tiger implements AutoCloseable {
  public Tiger() {
    System.out.println("TIGER is OPEN in the wild.");
  };
 
  public void hunt() throws Exception {
    throw new Exception("DeerNotFound says Tiger!");
  }
 
  public void close() throws Exception {
    System.out.println("TIGER is CLOSED in the cage.");
  }
}

TryWithResourcesExample.java

package com.javapapers.exceptionhandling;
 
 
public class TryWithResourcesExample {
 
  public static void main(String[] args) {
    try (Lion lion = new Lion(); Tiger tiger = new Tiger()) {
 
      lion.hunt();
      tiger.hunt();
 
    } catch (Exception e) {
      System.out.println(e);
    } finally {
      System.out.println("Finally.");
    }
   
  }
}

Output for above example:

  1. LION is OPEN in the wild.
  2. TIGER is OPEN in the wild.
  3. TIGER is CLOSED in the cage.
  4. LION is CLOSED in the cage.
  5. java.lang.Exception: DeerNotFound!
  6. Finally.
Look at the sequence of output listed above to understand the flow of control and the order in which the close method is invoked internally.

Throwable.getSuppressed Exceptions

Read the Lion and Tiger example line by line. There is an important point to understand is hidden in it. Now go back and read Lion.java
public void close() throws Exception {
  System.out.println("LION is CLOSED in the cage.");
  throw new Exception("Unable to close the cage!");
}
  • Lion’s close throws an exception, but where is it in the output? It got suppressed. That is the concept of try-with-resources.
  • If an exception is thrown in the try block, then the control will be transferred to catch. In between the jump to catch block the close() will be internally invoked for the registered resources.
  • Inside the close(), there is a possibility for an exception while closing the resource. If an exception is thrown (as given in Lion example), then it will be suppressed and the exception that is thrown in the try block (before calling close), will be exposed.
  • So how do we know about the suppressed exception? Here you go, use the Throwable.getSuppressed get the exceptions that are suppressed in try-with-resources.
  • If there are multiple resources to be closed and an exception is thrown during closing of one exception, irrespective of that all the resources will be closed.

When try-with-resources compiled to bytecode…

As given in Java Language Specification,
try (VariableModifiersopt R Identifier = Expression ...)
    Block
following is the equivalent code of above, when converted to without try-with-resources,
{
    final VariableModifiers_minus_final R Identifier = Expression;
    Throwable #primaryExc = null;
 
    try ResourceSpecification_tail
        Block
    catch (Throwable #t) {
        #primaryExc = #t;
        throw #t;
    } finally {
        if (Identifier != null) {
            if (#primaryExc != null) {
                try {
                    Identifier.close();
                } catch (Throwable #suppressedExc) {
                    #primaryExc.addSuppressed(#suppressedExc);
                }
            } else {
                Identifier.close();
            }
        }
    }
}

0 comments:

Post a Comment