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.role.impl;
27
28 import javax.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 }