BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Automatic Resource Management in Java

Automatic Resource Management in Java

Leia em Português

This item in japanese

Part of Project Coin is the ability to deal with Automatic Resource Management, or simply ARM. The purpose is to make it easier to work with external resources which need to be disposed or closed in case of errors or successful completion of a code block. Consider the following trivial file-copy operation, from the Java Bytestream Tutorial:

FileInputStream in = null;
FileOutputStream out = null;
try {
  in = new FileInputStream("xanadu.txt");
  out = new FileOutputStream("outagain.txt");
  int c;
  while ((c = in.read()) != -1)
    out.write(c);
} finally {
  if (in != null)
    in.close();
  if (out != null)
    out.close();
}

Not only is there a lot of boiler plate, but the documentation for InputStream.close() suggests that it can throw an IOException. (An exception is far more likely on the OutputStream but in any case, there needs to be an outer catch or propagation in order to successfully compile this code.)

The lexical scope of the try-catch-finally block also requires the variable for FileInputStream in and FileOutputStream out to be declared lexically outside the block itself. (If they were defined inside the try block, then they wouldn't be available inside the catch or finally blocks.)

To eliminate this boilerplate code, and to tighten the lexical scoping of the resources used inside the block, a new addition has been made to the try block in the Java language. An initial specification of the try-with-resources blocks (or ARM blocks) was made available via an ininitial implementation, which has subsequently made its way into build 105 of JDK 7.

A new interface, java.lang.AutoCloseable, has been added to the proposed API, which defines a single method close() which throws Exception. This has been retro-fitted as a parent of java.io.Closeable, which means that all InputStream and OutputStream automatically take advantage of this behaviour. In addition, FileLock and ImageInputStream have also been fitted with the AutoCloseable interface.

This permits the above example to be written as:

try (
  FileInputStream in = new FileInputStream("xanadu.txt");
  FileOutputStream out = new FileOutputStream("outagain.txt")
) {
  int c;
  while((c=in.read()) != -1 )
    out.write();
}

At the end of the try block, whether by completion normally or otherwise, both the out and in resources will have close() called automatically. Furthermore, unlike our original example, both out.close() and in.close() are guaranteed to be executed. (In the original example, had in.close() thrown an exception, then the subsequent out.close() would not have been executed.)

There are some subtle aspects to this which are worth noting:

  • As it currently stands, a trailing semi-colon is not permitted after the final resource in the resources section.
  • The resources block is separated with () rather than the more usual {}, to separate it from the existing try body. If present, it must contain one or more resource definitions.
  • Each resource definition is of the form type var = expression; you can't have general statements in the resource block.
  • The resources are implicitly final; that is, they behave as if the final modifier is present. Any attempt to assign to the resource variable is a compile-time error.
  • The resources must be a subtype of AutoCloseable; it is a compile-time error if this is not the case.
  • The order of closure is the reverse order in which the resources are defined. In other words, in the refined example, out.close() is called prior to in.close(). This permits nested streams to be built and then closed from outer-in, which makes more sense than in order (e.g. for flushing buffers before the underlying stream is closed).
  • Each block may generate n+1 exceptions, where n is the number of resources. This can occur if the main body throws an exception, and each resource closure throws an exception as well. In this situation, the body's exception will be thrown, but the others will be added to the exception's suppressed exception list. They can be accessed via getSuppressedExceptions().
  • Exception stack traces may now have code prefixed with Suppressed: in these cases; the format of the serialized Throwable is now different as well. (This may impact Java 6 clients invoking remote services on Java 7 runtimes, or vice-versa.)
  • javax.swing and java.sql do not participate in ARM at the current time; classes need to opt-in by inheriting from AutoCloseable to be used by ARM. JDBC 4.1, if part of JDK 7, will support ARM but it's not clear when this will happen.

The ability to remove boilerplate code from the Java developer's work-flow is likely to be a minor productivity boost; but although it's available in JDK 7, it will be sometime before source code can be written to take advantage of that fact. Many libraries will need to be compiled to run against Java 6; and any use of the automatic resource management will only be applicable for code compiled with -target 7 (or similar). Once Java 6 is EOL and Java 8 has been released, then using ARM will become an automatic way of working.

Rate this Article

Adoption
Style

BT