Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
Closer.java
Go to the documentation of this file.
1package dev.rafex.ether.di;
2
3/*-
4 * #%L
5 * ether-di
6 * %%
7 * Copyright (C) 2026 Raúl Eduardo González Argote
8 * %%
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 * #L%
27 */
28
29import java.util.ArrayDeque;
30import java.util.Deque;
31import java.util.concurrent.atomic.AtomicBoolean;
32
33/**
34 * Composite {@link AutoCloseable} that closes registered resources in LIFO order.
35 *
36 * <p>Resources are registered via {@link #register(AutoCloseable)} and are closed
37 * in reverse registration order (last-in, first-out) when {@link #close()} is called.
38 * If multiple resources throw during close, only the first exception is propagated;
39 * subsequent ones are attached as {@linkplain Throwable#addSuppressed suppressed}.
40 *
41 * <p>Calling {@link #close()} more than once is a no-op after the first call.
42 *
43 * <pre>{@code
44 * var closer = new Closer();
45 * var pool = closer.register(DataSourceFactory.create(config));
46 * var cache = closer.register(new CacheManager());
47 *
48 * try (closer) {
49 * runApplication(pool, cache);
50 * }
51 * // cache closed first, then pool
52 * }</pre>
53 */
54public final class Closer implements AutoCloseable {
55
56 private final Deque<AutoCloseable> resources = new ArrayDeque<>();
57 private final AtomicBoolean closed = new AtomicBoolean(false);
58
59 /**
60 * Registers a resource to be closed when {@link #close()} is called.
61 *
62 * @param <T> type of the resource
63 * @param resource the resource to register; must not be {@code null}
64 * @return the resource itself, for chained assignment
65 */
66 public <T extends AutoCloseable> T register(final T resource) {
67 if (resource == null) {
68 throw new NullPointerException("resource");
69 }
70 synchronized (resources) {
71 resources.push(resource);
72 }
73 return resource;
74 }
75
76 /**
77 * Closes all registered resources in LIFO order.
78 *
79 * <p>Idempotent: subsequent calls after the first are no-ops.
80 *
81 * @throws RuntimeException wrapping the first exception thrown during close,
82 * with any subsequent exceptions attached as suppressed
83 */
84 @Override
85 public void close() {
86 if (!closed.compareAndSet(false, true)) {
87 return;
88 }
89 Throwable first = null;
90 while (true) {
91 final AutoCloseable resource;
92 synchronized (resources) {
93 resource = resources.isEmpty() ? null : resources.pop();
94 }
95 if (resource == null) {
96 break;
97 }
98 try {
99 resource.close();
100 } catch (final Throwable t) {
101 if (first == null) {
102 first = t;
103 } else {
104 first.addSuppressed(t);
105 }
106 }
107 }
108 if (first != null) {
109 if (first instanceof RuntimeException re) {
110 throw re;
111 }
112 throw new RuntimeException("Error closing resources", first);
113 }
114 }
115
116 /**
117 * Returns {@code true} if {@link #close()} has already been called.
118 *
119 * @return {@code true} after the first call to {@link #close()}
120 */
121 public boolean isClosed() {
122 return closed.get();
123 }
124}
Composite AutoCloseable that closes registered resources in LIFO order.
Definition Closer.java:54
boolean isClosed()
Returns true if close() has already been called.
Definition Closer.java:121
void close()
Closes all registered resources in LIFO order.
Definition Closer.java:85