# InvokeConfig.java

    /*
    ** Module   : InvokeConfig.java
    ** Abstract : Defines a builder configuration with the details required to execute a RUN statement.
    **
    ** Copyright (c) 2018-2025, Golden Code Development Corporation.

    **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 CA  20181029 Created initial version.
    ** 002 CA  20190628 Refactored to allow OO invocations and to be used for appserver calls.  Moved invoke() API
    **                  to ControlFlowOps.
    ** 003 CA  20200427 Added support for Agent's SESSION:CURRENT-REQUEST-INFO, as sent by the remote
    **                  SERVER:REQUEST-INFO.
    **     CA  20200514 Added getter and setter for requestID.
    ** 004 IAS 20200908 Rework (de)serialization.
    ** 005 CA  20220405 Added authentication and authorization for web requests.  When this is enabled, the target
    **                  API call will be executed under the authenticated FWD context, and not the agent's context.
    **     CA  20220930 Refactored the callback invocation to be performed via a call-site and InvokeConfig, to
    **                  allow caching of the resolved target.
    **     CA  20221010 runSuper() and execute() calls always mark the configuration as FUNCTION.
    **     OM  20221017 Implemented toString() method for debugging usage.
    ** 006 CA  20220428 Fixed (de)serialization.
    ** 007 HC  20230116 Replaced some handle usages with the actual wrapped resources for
    **                  performance.
    ** 008 CA  20230215 Use PayloadSerializer for serialization.
    ** 009 CA  20231020 Fixed (de)serialization for inHandle referencing a ProxyProcedureWrapper.
    ** 010 GBB 20250403 restoreRequest made public to be accessed by CoreAppserver.
    */

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

    import static com.goldencode.util.NativeTypeSerializer.*;
    import java.io.*;
    import java.util.*;

    import com.goldencode.p2j.oo.lang.*;
    import com.goldencode.util.*;

    /**
     * Defines a configuration which can be used by a RUN or dynamic function call.  All the
     * information required to perform a call can be setup using a builder approach, as all setters
     * return this instance.
     * <p>
     * The final call can be delegated to {@link ControlFlowOps#invoke(InvokeConfig)}.
     */
    public class InvokeConfig
    implements Externalizable
    {
       /** Constant to identify field {@link #inHandle} during serialization. */
       private static final int FIELD_IN_HANDLE = 0;

       /** Constant to identify field {@link #function} during serialization. */
       private static final int FIELD_FUNCTION = FIELD_IN_HANDLE + 1;

       /** Constant to identify field {@link #persistent} during serialization. */
       private static final int FIELD_PERSISTENT = FIELD_FUNCTION + 1;

       /** Constant to identify field {@link #singleRun} during serialization. */
       private static final int FIELD_SINGLE_RUN = FIELD_PERSISTENT + 1;

       /** Constant to identify field {@link #singleton} during serialization. */
       private static final int FIELD_SINGLETON = FIELD_SINGLE_RUN + 1;

       /** Constant to identify field {@link #procedureHandle} during serialization. */
       private static final int FIELD_PROC_HANDLE = FIELD_SINGLETON + 1;

       /** Constant to identify field {@link #serverHandle} during serialization. */
       private static final int FIELD_SERVER_HANDLE = FIELD_PROC_HANDLE + 1;

       /** Constant to identify field {@link #transactionDistinct} during serialization. */
       private static final int FIELD_TX_DISTINCT = FIELD_SERVER_HANDLE + 1;

       /** Constant to identify field {@link #asynchronous} during serialization. */
       private static final int FIELD_ASYNC = FIELD_TX_DISTINCT + 1;

       /** Constant to identify field {@link #asyncHandle} during serialization. */
       private static final int FIELD_ASYNC_HANDLE = FIELD_ASYNC + 1;

       /** Constant to identify field {@link #eventProcedure} during serialization. */
       private static final int FIELD_EVT_PROC = FIELD_ASYNC_HANDLE + 1;

       /** Constant to identify field {@link #eventProcedureContext} during serialization. */
       private static final int FIELD_EVT_PROC_CONTEXT = FIELD_EVT_PROC + 1;

       /** Constant to identify field {@link #arguments} during serialization. */
       private static final int FIELD_ARGS = FIELD_EVT_PROC_CONTEXT + 1;

       /** Constant to identify field {@link #modes} during serialization. */
       private static final int FIELD_MODES = FIELD_ARGS + 1;

       /** Constant to identify field {@link #portHandle} during serialization. */
       private static final int FIELD_PORT_HANDLE = FIELD_MODES + 1;

       /** Constant to identify field {@link #dynamicFunction} during serialization. */
       private static final int FIELD_DYN_FUNC = FIELD_PORT_HANDLE + 1;

       /** Constant to identify field {@link #superCall} during serialization. */
       private static final int FIELD_SUPER_CALL = FIELD_DYN_FUNC + 1;

       /** Constant to identify field {@link #isClass} during serialization. */
       private static final int FIELD_IS_CLASS = FIELD_SUPER_CALL + 1;

       /** Constant to identify field {@link #returnValue} during serialization. */
       private static final int FIELD_RETURN_VALUE = FIELD_IS_CLASS + 1;

       /** Constant to identify field {@link #clientContextId} during serialization. */
       private static final int FIELD_CLIENTCONTEXTID = FIELD_RETURN_VALUE + 1;

       /** Constant to identify field {@link #requestId} during serialization. */
       private static final int FIELD_REQUESTID = FIELD_CLIENTCONTEXTID + 1;

       /** Constant to identify field {@link #clientPrincipal} during serialization. */
       private static final int FIELD_CLIENTPRINCIPAL = FIELD_REQUESTID + 1;

       /** Constant to identify field {@link #versionInfo} during serialization. */
       private static final int FIELD_VERSIONINFO = FIELD_CLIENTPRINCIPAL + 1;

       /** Constant to identify field {@link #clientType} during serialization. */
       private static final int FIELD_CLIENTTYPE = FIELD_VERSIONINFO + 1;

       /** Constant to identify field {@link #webServiceToken} during serialization. */
       private static final int FIELD_WEB_SERVICE_TOKEN = FIELD_CLIENTTYPE + 1;

       /** Constant to identify field {@link #asThread} during serialization. */
       private static final int FIELD_AS_THREAD = FIELD_WEB_SERVICE_TOKEN + 1;

       /** This call's target (a function, internal procedure or program name). */
       private String target = null;

       /** Flag indicating that this is a legacy OO class. */
       private boolean isClass = false;

       /**
        * When specified, it will mean the target will be a function or internal procedure, using
        * the given procedure handle as a start.
        */
       private handle inHandle = null;

       /** Flag indicating if this is a function or procedure call. */
       private boolean function = false;

       /** When set, this call will be an external program call, ran persistent. */
       private boolean persistent = false;

       /** When set, this call will be an external program call, ran in SINGLE-RUN mode. */
       private boolean singleRun = false;

       /** When set, this call will be an external program call, ran in SINGLETON mode. */
       private boolean singleton = false;

       /** The handle where to pass back the reference to the newly persistent-run external program. */
       private handle procedureHandle = null;

       /** The handle where to pass back the reference in case of a web service invocation. */
       private handle portHandle = null;

       /** The remote server handle where to run this request. */
       private handle serverHandle = null;

       /** Flag indicating if TRANSACTION-DISTINCT option is present. */
       private boolean transactionDistinct = false;

       /** Flag indicating if ASYNCHRONOUS option is present. */
       private boolean asynchronous = false;

       /** Flag indicating if AS-THREAD FWD-extension option is present. */
       private boolean asThread = false;

       /** The handle where to pass back the reference to the {@link AsyncRequest async resource}. */
       private handle asyncHandle = null;

       /**
        * This represents the name of the internal procedure for the EVENT-PROCEDURE clause, null if
        * this clause is not specified.
        */
       private character eventProcedure = null;

       /**
        * The handle to a procedure in which context the specified EVENT-PROCEDURE internal procedure
        * is found, null if EVENT-PROCEDURE clause is not specified.
        */
       private handle eventProcedureContext = null;

       /** The arguments for this call. */
       private Object[] arguments = new Object[0];

       /** A string representation of the modes of each parameter. */
       private String modes = null;

       /** Flag indicating if this is a DYNAMIC-FUNCTION call. */
       private boolean dynamicFunction = false;

       /** Flag indicating if this is a RUN SUPER or SUPER(). call. */
       private boolean superCall = false;

       /** Flag indicating if the OO method call is function-like (return value is needed). */
       private boolean returnValue = false;

       /** The value of the <code>OERequestInfo:</code> field. */
       private String clientContextId = null;

       /** The value of the <code>OERequestInfo:requestId</code> field. */
       private String requestId = null;

       /** The value of the <code>OERequestInfo:clientPrincipal</code> field. */
       private byte[] clientPrincipal = null;

       /** The value of the <code>OERequestInfo:versionInfo</code> field. */
       private String[] versionInfo = null;

       /** The value of the <code>OERequestInfo:clientType</code> field. */
       private String clientType = null;

       /** A token sent via a web service request, to execute the target service in the specified FWD context. */
       private String webServiceToken = null;

       /** The callsite instance (at the converted code) for this call. */
       private InvokeConfig callSite = null;

       /**
        * Default constructor.
        */
       public InvokeConfig()
       {
          // no-op
       }

       /**
        * Copy constructor.  This also saves the call-site reference, to be able to cache the target.
        */
       public InvokeConfig(InvokeConfig callSite)
       {
          this.callSite = callSite;

          target = callSite.target;
          isClass = callSite.isClass;
          inHandle = callSite.inHandle == null ? null : new handle(callSite.inHandle);
          function = callSite.function;
          persistent = callSite.persistent;
          singleRun = callSite.singleRun;
          singleton = callSite.singleton;
          procedureHandle = callSite.procedureHandle == null ? null : new handle(callSite.procedureHandle);
          portHandle = callSite.portHandle == null ? null : new handle(callSite.portHandle);
          serverHandle = callSite.serverHandle == null ? null : new handle(callSite.serverHandle);
          transactionDistinct = callSite.transactionDistinct;
          asynchronous = callSite.asynchronous;
          asThread = callSite.asThread;
          asyncHandle = callSite.asyncHandle == null ? null : new handle(callSite.asyncHandle);
          eventProcedure = callSite.eventProcedure == null ? null : new character(callSite.eventProcedure);
          eventProcedureContext = callSite.eventProcedureContext == null
                                     ? null
                                     : new handle(callSite.eventProcedureContext);
          if (callSite.arguments != null && callSite.arguments.length > 0)
          {
             arguments = Arrays.copyOf(callSite.arguments, callSite.arguments.length);
          }
          modes = callSite.modes;
          dynamicFunction = callSite.dynamicFunction;
          superCall = callSite.superCall;
          returnValue = callSite.returnValue;
          clientContextId = callSite.clientContextId;
          requestId = callSite.requestId;
          if (callSite.clientPrincipal != null)
          {
             clientPrincipal = Arrays.copyOf(callSite.clientPrincipal, callSite.clientPrincipal.length);
          }
          if (callSite.versionInfo != null)
          {
             versionInfo = Arrays.copyOf(callSite.versionInfo, callSite.versionInfo.length);
          }
          clientType = callSite.clientType;
          webServiceToken = callSite.webServiceToken;
       }

       /**
        * Create a new invocation for the given target.
        *
        * @param    target
        *           The target to be invoked.
        */
       public InvokeConfig(character target)
       {
          this.target = target.getValue();
       }

       /**
        * Create a new invocation for the given target.
        *
        * @param    target
        *           The target to be invoked.
        */
       public InvokeConfig(String target)
       {
          this.target = target;
       }

       /**
        * Clone this instance.
        *
        * @return   A copy of this instance.
        */
       @Override
       public InvokeConfig clone()
       {
          return new InvokeConfig(this);
       }

       /**
        * Execute this config for a callback procedure associated with a resource event.
        *
        * @param    resource
        *           The resource associated with the {@link #callSite call-site}.
        *
        * @return   The result of the invocation, if this is a function call.
        */
       public BaseDataType executeForResource(WrappedResource resource)
       {
          return ControlFlowOps.invoke(null, resource, this);
       }

       /**
        * Execute this config as a <code>RUN SUPER</code> statement with the provided arguments.
        *
        * @param    args
        *           The arguments.
        */
       public void runSuper(Object... args)
       {
          setArguments(args);
          setSuperCall(true);

          ControlFlowOps.invoke(null, null, this);
       }

       /**
        * Execute this config as a <code>SUPER()</code> function, with the provided arguments and return type.
        *
        * @param    type
        *           The return type.
        * @param    args
        *           The arguments.
        *
        * @return   The function's return value.
        */
       public <T extends BaseDataType> T runSuper(Class<T> type, Object... args)
       {
          setArguments(args);
          setSuperCall(true);
          setFunction(true);

          BaseDataType superVal = ControlFlowOps.invoke(type, null, this);

          T ret = null;
          if (type.isAssignableFrom(superVal.getClass()))
          {
             ret = (T) superVal;
          }
          else
          {
             ret = (T) BaseDataType.generateUnknown(type);
             ret.assign(superVal);
          }

          return ret;
       }

       /**
        * Execute this config for a RUN call.
        */
       public void run(Object... args)
       {
          setArguments(args);

          ControlFlowOps.invoke(null, null, this);
       }

       /**
        * Execute this config for a function typed call.
        *
        * @return   The function's returned value.
        */
       public <T extends BaseDataType> T execute(Class<T> cls, Object... args)
       {
          setArguments(args);
          setFunction(true);

          return (T) ControlFlowOps.invoke(cls, null, this);
       }

       /**
        * Execute this config for a function call.
        *
        * @return   The function's returned value.
        */
       public BaseDataType execute(Object... args)
       {
          setArguments(args);
          setFunction(true);

          return ControlFlowOps.invoke(null, null, this);
       }

       /**
        * Get the {@link #callSite}.
        *
        * @return   See above.
        */
       public InvokeConfig getCallSite()
       {
          return callSite;
       }

       /**
        * Set this invocation's {@link #target}.
        *
        * @param    target
        *           The target to be invoked.
        *
        * @return   This instance.
        */
       public InvokeConfig setTarget(character target)
       {
          this.target = target.getValue();

          return this;
       }

       /**
        * Set this invocation's {@link #target}.
        *
        * @param    target
        *           The target to be invoked.
        *
        * @return   This instance.
        */
       public InvokeConfig setTarget(String target)
       {
          this.target = target;

          return this;
       }

       /**
        * Mark this configuration as a OO invocation.
        *
        * @return   This instance.
        */
       public InvokeConfig setClass()
       {
          this.isClass = true;

          return this;
       }

       /**
        * Set this invocation's {@link #returnValue} flag.
        *
        * @param    returnValue
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setReturnValue(boolean returnValue)
       {
          this.returnValue = returnValue;

          return this;
       }

       /**
        * Set this invocation's {@link #function} flag.
        *
        * @param    function
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setFunction(boolean function)
       {
          this.function = function;

          return this;
       }

       /**
        * Set this invocation's {@link #function} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig function()
       {
          this.function = true;

          return this;
       }

       /**
        * Set this invocation's {@link #persistent} flag.
        *
        * @param    persistent
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setPersistent(boolean persistent)
       {
          this.persistent = persistent;

          return this;
       }

       /**
        * Set this invocation's {@link #persistent} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig persistent()
       {
          this.persistent = true;

          return this;
       }

       /**
        * Set this invocation's {@link #singleRun} flag.
        *
        * @param    singleRun
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setSingleRun(boolean singleRun)
       {
          this.singleRun = singleRun;

          return this;
       }

       /**
        * Set this invocation's {@link #singleRun} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig singleRun()
       {
          this.singleRun = true;

          return this;
       }

       /**
        * Set this invocation's {@link #singleton} flag.
        *
        * @param    singleton
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setSingleton(boolean singleton)
       {
          this.singleton = singleton;

          return this;
       }

       /**
        * Set this invocation's {@link #singleton} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig singleton()
       {
          this.singleton = true;

          return this;
       }

       /**
        * Set this invocation's {@link #procedureHandle} handle where to save the persistent program.
        *
        * @param    procedureHandle
        *           A handle variable or field reference.
        *
        * @return   This instance.
        */
       public InvokeConfig setProcedureHandle(handle procedureHandle)
       {
          // the reference is saved
          this.procedureHandle = procedureHandle;

          return this;
       }

       /**
        * Set this invocation's {@link #portHandle} handle where to save the web service.
        *
        * @param    portHandle
        *           A handle variable or field reference.
        *
        * @return   This instance.
        */
       public InvokeConfig setPortHandle(handle portHandle)
       {
          // the reference is saved
          this.portHandle = portHandle;

          return this;
       }

       /**
        * Set this invocation's {@link #serverHandle} remote handle where to perform the call.
        *
        * @param    serverHandle
        *           A handle to a remote appserver.
        *
        * @return   This instance.
        */
       public InvokeConfig setServerHandle(handle serverHandle)
       {
          this.serverHandle = serverHandle == null ? null : new handle(serverHandle);

          return this;
       }

       /**
        * Set this invocation's {@link #transactionDistinct} flag.
        *
        * @param    transactionDistinct
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setTransactionDistinct(boolean transactionDistinct)
       {
          this.transactionDistinct = transactionDistinct;

          return this;
       }

       /**
        * Set this invocation's {@link #transactionDistinct} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig transactionDistinct()
       {
          this.transactionDistinct = true;

          return this;
       }

       /**
        * Set this invocation's {@link #asynchronous} flag.
        *
        * @param    asynchronous
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setAsynchronous(boolean asynchronous)
       {
          this.asynchronous = asynchronous;

          return this;
       }

       /**
        * Set this invocation's {@link #asThread} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig asThread()
       {
          this.asThread = true;

          return this;
       }

       /**
        * Set this invocation's {@link #asThread} flag.
        *
        * @param    asThread
        *           The new value of this flag.
        *
        * @return   This instance.
        */
       public InvokeConfig setAsThread(boolean asThread)
       {
          this.asThread = asThread;

          return this;
       }

       /**
        * Set this invocation's {@link #asynchronous} flag to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig asynchronous()
       {
          this.asynchronous = true;

          return this;
       }

       /**
        * Set this invocation's {@link #asyncHandle} handle where to save the async resource.
        *
        * @param    asyncHandle
        *           A handle variable or field reference.
        *
        * @return   This instance.
        */
       public InvokeConfig setAsyncHandle(handle asyncHandle)
       {
          // the reference is saved
          this.asyncHandle = asyncHandle;

          return this;
       }

       /**
        * Set this invocation's {@link #eventProcedure EVENT-PROCEDURE} option.
        *
        * @param    eventProcedure
        *           The event procedure name.
        *
        * @return   This instance.
        */
       public InvokeConfig setEventProcedure(character eventProcedure)
       {
          this.eventProcedure = new character(eventProcedure);

          return this;
       }

       /**
        * Set this invocation's {@link #eventProcedure EVENT-PROCEDURE} option.
        *
        * @param    eventProcedure
        *           The event procedure name.
        *
        * @return   This instance.
        */
       public InvokeConfig setEventProcedure(String eventProcedure)
       {
          this.eventProcedure = new character(eventProcedure);

          return this;
       }

       /**
        * Set this invocation's {@link #eventProcedureContext EVENT-PROCEDURE-CONTEXT} option.
        *
        * @param    eventProcedureContext
        *           The event procedure context.
        *
        * @return   This instance.
        */
       public InvokeConfig setEventProcedureContext(handle eventProcedureContext)
       {
          this.eventProcedureContext = new handle(eventProcedureContext);

          return this;
       }

       /**
        * Set this invocation's {@link #inHandle IN handle} option.
        *
        * @param    inHandle
        *           A program handle where to perform the call.
        *
        * @return   This instance.
        */
       public InvokeConfig setInHandle(handle inHandle)
       {
          this.inHandle = inHandle == null ? null : new handle(inHandle);

          return this;
       }

       /**
        * Set this invocation's {@link #arguments}.
        *
        * @param    arguments
        *           A invocation's arguments.
        *
        * @return   This instance.
        */
       public InvokeConfig setArguments(Object... arguments)
       {
          this.arguments = arguments;

          return this;
       }

       /**
        * Set this invocation's {@link #modes argument modes}.
        *
        * @param    modes
        *           A invocation's argument modes.
        *
        * @return   This instance.
        */
       public InvokeConfig setModes(String modes)
       {
          this.modes = modes;

          return this;
       }

       /**
        * Set this invocation's {@link #superCall} flag.
        *
        * @param    superCall
        *           Flag indicating that this is a super-call.
        *
        * @return   This instance.
        */
       public InvokeConfig setSuperCall(boolean superCall)
       {
          this.superCall = superCall;

          return this;
       }

       /**
        * Set this invocation's {@link #dynamicFunction} flag.
        *
        * @param    dynamicFunction
        *           Flag indicating that this is a dynamic-function call.
        *
        * @return   This instance.
        */
       public InvokeConfig setDynamicFunction(boolean dynamicFunction)
       {
          this.dynamicFunction = dynamicFunction;

          return this;
       }

       /**
        * Set this invocation's {@link #dynamicFunction} and {@link #function} flags to <code>true</code>.
        *
        * @return   This instance.
        */
       public InvokeConfig dynamicFunction()
       {
          this.function = true;
          this.dynamicFunction = true;

          return this;
       }

       /**
        * Set the {@link #requestId}.
        *
        * @param    requestId
        *           The request ID.
        *
        * @return   This instance.
        */
       public InvokeConfig setRequestId(String requestId)
       {
          this.requestId = requestId;

          return this;
       }

       /**
        * Set the {@link #webServiceToken}.
        *
        * @param    webServiceToken
        *           The web service token.
        *
        * @return   This instance.
        */
       public InvokeConfig setWebServiceToken(String webServiceToken)
       {
          this.webServiceToken = webServiceToken;

          return this;
       }

       /**
        * Get the {@link #isClass} flag.
        *
        * @return   See above.
        */
       public boolean isClass()
       {
          return isClass;
       }

       /**
        * Get the {@link #returnValue} flag.
        *
        * @return   See above.
        */
       public boolean isReturnValue()
       {
          return returnValue;
       }

       /**
        * Get the {@link #function} flag.
        *
        * @return   See above.
        */
       public boolean isFunction()
       {
          return function;
       }

       /**
        * Get the {@link #persistent} flag.
        *
        * @return   See above.
        */
       public boolean isPersistent()
       {
          return persistent;
       }

       /**
        * Get the {@link #singleRun} flag.
        *
        * @return   See above.
        */
       public boolean isSingleRun()
       {
          return singleRun;
       }

       /**
        * Get the {@link #singleton} flag.
        *
        * @return   See above.
        */
       public boolean isSingleton()
       {
          return singleton;
       }

       /**
        * Get the {@link #transactionDistinct} flag.
        *
        * @return   See above.
        */
       public boolean isTransactionDistinct()
       {
          return transactionDistinct;
       }

       /**
        * Get the {@link #asynchronous} flag.
        *
        * @return   See above.
        */
       public boolean isAsynchronous()
       {
          return asynchronous;
       }

       /**
        * Get the {@link #asThread} flag.
        *
        * @return   See above.
        */
       public boolean isAsThread()
       {
          return asThread;
       }

       /**
        * Get the caller's target.
        *
        * @return   the {@link #target}.
        */
       public String getTarget()
       {
          return target;
       }

       /**
        * Get the target handle for internal entry call.
        *
        * @return   the {@link #inHandle}
        */
       public handle getInHandle()
       {
          return inHandle;
       }

       /**
        * Get the {@link #procedureHandle}.
        *
        * @return   the {@link #procedureHandle}
        */
       public handle getProcedureHandle()
       {
          return procedureHandle;
       }

       /**
        * Get the {@link #portHandle}.
        *
        * @return   the {@link #portHandle}
        */
       public handle getPortHandle()
       {
          return portHandle;
       }

       /**
        * Get the {@link #serverHandle}.
        *
        * @return   the {@link #serverHandle}
        */
       public handle getServerHandle()
       {
          return serverHandle;
       }

       /**
        * Get the caller's {@link #asyncHandle}.
        *
        * @return    the {@link #asyncHandle}.
        */
       public handle getAsyncHandle()
       {
          return asyncHandle;
       }

       /**
        * Get the caller's {@link #eventProcedure}.
        *
        * @return   the {@link #eventProcedure}
        */
       public character getEventProcedure()
       {
          return eventProcedure;
       }

       /**
        * Get the caller's {@link #eventProcedureContext}.
        *
        * @return   the {@link #eventProcedureContext}
        */
       public handle getEventProcedureContext()
       {
          return eventProcedureContext;
       }

       /**
        * Get the caller's {@link #arguments}.
        *
        * @return   the {@link #arguments}
        */
       public Object[] getArguments()
       {
          return arguments;
       }

       /**
        * Get the caller's {@link #modes}.
        *
        * @return   the {@link #modes}
        */
       public String getModes()
       {
          return modes;
       }

       /**
        * Get the caller's {@link #superCall}.
        *
        * @return   the {@link #superCall}
        */
       public boolean isSuperCall()
       {
          return superCall;
       }

       /**
        * Get the caller's {@link #dynamicFunction}.
        *
        * @return   the {@link #dynamicFunction}
        */
       public boolean isDynamicFunction()
       {
          return dynamicFunction;
       }

       /**
        * Get the request ID.
        *
        * @return   The {@link #requestId}.
        */
       public String getRequestId()
       {
          return requestId;
       }

       /**
        * Get the web service token.
        *
        * @return   The {@link #webServiceToken}.
        */
       public String getWebServiceToken()
       {
          return webServiceToken;
       }

       /**
        * Serialize this invoke configuration.  To reduce the size of the written data,
        * {@link #computeWriting()} will be used to set a bit for each field in this class, which was
        * changed from its default value.  Only the fields which are no longer holding the default
        * value will be serialized.
        * <p>
        * The {@link #target} field is always written.
        *
        * @param    out
        *           The output stream.
        */
       @Override
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          // compute what to write
          long writing = computeWriting();
          out.writeLong(writing);

          out.writeUTF(target);

          if (singleRun)
          {
             out.writeBoolean(singleRun);
          }
          if (singleton)
          {
             out.writeBoolean(singleton);
          }
          if (inHandle != null)
          {
             if (singleRun || singleton)
             {
                out.writeUTF(((DeferredProgram) inHandle.get()).getName());
             }
             else
             {
                WrappedResource res = inHandle.getResource();
                boolean isProxy = res instanceof ProxyProcedureWrapper;
                out.writeBoolean(isProxy);
                if (isProxy)
                {
                   ProxyProcedureWrapper proxy = (ProxyProcedureWrapper) res;
                   out.writeUTF(proxy.getFileName().getValue());
                   out.writeInt(proxy.getAgentId());
                }
                out.writeUTF((String) inHandle.get());
             }
          }
          if (function)
          {
             out.writeBoolean(function);
          }
          if (persistent)
          {
             out.writeBoolean(persistent);
          }
          if (procedureHandle != null)
          {
             procedureHandle.writeExternal(out);
          }
          if (serverHandle != null)
          {
             serverHandle.writeExternal(out);
          }
          if (transactionDistinct)
          {
             out.writeBoolean(transactionDistinct);
          }
          if (asynchronous)
          {
             out.writeBoolean(asynchronous);
          }
          if (asyncHandle != null)
          {
             asyncHandle.writeExternal(out);
          }
          if (eventProcedure != null)
          {
             eventProcedure.writeExternal(out);
          }
          if (eventProcedureContext != null)
          {
             eventProcedureContext.writeExternal(out);
          }
          if (arguments != null && arguments.length > 0)
          {
             PayloadSerializer.writePayload(out, arguments);
          }
          if (modes != null && !modes.isEmpty())
          {
             out.writeUTF(modes);
          }
          if (portHandle != null)
          {
             portHandle.writeExternal(out);
          }
          if (dynamicFunction)
          {
             out.writeBoolean(dynamicFunction);
          }
          if (superCall)
          {
             out.writeBoolean(superCall);
          }
          if (isClass)
          {
             out.writeBoolean(isClass);
          }
          if (returnValue)
          {
             out.writeBoolean(returnValue);
          }

          if (clientContextId != null)
          {
             out.writeUTF(clientContextId);
          }
          if (requestId != null)
          {
             out.writeUTF(requestId);
          }
          if (clientPrincipal != null)
          {
             writeByteArray(out, clientPrincipal);
          }
          if (versionInfo != null)
          {
             writeStringArray(out, versionInfo);
          }
          if (clientType != null)
          {
             out.writeUTF(clientType);
          }
          if (webServiceToken != null)
          {
             out.writeUTF(webServiceToken);
          }
          if (asThread)
          {
             out.writeBoolean(asThread);
          }
       }

       /**
        * Read this invoke configuration from a stream.  Only the fields which are no longer holding
        * the default value will be read (see {@link #computeWriting()}).
        * <p>
        * The {@link #target} field is always read.
        *
        * @param    in
        *           The input stream.
        */
       @Override
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          long writing = in.readLong();
          target = in.readUTF();

          if ((writing & (1l << FIELD_SINGLE_RUN)) != 0)
          {
             singleRun = in.readBoolean();
          }
          if ((writing & (1l << FIELD_SINGLETON)) != 0)
          {
             singleton = in.readBoolean();
          }
          if ((writing & (1l << FIELD_IN_HANDLE)) != 0)
          {
             inHandle = new handle();
             if (singleRun || singleton)
             {
                String code = in.readUTF();
                inHandle.assign(new DeferredProgramWrapper(new DeferredProgram(code)));
             }
             else
             {
                boolean isProxy = in.readBoolean();
                if (isProxy)
                {
                   String filename = in.readUTF();
                   int agentId = in.readInt();
                   String referent = in.readUTF();
                   ProxyProcedureWrapper proxy = new ProxyProcedureWrapper(referent, new character(filename), agentId);
                   inHandle.assign(proxy);
                }
                else
                {
                   String code = in.readUTF();
                   inHandle.assign(handle.fromString(code));
                }
             }
          }
          if ((writing & (1l << FIELD_FUNCTION)) != 0)
          {
             function = in.readBoolean();
          }
          if ((writing & (1l << FIELD_PERSISTENT)) != 0)
          {
             persistent = in.readBoolean();
          }
          if ((writing & (1l << FIELD_PROC_HANDLE)) != 0)
          {
             procedureHandle = new handle();
             procedureHandle.readExternal(in);
          }
          if ((writing & (1l << FIELD_SERVER_HANDLE)) != 0)
          {
             serverHandle = new handle();
             serverHandle.readExternal(in);
          }
          if ((writing & (1l << FIELD_TX_DISTINCT)) != 0)
          {
             transactionDistinct = in.readBoolean();
          }
          if ((writing & (1l << FIELD_ASYNC)) != 0)
          {
             asynchronous = in.readBoolean();
          }
          if ((writing & (1l << FIELD_ASYNC_HANDLE)) != 0)
          {
             asyncHandle = new handle();
             asyncHandle.readExternal(in);
          }
          if ((writing & (1l << FIELD_EVT_PROC)) != 0)
          {
             eventProcedure = new character();
             eventProcedure.readExternal(in);
          }
          if ((writing & (1l << FIELD_EVT_PROC_CONTEXT)) != 0)
          {
             eventProcedureContext = new handle();
             eventProcedureContext.readExternal(in);
          }
          if ((writing & (1l << FIELD_ARGS)) != 0)
          {
             arguments = (Object[]) PayloadSerializer.readPayload(in);
          }
          else
          {
             arguments = new Object[0];
          }
          if ((writing & (1l << FIELD_MODES)) != 0)
          {
             modes = in.readUTF();
          }
          if ((writing & (1l << FIELD_PORT_HANDLE)) != 0)
          {
             portHandle = new handle();
             portHandle.readExternal(in);
          }
          if ((writing & (1l << FIELD_DYN_FUNC)) != 0)
          {
             dynamicFunction = in.readBoolean();
          }
          if ((writing & (1l << FIELD_SUPER_CALL)) != 0)
          {
             superCall = in.readBoolean();
          }
          if ((writing & (1l << FIELD_IS_CLASS)) != 0)
          {
             isClass = in.readBoolean();
          }
          if ((writing & (1l << FIELD_RETURN_VALUE)) != 0)
          {
             returnValue = in.readBoolean();
          }

          if ((writing & (1l << FIELD_CLIENTCONTEXTID)) != 0)
          {
             clientContextId = in.readUTF();
          }
          if ((writing & (1l << FIELD_REQUESTID)) != 0)
          {
             requestId = in.readUTF();
          }
          if ((writing & (1l << FIELD_CLIENTPRINCIPAL)) != 0)
          {
             clientPrincipal = readByteArray(in);
          }
          if ((writing & (1l << FIELD_VERSIONINFO)) != 0)
          {
             versionInfo = readStringArray(in);
          }
          if ((writing & (1l << FIELD_CLIENTTYPE)) != 0)
          {
             clientType = in.readUTF();
          }
          if ((writing & (1l << FIELD_WEB_SERVICE_TOKEN)) != 0)
          {
             webServiceToken = in.readUTF();
          }
          if ((writing & (1l << FIELD_AS_THREAD)) != 0)
          {
             asThread = in.readBoolean();
          }
       }

       /**
        * Store the passed request info object, to be sent to a remote side.
        *
        * @param    requestInfo
        *           The {@link OerequestInfo} instance.
        *
        * @return   This instance.
        */
       InvokeConfig storeRequest(object<? extends OerequestInfo> requestInfo)
       {
          if (!requestInfo._isValid())
          {
             return this;
          }

          OerequestInfo req = requestInfo.ref();

          this.clientContextId = req.getClientContextId().getValue();
          this.requestId = AppServerManager.nextContextId(false);
          req.setRequestId(requestId);
          this.requestId = req.getRequestId().getValue();
          ClientPrincipal cp = req.getClientPrincipalNative();
          if (cp != null && cp.valid())
          {
             raw r = cp.exportPrincipal();
             this.clientPrincipal = r.getByteArray();
          }
          object<? extends OeversionInfo> oever = req.getVersionInfo();
          if (oever._isValid())
          {
             OeversionInfo ver = oever.ref();
             versionInfo = new String[]
             {
                ver.getOemajorVersion().getValue(),
                ver.getOeminorVersion().getValue(),
                ver.getOemaintVersion().getValue()
             };
             clientType = ver.getOeclientType().getValue();
          }

          return this;
       }

       /**
        * Restore the {@link OerequestInfo} from this call details, as received from a remote call.
        *
        * @param    req
        *           The request info for the Agent's SESSION.
        * @param    agentId
        *           The agent ID, as computed by the Agent.
        * @param    sessionId
        *           The sessionID, as computed by the Agent.
        * @param    threadId
        *           The threadID, as computed by the Agent.
        */
       public void restoreRequest(OerequestInfo req, Integer agentId, Integer sessionId, Integer threadId)
       {
          if (AppServerManager.isMultiSession())
          {
             // TODO: adapter type based on the request type...
             // REST
             // WEB
             // SOAP
             // req.setAdapterType()
          }

          req.initialize(target,
                         clientContextId,
                         requestId,
                         versionInfo,
                         clientType,
                         agentId,
                         sessionId,
                         threadId,
                         clientPrincipal);
       }

       /**
        * For each field, it sets a bit if the field's value is no longer the default value.
        * <p>
        * For {@link Boolean} fields, this means the field is set to <code>true</code>.
        * <p>
        * For {@link Object} fields, this means the field is now not-null.
        *
        * @return   A 64-bit value with bits set only for changed fields.
        */
       private long computeWriting()
       {
          long writing = 0;
          if (inHandle != null)
          {
             writing |= (1 << FIELD_IN_HANDLE);
          }
          if (function)
          {
             writing |= (1 << FIELD_FUNCTION);
          }
          if (persistent)
          {
             writing |= (1 << FIELD_PERSISTENT);
          }
          if (singleRun)
          {
             writing |= (1 << FIELD_SINGLE_RUN);
          }
          if (singleton)
          {
             writing |= (1 << FIELD_SINGLETON);
          }
          if (procedureHandle != null)
          {
             writing |= (1 << FIELD_PROC_HANDLE);
          }
          if (serverHandle != null)
          {
             writing |= (1 << FIELD_SERVER_HANDLE);
          }
          if (transactionDistinct)
          {
             writing |= (1 << FIELD_TX_DISTINCT);
          }
          if (asynchronous)
          {
             writing |= (1 << FIELD_ASYNC);
          }
          if (asyncHandle != null)
          {
             writing |= (1 << FIELD_ASYNC_HANDLE);
          }
          if (eventProcedure != null)
          {
             writing |= (1 << FIELD_EVT_PROC);
          }
          if (eventProcedureContext != null)
          {
             writing |= (1 << FIELD_EVT_PROC_CONTEXT);
          }
          if (arguments != null && arguments.length > 0)
          {
             writing |= (1 << FIELD_ARGS);
          }
          if (modes != null && !modes.isEmpty())
          {
             writing |= (1 << FIELD_MODES);
          }
          if (portHandle != null)
          {
             writing |= (1 << FIELD_PORT_HANDLE);
          }
          if (dynamicFunction)
          {
             writing |= (1 << FIELD_DYN_FUNC);
          }
          if (superCall)
          {
             writing |= (1 << FIELD_SUPER_CALL);
          }
          if (isClass)
          {
             writing |= (1 << FIELD_IS_CLASS);
          }
          if (returnValue)
          {
             writing |= (1 << FIELD_RETURN_VALUE);
          }

          if (clientContextId != null)
          {
             writing |= (1 << FIELD_CLIENTCONTEXTID);
          }
          if (requestId != null)
          {
             writing |= (1 << FIELD_REQUESTID);
          }
          if (clientPrincipal != null)
          {
             writing |= (1 << FIELD_CLIENTPRINCIPAL);
          }
          if (versionInfo != null)
          {
             writing |= (1 << FIELD_VERSIONINFO);
          }
          if (clientType != null)
          {
             writing |= (1 << FIELD_CLIENTTYPE);
          }
          if (webServiceToken != null)
          {
             writing |= (1 << FIELD_WEB_SERVICE_TOKEN);
          }
          if (asThread)
          {
             writing |= (1 << FIELD_AS_THREAD);
          }

          return writing;
       }

       /**
        * Construct and return a short representation of this object to be used mainly for debugging. In this
        * case, the returned string is composed of name and parameter types of the target routine.
        *
        * @return  name and parameter types (in, out) of the target routine.
        */
       @Override
       public String toString()
       {
          return target + "(" + modes + ")";
       }
    }