PortTypeWrapper.java

/*
** Module   : PortTypeWrapper.java
** Abstract : container that implements a port-type procedure.
**
** Copyright (c) 2013-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 002 CA  20131220 Created initial version.
** 003 VIG 20140116 Changed currentWindow method signature.
** 004 SVL 20170126 Added no-op setters for FILE-NAME.
** 005 CA  20190628 Added SINGLE-RUN and SINGLETON support.
** 006 OM  20201030 Invalid attribute API support for getters/setters.
**     OM  20201120 Added callback procedure management methods.
**     CA  20221006 UNIQUE-ID is kept as Java type instead of BDT.  Refs #6827
**     CA  20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
** 007 CA  20240918 Removed default methods from interfaces and moved them as concrete implementations, as
**                  default methods cause the Java metaspace to spike one order of magnitude.
** 008 CA  20250321 Added 'setCallback' overloads.
*/

/*
** 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 com.goldencode.p2j.ui.*;
import com.goldencode.p2j.util.ProcedureManager.SearchMode;
import com.goldencode.p2j.util.UniqueIdGenerator.IdKind;

/**
 * Implements a procedure connected to a WSDL port-type. Compared to the 
 * {@link ExternalProgramWrapper} implementation, port-type procedures are limited, most of the
 * APIs being no-op or returning default values. Only the following procedure-specific APIs are
 * implemented by port-types too:
 * <ul>
 *   <li>FILE-NAME</li>
 *   <li>PERSISTENT</li>
 *   <li>SERVER</li>
 *   <li>ASYNC-REQUEST-COUNT</li>
 *   <li>SET-CALLBACK-PROCEDURE</li>
 *   <li>SERVER-HANDLE</li>
 *   <li>CURRENT-WINDOW</li>
 * </ul>
 */
