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;
      }
   }
}