Core Java

Carefully Specify Multiple Resources in Single try-with-resources Statement

One of the more useful new features of Java 7 was the introduction of the try-with-resources statement [AKA Automatic Resource Management (ARM)]. The attractiveness of the try-with-resources statement lies in its promise to “ensure that each resource is closed at the end of the statement.” A “resource” in this context is any class that implements AutoCloseable and its close() method and is instantiated inside the “try” clause of the try-with-resources statement.

The Java Language Specification [JLS] describes the try-with-resource statement in detail in Section 14.20.3 (of Java SE 10 JLS in this case). The JLS states that the “try-with-resources statement is parameterized with local variables (known as resources) that are initialized before execution of the try block and closed automatically, in the reverse order from which they were initialized, after execution of the try block.”

The JLS clearly specifies that multiple resources can be defined in relation to a single try-with-resources statement and it specifies how multiple resources are specified. Specifically it indicates that try can be followed by a “ResourceSpecification” that is composed of a “ResourceList” that is composed of one or more “Resource“s. When there is more than a single declared resource, the multiple resources are delimited by semicolon (;). This specification of multiple resources in semicolon-delimited list is important because any candidate resources not declared in this manner will not be supported (will not be closed automatically) by the try-with-resources statement.

The most likely source of errors when specifying multiple resources in a try-with-resources statement is “nesting” instantiations of “resources” instead of explicitly instantiating local variables of each of them separately with semicolons between each instantiation. Examples that follow will illustrate the difference.

Two ridiculous but illustrative classes are shown next. Each class implements AutoCloseable and so can be used in conjunction with try-with-resources and will have its close() method called automatically when used correctly with the try-with-resources statement. They are named to reflect that the OuterResource can be instantiated with an instance of the InnerResource.

InnerResource.java

package dustin.examples.exceptions;

import static java.lang.System.out;

public class InnerResource implements AutoCloseable
{
   public InnerResource()
   {
      out.println("InnerResource created.");
   }

   public InnerResource(
      final RuntimeException exceptionToThrow)
   {
      throw  exceptionToThrow != null
         ? exceptionToThrow
         : new RuntimeException("InnerResource: No exception provided.");
   }

   @Override
   public void close() throws Exception
   {
      out.println("InnerResource closed.");
   }

   @Override
   public String toString()
   {
      return "InnerResource";
   }
}

OuterResource.java

package dustin.examples.exceptions;

import static java.lang.System.out;

public class OuterResource implements AutoCloseable
{
   private final InnerResource wrappedInnerResource;

   public OuterResource(final InnerResource newInnerResource)
   {
      out.println("OuterResource created.");
      wrappedInnerResource = newInnerResource;
   }

   public OuterResource(
      final InnerResource newInnerResource,
      final RuntimeException exceptionToThrow)
   {
      wrappedInnerResource = newInnerResource;
      throw  exceptionToThrow != null
           ? exceptionToThrow
           : new RuntimeException("OuterResource: No exception provided.");
   }

   @Override
   public void close() throws Exception
   {
      out.println("OuterResource closed.");
   }

   @Override
   public String toString()
   {
      return "OuterResource";
   }
}

The two classes just defined can now be used to demonstrate the difference between correctly declaring instances of each in the same try-with-resources statement in a semicolon-delimited list and incorrectly nesting instantiation of the inner resource within the constructor of the outer resource. The latter approach doesn’t work as well as hoped because the inner resource without a locally defined variable is not treated as a “resource” in terms of invoking its AutoCloseable.close() method.

The next code listing demonstrates the incorrect approach for instantiating “resources” in the try-with-resources statement.

Incorrect Approach for Instantiating Resources in try-with-resources Statement

try (OuterResource outer = new OuterResource(
        new InnerResource(), new RuntimeException("OUTER")))
{
   out.println(outer);
}
catch (Exception exception)
{
   out.println("ERROR: " + exception);
}

When the code above is executed, the output “InnerResource created.” is seen, but no output is ever presented related to the resource’s closure. This is because the instance of InnerResource was instantiated within the call to the constructor of the OuterResource class and was never assigned to its own separate variable in the resource list of the try-with-resource statement. With a real resource, the implication of this is that the resource is not closed properly.

The next code listing demonstrates the correct approach for instantiating “resources” in the try-with-resources statement.

Correct Approach for Instantiating Resources in try-with-resources Statement

try(InnerResource inner = new InnerResource();
    OuterResource outer = new OuterResource(inner, new RuntimeException("OUTER")))
{
   out.println(outer);
}
catch (Exception exception)
{
   out.println("ERROR: " + exception);
}

When the code immediately above is executed, the output includes both “InnerResource created.” AND “InnerResource closed.” because the InnerResource instance was properly assigned to a variable within the try-with-resources statement and so its close() method is properly called even when an exception occurs during its instantiation.

The try-with-resources Statement section of the Java Tutorials includes a couple of examples of correctly specifying the resources in the try-with-resources as semicolon-delimited individual variable definitions. One example shows this correct approach with java.util.zip.ZipFile and java.io.BufferedWriter and another example shows this correct approach with an instances of java.sql.Statement and java.sql.ResultSet.

The introduction of try-with-resources in JDK 7 was a welcome addition to the language that it made it easier for Java developers to write resource-safe applications that were not as likely to leak or waste resources. However, when multiple resources are declared within a single try-with-resources statement, it’s important to ensure that each resource is individually instantiated and assigned to its own variable declared within the try‘s resource specifier list to ensure that each and every resource is properly closed. A quick way to check this is to ensure that for n AutoCloseableimplementing resources specified in the try, there should be n-1 semicolons separating those instantiated resources.

Published on Java Code Geeks with permission by Dustin Marx, partner at our JCG program. See the original article here: Carefully Specify Multiple Resources in Single try-with-resources Statement

Opinions expressed by Java Code Geeks contributors are their own.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button