InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

Automatic Resource Management in Java

Posted by Alex Blewitt on Aug 23, 2010

Sections
Process & Practices,
Development
Topics
Java ,
Languages ,
Websphere ,
Programming ,
IBM ,
Application Servers ,
Agile in the Enterprise ,
Companies ,
Agile ,
Language ,
Change

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.

  • This article is part of a featured topic series on Agile
Hmmm..... by Clint Farleigh Posted
About freaking time!!!!!!! by Matt Giacomini Posted
Learning from others by Patrick Dreyer Posted
Re: Learning from others by James Watson Posted
Re: Learning from others by David Birdsall Posted
Re: Learning from others by Rob Elliot Posted
  1. Back to top

    Hmmm.....

    by Clint Farleigh

    I think I've seen this somewhere before... maybe about 5 years ago? :-)

  2. Back to top

    About freaking time!!!!!!!

    by Matt Giacomini

    .

  3. Back to top

    Learning from others

    by Patrick Dreyer

    One of the first lessons we as parents teach to our children is: Learn from others.
    Why does this not apply to programming languages?

    Try-catch-finally is about error handling and not about ARM.
    Thus, why complicate the try-catch-finally instead introducing "using" as .NET is using it?
    Have a look at msdn.microsoft.com/en-us/library/yh598w02%28VS.... it's simple, clear and straight forward.
    This way we can even rename "java.lang.AutoCloseable" to "java.lang.Closeable" as it's not about auto-closing but about a clearly programmed/stated behavior being part of "using".

    It's all about taking advantage of the fillets of each language.

    Note: I'm not going into a debate .NET vs. Java - I won't.

  4. Back to top

    Re: Learning from others

    by James Watson

    Try-catch-finally is about error handling and not about ARM.


    I have to agree that using the try keyword for this doesn't seem very good. I have to guess that the reason a new keyword wasn't used (such as 'using') is because the fear that existing code will not compile. Although, this did not prevent enum from being added which was more likely (I guess) to be used as a name in existing code.

  5. Back to top

    Re: Learning from others

    by David Birdsall

    what if it could be implemented using closures? Would coin's new syntax be able to do something similar to this, but without the extra boilerplate:


    Closeable.close(new FileInputStream("xanadu.txt")) {
    public void read(FileInputStream in) {
    in.read();
    }
    });

    ...where Closeable.close is a static generic method that returned an object specifically used for closing after reading. Could even be a static import, but you'd still have to define the read(T) override.

    At least it would be re-using another feature of the language (closures) and not introducing another keyword.

    When I see the using() {} statement in C# it makes me think of blocks in languages like Ruby/Groovy.

  6. Back to top

    Re: Learning from others

    by Rob Elliot

    I'd have thought it would just be a method on Closeable. So you could do something like:


    FileInputStream inputStream = new FileInputStream("xanadu.txt");
    inputStream.with({ FileInputStream in ->
    in.read();
    });


    Potentially if you made the instance variable final you wouldn't even need to pass it in as an argument to the lambda. The "with" method would call the closure in a try block and close itself cleanly in the finally block.

    It does seem odd that this should be implemented at the same time as lambdas if lambdas would allow it to be implemented cleanly without further new syntax. Perhaps there's some subtlety to it that I'm missing.

Educational Content

Evolution in Data Integration From EII to Big Data

Approaches to integrating data are changing with emergence of cloud computing.

Winning Hearts and Minds: How to Embed UX from Scratch in a Large Organization

Michele Ide-Smith presents the lessons learned in the process of introducing UX principles and techniques into a large organization through a series of small steps.

LMAX Disruptor: 100K TPS at Less than 1ms Latency

Dave Farley and Martin Thompson discuss solutions for doing low-latency high throughput transactions based on the Disruptor concurrency pattern.

Thoughts on Test Automation in Agile

Rajneesh Namta shares his thoughts, experiences, and some of the critical lessons learned while implementing software test automation on a recent Agile project.

Actor Interaction Patterns

Dale Schumacher presents several patterns of actor interaction that can be used in collaborative programs written in any language.

Scalaz: Functional Programming in Scala

Rúnar Bjarnason discusses Scalaz, a Scala library of pure data structures, type classes, highly generalized functions, and concurrency abstractions to perform functional programming in Scala.

Faster, Better, Higher – But How?

One of the main challenges when designing software architecture is considering quality attributes. Not only their design turns out to be difficult, but also the specification of these attributes.

Software Naturalism - Embracing the Real Behind the Ideal

Michael Feathers analyzes real code bases concluding that code is not nearly as beautiful as designers aspire to, discussing the everyday decisions that alter the code bit by bit.