View Javadoc
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 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   }