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 }