DefaultContextManager.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * TheseFoolishThings: Miscellaneous utilities
  5.  * http://tidalwave.it/projects/thesefoolishthings
  6.  *
  7.  * Copyright (C) 2009 - 2023 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
  12.  * the License. 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
  17.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  18.  * specific language governing permissions and limitations under the License.
  19.  *
  20.  * *********************************************************************************************************************
  21.  *
  22.  * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
  23.  * git clone https://github.com/tidalwave-it/thesefoolishthings-src
  24.  *
  25.  * *********************************************************************************************************************
  26.  */
  27. package it.tidalwave.role.spi;

  28. import javax.annotation.Nonnull;
  29. import java.util.ArrayList;
  30. import java.util.Collections;
  31. import java.util.List;
  32. import java.util.Stack;
  33. import it.tidalwave.util.NotFoundException;
  34. import it.tidalwave.role.ContextManager;
  35. import lombok.extern.slf4j.Slf4j;
  36. import static it.tidalwave.role.spi.impl.LogUtil.*;

  37. /***********************************************************************************************************************
  38.  *
  39.  * @author  Fabrizio Giudici
  40.  *
  41.  **********************************************************************************************************************/
  42. @Slf4j
  43. public class DefaultContextManager implements ContextManager
  44.   {
  45.     /** Useful for troubleshooting in cases when multiple instances are erroneously created. */
  46.     private static final boolean DUMP_STACK_AT_CREATION = Boolean.getBoolean(
  47.             DefaultContextManager.class.getName() + ".dumpStackAtCreation");

  48.     /** The list of global contexts, ordered by priority. */
  49.     private final List<Object> globalContexts = Collections.synchronizedList(new ArrayList<>());

  50.     /** The list of local contexts, ordered by priority. */
  51.     private final ThreadLocal<Stack<Object>> localContexts = new ThreadLocal<>()
  52.       {
  53.         @Override @Nonnull
  54.         protected Stack<Object> initialValue ()
  55.           {
  56.             return new Stack<>();
  57.           }
  58.       };

  59.     /*******************************************************************************************************************
  60.      *
  61.      *
  62.      *
  63.      ******************************************************************************************************************/
  64.     public DefaultContextManager()
  65.       {
  66.         if (DUMP_STACK_AT_CREATION)
  67.           {
  68.             try
  69.               {
  70.                 throw new RuntimeException();
  71.               }
  72.             catch (Exception e)
  73.               {
  74.                 log.trace(">>>> created context manager " + this, e);
  75.               }
  76.           }
  77.       }

  78.     /*******************************************************************************************************************
  79.      *
  80.      * {@inheritDoc}
  81.      *
  82.      ******************************************************************************************************************/
  83.     @Override @Nonnull
  84.     public List<Object> getContexts()
  85.       {
  86.         final List<Object> contexts = new ArrayList<>(localContexts.get());
  87.         Collections.reverse(contexts);
  88.         contexts.addAll(0, globalContexts);
  89.         return contexts;
  90.       }

  91.     /*******************************************************************************************************************
  92.      *
  93.      * {@inheritDoc}
  94.      *
  95.      ******************************************************************************************************************/
  96.     @Override @Nonnull
  97.     public <T> T findContextOfType (@Nonnull final Class<T> contextType)
  98.       throws NotFoundException
  99.       {
  100.         for (final var context : getContexts())
  101.           {
  102.             if (contextType.isAssignableFrom(context.getClass()))
  103.               {
  104.                 return contextType.cast(context);
  105.               }
  106.           }

  107.         throw new NotFoundException("No current context of type " + contextType);
  108.       }

  109.     /*******************************************************************************************************************
  110.      *
  111.      * {@inheritDoc}
  112.      *
  113.      ******************************************************************************************************************/
  114.     @Override
  115.     public void addGlobalContext (@Nonnull final Object context)
  116.       {
  117.         globalContexts.add(context);
  118.       }

  119.     /*******************************************************************************************************************
  120.      *
  121.      * {@inheritDoc}
  122.      *
  123.      ******************************************************************************************************************/
  124.     @Override
  125.     public void removeGlobalContext (@Nonnull final Object context)
  126.       {
  127.         globalContexts.remove(context);
  128.       }

  129.     /*******************************************************************************************************************
  130.      *
  131.      * {@inheritDoc}
  132.      *
  133.      ******************************************************************************************************************/
  134.     @Override
  135.     public void addLocalContext (@Nonnull final Object context)
  136.       {
  137.         localContexts.get().push(context);
  138.       }

  139.     /*******************************************************************************************************************
  140.      *
  141.      * {@inheritDoc}
  142.      *
  143.      ******************************************************************************************************************/
  144.     @Override
  145.     public void removeLocalContext (@Nonnull final Object context)
  146.       {
  147.         localContexts.get().remove(context);
  148.       }

  149.     /*******************************************************************************************************************
  150.      *
  151.      * {@inheritDoc}
  152.      *
  153.      ******************************************************************************************************************/
  154.     @Override @Nonnull
  155.     public <T, E extends Throwable> T runEWithContexts (@Nonnull final SupplierWithException<T, E> supplier,
  156.                                                         @Nonnull final Object ... contexts)
  157.             throws E
  158.       {
  159.         log.trace("runWithContexts({}, {})", shortId(supplier), shortIds(contexts));

  160.         try (final var __ = binder(contexts))
  161.           {
  162.             if (log.isTraceEnabled())
  163.               {
  164.                 log.trace(">>>> contexts now: {} - {}", getContexts(), this);
  165.               }

  166.             final var result = supplier.get();
  167.             log.trace(">>>> runWithContexts({}, {}) completed", shortId(supplier), shortIds(contexts));
  168.             return result;
  169.           }
  170.       }
  171.   }