DefaultContextManager.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * TheseFoolishThings: Miscellaneous utilities
  5.  * http://tidalwave.it/projects/thesefoolishthings
  6.  *
  7.  * Copyright (C) 2009 - 2021 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.util.Task;
  35. import it.tidalwave.role.ContextManager;
  36. import lombok.extern.slf4j.Slf4j;
  37. import static it.tidalwave.role.spi.impl.LogUtil.*;

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

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

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

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

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

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

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

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

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

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

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

  150.     /*******************************************************************************************************************
  151.      *
  152.      * {@inheritDoc}
  153.      *
  154.      ******************************************************************************************************************/
  155.     @Override @Nonnull
  156.     public <V, T extends Throwable> V runWithContext (@Nonnull final Object context,
  157.                                                       @Nonnull final Task<V, T> task)
  158.       throws T
  159.       {
  160.         return runWithContexts(Collections.singletonList(context), task);
  161.       }

  162.     /*******************************************************************************************************************
  163.      *
  164.      * {@inheritDoc}
  165.      *
  166.      ******************************************************************************************************************/
  167.     @Override @Nonnull
  168.     public <V, T extends Throwable> V runWithContexts (@Nonnull final List<Object> contexts,
  169.                                                        @Nonnull final Task<V, T> task)
  170.       throws T
  171.       {
  172.         final String taskId = shortId(task);
  173.         final String contextIds = shortIds(contexts);

  174.         try
  175.           {
  176.             log.trace("runWithContexts({}, {})", contextIds, taskId);

  177.             for (final Object context : contexts)
  178.               {
  179.                 addLocalContext(context);
  180.               }

  181.             if (log.isTraceEnabled())
  182.               {
  183.                 log.trace(">>>> contexts now: {} - {}", getContexts(), this);
  184.               }

  185.             return task.run();
  186.           }
  187.         finally
  188.           {
  189.             for (final Object context : contexts)
  190.               {
  191.                 removeLocalContext(context);
  192.               }

  193.             log.trace(">>>> runWithContexts({}, {}) completed", contextIds, taskId);
  194.           }
  195.       }

  196.     /*******************************************************************************************************************
  197.      *
  198.      * {@inheritDoc}
  199.      *
  200.      ******************************************************************************************************************/
  201.     @Override
  202.     public <V> V runWithContext (@Nonnull final Object context, @Nonnull final Supplier<V> task)
  203.       {
  204.         return runWithContext(context, new Task<V, RuntimeException>()
  205.           {
  206.             @Override
  207.             public V run()
  208.               {
  209.                 return task.get();
  210.               }
  211.           });
  212.       }

  213.     /*******************************************************************************************************************
  214.      *
  215.      * {@inheritDoc}
  216.      *
  217.      ******************************************************************************************************************/
  218.     @Override
  219.     public <V> V runWithContexts (@Nonnull final List<Object> contexts, @Nonnull final Supplier<V> task)
  220.       {
  221.         return runWithContext(contexts, new Task<V, RuntimeException>()
  222.           {
  223.             @Override
  224.             public V run()
  225.               {
  226.                 return task.get();
  227.               }
  228.           });
  229.       }
  230.   }