Skip to contentPackage: ContextManager$RunnableWithException
ContextManager$RunnableWithException
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 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 javax.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.role.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: }