OerequestInfo.java

/*
** Module   : OerequestInfo.java
** Abstract : Implementation of the builtin class.
**
** Copyright (c) 2019-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- -------------------------------Description--------------------------------
** 001 CA  20190526 First version, stubs taken by converting the skeleton using FWD.
** 002 CA  20190812 Added LegacyResource annotation.
** 003 CA  20191024 Added method support levels and updated the class support level.
** 004 CA  20200427 Completed runtime.  
** 005 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
**                  signature.
** 006 HC  20230116 Replaced some handle usages with the actual wrapped resources for
**                  performance.
** 007 CA  20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
**                  dropped if is a no-op.
** 008 GBB 20240112 Support adapterType enum.
** 009 GBB 20250403 Javadoc fixes.
*/

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

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

import static com.goldencode.p2j.util.BlockManager.*;
import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.InternalEntry.Type;

/**
 * Business logic (converted to Java from the 4GL source code
 * in Progress/Lang/OERequestInfo.cls).
 * 
 * Can be accessed in ABL with:
 * <ul>
 *    <li>server-handle:REQUEST-INFO</li>
 *    <li>server-handle:RESPONSE-INFO</li>
 *    <li>async-request-handle:REQUEST-INFO<</li>
 *    <li>async-request-handle:RESPONSE-INFO<</li>
 * </ul>
 * 
 * Can be accessed in appserver with:
 * <ul>
 *    <li>SESSION:CURRENT-REQUEST-INFO</li>
 *    <li>SESSION:CURRENT-RESPONSE-INFO</li>
 * </ul>
 */
