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.role.impl; 27 28 import jakarta.annotation.Nonnull; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Optional; 33 import java.util.Stack; 34 import it.tidalwave.util.ContextManager; 35 import lombok.extern.slf4j.Slf4j; 36 import static it.tidalwave.util.CollectionUtils.reversed; 37 import static it.tidalwave.util.ShortNames.*; 38 39 /*************************************************************************************************************************************************************** 40 * 41 * @author Fabrizio Giudici 42 * 43 **************************************************************************************************************************************************************/ 44 @Slf4j 45 public class DefaultContextManager implements ContextManager 46 { 47 /** Useful for troubleshooting in cases when multiple instances are erroneously created. */ 48 private static final boolean DUMP_STACK_AT_CREATION = Boolean.getBoolean( 49 DefaultContextManager.class.getName() + ".dumpStackAtCreation"); 50 51 /** The list of global contexts, ordered by priority. */ 52 private final List<Object> globalContexts = Collections.synchronizedList(new ArrayList<>()); 53 54 /** The list of local contexts, ordered by priority. */ 55 private final ThreadLocal<Stack<Object>> localContexts = new ThreadLocal<>() 56 { 57 @Override @Nonnull 58 protected Stack<Object> initialValue () 59 { 60 return new Stack<>(); 61 } 62 }; 63 64 /*********************************************************************************************************************************************************** 65 * 66 **********************************************************************************************************************************************************/ 67 public DefaultContextManager() 68 { 69 if (DUMP_STACK_AT_CREATION) 70 { 71 try 72 { 73 throw new RuntimeException(); 74 } 75 catch (Exception e) 76 { 77 log.trace(">>>> created context manager " + this, e); 78 } 79 } 80 } 81 82 /*********************************************************************************************************************************************************** 83 * {@inheritDoc} 84 **********************************************************************************************************************************************************/ 85 @Override @Nonnull 86 public List<Object> getContexts() 87 { 88 final var contexts = reversed(new ArrayList<>(localContexts.get())); 89 contexts.addAll(0, globalContexts); 90 return contexts; 91 } 92 93 /*********************************************************************************************************************************************************** 94 * {@inheritDoc} 95 **********************************************************************************************************************************************************/ 96 @Override @Nonnull @SuppressWarnings("BoundedWildcard") 97 public <T> Optional<T> findContextOfType (@Nonnull final Class<T> contextType) 98 { 99 for (final var context : getContexts()) 100 { 101 if (contextType.isAssignableFrom(context.getClass())) 102 { 103 return Optional.of(contextType.cast(context)); 104 } 105 } 106 107 return Optional.empty(); 108 } 109 110 /*********************************************************************************************************************************************************** 111 * {@inheritDoc} 112 **********************************************************************************************************************************************************/ 113 @Override 114 public void addGlobalContext (@Nonnull final Object context) 115 { 116 globalContexts.add(context); 117 } 118 119 /*********************************************************************************************************************************************************** 120 * {@inheritDoc} 121 **********************************************************************************************************************************************************/ 122 @Override 123 public void removeGlobalContext (@Nonnull final Object context) 124 { 125 globalContexts.remove(context); 126 } 127 128 /*********************************************************************************************************************************************************** 129 * {@inheritDoc} 130 **********************************************************************************************************************************************************/ 131 @Override 132 public void addLocalContext (@Nonnull final Object context) 133 { 134 localContexts.get().push(context); 135 } 136 137 /*********************************************************************************************************************************************************** 138 * {@inheritDoc} 139 **********************************************************************************************************************************************************/ 140 @Override 141 public void removeLocalContext (@Nonnull final Object context) 142 { 143 localContexts.get().remove(context); 144 } 145 146 /*********************************************************************************************************************************************************** 147 * {@inheritDoc} 148 **********************************************************************************************************************************************************/ 149 @Override @Nonnull 150 public <T, E extends Throwable> T runEWithContexts (@Nonnull final SupplierWithException<T, E> supplier, 151 @Nonnull final Object ... contexts) 152 throws E 153 { 154 log.trace("runWithContexts({}, {})", shortId(supplier), shortIds(contexts)); 155 156 try (final var unused = binder(contexts)) 157 { 158 if (log.isTraceEnabled()) 159 { 160 log.trace(">>>> contexts now: {} - {}", shortIds(getContexts()), this); 161 } 162 163 final var result = supplier.get(); 164 log.trace(">>>> runWithContexts({}, {}) completed", shortId(supplier), shortIds(contexts)); 165 return result; 166 } 167 } 168 }