
errorprone.bugpattern.CloseableProvides.md Maven / Gradle / Ivy
The newest version!
If you provide
[`Closeable`](https://docs.oracle.com/javase/7/docs/api/java/io/Closeable.html)
resources through dependency injection, it can be difficult to effectively
manage the lifecycle of the Closable:
```java
class MyModule extends AbstractModule() {
@Provides
FileOutputStream provideFileStream() {
return new FileOutputStream("/tmp/outfile");
}
}
...
class Client {
private final FileOutputStream fos;
@Inject Client(FileOutputStream fos, OtherDependency other, ...) {
this.fos = fos;
}
void doSomething() throws IOException {
fos.write("hello!");
}
}
```
There are a number of issues with this approach as it relates to resource
management:
* If multiple Client classes are constructed, multiple output streams are
opened against the same file, and writes to the file may clash with each
other.
* It's not clear which class has the responsibility of closing the
`FileOutputStream` resource:
* If the resource is created for the sole ownership of `Client`, then it
makes sense for the client to close it.
* However, if the resource is scoped (e.g.: you add `@Singleton` to the
`@Provides` method), then suddenly it's *not* the responsibility of
`Client` to close the resource. If one `Client` closes the stream, then
all of the other users of that stream will be dealing with a closed
resource. There needs to be some other 'resource manager' object that
also gets that stream, and its closing functions are call in the right
place. That can be tricky to do correctly.
* If, for example, the construction of the other dependencies of `Client`
fails (resulting in a `ProvisionException`), then the `FileOutputStream`
that may have been constructed leaks and isn't properly closed, even if
`Client` normally closes its resources correctly.
The preferred solution is to not inject closable resources, but instead, objects
that can expose short-lived closable resources that are used as necessary. The
following example uses Guava's
[CharSink](https://github.com/google/guava/wiki/IOExplained#sources-and-sinks)
as the resource manager object:
```java
class MyModule extends AbstractModule() {
@Provides
CharSink provideCharSink() {
return Files.asCharSink(new File("/tmp/outfile"), StandardCharsets.UTF_8);
}
}
...
class Client {
private final CharSink sink;
@Inject Client(CharSink sink, OtherDependency other, ...) {
this.sink = sink;
}
void doSomething() throws IOException {
sink.write("hello!"); // Opens the file at this point, and closes once its done.
}
}
```
If there's not a similar non-closable resource, you can write a simple wrapper:
```java
class ResourceManager {
@Inject ResourceManager(@Config String configs, ...) {}
/**
* Returns a new thing for you to use and dispose of
*/
OutputStream provideInstance() { return new...(); }
}
...
class Client {
private final ResourceManager resource;
@Inject Client(ResourceManager resource, OtherDependency other, ...) {
this.resource = resource;
}
void doSomething() {
try (OutputStream actualStream = resource.provideInstance()) {
// write to actualStream, closing with try-with-resources
}
}
}
```
This pattern can be extended to other resources: as opposed to injecting
database connection handles directly, inject connection pool objects that
require your object to ask for those connection objects when they're needed and
close them safely.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy