Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
Bootstrap.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.Objects;
30import java.util.function.Consumer;
31import java.util.function.Supplier;
32
33/**
34 * Generic application bootstrap that wires a container with lifecycle management.
35 *
36 * <p>{@link Bootstrap} ties together three lifecycle concerns:
37 * <ol>
38 * <li><b>Creation</b> — a {@link Supplier} builds the application-specific container.</li>
39 * <li><b>Warmup</b> — an optional {@link Consumer} eagerly initializes all components
40 * so startup failures surface immediately instead of at first request.</li>
41 * <li><b>Shutdown</b> — a JVM shutdown hook calls an optional pre-close callback,
42 * then closes the {@link Closer} in LIFO order.</li>
43 * </ol>
44 *
45 * <p>The returned {@link Runtime} is {@link AutoCloseable}, so it integrates naturally
46 * with try-with-resources for graceful shutdown in tests and CLI applications.
47 *
48 * <pre>{@code
49 * var runtime = Bootstrap.start(
50 * AppContainer::new, // factory
51 * AppContainer::warmup, // fail-fast eager init
52 * container -> {} // pre-shutdown callback
53 * );
54 *
55 * try (runtime) {
56 * JettyServer.start(runtime.container());
57 * }
58 * }</pre>
59 *
60 * <p>This class has no public constructors — use the static factory methods.
61 */
62public final class Bootstrap {
63
64 private Bootstrap() {
65 // static factory only
66 }
67
68 /**
69 * Immutable holder for the running application state.
70 *
71 * @param <C> type of the application container
72 */
73 public record Runtime<C>(C container, Closer closer) implements AutoCloseable {
74
75 /** Creates a Runtime; both parameters must be non-null. */
76 public Runtime {
77 Objects.requireNonNull(container, "container");
78 Objects.requireNonNull(closer, "closer");
79 }
80
81 /**
82 * Closes the underlying {@link Closer}, releasing all registered resources in LIFO order.
83 */
84 @Override
85 public void close() {
86 closer.close();
87 }
88 }
89
90 /**
91 * Creates and starts the application.
92 *
93 * <p>Steps performed:
94 * <ol>
95 * <li>Create a new {@link Closer}.</li>
96 * <li>Build the container via {@code containerFactory}.</li>
97 * <li>If the container implements {@link AutoCloseable}, register it with the closer.</li>
98 * <li>If {@code warmup} is non-null, invoke it — any exception aborts startup.</li>
99 * <li>Register a JVM shutdown hook that calls {@code onShutdown} (if non-null)
100 * then closes the closer.</li>
101 * </ol>
102 *
103 * @param <C> type of the application container
104 * @param containerFactory supplier that creates the container; must not return {@code null}
105 * @param warmup optional consumer invoked after container creation to eagerly
106 * initialize all components; {@code null} disables warmup
107 * @param onShutdown optional consumer invoked on JVM shutdown before resources are
108 * closed; {@code null} disables the pre-shutdown callback
109 * @return a {@link Runtime} holding the container and its closer
110 */
111 public static <C> Runtime<C> start(final Supplier<C> containerFactory,
112 final Consumer<C> warmup,
113 final Consumer<C> onShutdown) {
114 Objects.requireNonNull(containerFactory, "containerFactory");
115
116 final var closer = new Closer();
117 final C container = Objects.requireNonNull(containerFactory.get(), "containerFactory returned null");
118
119 if (container instanceof AutoCloseable ac) {
120 closer.register(ac);
121 }
122
123 if (warmup != null) {
124 warmup.accept(container);
125 }
126
127 final var runtime = new Runtime<>(container, closer);
128
129 java.lang.Runtime.getRuntime().addShutdownHook(new Thread(() -> {
130 if (onShutdown != null) {
131 try {
132 onShutdown.accept(container);
133 } catch (final Exception e) {
134 // best-effort; don't prevent closer from running
135 }
136 }
137 closer.close();
138 }, "ether-di-shutdown"));
139
140 return runtime;
141 }
142
143 /**
144 * Convenience overload — starts without a pre-shutdown callback.
145 *
146 * @param <C> type of the application container
147 * @param containerFactory supplier that creates the container
148 * @param warmup optional warmup consumer; {@code null} disables warmup
149 * @return a {@link Runtime} holding the container and its closer
150 * @see #start(Supplier, Consumer, Consumer)
151 */
152 public static <C> Runtime<C> start(final Supplier<C> containerFactory,
153 final Consumer<C> warmup) {
154 return start(containerFactory, warmup, null);
155 }
156
157 /**
158 * Convenience overload — starts without warmup or pre-shutdown callback.
159 *
160 * @param <C> type of the application container
161 * @param containerFactory supplier that creates the container
162 * @return a {@link Runtime} holding the container and its closer
163 * @see #start(Supplier, Consumer, Consumer)
164 */
165 public static <C> Runtime<C> start(final Supplier<C> containerFactory) {
166 return start(containerFactory, null, null);
167 }
168}
static< C > Runtime< C > start(final Supplier< C > containerFactory, final Consumer< C > warmup, final Consumer< C > onShutdown)
Creates and starts the application.
static< C > Runtime< C > start(final Supplier< C > containerFactory)
Convenience overload — starts without warmup or pre-shutdown callback.
static< C > Runtime< C > start(final Supplier< C > containerFactory, final Consumer< C > warmup)
Convenience overload — starts without a pre-shutdown callback.
record Runtime< C >(C container, Closer closer) implements AutoCloseable
Immutable holder for the running application state.
Composite AutoCloseable that closes registered resources in LIFO order.
Definition Closer.java:54