What java does if the closing of resource fails?

Viewed 971

In old times, where I had to use some resource, I declared it out of the try block, created it in the try block, and closed it in finally block. For the case if the closing will fail, it was put into inner try block.

Resource r=null;
try{
   r=new Resource();
   use(r); 
} 
catch(){
    outputAndLog(something);
}
finally{
    if(r != null){
        try{
        }
        catch(){
            outputAndLog(somethingElse);
        }
    }
}

Now we can use

try(Resource r = new Resource()){

java syntax. And the block finally becomes invisible for us. But what is in it? I mean, what happens, if the closing fails?

4 Answers

Resource: Link


First of all, the try-with-resources does not actually handle any Exceptions (like it is done in your code by logging). Basically it marshalls the possibly multiple Exceptions thrown from the try-catch-finally construct into a processable object - an Exception containing suppressed Exceptions.

If the try block throws an Exception, the resource will be closed and a resulting Exception will be suppressed. In consequence the thrown Exception object will have a reference to the suppressed Exception.
If the try block does not throw an Exception, the resource will be closed and a resulting Exception will be rethrown.

Thus we can conclude that your examples are not equivalent:
When using try-catch-finally an Exception caused by closing after successful processing will be treated as 'just a close error', while with the try-with-resources statement such an Exception will be handled equally to an Exception that occurred in the actual processing.

tl;dr
If handling the closure of resources includes more stuff, a try-with-resources statement might produce bugs that are not encountered with good old try-catch-finally. An example for such stuff is the committing and rollbacking of a jdbc connection.


So, to finally produce some code, for

try(Resource resource = new Resource()){
    // other code
}

the exact equivalent would be:

{
    Resource resource = null;
    Exception exc = null;
    try {
        resource = new Resource();
        // other code
    } catch (Exception e) {
        exc = e;
        throw e;
    } finally {
        if (resource != null) {
            if (exc != null) {
                try {
                    resource.close();
                } catch (Throwable t) {
                    exc.addSuppressed(t);
                }
            } else {
                resource.close();
            }
        }
    }
}

But what is in it?

try {
    Resource resource = initResource();
    Object possibleExceptionFromTry = null;
    ...
    if (stream != null) {
        if (possibleExceptionFromTry != null) {
            try {
                stream.close();
            } catch (Throwable exceptionFromClosing) {
                ((Throwable)possibleExceptionFromTry).addSuppressed(exceptionFromClosing);
            }
        } else {
            stream.close();
        }
    }
} catch (IOException exception) { ... }

What happens, if the closing fails?

If everything passes well in try, you will catch an exception from Resource#close() in catch. Otherwise, an exception from try will suppress an exception thrown from Resource#close and put it into an array that can be obtained by Throwable#getSuppressed:

} catch (IOException exception) {
    final Throwable[] suppressedExceptions = exception.getSuppressed();
    // exception = the exception from try
    // suppressedExceptions = the last exception (if any) is likely caused by closing
}

According to the Java tutorials:

If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception thrown by the block is the one that is thrown... You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block.

try-with-resources is calling implementation of method close from java.lang.AutoClosable interface.

From JLS 14.20.3

Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.

For your example try(Resource r = new Resource()) should be this full equivalent:

    Resource r = null;
    Exception ex = null;
    try {
        r = new Resource();
        use(r);
    } catch (Exception e){
        ex = e;
    }
    finally {
        if (r != null) {
            try {
                r.close();
            } catch (Exception e) {
                if (ex!=null){
                    ex.addSuppressed(e);
                } else {
                    ex = e;
                }
            }
        }
        if (ex != null){
            throw ex;
        }
    }
Related