- Exception Scenarios for Java’s try-with-resources
- Creating Resources
- Declare and initialize resource
- declare resource only
- multiple resources
- when creation fails with one resource
- when creation fails with multiple resources
- Working with resources
- Working with one resource
- when the work fails
- when work fails with multiple resources
- Closing resources
- when close fails with one resource
- When close fails with two resources
- When work and close fails
- Summary
- TestResource
Exception Scenarios for Java’s try-with-resources
Have you ever ran into an exception thrown by a try-with-resources block and wondered how it works? This article observes the behavior of try-with-resources under different scenarios and explains what is going on. Support for try-with-resources was added in java 7 and has been enhanced in java 9. A try-with-resources block is able to automatically close resources when execution of the block completes. There are many different scenarios where try-with-resources will throw exceptions. This article explores those scenarios. If you are following along with the test examples the TestResource class is attached at the bottom of this article. TestResource is a mock object used to simulate a resource. It can be configured to fail the work() and close() methods. The createFailure() method simulates a failure to create a resource within a try block.
Creating Resources
Declare and initialize resource
try(var r = new TestResource()) //work with resource here >
Resource Specification — «A resource specification uses variables to denote resources for the try statement, either by declaring local variables with initializer expressions or by referring to suitable existing variables. An existing variable is referred to by either an expression name (§6.5.6) or a field access expression (§15.11).» —Java Language Specification First r is declared and initialized. After the try block executes r is automatically closed and goes out of scope.
creating resource "resource" closing resource "resource"
declare resource only
In java 7 a resource needed to be declared and initialized in the resource specification. Since java 9 initialized is not required as long as the variables is effectively final. Here is an example:
try(r) //work with resource here >
multiple resources
try(var r1 = new TestResource("r1"); var r2 = new TestResource("r2")) //work with resource here >
creating resource "r1" creating resource "r2" closing resource "r2" closing resource "r1"
when creation fails with one resource
try(var r1 = createFailure()) //work with resource here >
create failure java.io.IOException: create failure at com.github.moaxcp.trywithresources.TestResource.createFailure(TestResource.java:12) at com.github.moaxcp.trywithresources.TestTryWithResources.creat_fails(TestTryWithResources.java:35)
try(var r1 = createFailure()) //work with resource here > catch (IOException e) System.out.println("caught exception " + e); >
create failure caught exception java.io.IOException: create failure
when creation fails with multiple resources
try(var r1 = new TestResource("r1"); var r2 = createFailure()) //work with resource here > catch (IOException e) System.out.println("caught exception " + e); >
creating resource "r1" create failure closing resource "r1" caught exception java.io.IOException: create failure
Working with resources
Now that we know the mechanics of creating a resoruce in a try-with-resources block lets examine working with resources inside the block.
Working with one resource
try(var r = new TestResource()) r.work(); >
creating resource "resource" performing work with resource "resource" closing resource "resource"
when the work fails
What happens with the work() method throws an exception? This can be done by setting the workFailure flag to true on the TestResource .
try(var r = new TestResource(true, false)) r.work(); >
creating resource "resource" performing work with resource "resource" work failure with resource "resource" closing resource "resource" work failure with resource "resource" java.io.IOException: work failure with resource "resource" at com.github.moaxcp.trywithresources.TestResource.work(TestResource.java:47) at com.github.moaxcp.trywithresources.TestTryWithResources.work_with_resource_fails(TestTryWithResources.java:68)
when work fails with multiple resources
When work fails with multiple resources the resources are closed in reverse order and the exception is caught or thrown.
try(var r1 = new TestResource("r1", false, false); var r2 = new TestResource("r2", true, false)) r1.work(); r2.work(); >
creating resource "r1" creating resource "r2" performing work with resource "r1" performing work with resource "r2" work failure with resource "r2" closing resource "r2" closing resource "r1" work failure with resource "r2" java.io.IOException: work failure with resource "r2" at com.github.moaxcp.trywithresources.TestResource.work(TestResource.java:47) at com.github.moaxcp.trywithresources.TestTryWithResources.work_with_multiple_resources_fails(TestTryWithResources.java:76)
As you can see, when r2.work() fails the resources are closed in reverse order and the exception is thrown.
Closing resources
As show before, a try-with-resources block will automatically close the resource but what happens when the close fails?
when close fails with one resource
try(var r = new TestResource(false, true)) r.work(); >
creating resource "resource" performing work with resource "resource" closing resource "resource" close failure with resource "resource" close failure with resource "resource" java.io.IOException: close failure with resource "resource" at com.github.moaxcp.trywithresources.TestResource.close(TestResource.java:56) at com.github.moaxcp.trywithresources.TestTryWithResources.close_with_resource_fails(TestTryWithResources.java:84)
When close fails with two resources
try(var r1 = new TestResource("r1", false, true); var r2 = new TestResource("r2", false, true)) r1.work(); r2.work(); >
Since resources are closed in reverse order the exception from r2 will suppress the exception from r1 .
creating resource "r1" creating resource "r2" performing work with resource "r1" performing work with resource "r2" closing resource "r2" close failure with resource "r2" closing resource "r1" close failure with resource "r1" close failure with resource "r2" java.io.IOException: close failure with resource "r2" at com.github.moaxcp.trywithresources.TestResource.close(TestResource.java:56) at com.github.moaxcp.trywithresources.TestTryWithResources.close_with_two_resources_fails(TestTryWithResources.java:92) Suppressed: java.io.IOException: close failure with resource "r1" at com.github.moaxcp.trywithresources.TestResource.close(TestResource.java:56) at com.github.moaxcp.trywithresources.TestTryWithResources.close_with_two_resources_fails(TestTryWithResources.java:89)
When work and close fails
try(var r1 = new TestResource("r1", true, true); var r2 = new TestResource("r2", true, true)) r1.work(); >
creating resource "r1" creating resource "r2" performing work with resource "r1" work failure with resource "r1" closing resource "r2" close failure with resource "r2" closing resource "r1" close failure with resource "r1" work failure with resource "r1" java.io.IOException: work failure with resource "r1" at com.github.moaxcp.trywithresources.TestResource.work(TestResource.java:47) at com.github.moaxcp.trywithresources.TestTryWithResources.work_and_close_with_two_resources_fails(TestTryWithResources.java:98) . Suppressed: java.io.IOException: close failure with resource "r2" at com.github.moaxcp.trywithresources.TestResource.close(TestResource.java:56) at com.github.moaxcp.trywithresources.TestTryWithResources.work_and_close_with_two_resources_fails(TestTryWithResources.java:97) . 88 more Suppressed: java.io.IOException: close failure with resource "r1" at com.github.moaxcp.trywithresources.TestResource.close(TestResource.java:56) at com.github.moaxcp.trywithresources.TestTryWithResources.work_and_close_with_two_resources_fails(TestTryWithResources.java:97) . 88 more
Summary
Try-with-resources is a very helpful tool when dealing with resources. It is especially helpful when dealing with multiple resources. It saves the developer from having to write correct closing code. It also makes suppressed exceptions available so developers know exactly what failures occurred. This article outlines different mechanics of try-with-resources in different scenarios. Can you think of any other scenarios for try-with-resources?
TestResource
Before I start showing examples there is some code for a TestResource and a failing factory method that is needed.
class TestResource implements AutoCloseable private final String name; private final boolean workFailure; private final boolean closeFailure; public static TestResource createFailure() throws IOException System.out.println("create failure"); throw new IOException("create failure"); > public TestResource() name = "resource"; System.out.println("creating resource \"" + name + "\""); workFailure = false; closeFailure = false; > public TestResource(String name) System.out.println("creating resource \"" + name + "\""); this.name = name; workFailure = false; closeFailure = false; > public TestResource(boolean workFailure, boolean closeFailure) throws IOException this.name = "resource"; System.out.println("creating resource \"" + name + "\""); this.workFailure = workFailure; this.closeFailure = closeFailure; > public TestResource(String name, boolean workFailure, boolean closeFailure) throws IOException System.out.println("creating resource \"" + name + "\""); this.name = name; this.workFailure = workFailure; this.closeFailure = closeFailure; > public void work() throws IOException System.out.println("performing work with resource \"" + name + "\""); if(workFailure) System.out.println("work failure with resource \"" + name + "\""); throw new IOException("work failure with resource \"" + name + "\""); > > @Override public void close() throws IOException System.out.println("closing resource \"" + name + "\""); if(closeFailure) System.out.println("close failure with resource \"" + name + "\""); throw new IOException("close failure with resource \"" + name + "\""); > > >