1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * TheseFoolishThings: Miscellaneous utilities
5 * http://tidalwave.it/projects/thesefoolishthings
6 *
7 * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22 * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.util;
27
28 import jakarta.annotation.Nonnull;
29 import java.util.List;
30 import java.util.Optional;
31 import java.util.ServiceLoader;
32 import java.util.function.Supplier;
33 import it.tidalwave.role.spi.ContextManagerProvider;
34 import static it.tidalwave.util.impl.ServiceLoaderLocator.lazySupplierOf;
35
36 /***************************************************************************************************************************************************************
37 *
38 * A facility to register and unregister global and local DCI contexts.
39 *
40 * @author Fabrizio Giudici
41 *
42 **************************************************************************************************************************************************************/
43 public interface ContextManager
44 {
45 /***********************************************************************************************************************************************************
46 * A locator for the {@link ContextManager} which uses the {@link ServiceLoader} facility to be independent of
47 * any DI framework.
48 *
49 * This locator caches the internal reference and this is ok for production use; during tests, since multiple
50 * contexts are typically created and destroyed for each test, you should call {@link #reset()} after each test
51 * has been completed.
52 **********************************************************************************************************************************************************/
53 static class Inner
54 {
55 private static final LazySupplier<ContextManager> CONTEXT_MANAGER_REF =
56 LazySupplier.of(() -> Inner.CONTEXT_MANAGER_PROVIDER_REF.get().getContextManager());
57
58 private static final LazySupplier<ContextManagerProvider> CONTEXT_MANAGER_PROVIDER_REF =
59 lazySupplierOf(ContextManagerProvider.class);
60 }
61
62 /***********************************************************************************************************************************************************
63 * Returns a singleton instance.
64 *
65 * @return the singleton instance
66 **********************************************************************************************************************************************************/
67 @Nonnull
68 public static ContextManager getInstance()
69 {
70 return Inner.CONTEXT_MANAGER_REF.get();
71 }
72
73 /***********************************************************************************************************************************************************
74 * <b>This method is for testing only.</b> Sets the global {@link ContextManagerProvider}. See note about
75 * {@link #reset()}.
76 *
77 * @param provider the provider
78 * @see #reset()
79 **********************************************************************************************************************************************************/
80 public static void set (@Nonnull final ContextManagerProvider provider)
81 {
82 Inner.CONTEXT_MANAGER_REF.clear();
83 Inner.CONTEXT_MANAGER_PROVIDER_REF.set(provider);
84 }
85
86 /***********************************************************************************************************************************************************
87 * <b>This method is for testing only.</b> Resets the global {@link ContextManagerProvider}; it must be called
88 * at the test completion whenever {@link #set(ContextManagerProvider)} has been called, to avoid polluting the
89 * context of further tests.
90 *
91 * @see #set(ContextManagerProvider)
92 **********************************************************************************************************************************************************/
93 public static void reset()
94 {
95 Inner.CONTEXT_MANAGER_REF.clear();
96 Inner.CONTEXT_MANAGER_PROVIDER_REF.clear();
97 }
98
99 @FunctionalInterface
100 public static interface RunnableWithException<E extends Throwable>
101 {
102 public void run()
103 throws E;
104 }
105
106 @FunctionalInterface
107 public static interface SupplierWithException<T, E extends Throwable>
108 {
109 public T get()
110 throws E;
111 }
112
113 /***********************************************************************************************************************************************************
114 * Returns the list of current contexts, ordered by their priority.
115 *
116 * @return the list of current contexts
117 **********************************************************************************************************************************************************/
118 @Nonnull
119 public List<Object> getContexts();
120
121 /***********************************************************************************************************************************************************
122 * Finds a current context instance of the given type.
123 *
124 * @param <T> the static context type
125 * @param contextType the dynamic context type
126 * @return the requested context
127 **********************************************************************************************************************************************************/
128 @Nonnull
129 public <T> Optional<T> findContextOfType (@Nonnull Class<T> contextType);
130
131 /***********************************************************************************************************************************************************
132 * Adds a global context.
133 *
134 * @param context the new context
135 **********************************************************************************************************************************************************/
136 public void addGlobalContext (@Nonnull Object context);
137
138 /***********************************************************************************************************************************************************
139 * Removes a global context.
140 *
141 * @param context the context
142 **********************************************************************************************************************************************************/
143 public void removeGlobalContext (@Nonnull Object context);
144
145 /***********************************************************************************************************************************************************
146 * Adds a local context.
147 *
148 * @param context the new context
149 **********************************************************************************************************************************************************/
150 public void addLocalContext (@Nonnull Object context);
151
152 /***********************************************************************************************************************************************************
153 * Removes a local context.
154 *
155 * @param context the context
156 **********************************************************************************************************************************************************/
157 public void removeLocalContext (@Nonnull Object context);
158
159 /***********************************************************************************************************************************************************
160 * Runs a {@link Task} associated with a new local context.
161 *
162 * @param <V> the type of the returned value
163 * @param <T> the type of the exception that can be thrown
164 * @param context the context
165 * @param task the task
166 * @return the value produced by the task
167 * @throws T the exception(s) thrown by the task
168 * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
169 **********************************************************************************************************************************************************/
170 @Deprecated
171 public default <V, T extends Throwable> V runWithContext (@Nonnull final Object context,
172 @Nonnull final Task<V, T> task)
173 throws T
174 {
175 return runWithContexts(List.of(context), task);
176 }
177
178 /***********************************************************************************************************************************************************
179 * Runs a {@link Task} associated with a new bunch of local contexts.
180 *
181 * @param <V> the type of the returned value
182 * @param <T> the type of the exception that can be thrown
183 * @param contexts the contexts
184 * @param task the task
185 * @return the value produced by the task
186 * @throws T the exception(s) thrown by the task
187 * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
188 **********************************************************************************************************************************************************/
189 @Deprecated
190 public default <V, T extends Throwable> V runWithContexts (@Nonnull final List<Object> contexts,
191 @Nonnull final Task<V, T> task)
192 throws T
193 {
194 return runEWithContexts(task::run, contexts.toArray());
195 }
196
197 /***********************************************************************************************************************************************************
198 * Runs a task associated with a new local context. This variant fits functional interfaces.
199 *
200 * @param <V> the type of the returned value of the task
201 * @param context the context
202 * @param task the task
203 * @return the value produced by the task
204 * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
205 **********************************************************************************************************************************************************/
206 @Deprecated
207 public default <V> V runWithContext (@Nonnull final Object context, @Nonnull final Supplier<V> task)
208 {
209 return runWithContexts(task, context);
210 }
211
212 /***********************************************************************************************************************************************************
213 * Runs a task associated with a new bunch of local contexts. This variant fits functional interfaces.
214 *
215 * @param <V> the type of the returned value
216 * @param contexts the contexts
217 * @param task the task
218 * @return the value produced by the task
219 * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
220 **********************************************************************************************************************************************************/
221 @Deprecated
222 public default <V> V runWithContexts (@Nonnull final List<Object> contexts, @Nonnull final Supplier<V> task)
223 {
224 return runWithContexts(task, contexts.toArray());
225 }
226
227 /***********************************************************************************************************************************************************
228 * Calls a runnable with some local contexts. This method fits functional interfaces.
229 *
230 * @param runnable the runnable
231 * @param contexts the contexts
232 * @since 3.2-ALPHA-12
233 **********************************************************************************************************************************************************/
234 public default void runWithContexts (@Nonnull final Runnable runnable, @Nonnull final Object ... contexts)
235 {
236 final SupplierWithException<Void, RuntimeException> se = () ->{ runnable.run(); return null; };
237 runEWithContexts(se, contexts);
238 }
239
240 /***********************************************************************************************************************************************************
241 * Calls a supplier with some local contexts. This method fits functional interfaces.
242 *
243 * @param <T> the type of the result
244 * @param supplier the supplier
245 * @param contexts the contexts
246 * @return the value returned by the supplier
247 * @since 3.2-ALPHA-12
248 **********************************************************************************************************************************************************/
249 @Nonnull
250 public default <T> T runWithContexts (@Nonnull final Supplier<T> supplier, @Nonnull final Object ... contexts)
251 {
252 final SupplierWithException<T, RuntimeException> se = supplier::get;
253 return runEWithContexts(se, contexts);
254 }
255
256 /***********************************************************************************************************************************************************
257 * Calls a runnable with some local contexts. This method fits functional interfaces.
258 *
259 * @param <E> the type of the thrown exception
260 * @param runnable the runnable to call
261 * @param contexts the contexts
262 * @throws E the original exception thrown by task
263 * @since 3.2-ALPHA-12
264 **********************************************************************************************************************************************************/
265 public default <E extends Throwable> void runEWithContexts (@Nonnull final RunnableWithException<E> runnable,
266 @Nonnull final Object ... contexts)
267 throws E
268 {
269 final SupplierWithException<Void, E> se = () ->{ runnable.run(); return null; };
270 runEWithContexts(se, contexts);
271 }
272
273 /***********************************************************************************************************************************************************
274 * Calls a task with some local contexts. This method fits functional interfaces.
275 *
276 * @param <T> the type of the returned value
277 * @param <E> the type of the thrown exception
278 * @param task the task to call
279 * @param contexts the contexts
280 * @return the value returned by the supplier
281 * @throws E the original exception thrown by task
282 * @since 3.2-ALPHA-12
283 **********************************************************************************************************************************************************/
284 @Nonnull
285 public <T, E extends Throwable> T runEWithContexts (@Nonnull SupplierWithException<T, E> task,
286 @Nonnull Object ... contexts)
287 throws E;
288
289 /***********************************************************************************************************************************************************
290 * Creates a binder that makes it possible to bind a local context by means of a try-with-resources instead of a
291 * try/finally.
292 *
293 * <pre>
294 * try (final ContextManager.Binder binder = contextManager.binder(context))
295 * {
296 * ...
297 * }
298 * </pre>
299 *
300 * @param contexts the contexts
301 * @return a binder that can be used in try-with-resources
302 * @since 3.2-ALPHA-12
303 **********************************************************************************************************************************************************/
304 @Nonnull
305 public default Binder binder (@Nonnull final Object ... contexts)
306 {
307 return new Binder(this, contexts);
308 }
309
310 /***********************************************************************************************************************************************************
311 * Used by
312 * @since 3.2-ALPHA-12
313 **********************************************************************************************************************************************************/
314 public static class Binder implements AutoCloseable
315 {
316 @Nonnull
317 private final ContextManager contextManager;
318
319 @Nonnull
320 private final Object[] contexts;
321
322 private Binder (@Nonnull final ContextManager contextManager, @Nonnull final Object[] contexts)
323 {
324 this.contextManager = contextManager;
325 this.contexts = contexts;
326
327 for (final var context : contexts)
328 {
329 this.contextManager.addLocalContext(context);
330 }
331 }
332
333 @Override
334 public void close()
335 {
336 for (final var context : contexts)
337 {
338 this.contextManager.removeLocalContext(context);
339 }
340 }
341 }
342 }