# RemoteObject.java

    /*
    ** Module   : RemoteObject.java
    ** Abstract : provides a simple mechanism to implement a remote interface
    **
    ** Copyright (c) 2006-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ------------------Description-------------------
    ** 001 GES 20060209   @24506 First version which can handle the export,
    **                           import and transaction processing for a
    **                           given list of interfaces. This handles all
    **                           of the requirements for creating a fully
    **                           functional remote interface, just by
    **                           defining and implementing a Java interface
    **                           as if it was a local object.  No net package
    **                           processing is needed.
    ** 002 GES 20060211   @24544 Added support for a configurable timeout,
    **                           and now defaults the timeout to 0.
    ** 003 NVS 20060222   @24623 Fixed bug in findMatchingStaticMethod so now
    **                           static methods are properly exported.
    ** 004 NVS 20060614   @27195 Implemented deferred registrations. Requests
    **                           to register interfaces are deferred if by the
    **                           time of call the Dispatcher is not running.
    **                           A new method registerDeferred() then can be
    **                           used to perform all deferred registrations
    **                           at once in the order they were received.
    ** 005 GES 20060717   @28152 Added logging of all method calls if level
    **                           FINER is specified. Added parameter and
    **                           return value logging if FINEST is specified.
    ** 006 GES 20061219   @31721 Removed deferred registration processing
    **                           since this is no longer needed. Added some
    **                           safety code to the invocation handler to
    **                           map calls to methods defined in Object to
    **                           the local instance since these are not remote
    **                           APIs.  For example, this allows such proxy
    **                           objects to be stored in hash maps.
    ** 007 GES 20070110   @31793 Fix to eliminate infinite recursion when
    **                           calling hashCode, toString or equals methods
    **                           on a remote proxy object.
    ** 008 GES 20070115   @31836 Match minor interface change in base class.
    ** 009 NVS 20070312   @32380 Added some missing logging of the exported
    **                           methods.
    ** 010 GES 20070406   @32849 Reworked H009 to remove security manager
    **                           dependency (which was inappropriate) and
    **                           to improve performance of the logging code
    **                           in the case where no logging will actually
    **                           occur.
    ** 011 ECF 20071102   @35886 Refactored net package. Replaced Queue with
    **                           Session. Use SessionManager instead of
    **                           SecurityManager.
    ** 012 CA  20090824   @43719 Errors caused by InterruptedException should
    **                           be handled separately, and be packed in a
    **                           generic RuntimeException. This fixes CTRL+C
    **                           pressed while retrieving the routing key for
    **                           an export.
    ** 013 SIY 20090831   @43796 Carefully ignore IllegalStateException.
    ** 014 CA  20090917   @43929 Fix abnormal connection end. If the routing key
    **                           can't be obtained or an IllegalStateException is
    **                           encountered, a SilentUnwindException will be
    **                           thrown, as the communication with the client
    **                           wasn't possible.
    ** 015 GES 20111012          Moved common invocation handler logic to a common
    **                           base class. Shifted HLO usage to a contained
    **                           member.
    ** 016 GES 20111018          Added local redirection support.
    ** 017 GES 20111111          Separated local and remote instance server
    **                           registration. The two use cases are never used
    **                           simultaneously (at least right now), so there is
    **                           no reason to do both in one call. It makes the
    **                           caller a bit more complex, but with the advantage
    **                           of having less overhead/memory usage.
    ** 018 CA  20111209          Added possibility to deregister a static network
    **                           server.
    ** 019 CA  20131218          Added logging for cases when a method backed by a static proxy can't
    **                           be resolved.
    ** 020 CA  20140513          Added a weight for the context-local var, to ensure predetermined
    **                           order during context reset.
    ** 021 ECF 20150715          Replace StringBuffer with StringBuilder.
    ** 022 VVT 20200203          registerStaticNetworkServer(): fixed error handling logic;
    **                           Missing @Override annotation added.
    **                           findMatchingStaticMethod(): code cleanup
    ** 023 CA  20220515          The active session is now set for leaf sessions, too - this is required for a
    **                           change in RemoteObject.obtainInstance, where first a check is done for an existing
    **                           local proxy, and after that a network instance is obtained (a requirement for the
    **                           server-side OS resources support, like memptr).
    ** 024 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    ** 025 GBB 20230517          Redundant isLoggable check removed.
    ** 026 GBB 20230725          RemoteAccess tracing flag replaced by direct call to LOG.isLoggable.
    ** 027 ICP 20240705          Fixed method of obtaining the class loader.
    */
    /*
    ** 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.net;

    import java.lang.reflect.*;
    import java.util.*;
    import java.util.logging.*;
    import com.goldencode.p2j.classloader.MultiClassLoader;
    import com.goldencode.p2j.security.*;
    import com.goldencode.p2j.util.*;
    import com.goldencode.p2j.util.logging.*;

    /**
     * Helper class which can handle the export, import and transaction
     * processing for a given list of interfaces. This handles all of the
     * requirements for creating a fully functional remote interface, just by
     * defining and implementing a Java interface as if it was a local object.
     * <p>
     * No net package processing is needed in the implementation class or in
     * the interface.  This class is designed for convenience.  Usage avoids the
     * tedious, hard-coded requirements of exporting and importing network
     * interfaces.  To use it:
     * <ol>
     *    <li> Define an interface which provides the service or API required.
     *    <li> Create one or more implementation classes that implement the
     *         interface, according to your application needs.
     *    <li> On the network node which provides (exports) the API, use one of
     *         the following mechanisms:
     *    <ul>
     *       <li> Instantiate an instance of every object you wish to export.
     *            For each object, call the {@link #registerNetworkServer} method
     *            passing the list of interfaces to be exported and the instance
     *            that provides the implementation.  This exports your interface.
     *            Calls will now be dispatched to this object with no further
     *            work.
     *       <li> Call the {@link #registerStaticNetworkServer} method with the
     *            list of interfaces to be exported and the class object that
     *            provides the <code>static</code> implementation.  This exports
     *            your interface.  Calls will now be dispatched to the
     *            <code>static</code> methods of that class with no further work.
     *    </ul>
     *    <li> On the network node that needs to remotely access the interface,
     *         call the {@link #obtainNetworkInstance} method passing the
     *         interface that is being requested. This assumes that a connection
     *         to the remote node already exists.  The object returned will
     *         provide transparent access to the remote API with no further
     *         effort.
     *    <li> It is possible to use local redirection. This is non-network usage
     *         where both sides of the invocation chain are in the same JVM. This
     *         allows systems which were designed to be split over the network to
     *         also be used within a single JVM without any networking in between.
     *         The registration is the same as above (for the exports).  But to
     *         obtain an instance of the requested interface, the user calls the
     *         {@link #obtainLocalInstance} method. No network connection or
     *         session is needed.
     * </ol>
     */
    public class RemoteObject
    {
       /** Logger (safe as a JVM-wide value rather than as context-local). */
       private static final CentralLogger LOG =
          CentralLogger.get(RemoteObject.class.getName());

       /** Stores context-local state variables. */
       private static final ContextLocal<WorkArea> work =
       new ContextLocal<WorkArea>()
       {
          @Override
          public WeightFactor getWeight()
          {
             return WeightFactor.LAST;
          };

          @Override
          protected synchronized WorkArea initialValue()
          {
             return new WorkArea();
          }
       };

       /** Stores static server method mappings (global for the server JVM). */
       private static final Map<Class<?>, Map<Method, Method>> localStaticServers = new HashMap<>();

       /** Stores static server mappings (global for the server JVM). */
       private static final Map<Class<?>, Class<?>> localImplStaticServers = new HashMap<>();

       /**
        * Exports all methods of the given interface from the local node (to
        * other nodes). All remote calls to these exported methods will be
        * subsequently dispatched to the given implementation instance which
        * is required to implement the exported interface.
        *
        * @param    iface
        *           The interface to export.  It will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    impl
        *           The instance of an object that implements the exported
        *           interface.  All method calls to the exported methods will be
        *           redirected to the given instance.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerNetworkServer(Class<?> iface, Object impl)
       {
          return registerNetworkServer(new Class[] { iface }, impl, null);
       }

       /**
        * Exports all methods of the given interface from the local node (to
        * other nodes). All remote calls to these exported methods will be
        * subsequently dispatched to the given implementation instance which
        * is required to implement the exported interface.
        *
        * @param    iface
        *           The interface to export.  It will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    impl
        *           The instance of an object that implements the exported
        *           interface.  All method calls to the exported methods will be
        *           redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerNetworkServer(Class<?> iface,
                                                  Object   impl,
                                                  Object   modToken)
       {
          return registerNetworkServer(new Class[] { iface }, impl, modToken);
       }

       /**
        * Exports all methods in the given list of interfaces from the local node
        * (to other nodes).  All remote calls to these exported methods will be
        * subsequently dispatched to the given implementation instance which
        * is required to implement all of the exported interfaces.
        *
        * @param    list
        *           The interfaces to export.  Each one will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the exported
        *           methods will be redirected to the given instance.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerNetworkServer(Class<?>[] list, Object impl)
       {
          return registerNetworkServer(list, impl, null);
       }

       /**
        * Exports all methods in the given list of interfaces from the local node
        * (to other nodes).  All remote calls to these exported methods will be
        * subsequently dispatched to the given implementation instance which
        * is required to implement all of the exported interfaces.
        *
        * @param    list
        *           The interfaces to export.  Each one will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the exported
        *           methods will be redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerNetworkServer(Class<?>[] list,
                                                  Object     impl,
                                                  Object     modToken)
       {
          // check to see if the Dispatcher is up
          if (!Dispatcher.isRunning())
          {
             throw new IllegalStateException("Must only be called AFTER the " +
                                             "Dispatcher is initialized.");
          }

          if (modToken == null)
          {
             // create and use the same instance for all registrations associated
             // with this same method call
             modToken = new Object();
          }

          // loop through interface list
          for (int i = 0; i < list.length; i++)
          {
             String   group   = calcGroupName(list[i]);
             Method[] methods = calcExportedMethods(list[i]);

             for (int j = 0; j < methods.length; j++)
             {
                String name = calcKeyName(methods[j]);

                // generate a routing key
                RoutingKey key = Dispatcher.generateRoutingKey(group, name);

                if (key == null)
                {
                   throw new RuntimeException("Unable to get routing key for " +
                                              group + ":" + name);
                }

                // add the method to the dispatcher
                Dispatcher.addMethod(key, impl, methods[j], modToken);
                logExport(group, name);
             }
          }

          return modToken;
       }

       /**
        * Register all methods in the given interface from the local node as a
        * local server.  These interfaces are only available in-process, they
        * will not be accessible via remote access.
        *
        * @param    iface
        *           The interface to register.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the registered
        *           methods will be redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerLocalServer(Class<?> iface,
                                                Object   impl,
                                                Object   modToken)
       {
          return registerLocalServer(new Class[] { iface }, impl, modToken);
       }

       /**
        * Register all methods in the given list of interfaces from the local node
        * as a local server.  These interfaces are only available in-process, they
        * will not be accessible via remote access.
        *
        * @param    list
        *           The interfaces to register.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the registered
        *           methods will be redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerLocalServer(Class<?>[] list,
                                                Object     impl,
                                                Object     modToken)
       {
          if (modToken == null)
             modToken = new Object();

          // loop through interface list
          for (int i = 0; i < list.length; i++)
          {
             Method[] methods = calcExportedMethods(list[i]);

             WorkArea wa = work.get();

             // save off context-local state to allow local redirection
             synchronized (wa)
             {
                Object authToken = wa.modTokens.get(list[i]);

                if (authToken == null || authToken == modToken)
                {
                   Set<Method> set = new HashSet<Method>(Arrays.asList(methods));
                   Set<Method> ro  = Collections.unmodifiableSet(set);
                   wa.localInstanceMethods.put(list[i], ro);
                   wa.localInstanceServers.put(list[i], impl);
                }
             }
          }

          return modToken;
       }

       /**
        * Register all methods in the given interface from the local node as EITHER:
        * <p>
        * <ol>
        *    <li> If <code>single</code> is <code>true</code> then the methods
        *         will be exported for remote access using
        *         {@link #registerNetworkServer}.
        *    <li> If <code>single</code> is <code>false</code> then the methods
        *         will be exported for local access using
        *         {@link #registerLocalServer}.
        * </ol>
        *
        * @param    iface
        *           The interface to register.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the registered
        *           methods will be redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        * @param    local
        *           <code>true</code> to register purely on a local basis (no
        *           remote/network access to these methods).
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerServer(Class<?> iface,
                                           Object   impl,
                                           Object   modToken,
                                           boolean  local)
       {
          return registerServer(new Class[] { iface }, impl, modToken, local);
       }

       /**
        * Register all methods in the given interface from the local node as EITHER:
        * <p>
        * <ol>
        *    <li> If <code>single</code> is <code>true</code> then the methods
        *         will be exported for remote access using
        *         {@link #registerNetworkServer}.
        *    <li> If <code>single</code> is <code>false</code> then the methods
        *         will be exported for local access using
        *         {@link #registerLocalServer}.
        * </ol>
        *
        * @param    list
        *           The interfaces to register.
        * @param    impl
        *           The instance of an object that implements all of the
        *           exported interfaces.  All method calls to the registered
        *           methods will be redirected to the given instance.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        * @param    local
        *           <code>true</code> to register purely on a local basis (no
        *           remote/network access to these methods).
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerServer(Class<?>[] list,
                                           Object     impl,
                                           Object     modToken,
                                           boolean    local)
       {
          if (local)
          {
             return registerLocalServer(list, impl, modToken);
          }
          return registerNetworkServer(list, impl, modToken);
       }

       /**
        * Exports all <code>static</code> methods in the given backing class that
        * exactly match the method signatures in the given list of interfaces
        * from the local node (to other nodes).  All remote calls to these
        * exported methods will be subsequently dispatched to the given
        * implementation class.
        * <p>
        * The nature of Java interfaces precludes a <code>static</code>
        * implementation.  This means that the backing class can't use the
        * <code>implements</code> keyword even though it must have an exact
        * match of the exported methods actually implemented.
        *
        * @param    list
        *           The interfaces to export.  Each one will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    backing
        *           The class that implements all of the exported interfaces as
        *           <code>static</code> methods.  All method calls to the
        *           exported methods will be redirected to that class on a
        *           <code>static</code> basis.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerStaticNetworkServer(Class<?>[] list,
                                                        Class<?> backing)
       {
          return registerStaticNetworkServer(list, backing, null);
       }

       /**
        * Get the implementation class for the given interface, exported as a
        * static server.  If the interface is not exported, then it returns
        * <code>null</code>
        *
        * @param   ifaceName
        *          The interface name.
        *
        * @return  See above.
        */
       public static Class<?> getImplClass(String ifaceName)
       {
          Class<?> iface = null;
          Class<?> impl  = null;
          try
          {
             iface = MultiClassLoader.getClassLoader().loadClass(ifaceName);
          }
          catch (@SuppressWarnings("unused") ClassNotFoundException e)
          {
             // interface not found
             return null;
          }

          synchronized (localStaticServers)
          {
             impl = localImplStaticServers.get(iface);
          }

          return impl;
       }

       /**
        * Lock all static exports which are specified in the given list. Note that
        * the list can contain an interface which is not exported.
        *
        * @param   list
        *          The interface name list.
        *
        * @return  <code>true</code> if all static exports in this list were
        *          locked.
        */
       public static boolean deregisterStaticNetworkServer(String[] list)
       {
          List<Class<?>> exported = new ArrayList<Class<?>>();

          synchronized (localStaticServers)
          {
             // get all exported interfaces which are contained in the list
             for (int i = 0; i < list.length; i++)
             {
                Class<?> iface = null;
                try
                {
                   iface = MultiClassLoader.getClassLoader().loadClass(list[i]);
                }
                catch (@SuppressWarnings("unused") ClassNotFoundException e)
                {
                   // ignore, can not happen
                }

                if (localStaticServers.containsKey(iface))
                {
                   exported.add(iface);
                }
             }
          }

          Class<?>[] ifcs = exported.toArray(new Class<?>[0]);

          return deregisterStaticNetworkServer(ifcs);
       }

       /**
        * Exports all <code>static</code> methods in the given backing class that
        * exactly match the method signatures in the given list of interfaces
        * from the local node (to other nodes).  All remote calls to these
        * exported methods will be subsequently dispatched to the given
        * implementation class.
        * <p>
        * The nature of Java interfaces precludes a <code>static</code>
        * implementation.  This means that the backing class can't use the
        * <code>implements</code> keyword even though it must have an exact
        * match of the exported methods actually implemented.
        *
        * @param    list
        *           The interfaces to export.  Each one will be exported using
        *           the fully qualified interface name as the group and each
        *           method will be exported by its name.
        * @param    backing
        *           The class that implements all of the exported interfaces as
        *           <code>static</code> methods.  All method calls to the
        *           exported methods will be redirected to that class on a
        *           <code>static</code> basis.
        * @param    modToken
        *           The modification token retured from the original registration
        *           call (to this same method). May be <code>null</code> on the
        *           first call for this interface (a new token will be created
        *           and returned in this case). If <code>non-null</code> on the
        *           first call, that object will be used as the modification token
        *           from then on.
        *
        * @return   The token used to authenticate the caller when a subsequent
        *           modification is requested.
        */
       public static Object registerStaticNetworkServer(Class<?>[] list,
                                                        Class<?> backing,
                                                        Object   modToken)
       {
          // check to see if the Dispatcher is up
          if (!Dispatcher.isRunning())
          {
             throw new IllegalStateException("Must only be called AFTER the " +
                                             "Dispatcher is initialized.");
          }

          if (modToken == null)
          {
             // create and use the same instance for all registrations associated
             // with this same method call
             modToken = new Object();
          }

          Method[][] methods = calcExportedMethods(list);

          // go through and lock all exports first
          if (!Dispatcher.lockMethods(list, methods, modToken, true))
          {
             return false;
          }

          // loop through interface list
          try
          {
             for (int i = 0; i < list.length; i++)
             {
                String group = calcGroupName(list[i]);

                HashMap<Method, Method> map = new HashMap<Method, Method>();

                for (int j = 0; j < methods[i].length; j++)
                {
                   String name = calcKeyName(methods[i][j]);
                   Method real = findMatchingStaticMethod(backing, methods[i][j]);

                   if (real == null && LOG.isLoggable(Level.SEVERE))
                   {
                      String msg = "Unable to find matching static method %s in backing class %s.";
                      LOG.logp(Level.SEVERE, "RemoteObject", "registerStaticNetworkServer",
                               String.format(msg, name, backing.getName()));
                      continue;
                   }

                   // generate a routing key
                   RoutingKey key = Dispatcher.generateRoutingKey(group, name);

                   if (key == null)
                   {
                      throw new RuntimeException("Unable to get routing key for " +
                                                 group + ":" + name);
                   }

                   // create method mappings for local redirection
                   map.put(methods[i][j], real);

                   // add the static method to the dispatcher
                   Dispatcher.addMethod(key, null, real, modToken);
                   logExport(group, name);
                }

                // save off method mapping state
                synchronized (localStaticServers)
                {
                   localStaticServers.put(list[i], Collections.unmodifiableMap(map));
                   localImplStaticServers.put(list[i], backing);
                }
             }
          }
          finally
          {
             Dispatcher.releaseMethods(methods);
          }

          return modToken;
       }

       /**
        * Remove all exported <code>static</code> methods in the given list of
        * interfaces.
        * <p>
        * If there are active sessions which have used API(s) in these exported
        * interface, the deregistration will not be performed for any of the
        * exported interface.
        * <p>
        * The deregistration bypasses the <code>modToken</code> check, as this
        * will be done by other clients (i.e. from the admin applet).
        *
        * @param    list
        *           The exported interfaces to be removed.
        *
        * @return   <code>true</code> if deregistration was completed fully.
        */
       static boolean deregisterStaticNetworkServer(Class<?>[] list)
       {
          // check to see if the Dispatcher is up
          if (!Dispatcher.isRunning())
          {
             throw new IllegalStateException("Must only be called AFTER the " +
                                             "Dispatcher is initialized.");
          }

          Method[][] methods = calcExportedMethods(list);

          // go through and lock all exports first
          if (!Dispatcher.lockMethods(list, methods, null, false))
          {
             return false;
          }

          try
          {
             for (int i = 0; i < list.length; i++)
             {
                HashMap<Method, Method> map = null;
                String   group   = calcGroupName(list[i]);

                // obtain a copy of the exports
                synchronized (localStaticServers)
                {
                   map = new HashMap<Method, Method>();
                   map.putAll(localStaticServers.get(list[i]));
                }

                for (int j = 0; j < methods[i].length; j++)
                {
                   String name = calcKeyName(methods[i][j]);

                   // get the routing key
                   RoutingKey key = Dispatcher.getRoutingKey(group, name);

                   if (key == null)
                   {
                      throw new RuntimeException("Unable to get routing key for " +
                                                 group + ":" + name);
                   }

                   // at this point, this method is locked so no other clients can
                   // invoke this method and also there are no active sessions;
                   // it is safe to remove it.
                   Dispatcher.removeMethod(key);
                   map.remove(methods[i][j]);
                }

                // save off method mapping state
                synchronized (localStaticServers)
                {
                   if (map.isEmpty())
                   {
                      localStaticServers.remove(list[i]);
                      localImplStaticServers.remove(list[i]);
                   }
                   else
                   {
                      localStaticServers.put(list[i],
                                             Collections.unmodifiableMap(map));
                   }
                }
             }
          }
          finally
          {
             Dispatcher.releaseMethods(methods);
          }

          return true;
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interface and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the current
        * session's network connection (message queue).
        * <p>
        * Synchronous transactions will have a timeout set to 0 (indefinite
        * wait).
        *
        * @param    iface
        *           The interface for which a network instance must be created.
        *
        * @return   A network aware object that implements the given interface.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?> iface)
       {
          return obtainNetworkInstance(new Class[] { iface }, 0);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interface and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the current
        * session's network connection (message queue).
        *
        * @param    iface
        *           The interface for which a network instance must be created.
        * @param    timeout
        *           Timeout in milliseconds for synchronous transactions to
        *           wait before automatically unblocking.  Use 0 to set an
        *           indefinite wait.
        *
        * @return   A network aware object that implements the given interface.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?> iface, int timeout)
       {
          return obtainNetworkInstance(new Class[] { iface }, timeout);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interface and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the specified
        * network connection (message queue).
        * <p>
        * Synchronous transactions will have a timeout set to 0 (indefinite
        * wait).
        *
        * @param    iface
        *           The interface for which a network instance must be created.
        * @param    session
        *           The network connection which the returned object should
        *           represent.  If <code>null</code> the current session will be
        *           used.
        *
        * @return   A network aware object that implements the given interface.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?> iface, Session session)
       {
          return obtainNetworkInstance(new Class[] { iface }, session, 0);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interface and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the specified
        * network connection (message queue).
        *
        * @param    iface
        *           The interface for which a network instance must be created.
        * @param    session
        *           The network connection which the returned object should
        *           represent.  If <code>null</code> the current session will be
        *           used.
        * @param    timeout
        *           Timeout in milliseconds for synchronous transactions to
        *           wait before automatically unblocking.  Use 0 to set an
        *           indefinite wait.
        *
        * @return   A network aware object that implements the given interface.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?> iface,
                                                  Session session,
                                                  int timeout)
       {
          return obtainNetworkInstance(new Class[] { iface }, session, timeout);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interfaces and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the current
        * session's network connection (message queue).
        * <p>
        * Synchronous transactions will have a timeout set to 0 (indefinite
        * wait).
        *
        * @param    list
        *           The interfaces for which a network instance must be created.
        *
        * @return   A network aware object that implements the given interfaces.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?>[] list)
       {
          return obtainNetworkInstance(list, null, 0);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interfaces and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the current
        * session's network connection (message queue).
        *
        * @param    list
        *           The interfaces for which a network instance must be created.
        * @param    timeout
        *           Timeout in milliseconds for synchronous transactions to
        *           wait before automatically unblocking.  Use 0 to set an
        *           indefinite wait.
        *
        * @return   A network aware object that implements the given interfaces.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?>[] list, int timeout)
       {
          return obtainNetworkInstance(list, null, timeout);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interfaces and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the specified
        * network connection (message queue).
        * <p>
        * Synchronous transactions will have a timeout set to 0 (indefinite
        * wait).
        *
        * @param    list
        *           The interfaces for which a network instance must be created.
        * @param    session
        *           The network connection which the returned object should
        *           represent.  If <code>null</code> the current session will be
        *           used.
        *
        * @return   A network aware object that implements the given interfaces.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?>[] list, Session session)
       {
          return obtainNetworkInstance(list, session, 0);
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interfaces and upon which any method calls will be transparently
        * executed by the exported methods on the other side of the specified
        * network connection (message queue).
        *
        * @param    list
        *           The interfaces for which a network instance must be created.
        * @param    session
        *           The network connection which the returned object should
        *           represent.  If <code>null</code> the current session will be
        *           used.
        * @param    timeout
        *           Timeout in milliseconds for synchronous transactions to
        *           wait before automatically unblocking.  Use 0 to set an
        *           indefinite wait.
        *
        * @return   A network aware object that implements the given interfaces.
        *           All access to this local object will be redirected to the
        *           exported object on the other side of the network connection.
        */
       public static Object obtainNetworkInstance(Class<?>[] list,
                                                  Session session,
                                                  int timeout)
       {
          ClassLoader loader = RemoteObject.class.getClassLoader();

          if (session == null)
          {
             session = SessionManager.get().getSession();
          }

          RemoteAccess handler = new RemoteAccess(session, timeout);

          return Proxy.newProxyInstance(loader, list, handler);
       }

       /**
        * Convenience method which returns a unique instance of an object that
        * implements the given interface.  Any method calls on this instance will
        * be EITHER:
        * <p>
        * <ol>
        *    <li> If there is a valid network session associated with this
        *         context, then the methods calls will be transparently executed
        *         by the exported methods on the other side of the network
        *         connection (message queue). This is the equivalent of calling
        *         {@link #obtainNetworkInstance}.
        *    <li> Otherwise a local instance is obtained. Method calls are
        *         transparently executed by the exported methods of the registered
        *         server in the SAME JVM. The fact of whether the server is an
        *         instance or static server is completely hidden by the
        *         implementation. This is the equivalent of calling
        *         {@link #obtainLocalInstance}.
        * </ol>
        *
        * @param    cls
        *           The interface for which a local instance must be created.
        * @param    left
        *           <code>true</code> if the caller is considered the "left"
        *           side of the local session, <code>false</code> for the "right"
        *           side. This controls the direction of synchronization on method
        *           calls for the returned instance.
        *
        * @return   An object that implements the given interface or if there is no
        *           registered server for the given interface, <code>null</code> is
        *           returned.
        */
       public static Object obtainInstance(Class<?> cls, boolean left)
       {
          Session session  = SessionManager.get().getSession();
          Object  instance = RemoteObject.obtainLocalInstance(cls, left);

          if (session != null && instance == null)
          {
             instance = RemoteObject.obtainNetworkInstance(cls, session);
          }

          return instance;
       }

       /**
        * Sets the instance of the state synchronizer for the "left" side of a
        * "local" (same-JVM) proxy environment. When obtaining a local instance
        * the requester must specify if they are on the left or right side. This
        * is an arbitrary split that determines if the synchronizer is used as
        * the source or sink when a method call is made. When a left side requester
        * calls a method for the right side, then the left synchronizer is used
        * as the source and the right synchronizer is used as the sink. When a
        * right side requester calls a left side method, then the right synchronizer
        * is the source and the left synchronizer is the sink.  Synchronization is
        * only done once for each call and it is done before the method is invoked.
        *
        * @param   left
        *          The synchronizer to register or <code>null</code> if the
        *          currently registered synchronizer should be deregistered.
        */
       public static void registerLeftSynchronizer(StateSynchronizer left)
       {
          WorkArea wa = work.get();

          synchronized (wa)
          {
             wa.left = left;
          }
       }

       /**
        * Sets the instance of the state synchronizer for the "right" side of a
        * "local" (same-JVM) proxy environment. When obtaining a local instance
        * the requester must specify if they are on the left or right side. This
        * is an arbitrary split that determines if the synchronizer is used as
        * the source or sink when a method call is made. When a left side requester
        * calls a method for the right side, then the left synchronizer is used
        * as the source and the right synchronizer is used as the sink. When a
        * right side requester calls a left side method, then the right synchronizer
        * is the source and the left synchronizer is the sink.  Synchronization is
        * only done once for each call and it is done before the method is invoked.
        *
        * @param   right
        *          The synchronizer to register or <code>null</code> if the
        *          currently registered synchronizer should be deregistered.
        */
       public static void registerRightSynchronizer(StateSynchronizer right)
       {
          WorkArea wa = work.get();

          synchronized (wa)
          {
             wa.right = right;
          }
       }

       /**
        * Creates a unique instance of an object that implements the given
        * interface and upon which any method calls will be transparently
        * executed by the exported methods of the registered server in the
        * SAME JVM. The fact of whether the server is an instance or static
        * server is completely hidden by the implementation.
        *
        * @param    cls
        *           The interface for which a local instance must be created.
        * @param    left
        *           <code>true</code> if the caller is considered the "left"
        *           side of the local session, <code>false</code> for the "right"
        *           side. This controls the direction of synchronization on method
        *           calls for the returned instance.
        *
        * @return   An object that implements the given interface. All access to
        *           this object will be redirected to the locally exported object
        *           within this JVM. If there is no registered server for the
        *           given interface, <code>null</code> is returned.
        */
       public static Object obtainLocalInstance(Class<?> cls, boolean left)
       {
          ClassLoader         loader  = RemoteObject.class.getClassLoader();
          WorkArea            wa      = work.get();
          LocalStateSync      sync    = getSynchronizer(wa, left);
          InvocationHandler   handler = null;
          Map<Method, Method> map     = null;

          // check for a static server first
          synchronized (localStaticServers)
          {
             map = localStaticServers.get(cls);
          }

          if (map != null)
          {
             Class<?>[] list = new Class<?>[] { cls };
             Method[][] methods = calcExportedMethods(list);

             if (!Dispatcher.lockMethods(list, methods, null, false, false))
             {
                return null;
             }

             try
             {
                handler = new LocalStaticRedirector(map, sync);
             }
             finally
             {
                Dispatcher.releaseMethods(methods);
             }
          }
          else
          {
             Object      impl = null;
             Set<Method> set  = null;

             // now check for an instance server
             synchronized (wa)
             {
                impl = wa.localInstanceServers.get(cls);

                if (impl == null)
                {
                   // no registered server matches the requested interface
                   return null;
                }

                set = wa.localInstanceMethods.get(cls);
             }

             handler = new LocalRedirector(impl, set, sync);
          }

          return Proxy.newProxyInstance(loader, new Class<?>[] { cls }, handler);
       }

       /**
        * Find the matching <code>static</code> method in the given class that
        * corresponds to the given method.  A match is based on method name,
        * return type and parameter number/order/types.  All criteria must
        * match exactly.  Note that only the <code>static</code> modifier is
        * ignored in this comparison.
        *
        * @param    cls
        *           The class in which to search.
        * @param    method
        *           The instance method signature to match.
        *
        * @return   The matching method or <code>null</code> if no match was
        *           found.
        */
       public static Method findMatchingStaticMethod(Class<?> cls, Method method)
       {
          Method[]   target  = cls.getMethods();
          Class<?>[] parms   = method.getParameterTypes();
          Class<?>   rettype = method.getReturnType();
          String     name    = method.getName();

          outer:
          for (int i = 0; i < target.length; i++)
          {
             // check name, return type and that the method is static
             Method m = target[i];
             if (m.getName().equals(name)          &&
                 m.getReturnType().equals(rettype) &&
                 Modifier.isStatic(m.getModifiers()))
             {
                Class<?>[] possible = m.getParameterTypes();

                if (possible.length != parms.length)
                {
                   // not a match
                   continue outer;
                }

                // check parameters
                for (int j = 0; j < parms.length; j++)
                {
                   if (!parms[j].equals(possible[j]))
                   {
                      continue outer;
                   }
                }

                // we found our result
                return m;
             }
          }

          return null;
       }

       /**
        * Return a unique group name for exporting this interface.
        *
        * @param   iface
        *          The interface to be exported.
        *
        * @return   The group name that is unique to this interface.
        */
       static String calcGroupName(Class<?> iface)
       {
          return iface.getName();
       }

       /**
        * Configure and return an instance of the local state synchronizer.
        *
        * @param    wa
        *           Context-local state.
        * @param    leftSide
        *           <code>true</code> if the caller is considered the "left"
        *           side of the local session, <code>false</code> for the "right"
        *           side. This controls the direction of synchronization on method
        *           calls for the returned instance.
        *
        * @return   The properly configured state synchronizer or <code>null</code>
        *           if state synchronization is not enabled.
        */
       private static LocalStateSync getSynchronizer(WorkArea wa, boolean leftSide)
       {
          LocalStateSync    sync  = null;
          StateSynchronizer left  = null;
          StateSynchronizer right = null;

          synchronized (wa)
          {
             left  = wa.left;
             right = wa.right;
          }

          if (left != null && right != null)
          {
             sync = new LocalStateSync(leftSide ? left  : right,
                                       leftSide ? right : left);
          }

          return sync;
       }

       /**
        * If logging is enabled at <code>FINER</code> or greater detail, log
        * that an export has been created for the given group and method name.
        *
        * @param    group
        *           Group name of the export.
        * @param    name
        *           Method name of the export.
        */
       private static void logExport(String group, String name)
       {
          if (LOG.isLoggable(Level.FINER))
          {
             LOG.finer("exported " + group + ':' + name);
          }
       }

       /**
        * Return the list of methods for all interfaces in the given list.
        *
        * @param   list
        *          The list of interfaces to be exported.
        *
        * @return   The methods to export, grouped by interface.
        */
       private static Method[][] calcExportedMethods(Class<?>[] list)
       {
          Method[][] methods = new Method[list.length][];
          for (int i = 0; i < list.length; i++)
          {
             methods[i] = calcExportedMethods(list[i]);
          }

          return methods;
       }

       /**
        * Return a list of methods in a deterministic order such that for the
        * same interface (with identical methods) the list will be identical
        * on both the server and client.
        *
        * @param   iface
        *          The interface to be exported.
        *
        * @return   The list of methods to export.
        */
       private static Method[] calcExportedMethods(Class<?> iface)
       {
          // actually, no sorting is needed since the client and server
          // rendezvous using a shared name (that we calculate using the method
          // object on both sides)
          return iface.getMethods();
       }

       /**
        * Return a unique method name corresponding to this given method.
        *
        * @param    method
        *           The method for which a name must be calculated.
        *
        * @return   The name that is unique to this method.
        */
       static String calcKeyName(Method method)
       {
          return method.toString();
       }

       /**
        * Provides the proxy implementation for a remote object.  This handles
        * all lookup and dispatching for the remote side of the given interface.
        */
       private static class RemoteAccess
       extends InvocationStub
       {
          /** Routing key cache mapped by method object. */
          private Map<Method, RoutingKey> keylist =
             new HashMap<Method, RoutingKey>();

          /** Remote invocation helper. */
          private HighLevelObject hlo = null;

          /**
           * Creates a unique instance of an object that can handle invocations
           * of any proxied method by redirecting the method calls to a remote
           * node's exported API and returning the result or throwing any
           * exceptions as needed.
           *
           * @param    session
           *           The network connection which the returned object should
           *           represent.  Must not be <code>null</code>.
           * @param    timeout
           *           Timeout in milliseconds for synchronous transactions to
           *           wait before automatically unblocking.  Use 0 to set an
           *           indefinite wait.
           */
          RemoteAccess(Session session, int timeout)
          {
             this.hlo = new HighLevelObject(session, timeout);
          }

          /**
           * This method acts as an interceptor which is called every time a
           * method of the proxy object is invoked. It allows us to perform pre-
           * and post-processing for these methods, and to make the remote
           * method call to the exported interface (over the network to the
           * other node).
           *
           * @param    proxy
           *           Proxy object upon which the intercepted method is invoked.
           * @param    method
           *           Method to be invoked through reflection.
           * @param    args
           *           Arguments, if any, to <code>method</code>.
           *
           * @return   Result of remote call.
           *
           * @throws   Throwable
           *           if an exception/error occurs upon the invocation target,
           *           or if any problem occurs during pre- or post-processing.
           */
          @Override
          public Object invokeCore(Object proxy, Method method, Object[] args)
          throws Throwable
          {
             // lookup the cached key or obtain a new key (the first time this
             // is invoked, it will cause a lookup transaction with the other
             // side whose result will be cached)
             RoutingKey key = obtainRoutingKey(method, proxy);

             if (key == null)
             {
                throw new SilentUnwindException();
             }

             // execute the remote method and return the result
             return LOG.isLoggable(Level.FINER) ?
                trace(proxy, method, args, key) :
                hlo.transact(key, args);
          }

          /**
           * Tracing worker that handles the remote method call to the exported
           * interface (over the network to the other node) and logs when the call
           * is made (at FINER level) and logs parameters, return values and an
           * elapsed time in millis (at FINEST level).
           *
           * @param    proxy
           *           Proxy object upon which the intercepted method is invoked.
           * @param    method
           *           Method to be invoked through reflection.
           * @param    args
           *           Arguments, if any, to <code>method</code>.
           * @param    key
           *           The routing key used for the remote method invocation.
           *
           * @return   Result of remote call.
           *
           * @throws   Throwable
           *           If an exception/error occurs upon the invocation target,
           *           or if any problem occurs during pre- or post-processing.
           */
          public Object trace(Object     proxy,
                              Method     method,
                              Object[]   args,
                              RoutingKey key)
          throws Throwable
          {
             return TraceHelper.trace(new Invoker(key, calcKeyName(method)),
                                      args,
                                      LOG,
                                      "RemoteObject.invoke()");
          }

          /**
           * Gets the routing key associated with a specific method.  If this
           * key has been previously found it will be obtained via a cache,
           * otherwise it will be looked up upon first request (a type of lazy
           * initialization).
           *
           * @param    method
           *           The method for which the key must be obtained.
           *
           * @return   The routing key which corresponds to the given method.
           */
          private RoutingKey obtainRoutingKey(Method method, Object proxy)
          {
             RoutingKey key = keylist.get(method);

             // check if this is the first use
             if (key == null)
             {
                // the method's declaring interface may not be the same as the one
                // used as the group name when this object was registered, so we
                // have to find the most specific interface which includes the
                // method (i.e. via extending the declaring interface)
                Class<?> match = null;
                for (Class<?> next : proxy.getClass().getInterfaces())
                {
                   if (hierarchyDeclaresMethod(method, next))
                   {
                      if (match != null)
                      {
                         throw new RuntimeException(
                            "Ambiguous remote method (" + method.getName() + ")");
                      }

                      match = next;
                   }
                }

                String group = calcGroupName(match);
                String name  = calcKeyName(method);

                try
                {
                   // lookup the key
                   key = hlo.getKey(group, name);

                   if (key == null)
                      return null;

                   // cache the found key
                   keylist.put(method, key);
                }
                catch (@SuppressWarnings("unused") IllegalStateException ise)
                {
                   return null;
                }
                catch (Exception exc)
                {
                   // check if the cause is a InterruptedException. in this case,
                   // it should handled
                   Throwable cause = exc;
                   while (cause != null)
                   {
                      if (cause instanceof InterruptedException)
                      {
                         Control.handleInterrupted((InterruptedException) cause);
                         break;
                      }

                      cause = cause.getCause();
                   }

                   throw new RuntimeException("Unresolvable remote export " +
                                              name + ".",
                                              exc);
                }
             }

             return key;
          }

          /**
           * Determine whether the given interface or any of its ancestors
           * declare the given method.  This method is recursive.
           *
           * @param   method
           *          Method to search for.
           * @param   iface
           *          Interface whose inheritance hierarchy is to be searched.
           *
           * @return  <code>true</code> if <code>iface</code> or any of its
           *          ancestor superinterfaces declares <code>method</code>, else
           *          <code>false</code>.
           */
          private boolean hierarchyDeclaresMethod(Method method, Class<?> iface)
          {
             if (iface.equals(method.getDeclaringClass()))
             {
                return true;
             }

             for (Class<?> next : iface.getInterfaces())
             {
                if (hierarchyDeclaresMethod(method, next))
                {
                   return true;
                }
             }

             return false;
          }

          /**
           * Helper class to delegate execution of the <code>transact</code>
           * method.
           */
          private class Invoker
          implements Invocable
          {
             /** Routing key for the <code>transact</code> method. */
             private RoutingKey key = null;

             /** The description of the method being executed. */
             private String description = null;

             /**
              * Construct an instance that can make an invocation of a specific
              * remote method.
              *
              * @param    key
              *           The routing key to use.
              * @param    description
              *           The description of the method being invoked.
              */
             public Invoker(RoutingKey key, String description)
             {
                this.key         = key;
                this.description = description;
             }

             /**
              * Return a text description of the method being invoked.
              *
              * @return   The description.
              */
             @Override
             public String describe()
             {
                return description;
             }

             /**
              * Execute or "run" the object's functionality.
              *
              * @param    args
              *           The method arguments or <code>null</code> for no arguments.
              *
              * @return   The called method's return value or <code>null</code> if
              *           there is no return value.  Note that this design does not
              *           allow the caller to determine the difference between a
              *           <code>void</code> return and a return of a genuine
              *           <code>null</code>.
              *
              * @throws   Throwable
              *           If the called method generates any exception or error.
              */
             @Override
             public Object invoke(Object[] args)
             throws Throwable
             {
                return hlo.transact(key, args);
             }
          }
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** Storage for local instance method maps. */
          private Map<Class<?>, Set<Method>> localInstanceMethods = new HashMap<>();

          /** Storage for local instance servers. */
          private Map<Class<?>, Object> localInstanceServers = new HashMap<>();

          /** Authenticates the caller as allowed to modify registrations. */
          private HashMap<Class<?>, Object> modTokens = new HashMap<>();

          /** Left side state synchronizer. */
          private StateSynchronizer left = null;

          /** Right side state synchronizer. */
          private StateSynchronizer right = null;

          /** Left side is the source and right is the sink. */
          private LocalStateSync leftToRight = null;

          /** Right side is the source and left is the sink. */
          private LocalStateSync rightToLeft = null;
       }
    }