Reputation: 36892
I am creating a variable amount of AutoCloseable
objects in a try-with-resources
block. At any point of exit, I want all of the allocated resources closed.
I can imagine writing something myself to do this, but is there an existing utility similar to Python's contextlib.ExitStack that will close allocated resources? I would expect it to look like this:
try (ExitStack exitStack = new ExitStack()) {
List<Widget> widgets = new ArrayList<>();
for (...) {
widgets.add(exitStack.add(new Widget()));
}
// use widgets
}
(Note: this is not this question because I don't know how many resources I'll have ahead of time.
Hey close voters I'm not asking for a library, I'm asking how you would accomplish the task of safely closing a dynamic number of AutoCloseable
s, if there's a language feature for it, great, if there's a standard library function, also great, if I have to write my own, fine. If you'd like to recommend a third-party library in common use that has this in it then sure.
Upvotes: 8
Views: 2223
Reputation: 36892
Given that this utility does not appear to exist, I wrote one. It wraps up any thrown exceptions and then only throws if a resource's close() threw. Always closes everything before returning.
public class ClosingException extends Exception { }
And
import java.util.Deque;
import java.util.ArrayDeque;
public final class ClosingStack implements AutoCloseable {
public void close() throws ClosingException {
ClosingException allClosingExceptions = new ClosingException();
while (!resources.isEmpty()) {
try {
resources.removeLast().close();
} catch (Throwable e) {
allClosingExceptions.addSuppressed(e);
}
}
if (allClosingExceptions.getSuppressed().length != 0) {
throw allClosingExceptions;
}
}
public <T extends AutoCloseable> T add(T resource) {
resources.addLast(resource);
return resource;
}
private Deque<AutoCloseable> resources = new ArrayDeque<>();
}
And use:
try (ClosingStack closingStack = new ClosingStack()) {
List<Widget> widgets = new ArrayList<>();
for (...) {
widgets.add(closingStack.add(new Widget()));
}
// use widgets
}
Upvotes: 4
Reputation: 140554
Perhaps you could do something like this:
<T extends AutoCloseable> void recursively(
List<T> things,
Iterator<? extends Supplier<? extends T>> thingSuppliers,
Consumer<List<T>> whenEmpty) {
if (!thingSuppliers.hasNext()) {
// No more to create. Pass all the things to the consumer.
whenEmpty.accept(things);
return;
}
// Create a new thing, and make a recursive call. This thing gets
// closed as the stack unwinds.
try (T thing = thingSuppliers.next().get()) {
things.add(thing);
recursively(things, thingSuppliers, whenEmpty);
}
}
// Some means of starting the recursion.
<T extends AutoCloseable> void recursively(
Iterable<? extends Supplier<? extends T>> thingSuppliers,
Consumer<List<T>> whenEmpty) {
recursively(new ArrayList<>(), thingSuppliers.iterator(), whenEmpty);
}
Example invocation:
recursively(
Arrays.asList(Widget::new, Widget::new),
System.out::println);
Upvotes: 0
Reputation: 81174
I think you'll find Guava's Closer
class to be what you need here:
try (Closer closer = Closer.create()) {
InputStream in1 = closer.register(new FileInputStream("foo"));
InputStream in2 = closer.register(new FileInputStream("bar"));
// use in1 and in2
}
// in2 and in1 closed in that order
The class is still marked as Beta mind you, but has appeared to stick around. The original intent was to provide a try-with-resources experience without Java 7 language feature support, however a useful side effect is that it should work with a dynamic number of resources.
Upvotes: 3