Automatic Resource Management

Allen, Chris Chris.Allen at nationalcity.com
Mon Apr 13 09:26:18 PDT 2009


I've looked at Automatic Resource Management proposal, and written this
email and then sat on it for several days, and it seems that the current
proposal, adding a Disposable interface, is arbitrarily restrictive and
that by using an attribute combined with a signature matching we could
get better bang for our buck.

We already use annotations to describe classes such as persistable or
deprecated, why not as a resource?  I've read the objections on this
list to using an annotation, and the interface proposal does list common
resources such as Streams and SQL constructs, but while the interface
would probably be the preferred way to add this if we were designing the
language from scratch, we already have a huge collection of code out
there where a typical resource pattern is already used - but where
close() is not the disposal method.  People have pointed out locks for
instance, but were told that locks were beyond the scope of this small
change.

Why?

We're looking to make a small change to Java that will make source code
easier to read, write and maintain while making the new constructs easy
to recognize.

The try() syntax that's been proposed seems to meet that criteria,
although I would argue against multiple resources being opened in one
try statement (that's not easy to read) and propose this two-step
processes to find the method called to dispose the target of the try:

1. If the instance's class or one of it parent classes or interfaces has
an attribute of @Disposable the method named in the attributes "value"
value is the name of the method to call.  The default value for "value"
is "close".

So:

    @Disposable("close")
    public abstract java.io.InputStream { ...

    or

    @Disposable
    public abstract java.io.InputStream { ...

    Would call close() to dispose of an InputStream.

where as

    @Disposable("unlock")
   public interface java.util.concurrent.locks.Lock { ...

    Would call unlock() to dispose of a Lock.

2. If the instance's class and all of it's parent classes and interfaces
has no @Disposable attribute, and a instance method matching this
signature 'public void close()' exists, regardless of exception thrown,
it can also be used as a resource.

So:

public void MyClass {

   public void close() throws MyOwnException;

}

Could still be used even without the @Disposable attribute even if it's
compiled in some jar file that I don't have the source for.

I understand the parallelism that we'd like to achieve with the
interface decisions made with Iterator.  There a very small change was
introduced in by the addition of an interface, and voila for-each loops.
But we're talking a much broader range of classes here, including
various 3rd party binaries that have classes that are resources.  With
Iterator there was a very small set of classes affected - mainly
Collection and arrays, but resources could be database connections, text
files, locks, threads, servers, memory, scripting engine or any number
of other things.

Besides, the Disposable interface ends up basically being a marker
interface.  Since no one is going to go around throwing Exception from
the Disposable interface, you're always going to have to narrow the list
of thrown exceptions to something like java.sql.ResultSet so that you
can deal with SQLException instead.  I can't think of a situation where
you're going to be using a Disposable objects directly (other than
perhaps some resource manager).  So Disposable degrades into a marker
interface, and when I think marker interface, I squirm a bit.

Here's how the annotation might be described.

@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.TYPE})
public @interface java.lang.Disposable {
  String value() default "close";
}

The compiler simply picks the correct dispose method (or methods if
parent classes define additional dispose methods) at compile time so
this has no effect on runtime speed.  Remember, the whole idea is to
change:

A a = new A();
try {
    B b = new B();
    try {   
        a.doSomethingTo(b);
    }
    finally {
        b.dispose();
    }
}
finally {
   a.dispose();
}

into an easier to read, easier to understand, easier to write:

try (A a = new A()) {
    try (B b = new B()) {
        a.doSomethingTo(b);
    }
}

Why should it be limited to classes that have a close() method and have
to have a Disposable interface, too?

As an aside, one of the ideas that needs to be promoted in any of these
proposals, is the idea that the exception thrown during the main life of
the resource receives priority over any exception thrown by the dispose
method(s).  This is a fantastic idea, but sounds fishy at first glance.
By responsibly closing your resource even during some catastrophic event
you can actually erase what the catastrophic event was and waste many
hours of debugging.  This idea needs to be made clear: the proposal
doesn't not hide the dispose exception, just doesn't favor it when
competing with the primary exception.

We already have a lot of JDK code out there and a good portion of it
does not use close() as it's dispose method.  Adding the Dispose
interface to so many classes seems to break the "small change rule".  An
annotation combined with a backup method signature match, covers many
more resources than the interface proposal and is more compatible with
the various types of resources already in use.

P.S. Apologies to any people who's toes I've stepped on, some of the
ideas I've stated here have been stated by others, too.

-Chris Allen

-------------------------------------------------------------------------------------------
***National City made the following annotations
-------------------------------------------------------------------------------------------
This communication is a confidential and proprietary business communication.  
It is intended solely for the use of the designated recipient(s).  If this
communication is received in error, please contact the sender and delete 
this communication.
===========================================================================================


More information about the coin-dev mailing list