public class PortTypeWrapper
extends HandleChain
implements PersistentProcedure,
           AsyncRequestListener
{
   /** Constant identifying the <code>REQUEST-HEADER</code> callback. */
   private static final String CALLBACK_REQUEST_HEADER = "REQUEST-HEADER";
   
   /** Constant identifying the <code>RESPONSE-HEADER</code> callback. */
   private static final String CALLBACK_RESPONSE_HEADER = "RESPONSE-HEADER";
   
   /** The {@link ServerImpl} instance associated with this port-type procedure. */
   private final ServerImpl server;
   
   /** The ADM-DATA of this port-type procedure. */
   private String admData = null;
   
   /** The UNIQUE-ID of this port-type procedure. */
   private Long uniqueId = null;
   
   /**
    * The name of request-header callback procedure.  Unknown if request-header callback is not 
    * used.
    */
   private String requestHeaderProc = null;

   /**
    * The context for the request-header procedure.  Unknown if request-header callback is not 
    * used.
    */
   private WrappedResource requestHeaderContext = null;
   
   /**
    * The name of response-header callback procedure.  Unknown if response-header callback is not 
    * used.
    */
   private String responseHeaderProc = null;
   
   /**
    * The context for the response-header procedure.  Unknown if response-header callback is not 
    * used.
    */
   private WrappedResource responseHeaderContext = null;
   
   /** The number of active asynchronous requests. */
   private long asyncRequestCount = 0;

   /** Flag indicating if this port-type procedure was deleted or not. */
   private boolean deleted = false;

   /**
    * Instantiate a new port-type procedure by specifying the port-type name and the associated
    * {@link ServerImpl} instance.
    * 
    * @param    name
    *           The port-type name.  This will be reported by the NAME and FILE-NAME attributes.
    * @param    server
    *           The associated server instance.
    */
   PortTypeWrapper(String name, ServerImpl server)
   {
      // port-type procedures do not collide with external or proxy procedures (thus the first/last
      // resource must not be set).
      super(false, false);

      this.name = name;
      this.server = server;
   }

   /**
    * Reports if this object is valid for use.  
    * <p>
    * A port-type is valid only if it was not yet deleted.
    * 
    * @return   <code>true</code> if we are valid (can be used).
    */
   @Override
   public boolean valid()
   {
      return !deleted;
   }

   /**
    * Get the <code>file-name</code> attribute of this handle.
    * 
    * @return   See above.
    */
   @Override
   public character getFileName()
   {
      return new character(name());
   }

   /**
    * Returns a handle to the app server in which the procedure is ran.
    * 
    * @return   See above
    */
   @Override
   public handle getServerHandle()
   {
      return new handle(server);
   }

   /**
    * Returns the number of asynchronous requests.
    * 
    * @return   See above.
    */
   @Override
   public integer getAsyncRequestCount()
   {
      return new integer(asyncRequestCount);
   }

   /**
    * Get the <code>persistent</code> attribute of this port-type procedure.
    * 
    * @return   Always <code>true</code>.
    */
   @Override
   public logical isPersistent()
   {
      return new logical(true);
   }

   /**
    * Get the <code>singleton</code> attribute of this port-type procedure.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical isSingleton()
   {
      return new logical(false);
   }

   /**
    * Get the <code>single-run</code> attribute of this port-type procedure.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical isSingleRun()
   {
      return new logical(false);
   }

   /**
    * Applies a callback procedure, which allows execution of a defined event without duplicating
    * the event procedure definition.
    *
    * @param   eventName
    *          The event whose callback will be called.
    *
    * @return  {@code true} if operation is successful.
    */
   @Override
   public logical applyCallback(Text eventName)
   {
      return applyCallback(eventName.toJavaType());
   }

   /**
    * Retrieves the name of the internal procedure associated with the ABL callback for the
    * specified event.
    *
    * @param   eventName
    *          The name of the event.
    *
    * @return  the name of the internal procedure associated with specified event.
    */
   @Override
   public character getCallbackProcName(Text eventName)
   {
      return getCallbackProcName(eventName.toJavaType());
   }
   
   /**
    * Retrieves the handle of the procedure that contains the internal procedure associated
    * with the ABL callback for the specified event
    *
    * @param   eventName
    *          The name of the event.
    *
    * @return  a handle to the procedure that contains the callback procedure.
    */
   @Override
   public handle getCallbackProcContext(Text eventName)
   {
      return getCallbackProcContext(eventName.toJavaType());
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, Text routineName, object<?> context)
   {
      return setCallback(callbackName.toJavaType(), routineName.toJavaType(), context);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(String callbackName, String routineName, handle context)
   {
      return setCallbackProcedure(callbackName, routineName, context);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, Text routineName, handle context)
   {
      return setCallbackProcedure(callbackName.toJavaType(), routineName.toJavaType(), context);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(String callbackName, Text routineName, handle context)
   {
      return setCallbackProcedure(callbackName, routineName.toJavaType(), context);
   }

   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(String callbackName, String routineName)
   {
      return setCallback(callbackName, routineName, (object<?>) null);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, Text routineName)
   {
      return setCallback(callbackName.toJavaType(), routineName.toJavaType(), (object<?>) null);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, String routineName)
   {
      return setCallback(callbackName.toJavaType(), routineName);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(String callbackName, Text routineName)
   {
      return setCallback(callbackName, routineName.toJavaType());
   }

   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(String callbackName, Text routineName, object<?> context)
   {
      return setCallback(callbackName, routineName.toJavaType(), context);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, String routineName, object<?> context)
   {
      return setCallback(callbackName.toJavaType(), routineName, context);
   }
   
   /**
    * Configures a callback. Creates an association between a method within a class instance or
    * an internal procedure within a persistent procedure, with an ABL callback event.
    *
    * @param   callbackName
    *          The name of a callback.
    * @param   routineName
    *          The name of a method or an internal procedure to be associated.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return  {@code true} if operation is successful and {@code false} if the parameters are
    *          invalid.
    */
   @Override
   public logical setCallback(Text callbackName, String routineName, handle context)
   {
      return setCallback(callbackName.toJavaType(), routineName, context);
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event, which will be executed using
    *           the current THIS-PROCEDURE reference. To remove a callback mapping, set the this
    *           parameter to the empty string.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(String event, String proc)
   {
      return setCallbackProcedure(new character(event), new character(proc),
                                  ProcedureManager.thisProcedure());
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event, which will be executed using
    *           the current THIS-PROCEDURE reference. To remove a callback mapping, set the this
    *           parameter to the empty string.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(character event, String proc)
   {
      return setCallbackProcedure(event, new character(proc), ProcedureManager.thisProcedure());
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event, which will be executed using
    *           the current THIS-PROCEDURE reference. To remove a callback mapping, set the this
    *           parameter to the empty string.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(String event, character proc)
   {
      return setCallbackProcedure(new character(event), proc, ProcedureManager.thisProcedure());
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event, which will be executed using
    *           the current THIS-PROCEDURE reference. To remove a callback mapping, set the this
    *           parameter to the empty string.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(character event, character proc)
   {
      return setCallbackProcedure(event, proc, ProcedureManager.thisProcedure());
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid or if the procedure's context is
    * not a valid handle.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event. To remove a callback 
    *           mapping, set the this parameter to the empty string.
    * @param    context
    *           The procedure handle where the internal procedure will be searched and executed.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(String event, String proc, handle context)
   {
      return setCallbackProcedure(new character(event), new character(proc), context);
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid or if the procedure's context is
    * not a valid handle.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event. To remove a callback 
    *           mapping, set the this parameter to the empty string.
    * @param    context
    *           The procedure handle where the internal procedure will be searched and executed.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(character event, String proc, handle context)
   {
      return setCallbackProcedure(event, new character(proc), context);
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid or if the procedure's context is
    * not a valid handle.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event. To remove a callback 
    *           mapping, set the this parameter to the empty string.
    * @param    context
    *           The procedure handle where the internal procedure will be searched and executed.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(String event, character proc, handle context)
   {
      return setCallbackProcedure(new character(event), proc, context);
   }

   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid or if the procedure's context is
    * not a valid handle.
    * 
    * @param    event
    *           The event name, must evaluate to one of the "REQUEST-HEADER" or the 
    *           "RESPONSE-HEADER" strings. 
    * @param    proc
    *           The internal procedure to be executed for this event. To remove a callback 
    *           mapping, set the this parameter to the empty string.
    * @param    context
    *           The procedure handle where the internal procedure will be searched and executed.
    *           
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallbackProcedure(character event, character proc, handle context)
   {
      String evt = (event.isUnknown() ? "" : event.toStringMessage());
      
      if (!(evt.equalsIgnoreCase(CALLBACK_REQUEST_HEADER) ||
            evt.equalsIgnoreCase(CALLBACK_RESPONSE_HEADER)))
      {
         final int[] errCodes = { 11576, 4065 };
         final String msg1 = "Invalid callback type passed to SET-CALLBACK-PROCEDURE; %s. " +
            "Valid callback types are: REQUEST-HEADER or RESPONSE-HEADER (11576)";
         final String msg2 = "**The SET-CALLBACK-PROCEDURE attribute on the PROCEDURE widget " +
            "has invalid arguments. (4065)";
         
         ErrorManager.recordOrShowError(errCodes, new String[] {String.format(msg1, evt), msg2},
                                        false, false, false, true);

         return new logical(false);
      }
      
      if (!context._isValid() || !(context.getResource() instanceof PersistentProcedure))
      {
         final int[] errCodes = {11982, 4065};
         final String msg1 = 
            "Third argument to SET-CALLBACK-PROCEDURE must be a valid procedure handle";
         final String msg2 =
           "**The SET-CALLBACK-PROCEDURE attribute on the PROCEDURE widget has invalid arguments";
         ErrorManager.recordOrShowError(errCodes, new String[] {msg1, msg2},
                                        false, false, false);
         
         return new logical(false);
      }
      
      // save the procedure and context
      if (CALLBACK_REQUEST_HEADER.equalsIgnoreCase(evt))
      {
         if (proc.toStringMessage().length() == 0)
         {
            // reset them
            this.requestHeaderContext = null;
            this.requestHeaderProc = null;
         }
         else
         {
            this.requestHeaderContext = context.getResource();
            this.requestHeaderProc = proc.toStringMessage();
         }
      }
      else
      {
         if (proc.toStringMessage().length() == 0)
         {
            // reset them
            this.responseHeaderContext = null;
            this.responseHeaderProc = null;
         }
         else
         {
            this.responseHeaderContext = context.getResource();
            this.responseHeaderProc = proc.toStringMessage();
         }
      }
      
      // false is always returned
      return new logical(false);
   }
   
   /**
    * Retrieves the handle of the procedure that contains the internal procedure associated
    * with the ABL callback for the specified event
    *
    * @param   eventName
    *          The name of the event.
    *
    * @return  a handle to the procedure that contains the callback procedure.
    */
   @Override
   public handle getCallbackProcContext(String eventName)
   {
      UnimplementedFeature.missing("GET-CALLBACK-PROC-CONTEXT(String event)");
      return new handle();
   }
   
   /**
    * Retrieves the name of the internal procedure associated with the ABL callback for the
    * specified event.
    *
    * @param   eventName
    *          The name of the event.
    *
    * @return  the name of the internal procedure associated with specified event.
    */
   @Override
   public character getCallbackProcName(String eventName)
   {
      UnimplementedFeature.missing("GET-CALLBACK-PROC-NAME(String event)");
      return new character();
   }
   
   /**
    * Applies a callback procedure, which allows execution of a defined event without duplicating
    * the event procedure definition.
    *
    * @param   eventName
    *          The event whose callback will be called.
    *
    * @return  {@code true} if operation is successful.
    */
   @Override
   public logical applyCallback(String eventName)
   {
      UnimplementedFeature.missing("APPLY-CALLBACK(String event)");
      return new logical();
   }
   
   /**
    * Associate the internal procedure with the given web-services event.
    * <p>
    * This method returns false if the event name is not valid.
    * <p>
    * For external procedures, this always shows an error.
    *
    * @param   event
    *          The event name, must evaluate to one of the "REQUEST-HEADER" or the "RESPONSE-HEADER" strings. 
    * @param   proc
    *          The internal procedure to be executed for this event, which will be executed using the current
    *          THIS-PROCEDURE reference. To remove a callback mapping, set the this parameter to the empty
    *          string.
    * @param   context
    *          The context in which the callback will be executed.
    *
    * @return   <code>true</code> if the event was registered.
    */
   @Override
   public logical setCallback(String event, String proc, object<?> context)
   {
      UnimplementedFeature.missing("SET-CALLBACK-PROCEDURE(String event, String proc, object<?> context)");
      return new logical();
   }
   
   /**
    * Get the value of the ADM-DATA attribute.
    * 
    * @return   See above.
    */
   @Override
   public character getADMData()
   {
      return new character(this.admData);
   }

   /**
    * Set the value of the ADM-DATA attribute.
    * 
    * @param    value
    *           The new value.
    */
   @Override
   public void setADMData(String value)
   {
      this.admData = value;
   }

   /**
    * Set the value of the ADM-DATA attribute.
    * 
    * @param    value
    *           The new value.
    */
   @Override
   public void setADMData(character value)
   {
      this.admData = value.getValue();
   }

   /**
    * Gets the the unique ID number associated to this object by the underlying system. Not the 
    * same as the handle value itself.
    * 
    * @return   See above.
    */
   @Override
   public integer getUniqueID()
   {
      if (uniqueId == null)
      {
         this.uniqueId = UniqueIdGenerator.getUniqueId(IdKind.PROCEDURE); 
      }
      return new integer(this.uniqueId);
   }

   /**
    * Implements the <code>PUBLISHED-EVENTS</code> attribute.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always the empty string.
    */
   @Override
   public character getPublishedEvents()
   {
      return new character("");
   }

   /**
    * Implements the <code>SUPER-PROCEDURES</code> attribute.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always the empty string.
    */
   @Override
   public character superProcedures()
   {
      return new character("");
   }

   /**
    * Implements the <code>ADD-SUPER-PROCEDURE</code> method.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical addSuperProcedure(handle h)
   {
      return new logical(false);
   }

   /**
    * Implements the <code>ADD-SUPER-PROCEDURE</code> method.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical addSuperProcedure(handle h, SearchMode mode)
   {
      return new logical(false);
   }

   /**
    * Implements the <code>ADD-SUPER-PROCEDURE</code> method.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical addSuperProcedure(handle h, int mode)
   {
      return new logical(false);
   }

   /**
    * Implements the <code>ADD-SUPER-PROCEDURE</code> method.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical addSuperProcedure(handle h, integer mode)
   {
      return new logical(false);
   }

   /**
    * Implements the <code>REMOVE-SUPER-PROCEDURE</code> method.
    * <p>
    * This is a no-op for port-type procedures.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical removeSuperProcedure(handle h)
   {
      return new logical(false);
   }

   /**
    * Implements the <code>REMOTE</code> attribute.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical isRemote()
   {
      return new logical(false);
   }

   /**
    * Implements the <code>INTERNAL-ENTRIES</code> attribute.
    * 
    * @return   Always unknown.
    */
   @Override
   public character internalEntries()
   {
      return new character();
   }

   /**
    * Implements the <code>GET-SIGNATURE</code> method.
    * 
    * @return   Always the empty string.
    */
   @Override
   public character getSignature(String internalEntry)
   {
      return new character("");
   }

   /**
    * Implements the <code>GET-SIGNATURE</code> method.
    * 
    * @return   Always the empty string.
    */
   @Override
   public character getSignature(character internalEntry)
   {
      return new character("");
   }

   /**
    * Get CURRENT-WINDOW attribute for given procedure handle.
    * <p>
    * 
    * @return   Alwasy unknown.
    */
   @Override
   public handle currentWindow()
   {
      // TODO: return unknown
      return null;
   }

   /**
    * Set CURRENT-WINDOW attribute for this procedure handle.
    * <p>
    * This is a no-op, but the handle is validated.
    * 
    * @param    h
    *           A handle for the window.
    */
   @Override
   public void setCurrentWindow(handle h)
   {
      // TODO: validate it
   }

   /**
    * Set CURRENT-WINDOW attribute for this procedure handle.
    * <p>
    * This is a no-op, but the window is validated.
    * 
    * @param    win
    *           Window reference.
    */
   @Override
   public void setCurrentWindow(WindowWidget win)
   {
      // TODO: validate it
   }

   /**
    * This is a no-op, always return <code>false</code>.
    * 
    * @return   Always <code>false</code>.
    */
   @Override
   public logical isProxy()
   {
      return new logical(false);
   }

   /**
    * This is a no-op, always return unknown handle.
    * 
    * @return   Always unknown handle.
    */
   @Override
   public handle getTransaction()
   {
      return new handle();
   }
   
   /**
    * Called when an internal-entry of this proxy procedure is invoked async.
    */
   @Override
   public synchronized void notifyStart()
   {
      asyncRequestCount++;
   }

   /**
    * Called when the async call of an internal-entry for proxy procedure has finished.
    */
   @Override
   public synchronized void notifyFinish()
   {
      asyncRequestCount--;
   }
   
   /**
    * Displays "FILE-INFO is not a setable attribute for PROCEDURE widget" error message.
    *
    * @param    name
    *           Not used.
    */
   @Override
   public void initFileInfo(character name)
   {
      handle.invalidAttribute("FILE-NAME", true, LegacyResource.PROCEDURE);
   }
   
   /**
    * Displays "FILE-INFO is not a setable attribute for PROCEDURE widget" error message.
    *
    * @param    name
    *           Not used.
    */
   @Override
   public void initFileInfo(String name)
   {
      initFileInfo(new character(name));
   }

   /**
    * Check if this port-type procedure can be deleted.
    * 
    * @return   <code>true</code> if the port-type procedure can be deleted.
    */
   @Override
   protected boolean resourceDelete()
   {
      if (asyncRequestCount > 0)
      {
         // can't delete if async req count > 0
         final String msg = "Handle value supplied to the DELETE PROCEDURE/OBJECT statement " +
            "has outstanding asynchronous requests. Procedure '%s'";

         ErrorManager.recordOrThrowError(8980, String.format(msg, name == null ? "?" : name));
         return false;
      }
      
      if (!server.valid())
      {
         final String err =
            "Invalid or inappropriate SERVER handle implied by DELETE PROCEDURE statement";
         ErrorManager.displayError(5455, err, false);
         return false;
      }

      if (!server._connected())
      {
         final String err = "SERVER  is not connected; cannot delete procedure handle or object";
         ErrorManager.displayError(5456, err, false);
         return false;
      }

      deleted = true;
      return true;
   }
   
   /**
    * Get the {@link ServerImpl} instance associated with this port-type.
    * 
    * @return   See above.
    */
   ServerImpl getServer()
   {
      return this.server;
   }

   /**
    * Get the {@link #requestHeaderProc} for this port-type. When <code>null</code>, then no
    * <code>request-header</code> procedure is set.
    * 
    * @return   See above.
    */
   String getRequestHeaderProc()
   {
      return requestHeaderProc == null ? null : requestHeaderProc;
   }
   
   /**
    * Get the {@link #requestHeaderContext} for this port-type.
    * 
    * @return   See above.
    * 
    * @see   #getRequestHeaderProc()
    */
   handle getRequestHeaderContext()
   {
      return new handle(requestHeaderContext);
   }
   
   /**
    * Get the {@link #responseHeaderProc} for this port-type. When <code>null</code>, then no
    * <code>response-header</code> procedure is set.
    * 
    * @return   See above.
    */
   String getResponseHeaderProc()
   {
      return responseHeaderProc == null ? null : responseHeaderProc;
   }
   
   /**
    * Get the {@link #responseHeaderContext} for this port-type.
    * 
    * @return   See above.
    * 
    * @see   #getResponseHeaderProc()
    */
   handle getResponseHeaderContext()
   {
      return new handle(responseHeaderContext);
   }
}