# RelatedThread.java

    /*
    ** Module   : RelatedThread.java
    ** Abstract : a new thread which inherits the account of the creating thread but uses a related
    **            security context (one that shares only thread-safe context)
    **
    ** Copyright (c) 2020, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- -----------------------Description------------------------
    ** 001 GES 20200204 First version.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.security;

    import java.util.*;

    /**
     * Provides a thread which automatically inherits the account of the creating thread but uses a
     * "related" security context.  A related context is one that shares only thread-safe context.
     * When the thread exits, it will automatically cleanup the context (decrementing the reference
     * counts of any related resources and so forth). This makes it easy to implement additional
     * threads which all share the same thread-safe resources, the same subjects and the same rights
     * without sharing all context-local data.
     */
    public class RelatedThread
    extends ContextAwareThread
    {
       /** Stores a map that is shared between all related sessions. */
       private final static HashMap<Thread, Map<Object, Object>> storage = new HashMap<>();

       /** The "main" thread of the related threads. */
       private final Thread main;

       /**
        * Constructor which allows the core logic to be specified.
        *
        * @param    core
        *           The logic to execute when the thread is started.  Must not be {@code null}.
        *
        * @throws   NullPointerException
        *           If the instantiating thread has no security context or if {@code core} is
        *           {@code null}.
        */
       public RelatedThread(Runnable core)
       throws NullPointerException
       {
          super(core, sm.isServer() ? SecurityContextStack.getContext().related() : null);

          Thread current = Thread.currentThread();

          if (current instanceof RelatedThread)
          {
             main = ((RelatedThread) current).main;
          }
          else
          {
             main = current;

             synchronized (RelatedThread.class)
             {
                if (!storage.containsKey(main))
                {
                   storage.put(main, new HashMap<Object, Object>());
                }
             }
          }
       }

       /**
        * Obtain the common object mapped by the given key for the current thread's related thread
        * group.  If the current thread is not part of a group of related threads (either as the
        * main thread or as a {@code RelatedThread} instance), then there will be no object returned.
        *
        * @param    key
        *           Object used as a key in the lookup.
        *
        * @return   The object mapped for the related thread group or {@code null} if there is no
        *           object mapped under that key OR if the current thread is not part of a related
        *           thread group.
        */
       public synchronized static Object get(Object key)
       {
          Map<Object, Object> related = relatedStorage();

          return (related != null) ? related.get(key) : null;
       }

       /**
        * Store the common object using the given key for the current thread's related thread
        * group.  If the current thread is not part of a group of related threads (either as the
        * main thread or as a {@code RelatedThread} instance), then there will be no object stored.
        * <p>
        * <b>Do NOT use an easily guessed key (e.g. a string) for any stored resource. This storage
        * facility is by nature a public facility. Any code that can run on the current thread can
        * read a stored value if the key is known.  If you have a named resource that you need to
        * store, do not use the name as a key.  Keep your own private map of the names to keys and
        * use a new Object instance as the keys.</b>
        *
        * @param    key
        *           Object used as a key in the mapping.
        * @param    value
        *           The object to be stored.
        */
       public synchronized static void set(Object key, Object value)
       {
          Map<Object, Object> related = relatedStorage();

          if (related != null)
          {
             related.put(key, value);
          }
       }

       /**
        * Remove the mapping (both the key and the value), if one is present for that key.
        *
        * @param    key
        *           Object used as a key in the mapping.
        *
        * @return   The object which was mapped, if present, otherwise {@code null}.
        */
       public synchronized static Object remove(Object key)
       {
          Map<Object, Object> related = relatedStorage();

          if (related != null)
          {
             return related.remove(key);
          }

          return null;
       }

       /**
        * Report if this thread should create a new session when the security context is pushed or if
        * the existing session should be reused.
        *
        * @return   {@code true} if a new session must be created.
        */
       public boolean needsSession()
       {
          return false;
       }

       /**
        * Lookup the related thread storage for the given thread. If the current thread is not part
        * of a group of related threads (either as the main thread or as a {@code RelatedThread}
        * instance), then there will be no storage returned.
        *
        * @return   The related thread storage or {@code null} if this thread is not part of a group
        *           of related threads.
        */
       private synchronized static Map<Object, Object> relatedStorage()
       {
          Thread current = Thread.currentThread();

          return storage.get((current instanceof RelatedThread) ? ((RelatedThread) current).main
                                                                : current);
       }
    }