Skip to content

Package: ContextManager$SupplierWithException

ContextManager$SupplierWithException

Coverage

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