@LegacyResource(resource = "Progress.Lang.OERequestInfo")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB)
public final class OerequestInfo
extends BaseObject
{
   /** The adapter type. */
   private object<? extends AdapterTypes> adapterType = TypeFactory.object(AdapterTypes.class);

   /** The agent ID. Only in multi-session agent appservers. */
   private integer agentId = TypeFactory.integer();

   /** The session ID. Only in multi-session agent appservers. */
   private integer sessionId = TypeFactory.integer();

   /** The thread ID. Only in multi-session agent appservers. */
   private integer threadId = TypeFactory.integer();

   /** The client context ID. */
   private character clientContextId = TypeFactory.character();

   /** The procedure name. */
   private character procedureName = TypeFactory.character();

   /** The request ID. */
   private character requestId = TypeFactory.character();

   /** The version info. */
   private object<? extends OeversionInfo> versionInfo = TypeFactory.object(OeversionInfo.class);

   /** The client principal. */
   private handle clientPrincipal = TypeFactory.handle();

   /**
    * Default constructor.  Sets only the {@link #versionInfo}.
    */
   @LegacySignature(type = Type.CONSTRUCTOR)
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public void __lang_OerequestInfo_constructor__()
   {
      internalProcedure(this, "__lang_OerequestInfo_constructor__", new Block((Body) () -> 
      {
         __lang_BaseObject_constructor__();
  
         // AdapterType property: only SESSION:request
         
         // ClientContextId property: 
         // - get/set, generated on the SERVER:request when SERVER is built 
         // - transported to the SESSION:request via CONNECT/RUN and read via SESSION:request. 
         // - SESSION:response will send it back
         
         // RequestId property: 
         // - get, SESSION/SERVER:request/response, generated by the client and sent with each RUN  

         // ProcedureName property
         // - only SESSION:request
         // - the original (rel/full) path of the external program invoked by the client

         // AgentId, SessionId, ThreadId:
         // - set by appserver, SESSION:request/response, SERVER:response

         // VersionInfo property: 
         // - r/o, REQUEST: ver of the client, RESPONSE: ver of the appserver
         this.versionInfo.assign(SessionUtils.localVersionInfo());
         
         // GetClientPrincipal and SetClientPrincipal
         // - both are transported via each CONNECT/RUN 
      }));
   }

   /**
    * Get the {@link #adapterType} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "OBJECT", qualified = "Progress.ApplicationServer.AdapterTypes", type = Type.GETTER, name = "AdapterType")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public object<? extends com.goldencode.p2j.oo.applicationserver.AdapterTypes> getAdapterType()
   {
      return function(this, "AdapterType", object.class, new Block((Body) () -> 
      {
         returnNormal(adapterType);
      }));
   }

   /**
    * Get the {@link #agentId} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "AgentId")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public integer getAgentId()
   {
      return function(this, "AgentId", integer.class, new Block((Body) () -> 
      {
         returnNormal(agentId);
      }));
   }

   /**
    * Get the {@link #clientContextId} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ClientContextId")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public character getClientContextId()
   {
      return function(this, "ClientContextId", character.class, new Block((Body) () -> 
      {
         returnNormal(clientContextId);
      }));
   }

   /**
    * Set the {@link #clientContextId} field.
    * 
    * @param    _var
    *           The client context ID.
    */
   @LegacySignature(type = Type.SETTER, name = "ClientContextId", parameters = 
   {
      @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT")
   })
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public void setClientContextId(final character _var)
   {
      character var = TypeFactory.initInput(_var);
      
      internalProcedure(this, "ClientContextId", new Block((Body) () -> 
      {
         clientContextId.assign(var);
      }));
   }

   /**
    * Get the {@link #procedureName} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ProcedureName")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public character getProcedureName()
   {
      return function(this, "ProcedureName", character.class, new Block((Body) () -> 
      {
         returnNormal(procedureName);
      }));
   }

   /**
    * Get the {@link #requestId} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RequestId")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public character getRequestId()
   {
      return function(this, "RequestId", character.class, new Block((Body) () -> 
      {
         returnNormal(requestId);
      }));
   }

   /**
    * Get the {@link #sessionId} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "SessionId")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public integer getSessionId()
   {
      return function(this, "SessionId", integer.class, new Block((Body) () -> 
      {
         returnNormal(sessionId);
      }));
   }

   /**
    * Get the {@link #threadId} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "ThreadId")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public integer getThreadId()
   {
      return function(this, "ThreadId", integer.class, new Block((Body) () -> 
      {
         returnNormal(threadId);
      }));
   }

   /**
    * Get the {@link #versionInfo} field.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "OBJECT", qualified = "Progress.Lang.OEVersionInfo", type = Type.GETTER, name = "VersionInfo")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public object<? extends com.goldencode.p2j.oo.lang.OeversionInfo> getVersionInfo()
   {
      return function(this, "VersionInfo", object.class, new Block((Body) () -> 
      {
         returnNormal(versionInfo);
      }));
   }

   /**
    * Get the {@link #clientPrincipal}.
    * 
    * @return   See above.
    */
   @LegacySignature(returns = "HANDLE", type = Type.METHOD, name = "GetClientPrincipal")
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public handle getClientPrincipal()
   {
      return function(this, "GetClientPrincipal", handle.class, new Block((Body) () -> 
      {
         returnNormal(this.clientPrincipal);
      }));
   }

   /**
    * Get the {@link #clientPrincipal}.
    *
    * @return   See above.
    */
   public ClientPrincipal getClientPrincipalNative()
   {
      return (ClientPrincipal) this.clientPrincipal.getResource();
   }

   /**
    * Set the {@link #clientPrincipal}.  This deletes any other instance it may reference via the
    * {@link #clientPrincipal}.
    * 
    * @param    _h
    *           The client principal handle.
    *           
    * @return   <code>true</code> if the assign was possible.
    */
   @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "SetClientPrincipal", parameters = 
   {
      @LegacyParameter(name = "h", type = "HANDLE", mode = "INPUT")
   })
   @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
   public logical setClientPrincipal(final handle _h)
   {
      handle h = TypeFactory.initInput(_h);
      
      return function(this, "SetClientPrincipal", logical.class, new Block((Body) () -> 
      {
         if (h.isUnknown())
         {
            if (this.clientPrincipal._isValid())
            {
               HandleOps.delete(this.clientPrincipal);
               this.clientPrincipal.setUnknown();
            }
            
            returnNormal(true);
         }
         else if (!(h.getResource() instanceof ClientPrincipal) || !h._isValid())
         {
            returnNormal(false);
         }

         this.clientPrincipal.assign(h);
         
         returnNormal(true);
      }));
   }

   /**
    * Set the {@link #requestId}.  This is not a legacy setter!
    * 
    * @param    requestId
    *           The request ID.
    */
   public void setRequestId(String requestId)
   {
      this.requestId.assign(requestId);
   }

   /**
    * Initialize this instance with the specified values for each field.
    * 
    * @param    procedureName
    *           The procedure name.
    * @param    clientContextId
    *           The client context ID.
    * @param    requestId
    *           The request ID.
    * @param    versionInfo
    *           The version info, in the [major, minor, maint] format.
    * @param    clientType
    *           The client type.
    * @param    agentId
    *           The agent ID.
    * @param    sessionId
    *           The session ID.
    * @param    threadId
    *           The thread ID.
    * @param    clientPrincipal
    *           The serialized client principal.
    */
   public void initialize(String   procedureName,
                          String   clientContextId, 
                          String   requestId, 
                          String[] versionInfo,
                          String   clientType,
                          Integer  agentId,
                          Integer  sessionId,
                          Integer  threadId,
                          byte[]   clientPrincipal)
   {
      this.procedureName.assign(procedureName);
      this.clientContextId.assign(clientContextId);
      this.requestId.assign(requestId);
      this.versionInfo.assign(OeversionInfo.newInstance(versionInfo == null ? null : versionInfo[0], 
                                                        versionInfo == null ? null : versionInfo[1], 
                                                        versionInfo == null ? null : versionInfo[2], 
                                                        clientType));
      this.agentId.assign(agentId);
      this.sessionId.assign(sessionId);
      this.threadId.assign(threadId);
      
      if (this.clientPrincipal._isValid())
      {
         HandleOps.delete(this.clientPrincipal);
      }
      
      if (clientPrincipal == null)
      {
         this.clientPrincipal.setUnknown();
      }
      else
      {
         ClientPrincipal.create(this.clientPrincipal);
         this.clientPrincipal.unwrapClientPrincipal().importPrincipal(new raw(clientPrincipal));
      }
   }

   /**
    * Initialize this instance from the specified reference, by copying the state.
    * 
    * @param    req
    *           The request instance to initialize from.
    * @param    request
    *           Flag indicating if the current instance is for a request - if so, set the 
    *           {@link #procedureName}.
    */
   public void initialize(OerequestInfo req, boolean request)
   {
      if (request)
      {
         // do not set it in response
         this.procedureName.assign(req.procedureName);
      }
      else
      {
         this.procedureName.setUnknown();
      }
      
      this.clientContextId.assign(req.clientContextId);
      this.requestId.assign(req.requestId);
      
      this.agentId.assign(req.agentId);
      this.sessionId.assign(req.sessionId);
      this.threadId.assign(req.threadId);

      this.versionInfo.assign(SessionUtils.localVersionInfo());
      
      if (req.clientPrincipal._isValid())
      {
         this.clientPrincipal.set(((ClientPrincipal) req.clientPrincipal.getResource()).clone());
      }
   }
   
   /**
    * Obtain an array representation of this instance, for serialization purposes.
    * 
    * @return   The instance fields, in the [adapterType, procedureName, clientContextId, requestId,
    *           agentId, sessionId, threadId, [major, minor, maint, clientType], clientPrincipal]
    *           order.
    */
   public Object[] toArray()
   {
      OeversionInfo ver = versionInfo.ref();
      
      Object[] res = new Object[9];
      res[0] = adapterType._isValid() ? adapterType.ref().toLegacyString() : null;
      res[1] = procedureName.getValue();
      res[2] = clientContextId.getValue();
      res[3] = requestId.getValue();
      res[4] = agentId.toJavaIntegerType();
      res[5] = sessionId.toJavaIntegerType();
      res[6] = threadId.toJavaIntegerType();
      res[7] = new String[] 
      {
         ver.getOemajorVersion().getValue(),
         ver.getOeminorVersion().getValue(),
         ver.getOemaintVersion().getValue(),
         ver.getOeclientType().getValue()
      };
      res[8] = null;
      if (clientPrincipal._isValid())
      {
         raw r = ((ClientPrincipal) clientPrincipal.getResource()).exportPrincipal();
         res[8] = r.getByteArray();
      }
      
      return res;
   }
   
   /**
    * Initialize this instance from the given array representation (from a previous 
    * {@link #toArray()} call).
    * 
    * @param    arr
    *           The serialized instance, as was returned by {@link #toArray()}.
    */
   public void fromArray(Object[] arr)
   {
      adapterType.assign(arr[0]);
      procedureName.assign(arr[1]);
      clientContextId.assign(arr[2]);
      requestId.assign(arr[3]);
      agentId.assign(arr[4]);
      sessionId.assign(arr[5]);
      threadId.assign(arr[6]);
      String[] ver = (String[]) arr[7];
      versionInfo.assign(OeversionInfo.newInstance(ver[0], ver[1], ver[2], ver[3]));
      
      if (clientPrincipal._isValid())
      {
         HandleOps.delete(clientPrincipal);
      }

      byte[] cp = (byte[]) arr[8];
      if (cp == null)
      {
         this.clientPrincipal.setUnknown();
      }
      else
      {
         ClientPrincipal.create(clientPrincipal);
         ((ClientPrincipal) clientPrincipal.getResource()).importPrincipal(new raw(cp));
      }
   }
}