# AppServerInvocationResult.java

    /*
    ** Module   : AppServerInvocationResult.java
    ** Abstract : Container for the result of a remote appserver procedure call.
    **
    ** Copyright (c) 2013-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------Description----------------------------------
    ** 001 CA  20130529 Created initial version.
    ** 002 CA  20130829 Return the resolved parameter modes to the caller too.
    ** 003 CA  20131220 Extracted data common with async web service calls to RemoteInvocationResult.
    ** 004 CA  20200427 Added support for OERequestInfo legacy class, in response and request.
    ** 005 IAS 20200908 Rework (de)serialization.
    ** 006 IAS 20200922 Fix typo in deserialization.
    **     CA  20201027 The remote side can delete the IN HANDLE used by the requester at the RUN statement.
    ** 007 CA  20230215 Use PayloadSerializer for serialization.
    ** 008 GBB 20250403 Adding childConnectionID and appServerConnectionModel to support MSA appservers.
    */
    /*
    ** 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.main;

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

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

    /**
     * This class acts as a container for the various data which is returned by or updated during
     * remote appserver calls.  This includes the function/procedure return value, updated
     * OUTPUT/INPUT-OUTPUT parameters or the persistent procedure handle for a RUN ... PERSISTENT
     * call.
     */
    public class AppServerInvocationResult
    extends RemoteInvocationResult
    implements Externalizable
    {
       /** The remote procedure handle, used to build a {@link ProxyProcedureWrapper} instance. */
       private String procedureId;

       /** The connection ID, returned during the connect request. */
       private String connectionID;

       /** The child connection ID, returned during the connect request. */
       private String childConnectionID;

       /** The session model used by the connection running this request. */
       private AppServerSessionModel appServerConnectionModel;

       /** The response for the <code>OERequestInfo:clientContextId</code> value. */
       private String clientContextId;

       /** The response for the <code>OERequestInfo:requestId</code> value. */
       private String requestId;

       /** The response for the <code>OERequestInfo:versionInfo</code> value. */
       private String[] versionInfo;  // major, minor, maint

       /** The response for the <code>OERequestInfo:versionInfo</code> value. */
       private String clientType;

       /** The response for the <code>OERequestInfo:agentId</code> value. */
       private Integer agentId;

       /** The response for the <code>OERequestInfo:sessionId</code> value. */
       private Integer sessionId;

       /** The response for the <code>OERequestInfo:threadId</code> value. */
       private Integer threadId;

       /** The response for the <code>OERequestInfo:clientPrincipal</code> value. */
       private byte[] clientPrincipal;

       /** Flag indicating that the requester's IN HANDLE has been deleted by the remote side. */
       private boolean invalidInHandle;

       /**
        * Serialize this object, to be picked up by the other side.
        *
        * @param    out
        *           The output destination to which the data will be sent.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          PayloadSerializer.writePayload(out, arguments);

          PayloadSerializer.writePayload(out, result);
          writeString(out, procedureId);
          PayloadSerializer.writePayload(out, error);
          writeString(out, connectionID);
          writeString(out, childConnectionID);
          writeString(out, appServerConnectionModel == null ? null : appServerConnectionModel.toString());
          writeString(out, modes);

          // write the data to build the server:RESPONSE-INFO on the caller's side
          writeString(out, clientContextId);
          writeString(out, requestId);
          writeStringArray(out, versionInfo);
          writeString(out, clientType);
          writeInteger(out, agentId);
          writeInteger(out, sessionId);
          writeInteger(out, threadId);
          writeByteArray(out, clientPrincipal);

          out.writeBoolean(invalidInHandle);
       }

       /**
        * Used the specified input source and initialize this instance.
        *
        * @param    in
        *           The input source from which the data will be restored.
        *
        * @throws   IOException
        *           In case of I/O errors.
        * @throws   ClassNotFoundException
        *           If payload can't be instantiated.
        */
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          arguments = (Object[]) PayloadSerializer.readPayload(in);

          result = PayloadSerializer.readPayload(in);
          procedureId = readString(in);
          error = (Throwable) PayloadSerializer.readPayload(in);
          connectionID = readString(in);
          childConnectionID = readString(in);
          appServerConnectionModel = AppServerSessionModel.fromString(readString(in));
          modes = readString(in);

          // read the data to build the server:RESPONSE-INFO
          clientContextId = readString(in);
          requestId = readString(in);
          versionInfo = readStringArray(in);
          clientType = readString(in);
          agentId = readInteger(in);
          sessionId = readInteger(in);
          threadId = readInteger(in);
          clientPrincipal = readByteArray(in);
          invalidInHandle = in.readBoolean();
       }

       /**
        * Get the state of the {@link #invalidInHandle} flag.
        *
        * @return   See above.
        */
       public boolean isInvalidInHandle()
       {
          return invalidInHandle;
       }

       /**
        * Set the state of the {@link #invalidInHandle} flag.
        *
        * @param    invalidInHandle
        *           The new flag value.
        */
       public void setInvalidInHandle(boolean invalidInHandle)
       {
          this.invalidInHandle = invalidInHandle;
       }

       /**
        * Get the handle code for the remote persistent procedure.
        *
        * @return   The {@link #procedureId} field.
        */
       public String getProcedureId()
       {
          return procedureId;
       }

       /**
        * Save the remote persistent procedure handle's code.
        *
        * @param    procedureId
        *           The code for the remote persistent procedure handle.
        */
       public void setProcedureId(String procedureId)
       {
          this.procedureId = procedureId;
       }

       /**
        * Get the established connection ID, for a connect request.
        *
        * @return   See above.
        */
       public String getConnectionID()
       {
          return connectionID;
       }

       /**
        * Set the new connection ID, after a connect request.
        *
        * @param    connectionID
        *           The connection ID, which will be unknown if there are any {@link #error errors}.
        */
       public void setConnectionID(String connectionID)
       {
          this.connectionID = connectionID;
       }

       /**
        * Get the ID of the child connection handling the request.
        *
        * @return   See above.
        */
       public String getChildConnectionID()
       {
          return childConnectionID;
       }

       /**
        * Set the ID of the child connection handling the request.
        *
        * @param    childConnectionID
        *           The ID of the child connection handling the request.
        */
       public void setChildConnectionID(String childConnectionID)
       {
          this.childConnectionID = childConnectionID;
       }

       /**
        * Get the session model used by the connection running this request.
        *
        * @return See above.
        */
       public AppServerSessionModel getConnectionModel()
       {
          return appServerConnectionModel;
       }

       /**
        * Set the ID of the child connection handling the request.
        *
        * @param    appServerConnectionModel
        *           The session model used by the connection running this request.
        */
       public void setConnectionModel(AppServerSessionModel appServerConnectionModel)
       {
          this.appServerConnectionModel = appServerConnectionModel;
       }

       /**
        * Check if the RETURN-VALUE will be set.
        *
        * @return   In case of appserver results, it returns <code>true</code>.
        */
       public boolean setReturnValue()
       {
          return true;
       }

       /**
        * Get the {@link #clientContextId}.
        *
        * @return   See above.
        */
       public String getClientContextId()
       {
          return clientContextId;
       }

       /**
        * Set the {@link #clientContextId}.
        *
        * @param    clientContextId
        *           The clientContextId to set
        */
       public void setClientContextId(String clientContextId)
       {
          this.clientContextId = clientContextId;
       }

       /**
        * Get the {@link #requestId}.
        *
        * @return   See above.
        */
       public String getRequestId()
       {
          return requestId;
       }

       /**
        * Set the {@link #requestId}.
        *
        * @param    requestId
        *           The requestId to set
        */
       public void setRequestId(String requestId)
       {
          this.requestId = requestId;
       }

       /**
        * Get the {@link #versionInfo}.
        *
        * @return   See above.
        */
       public String[] getVersionInfo()
       {
          return versionInfo;
       }

       /**
        * Set the {@link #versionInfo}.
        *
        * @param    versionInfo
        *           The versionInfo to set
        */
       public void setVersionInfo(String[] versionInfo)
       {
          this.versionInfo = versionInfo;
       }

       /**
        * Get the {@link #clientType}.
        *
        * @return   See above.
        */
       public String getClientType()
       {
          return clientType;
       }

       /**
        * Set the {@link #clientType}.
        *
        * @param    clientType
        *           The clientType to set
        */
       public void setClientType(String clientType)
       {
          this.clientType = clientType;
       }

       /**
        * Get the {@link #agentId}.
        *
        * @return   See above.
        */
       public Integer getAgentId()
       {
          return agentId;
       }

       /**
        * Set the {@link #agentId}.
        *
        * @param    agentId
        *           The agentId to set
        */
       public void setAgentId(Integer agentId)
       {
          this.agentId = agentId;
       }

       /**
        * Get the {@link #sessionId}.
        *
        * @return   See above.
        */
       public Integer getSessionId()
       {
          return sessionId;
       }

       /**
        * Set the {@link #sessionId}.
        *
        * @param    sessionId
        *           The sessionId to set
        */
       public void setSessionId(Integer sessionId)
       {
          this.sessionId = sessionId;
       }

       /**
        * Get the {@link #threadId}.
        *
        * @return   See above.
        */
       public Integer getThreadId()
       {
          return threadId;
       }

       /**
        * Set the {@link #threadId}.
        *
        * @param    threadId
        *           The threadId to set
        */
       public void setThreadId(Integer threadId)
       {
          this.threadId = threadId;
       }

       /**
        * Get the {@link #clientPrincipal}.
        *
        * @return   See above.
        */
       public byte[] getClientPrincipal()
       {
          return clientPrincipal;
       }

       /**
        * Set the {@link #clientPrincipal}.
        *
        * @param    clientPrincipal
        *           The clientPrincipal to set
        */
       public void setClientPrincipal(byte[] clientPrincipal)
       {
          this.clientPrincipal = clientPrincipal;
       }

       /**
        * Store a {@link OerequestInfo} response from the appserver agent, to be sent to the caller.
        *
        * @param    currentResponseInfo
        *           The <code>SESSION:CURRENT-RESPONSE-INFO</code> instance.
        */
       public void storeResponse(object<? extends OerequestInfo> currentResponseInfo)
       {
          if (currentResponseInfo.isUnknown() || !currentResponseInfo._isValid())
          {
             return;
          }
          OerequestInfo response = currentResponseInfo.ref();

          this.clientContextId = response.getClientContextId().getValue();
          this.requestId = response.getRequestId().getValue();
          if (response.getVersionInfo()._isValid())
          {
             OeversionInfo oever = response.getVersionInfo().ref();

             this.versionInfo = new String[3];
             this.versionInfo[0] = oever.getOemajorVersion().getValue();
             this.versionInfo[1] = oever.getOeminorVersion().getValue();
             this.versionInfo[2] = oever.getOemaintVersion().getValue();

             this.clientType = oever.getOeclientType().getValue();
          }
          this.agentId = response.getAgentId().toJavaIntegerType();
          this.sessionId = response.getSessionId().toJavaIntegerType();
          this.threadId = response.getThreadId().toJavaIntegerType();

          handle cp = response.getClientPrincipal();
          if (cp._isValid())
          {
             raw r = cp.unwrapClientPrincipal().exportPrincipal();
             this.clientPrincipal = r.getByteArray();
          }
          else
          {
             this.clientPrincipal = null;
          }
       }
    }