# ContextLocal.java

    /*
    ** Module   : ContextLocal.java
    ** Abstract : Container for a context local variable, using the ThreadLocal idiom
    **
    ** Copyright (c) 2006-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 ECF 20060120   @24009 Created initial version. Contains a context
    **                           local variable and exposes access to it using
    **                           the ThreadLocal idiom.
    ** 002 NVS 20060305   @24904 Made modes more reliable and complete. Now
    **                           there is STANDALONE mode (no security at all)
    **                           and distinct CLIENT and SERVER modes. CLIENT
    **                           mode utilizes a static map for storing tokens
    **                           so that all client threads share a single
    **                           virtual context.
    ** 003 ECF 20060718   @28091 Fixed memory leak. Storage was not being
    **                           cleaned up properly at end of context life
    **                           in server and client modes.
    ** 004 NVS 20070328   @32636 Fixed a bug in CLIENT mode implementation.
    **                           The static map for the tokens should be
    **                           allocated only once.
    ** 005 ECF 20070708   @34417 Optimized get() and set(). Only retrieve the
    **                           Wrapper object once, rather than checking for
    **                           its existence first and then retrieving.
    ** 006 ECF 20070921   @35244 Reimplemented as generic.
    ** 007 ECF 20071025   @35525 Made setting of mode threadsafe. Changes
    **                           prevent a potential race condition in
    **                           setMode().
    ** 008 ECF 20080506   @38221 Fixed setMode(). Race condition was still
    **                           possible, because mode was being set before
    **                           all backing support was initialized. Mode is
    **                           now set as the last action of setMode().
    ** 009 CA  20101217          Allow multiple direct session on client nodes -
    **                           the client-side context storage access must be
    **                           synchronized.
    ** 010 OM  20121016          Added comment description about how to avoid
    **                           issue #1577.
    ** 011 CA  20130628          Added possibility to reset a context (needed by appservers running in
    **                           State-reset operating mode).
    ** 012 ECF 20131113          Added a check during context cleanup in server and client modes,
    **                           which throws ContextLocalCleanupException if the get or set methods
    **                           are invoked during context cleanup, as this is a potentially
    **                           dangerous action (particularly in server mode, since the security
    **                           context will have been popped by that time, leading to unpredictable
    **                           results). Also did some minor cleanup of generics.
    ** 013 CA  20140513          Added some debug helper APIs, to collect dependencies between
    **                           context-local vars.  Added get(create), which conditionally
    **                           initializes the context-local var, if not already set.
    ** 014 HC  20141005          Added the possibility to split the context local value
    **                           initialization from its instantiation.
    ** 015 HC  20141106          Javadocs fixed
    ** 016 CA  20150131          Clean the security caches when client restarts.
    ** 017 CA  20150228          Ignore the UI Proxy frame classes during debug.
    ** 018 ECF 20151111          Performance fix: minimize native calls from frequently invoked
    **                           methods.
    ** 019 EVL 20160224          Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
    ** 020 IAS 20160408          Use synthetic key as context key.
    ** 021 EVL 20160411          Javadoc fixes.
    ** 022 GES 20190322          On conversation threads, we know that the security context will
    **                           never change. This means it is safe to use a ThreadLocal cache for
    **                           get() which is MUCH faster than the SecurityManager lookup. In all
    **                           initialization, termination or set operations, the ThreadLocal will
    **                           be updated.  The reference is still kept inside the SecurityManager
    **                           so that other (non-conversation) threads can still access this state
    **                           as before AND so that the proper session cleanup can still occur.
    **                           We are trading a small amount of memory and extra processing for
    **                           init/term/set to make get much faster.  It is worth it.
    ** 023 ECF 20220515          Make get(boolean create) work consistently in MODE_STANDALONE, if value
    **                           previously was set to null; that is, ensure initialValue() is invoked instead of
    **                           returning null, when the 'create' parameter is true. The difference was caused
    **                           by the fact that a null value in the fallback implementation is different than
    **                           an uninitialized thread-local variable. initialValue() is only invoked once for
    **                           a thread-local, unless remove() is called.
    **     CA  20221031          Added JMX instrumentation for the 'get()' method.
    ** 024 AL2 20230216          Avoid using an array to store the return value of the JMX instrumentation
    **     CA  20230224          The JMX instrumentation was removed in the 'ContextLocal.get()' as this gets
    **                           invoked millions of times during conversion (TRPL processing) of dynamic queries.
    **                           The cause is assumed to be the lambda expression capturing the local variable,
    **                           and gets re-built on each invocation.
    ** 025 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    ** 026 GBB 20230825          SecurityManager context methods calls updated.
    ** 027 HC  20240222          Enabled JMX on FWD Client.
    ** 028 CA  20240829          'fallback' field needs to be a ThreadLocal instance which always exists.
    **                           Otherwise, as a certain ContextLocal instance is global per JVM, race conditions
    **                           can appear between i.e. Dispatcher and Conversation threads, where 'fallback' can
    **                           be set to null by Dispatcher while Conversation has just assigned a value.
    ** 029 CA  20240903          For standalone mode, 'fallback' thread-local var must be assigned on first usage
    **                           in get/set.
    */

    /*
    ** 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.io.*;
    import java.util.*;

    import com.goldencode.p2j.jmx.*;
    import com.goldencode.p2j.net.SessionManager;  // avoid name conflicts
    import com.goldencode.p2j.util.logging.*;

    /**
     * An implementation of a container for an arbitrary object reference, stored
     * context locally.  Uses the same idiom used by the <code>ThreadLocal</code>
     * class in J2SE, in terms of setting and accessing the reference, and
     * generating an initial value for the container.  This implementation adds
     * an additional feature:  context-specific finalization.  Subclasses can be
     * hooked at the end of a context's life by overriding the {@link #cleanup}
     * method to perform any necessary housekeeping.
     * <p>
     * This class serves a dual role, in that it falls back to a mode which uses
     * a <code>ThreadLocal</code> as backing storage if not running within a
     * security context.  When in server mode, it uses the context-specific
     * token-storing services of <code>SecurityManager</code>.  More recently, a
     * client mode has been added (see below).
     * <p>
     * <b>Server Mode</b><br>
     * This is the mode used in production server use cases (i.e., when a security
     * environment is present).  When running in this mode, the implementation
     * relies upon the following <code>SecurityManager</code> methods:
     * <ul>
     *   <li>{@link SecurityManager.ContextSecurityManager#getToken(ContextKey)}
     *   <li>{@link SecurityManager.ContextSecurityManager#addToken(ContextKey, Object)}
     *   <li>{@link SecurityManager.ContextSecurityManager#removeToken(ContextKey)}
     * </ul>
     * <p>
     * An instance of this class uses itself as the lookup key for these methods.
     * We thus rely upon object identity to guarantee a lookup key is unique.
     * Because of this, this class' {@link #equals(Object)} and {@link
     * #hashCode()} methods are identity-based implementations which may not be
     * overridden.
     * <p>
     * <b>Fallback Mode</b><br>
     * This mode, which stores the object reference in a <code>ThreadLocal</code>,
     * is useful in cases where subsystem code which uses a context local
     * variable is executed in a standalone mode, where no security environment
     * has been created.  In this mode, since there is no security context
     * present, there is no context finalization.  In this mode, the
     * <code>cleanup</code> method will NOT be executed when the local thread
     * expires.  This mode is <i>only</i> used if no <code>SecurityManager</code>
     * instance exists.
     * <p>
     * <b>Client Mode</b><br>
     * This mode is used in a production, client environment (i.e., a security
     * manager is available and it reports that it is a client).  This mode
     * assumes a single context and uses a simple map as backing storage.  As
     * with server mode, an instance of this class uses itself as the lookup key.
     *
     * <p>
     * <strong>Note</strong><br>
     * Do not use <code>ContextLocal</code> object references in the
     * <code>cleanup()</code> method of <code>Cleanable</code> objects as, at the
     * time of this call, the context-local variables are already deleted from the
     * SecurityContext (inside the <code>SecurityContext.cleanup()</code> method).
     * <p>
     * <strong>Value initialization</strong>
     * Optionally, the stored object may implement {@link Initializable} to separate
     * instantiation from object initialization. This may be useful in cases
     * the initialization logic of the stored object causes a recursive call to
     * {@linkplain #get()} which would end up in multiple instances to be registered
     * in the context. With the implemented interface the initialization life cycle
     * is as follows: {@linkplain #get()} is called, {@linkplain #initialValue()} is
     * invoked to retrieve the initial value, the value is registered in the context,
     * {@linkplain Initializable#init()} is invoked on the initial value.
     * When exception is thrown during the call to {@linkplain Initializable#init()}
     * the value is removed from the context and the exception is re-raised.
     * @param   <T>
     *          Type of object stored context locally.
     */
    public class ContextLocal<T>
    extends ContextKey
    implements WeightedToken
    {
       /** Logger */
       private static final CentralLogger LOG = CentralLogger.get(ContextLocal.class);

       /** Instrumentation for {@link #get(boolean)}. */
       private static final NanoTimer GET = NanoTimer.getInstance(FwdServerJMX.TimeStat.ContextLocalGet);

       /** Constant indicating storage mode has not been set */
       private static final int MODE_UNSET = 0;

       /** Constant indicating security context based storage mode */
       private static final int MODE_SERVER = 1;

       /** Constant indicating client side storage mode */
       private static final int MODE_CLIENT = 2;

       /** Constant indicating thread local based storage mode */
       private static final int MODE_STANDALONE = 3;

       /** Client side storage for security context tokens */
       private static Map contextStorage = null;

       /** Flag indicating if dependencies should be collected or not, by {@link #checkCaller}. */
       private static boolean debug = false;

       /** Map holding dependencies.  Used only if {@link #debug} is enabled. */
       private static final Map<String, Set<String>> dependencies = new TreeMap<>();

       /** Security manager instance (null if no security environment exists) */
       private SecurityManager security = null;

       /** An instance of our fallback (ThreadLocal-based) storage mechanism */
       private final ThreadLocal<Fallback> fallback = new ThreadLocal<>();

       /** Storage mode */
       private volatile int mode = MODE_UNSET;

       /**
        * Default constructor.
        */
       public ContextLocal()
       {
          // check who calls this
          if (debug)
          {
             checkCaller(this.getClass());
          }
       }

       /**
        * Clean the storage caches.
        */
       protected static synchronized void clearInstance()
       {
          dependencies.clear();
          if (contextStorage != null)
          {
             contextStorage.clear();
          }
       }

       /**
        * If {@link #debug} mode is enabled, dump the dependencies to STDOUT.
        */
       protected static void dumpDependencies()
       {
          if (!debug)
          {
             return;
          }

          try
          {
             String prefix = "   ";
             PrintWriter bw = new PrintWriter(new FileWriter("context-local.gml"));
             bw.write("graph\n");
             bw.write("[\n");
             bw.write(prefix + "directed 1\n");

             Map<String, Integer> ids = new HashMap<>();
             int id = 0;

             // write nodes
             for (String node : dependencies.keySet())
             {
                id = id + 1;
                ids.put(node, id);

                bw.write(String.format("%snode\n", prefix));
                bw.write(String.format("%s[\n", prefix));
                bw.write(String.format("%s%sid %d\n", prefix, prefix, id));
                bw.write(String.format("%s%slabel \"%s\"\n", prefix, prefix, node));
                bw.write(String.format("%s]\n", prefix));

                Set<String> parents = dependencies.get(node);
                for (String parent : parents)
                {
                   if (ids.containsKey(parent))
                      continue;

                   id = id + 1;
                   ids.put(parent, id);

                   bw.write(String.format("%snode\n", prefix));
                   bw.write(String.format("%s[\n", prefix));
                   bw.write(String.format("%s%sid %d\n", prefix, prefix, id));
                   bw.write(String.format("%s%slabel \"%s\"\n", prefix, prefix, parent));
                   bw.write(String.format("%s]\n", prefix));
                }
             }

             // write edges
             for (String child : dependencies.keySet())
             {
                int idch = ids.get(child);
                Set<String> parents = dependencies.get(child);

                for (String parent : parents)
                {
                   int idp = ids.get(parent);

                   bw.write(String.format("%sedge\n", prefix));
                   bw.write(String.format("%s[\n", prefix));
                   bw.write(String.format("%s%ssource %d\n", prefix, prefix, idp));
                   bw.write(String.format("%s%starget %d\n", prefix, prefix, idch));
                   bw.write(String.format("%s]\n", prefix));
                }
             }

             bw.write("]\n");
             // open the file with yEd

             bw.close();
          }
          catch (Exception e)
          {
             LOG.severe("", e);
          }
       }

       /**
        * Set the debug mode, to enable or stop caller dependency checks.
        * <p>
        * It will clear all existing dependencies.
        * <p>
        * WARNING! Do not enable debugging in a production environment, as the {@link #checkCaller}
        * API is very time expensive!
        *
        * @param    state
        *           The new debug state.
        */
       protected static void setDebug(boolean state)
       {
          debug = state;
          dependencies.clear();
       }

       /**
        * Debug aid method to check the dependencies between context-local vars.
        * <p>
        * For each variable, it will load the back-dependencies (i.e. context-local variables which
        * access this context-local var).
        *
        * @param    localVar
        *           The type of the context local variable being accessed.
        */
       private static void checkCaller(Class<? extends ContextLocal> localVar)
       {
          String thisClsName = localVar.getName();
          StackTraceElement[] stack = Thread.currentThread().getStackTrace();
          for (StackTraceElement e : stack)
          {
             try
             {
                String clsName = e.getClassName();

                if (!clsName.startsWith("com.goldencode")                     ||
                    clsName.startsWith("com.goldencode.p2j.persist.$__Proxy") ||
                    clsName.startsWith("com.goldencode.p2j.ui.$__Proxy")      ||
                    clsName.equals(thisClsName)                               ||
                    clsName.equals(ContextLocal.class.getName()))
                {
                   continue;
                }

                Class<?> cls = Class.forName(e.getClassName());

                if (ContextLocal.class.isAssignableFrom(cls))
                {
                   synchronized (ContextLocal.class)
                   {
                      String key = thisClsName.substring("com.goldencode.p2j.".length());
                      Set<String> parents = dependencies.get(key);

                      if (parents == null)
                      {
                         dependencies.put(key, parents = new TreeSet<>());
                      }

                      String val = clsName.substring("com.goldencode.p2j.".length());
                      parents.add(val);
                   }
                }
             }
             catch (ClassNotFoundException e1)
             {
                LOG.severe("", e1);
             }
          }
       }

       /**
        * Get the weight of this context-local var.  The variables will be ordered by their
        * {@link WeightFactor weight}, when security context is being reset.  Note that the security
        * context reset can't ensure a specific order among vars with the same weight.
        *
        * @return   The weight of this context-local var.  By default, it returns
        *           {@link WeightFactor#NO_DEPENDENCY} - thus, unless overwritten, the context-local
        *           var will be cleaned up first.
        */
       @Override
       public WeightFactor getWeight()
       {
          return WeightFactor.NO_DEPENDENCY;
       }

       /**
        * Get the value of the current context's copy of this context local
        * variable.  The first time this method is invoked, if no value has yet
        * been set into this container, {@link #initialValue} will be invoked.
        *
        * @return  Context local variable.
        */
       public final T get()
       {
          return getImpl(true);
       }

       /**
        * Get the value of the current context's copy of this context local
        * variable.  The first time this method is invoked, if no value has yet
        * been set into this container, {@link #initialValue} will be invoked.
        *
        * @param   create
        *          Flag indicating if the context local variable needs to be created, if it was not
        *          already set.
        *
        * @return  Context local variable.
        */
       public final T get(boolean create)
       {
          // CA: the JMX instrumentation was removed as the 'ContextLocal.get()' gets invoked millions of times
          // during conversion (TRPL processing) of dynamic queries.  The cause is assumed to be the lambda
          // expression capturing the local variable, and gets re-built on each invocation.
          // return GET.timerWithReturn(() -> getImpl(create));
          return getImpl(create);
       }

       /**
        * Get the value of the current context's copy of this context local
        * variable.  The first time this method is invoked, if no value has yet
        * been set into this container, {@link #initialValue} will be invoked.
        *
        * @param   create
        *          Flag indicating if the context local variable needs to be created, if it was not
        *          already set.
        *
        * @return  Context local variable.
        */
       private T getImpl(boolean create)
       {
          // check who calls this
          if (debug)
          {
             checkCaller(this.getClass());
          }

          if (mode == MODE_UNSET)
          {
             setMode();
          }

          T value = null;

          if (mode == MODE_STANDALONE)
          {
            Fallback f = fallback.get();
            if (f == null)
            {
               f = new Fallback();
               fallback.set(f);
            }
            value = f.get();

            if (value == null && create)
            {
               value = initialValue();
               set(value);
               initializeValue(value);
            }
          }
          else if (mode == MODE_SERVER)
          {
             // use the optimized get from ThreadLocal if it exists
             Fallback f = fallback.get();
             if (f != null)
             {
                value = f.get();
             }

             if (value == null)
             {
                // slower SecurityManager approach
                Wrapper w = (Wrapper) security.contextSm.getToken(this);

                if (w != null)
                {
                   value = w.get();
                }
             }

             if (f == null && value != null && hasReadCache())
             {
                // re-create the cache if someone invalidated it
                f = new ReadCache();
                f.set(value);
                fallback.set(f);
             }

             if (value == null && create)
             {
                value = initialValue();
                set(value);
                initializeValue(value);
             }
          }
          else
          {
             synchronized (contextStorage)
             {
                Wrapper w = (Wrapper) contextStorage.get(this);
                if (w != null)
                {
                   value = w.get();
                }
                else if (create)
                {
                   value = initialValue();
                   set(value);
                   initializeValue(value);
                }
             }
          }

          return value;
       }

       /**
        * Set the current value of this context local variable.
        *
        * @param   value
        *          Current value.
        */
       public final void set(T value)
       {
          // check who calls this
          if (debug)
          {
             checkCaller(this.getClass());
          }

          if (mode == MODE_UNSET)
          {
             setMode();
          }

          if (mode == MODE_STANDALONE)
          {
             Fallback f = fallback.get();
             if (f == null)
             {
                f = new Fallback();
                fallback.set(f);
             }
             f.set(value);
             return;
          }

          if (mode == MODE_SERVER)
          {
             Wrapper w = (Wrapper) security.contextSm.getToken(this);

             Fallback f = fallback.get();

             if (w != null)
             {
                w.set(value);

                // if we have a ThreadLocal cache (for get()), propagate changes there
                if (f != null)
                {
                   if (hasReadCache())
                   {
                      f.set(value);
                   }
                   else
                   {
                      // let the conversation thread re-create it when it reads it next.
                      fallback.set(null);

                      // release local var, too
                      f = null;
                   }
                }
             }
             else
             {
                security.contextSm.addToken(this, new Wrapper(value));

                // if we have a ThreadLocal cache (for get()), propagate changes there
                if (f != null)
                {
                   if (hasReadCache())
                   {
                      f.set(value);
                   }
                   else
                   {
                      // let the conversation thread re-create it when it reads it next.
                      fallback.set(null);

                      // release local var, too
                      f = null;
                   }
                }
             }
          }
          else
          {
             synchronized (contextStorage)
             {
                Wrapper w = (Wrapper) contextStorage.get(this);
                if (w != null)
                {
                   w.set(value);
                }
                else
                {
                   contextStorage.put(this, new Wrapper(value));
                }
             }
          }
       }

       /**
        * This method will be invoked to retrieve the initial value of this
        * context local variable, the first time {@link #get} is invoked (but
        * only if {@link #set} was not previously invoked.  The returned value
        * will be stored as the variable's initial value.
        * <p>
        * This default implementation simply returns <code>null</code>.
        * Subclasses which require a different initial value must override this
        * method.
        *
        * @return  <code>null</code> by default.
        */
       protected T initialValue()
       {
          return null;
       }

       /**
        * This method is invoked at the end of the security context's life.  It
        * should be overridden by subclasses which need to perform cleanup at
        * this point.  Implementors should not use security specific services at
        * this point.
        *
        * @param   value
        *          The last value this context local variable contained.
        */
       protected void cleanup(T value)
       {
       }

       /**
        * Check if this instance is allowed to be reset.
        *
        * @return   <code>true</code> by default.
        */
       protected boolean isResetAllowed()
       {
          return true;
       }

       /**
        * Performs value initialization. When <code>value</code> implements {@link Initializable}
        * the method calls its {@link Initializable#init()}. When an exception is thrown inside
        * the init routine, the value is removed from the context and the exception is re-raised.
        *
        * @param   value
        *          The value to be initialized.
        */
       private void initializeValue(T value)
       {
          if (value instanceof Initializable)
          {
             try
             {
                ((Initializable) value).init();
             }
             catch (Exception ex)
             {
                if (mode == MODE_STANDALONE)
                {
                   fallback.remove();
                }
                else if (mode == MODE_SERVER)
                {
                   security.contextSm.removeToken(this);

                   // if we have a ThreadLocal cache (for get()), cleanup
                   fallback.remove();
                }
                else
                {
                   synchronized (contextStorage)
                   {
                      contextStorage.remove(this);
                   }
                }

                throw ex;
             }
          }
       }

       /**
        * Set the mode used to store the local copy of the variable.  If no
        * security manager has been created, thread local storage is used, else
        * security context storage is used.
        */
       private synchronized void setMode()
       {
          // Resolve possible race condition.
          if (mode != MODE_UNSET)
          {
             return;
          }

          security = SecurityManager.getInstance();

          if (security == null)
          {
             fallback.set(new Fallback());
             mode = MODE_STANDALONE;
             return;
          }

          if (security.isServer())
          {
             // if conversation mode, this is a long running thread and we can cache
             // data in the thread local, this will optimize get()
             if (hasReadCache())
             {
                fallback.set(new ReadCache());
             }

             mode = MODE_SERVER;
          }
          else
          {
             if (contextStorage == null)
             {
                contextStorage = new HashMap();
             }
             mode = MODE_CLIENT;
          }
       }

       /**
        * Check if the current thread can use the read cache of the context's value.
        *
        * @return   See above.
        */
       private static boolean hasReadCache()
       {
          return SessionManager.isConversationThread();
       }

       /**
        * An instance of this class is used to wrap the variable value when it is
        * stored in the security context.  Its primary purpose is to implement
        * the {@link #cleanup} method, which the current security context will
        * invoke at the end of its life.  Only one instance of this class is
        * created per context, per variable, the first time a value needs to be
        * stored.  Thereafter, each time the variable's value is updated, the
        * contents of this wrapper are merely reset.
        */
       private class Wrapper
       implements Cleanable
       {
          /** Data value stored by this context local variable */
          private T contents = null;

          /** Flag indicating we are in cleanup mode */
          private boolean cleaning = false;

          /**
           * Convenience constructor which assigns contents.
           *
           * @param   contents
           *          Data value stored by this context local variable
           */
          Wrapper(T contents)
          {
             set(contents);
          }

          /**
           * Invoked at security context end of life.  Removes this token from
           * storage and invokes the enclosing class' <code>cleanup</code>
           * method, to give subclasses a chance to perform housekeeping.
           */
          public void cleanup()
          {
             cleaning = true;

             try
             {
                ContextLocal.this.cleanup(contents);
             }
             finally
             {
                cleaning = false;
                switch (mode)
                {
                   case MODE_SERVER:
                      security.contextSm.removeToken(ContextLocal.this);

                      // if we have a ThreadLocal cache, cleanup
                      fallback.remove();
                      break;
                   case MODE_CLIENT:
                      contextStorage.remove(ContextLocal.this);
                      break;
                }
             }
          }

          /**
           * Get the contents of the context local variable.
           *
           * @return  Current contents of the variable.
           *
           * @throws  ContextLocalCleanupException
           *          if this method is invoked during context cleanup.
           */
          T get()
          {
             checkCleaning();

             return contents;
          }

          /**
           * Set the contents of the context local variable.
           *
           * @param   contents
           *          Contents to be stored in the variable.
           *
           * @throws  ContextLocalCleanupException
           *          if this method is invoked during context cleanup.
           */
          void set(T contents)
          {
             checkCleaning();

             this.contents = contents;
          }

          /**
           * Throw an exception indicating a get or set attempt was made during context cleanup.
           *
           * @throws  ContextLocalCleanupException
           *          if and only if we are in cleanup mode.
           */
          private void checkCleaning()
          {
             if (cleaning)
             {
                throw new ContextLocalCleanupException(
                   "Cannot get or set context local value during context cleanup");
             }
          }
       }

       /**
        * A wrapper of the context-local value kept in the {@link ContextLocal#fallback} thread-local storage.
        * This is used as backing storage for this context local variable when no security environment is present.
        * This implementation is necessary to allow the fallback thread local variable's <code>initialValue</code>
        * method to invoke the enclosing class' implementation (which may have been overridden by a subclass).
        */
       private class Fallback
       {
          /** Cached value for this context-local instance. */
          private T value = null;

          /**
           * Retrieved the cached {@link #value}.  If not set, call {@link #initialValue()}.
           */
          public T get()
          {
             if (value == null)
             {
                value = initialValue();
             }
             return value;
          }

          /**
           * Set the {@link #value}.
           *
           * @param    value
           *           The current value for this context-local instance.
           */
          public void set(T value)
          {
             this.value = value;
          }

          /**
           * Reroute an invocation of thread local's <code>initialValue</code>
           * implementation to the enclosing class' method of the same name.
           *
           * @return  The initial value returned by the enclosing class'
           *          <code>initialValue</code> implementation.
           */
          protected T initialValue()
          {
             T value = ContextLocal.this.initialValue();
             initializeValue(value);
             return value;
          }
       }

       /**
        * An extension of {@link Fallback} which is used as the backing storage for a read cache to optimize
        * {@link ContextLocal#get()}.
        */
       private class ReadCache
       extends Fallback
       {
          /**
           * No <code>initialValue</code> is needed since it will be done in the normal
           * server-side implementation.  This is a NOP.
           *
           * @return  Always <code>null</code>.
           */
          @Override
          protected T initialValue()
          {
             return null;
          }
       }
    }