Skip to content

Sample LE-Language Exit Modules

A limited number of sample exit programs are provided based on those used for testing. These samples show basic operations such as field preparation and validation along with more involved processes such as executing ZMF XML requests, making Db2 requests, file allocation and reading, and use of the CMNVPOOL facility.

The samples are available in the CMNZMF SAMPLES distribution library with names HXC (COBOL) and HXP (PL/I).

Caution

All LE exits and all called subroutines must be reentrant. It is also required that you use ALL31(ON) to ensure that LE HLL exit processing is able to support a high level of concurrency. Error messages are sent to the HLLXMSG ddname. Trace output is sent to TRCnnnnn ddnames.

The majority of the LE exits are called in their own enclave where the exit is the first program to gain control in the enclave. This is to allow the exit to call other facilities which require this kind of isolated environment within an overall, multi-processing environment like the HLLX started task. The exceptions to this rule are those exits in the SYSL and STDL functional areas, which are called as subroutines from within an already established enclave (i.e. they are called by a MAIN program).

For PL/1 exits this means that all bar SYSL and STDL exits must be coded as MAIN programs, and SYSL and STDL exits must have the FETCHABLE option in this case.

Once you’ve seen how one exit point works, that knowledge can be applied to any of them. In general, these samples are neither guaranteed nor supported. The specific items that Serena supports are:

  • The format of the data passed to the exit.

  • If any of the data is changed, the function in progress will pick up those changes and act on them.

  • Use of LE GETMAIN services to extend the length of passed variable lists.

  • Use of ZMF XML services from within the exits.

  • Use of the CMNVPOOL facility.

Note

Compiled REXX routines are supported in exactly the same manner as standard REXX routines. However, REXX routines built as standard load modules using MVS stub functionality are neither REXX nor LE-compliant. Hence they are not supported in HLLX.

The following code snippets explain how LE languages process the variable blocks in the package-create function. The data interface for the high-level language exits is given for each functional area in the ZMF/HLL Exit Interface section. For COBOL:

...

 S500-VARIABLE-BLOCK.
*********************
*        * NOW PROCESS EACH VARIABLE *
*        * BLOCK THAT MAY HAVE BEEN *
*        * PASSED TO US *
*        ******************************
*        * CALLED FROM: *
*        * S500-VARIABLE-BLOCK *
*        ******************************
    PERFORM S510-PROCESSVB1 UNTIL VB1DONE.

 S510-PROCESSVB1.
*****************
*        * DISPLAY VARIABLES FROM THE *
*        * FIRST PARM BLOCK *
*        * THEN USE THE NEXT POINTER *
*        * TO GET ADDRESSABILITY TILL *
*        * THAT POINTER IS NULL TO *
*        * DENOTE END OF LINKED LIST *
*        ******************************
*        * CALLED FROM: *
*        * S500-VARIABLE-BLOCK *
*        ******************************
    DISPLAY 'PACKAGE DESCRIPTION : ' PCRTPDSC.
    IF PTR-NEXT-PCRTVB1 NOT = NULLS
        SET ADDRESS OF PCRTVB1 TO PTR-NEXT-PCRTVB1
    ELSE
        MOVE "Y" TO WS-VB1DONE
    END-IF

For PL/I, using the same data structure:

...

*PROCESS NAME('PLI7PRE') INCLUDE MARGINS(2,72,1) OPTIMIZE(TIME);
1PLI7PRE:PROC(PARM) OPTIONS (MAIN,ASM);

   DCL   SYSNULL       BUILTIN;
   DCL   DESC_DONE     CHAR(1);
   DCL   PARM          CHAR(100) VARYING;
   DCL   PARM_ADDR     PTR;

   /* API data fields layout */
      %INCLUDE CMNPXPCR;

PARM_ADDR = ADDR(PARM);
...

IF PCRTVB1P ¬= SYSNULL()  THEN DO;
   DESC_DONE = 'N';
   WORKVB1P = PCRTVB1P;

DO WHILE (DESC_DONE = 'N');
   PUT SKIP LIST('DESC LINE: '||PCRTPDSC);
   IF PTR_NEXT_PCRTVB1 = SYSNULL() THEN
      DESC_DONE = 'Y';
   ELSE
      WORKVB1P = PTR_NEXT_PCRTVB1;
  END;
END;

...

Sample REXX Execs

Similar comments apply to sample REXX execs. Sample REXX execs are in the CMNZMF SAMPLES distribution library with names HXR*.

Note

To avoid clashes in variable names when you call SERXMLRC for ZMF services, ensure that your stem variable name prepends something prior to the tagname. For example:

...

  stem = 'HLL7.ZMF_'
  drop HLL7.
  HLL7.                     = ""
HLL7.ZMF_Subsys             = zmfSubs
HLL7.ZMF_Userid             = userid
HLL7.ZMF_Test               = " "
HLL7.ZMF_Product            = "CMN"
HLL7.ZMF_lproduct           = "ZMF"
HLL7.ZMF_Service            = "PARMS"
HLL7.ZMF_Message            = "LIST"
HLL7.ZMF_Scope              = "APL"
HLL7.ZMF_applName           = applName

HLL7.ZMF_includeInResult.1  = "applDesc"

address LINKMVS "SERXMLRC stem"

...

Variable Pool Function - CMNVPOOL

The purpose of this (optional) function is to provide a simple method of saving and accessing variable data across HLL exit execution. The function can be called from either LE programs (using the CMNLPOOL front end) or from REXX execs (using CMNRPOOL). Examples are given below.

Sample JCL to allocate the CMNVPOOL data set is provided in the HLLXVPL member of the CMNZMF.V8R1M4.CNTL distribution library.

The repository for customer-defined variable data is a VSAM KSDS, which is defined in a similar manner to the following:

...

//*
//IDCAMS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//DUMMY      DD *
DUMMY RECORD
//SYSIN      DD *
  DELETE CMNDEV.CMNSYS.U810ALL.CMNVPOOL
  SET MAXCC = 0
/*
/* Delete, Define, and Initialize the ZMF variable pool dataset.
/*
DEFINE CLUSTER (NAME(CMNDEV.CMNSYS.U810ALL.CMNVPOOL)        +
  RECORDSIZE(40,4096) UNIQUE INDEXED KEYS(24,0)             +
  FREESPACE(20 20)                                          +
  SHAREOPTIONS(2,3))                                        +
DATA(NAME(CMNDEV.CMNSYS.U810ALL.CMNVPOOL.DATA)              +
    CYLINDERS(1, 1)                                         +
    CISZ(32768))                                            +
INDEX(NAME(CMNDEV.CMNSYS.U810ALL.CMNVPOOL.INDEX)            +
    CYLINDERS(1,1)                                          +
    CISZ(2048))

REPRO IFILE(DUMMY) ODS(CMNDEV.CMNSYS.U810ALL.CMNVPOOL)

//*
//* Delete DUMMY record from VSAM file
//*
//VPOOL   EXEC PGM=CMNVINIT
//CMNVSAM   DD DISP=SHR,DSN=CMNDEV.CMNSYS.U810ALL.CMNVPOOL

The key to the variable value is an 8-byte pool name concatenated with a 16-byte variable name. The length of the variable data is up to 4070 bytes.

You can choose any name for the pool name but it will usually be the userid so that any single user can own his or her own copy of a set of common variables (much like a set of ISPF profile variables). However, obviously, global or application (or any other) groupings of variables can be implemented, completely at the customer’s discretion.

The actual variable name is limited to 16 bytes in length and is fixed in length (but can be padded with blanks as necessary). We then have the variable value which is stored in a VARCHAR format. An example of one such variable residing in our test data set looks like this:

...

  ....+....1....+....2....+....3....+....4..
  WSER58 TrialVariable ..CheckThisOut
  EECDFF44E9889E898889844401C8889E88ADAA4444
  625958003991351991235000003853238926430000

We have a pool name of WSER58 (padded to 8 bytes) and a variable name of TrialVariable (padded to 16 bytes). This is followed by the length of the value, x’0010’ = 16, in this case. The value of the variable follows.

In the HLLX procedure JCL this KSDS is defined thus:

...

 //*
 //CMNVPALT DD DISP=SHR,DSN=CMNDEV.CMNSYS.U810ALL.CMNVPOOL
 //CMNVPOOL DD SUBSYS=(BLSR,'DDNAME=CMNVPALT','STRNO=255')
 //*

The functions provided to allow access to these variables are primarily intended for use within the HLLX address space. However, they have also been coded so that they can be used anywhere that the VSAM KSDS has been allocated to ddname CMNVPOOL. However, note that functions executing outside of the HLLX environment are only allowed read access to the CMNVPOOL VSAM file, thereby limiting the available functions to INIT, GET, and TERM.

The HLLX address space will open the CMNVPOOL ACB (if the ddname is present) as part of its initialization process, and will close it on termination. If there is a problem opening the CMNVPOOL file, the following message will be issued:

07.11.16 S0069082 CMNX100E Failed to open CMNVPOOL KSDS, fdbk: 000000BC

The fdbk code is a standard VSAM open macro feedback code, which, in this case, is indicating that the data set we are attempting to open is not a VSAM file.

If any of the vpool functions are running under HLLX (the code detects this) they assume the ACB is already available in HLLX common storage. Each user subtask works with its own RPL storage. If the functions are invoked outside of the HLLX address space, the ACB is opened as part of the process. (That is, the functions will work inside or outside the HLLX address space but will be far more efficient within.)

The calls supported are:

Call Description
INIT: Establishes a conversation within which the other functions may be executed without incurring repeated initialization overhead.This is not so important within the HLLX address space as the biggest overhead is in setting up and opening the ACB (which is already done by the HLLX main task). Any conversation begun with an INIT call must be ended with a TERM call. If INIT/TERM are not used then each individual call is wrapped with an internal INIT/TERM pair.
TERM: Terminates a conversation previously started via an INIT call.
DEF: Defines a variable (similar to an ISPF VDEFINE call). It is used, in particular, to establish a length for the variable value so that subsequent processes have a solid reference when moving data around (thereby avoiding S0C4s). The call writes a record to the VSAM file of the appropriate length and filled with blanks. If the record already exists then we return the length already defined for it and RC=4. Only available from within HLLX.
DEL: Deletes the variable record. (To change the length of a previously defined variable you would first delete it and then define it again with the new length.) Only available from within HLLX.
PUT: Updates the variable record with a value. Only available from within HLLX.
GET: Extracts the current variable value.

CMNLPOOL is intended to be called from LE programs using a standard call parameter list:

...

****************************************
*DEFINE THE PARAMETER LIST USED BY THE
*VARIABLE POOL ROUTINE - CMNPOOL
*Function may be one of:
*   INIT    - INITALISE THE VARIABLE POOL
*   DEF     - DEFINE A VARIABLE
*   DEL     - DELETE A VARIABLE
*   PUT     - DEFEINE THE VALUE OF THE VARIABLE
*   GET     - RETRIEVE THE VALUE OF A VARIABLE
*   TERM    - TERMINATE THE VARIABLE POOL
****************************************
*
    03  WS-VP-FUNCTION      PIC X(4)    VALUE SPACES.
    03  WS-VP-MSGAREA       PIC X(128)  VALUE ' '.
    03  WS-VP-CONTEXT       PIC S9(8)   COMP VALUE +0.
    03  WS-VP-POOL          PIC X(8)    VALUE SPACES.
    03  WS-VP-VARNAME       PIC X(16)   VALUE SPACES.
    03  WS-VP-VARLEN        PIC S9(4)   VALUE +256.
    03  WS-VP-VARVALUE      PIC X(256)  VALUE SPACES.
*

A sample call sequence is shown below. Note that the INIT/TERM calls are not strictly necessary within HLLX but are recommended for use outside of HLLX:

...

********************
*           *1)Start a VPOOL conversation*
*           *2)Define a variable *
*           *3)Modify that variable *
*           *4)Get its updated value *
*           *4)End the VPOOL conversation*
*           ******************************
*
*           ******************************
*           * Initialise the variable *
*           * pool access conversation *
*           ******************************
    MOVE 'INIT' TO WS-VP-FUNCTION.
    CALL CMNLPOOL USING WS-VP-FUNCTION
                WS-VP-MSGAREA
                WS-VP-CONTEXT.
    DISPLAY 'INIT RETURN-CODE: ' RETURN-CODE.
    DISPLAY 'INIT CONTEXT : ' WS-VP-CONTEXT.
*
*           ******************************
*           * Define a variable - TestVar*
*           * data length - 256 *
*           * assigned to pool - WSER58 *
*           ******************************

...

...

    MOVE 'DEF ' TO WS-VP-FUNCTION.
    MOVE 'TestVar' TO WS-VP-VARNAME
    MOVE 256 TO WS-VP-VARLEN.
    MOVE 'WSER58' TO WS-VP-POOL.
*
    CALL CMNLPOOL USING WS-VP-FUNCTION
                        WS-VP-MSGAREA
                        WS-VP-CONTEXT
                        WS-VP-POOL
                        WS-VP-VARNAME
                        WS-VP-VARLEN.
    DISPLAY 'DEF RETURN-CODE: ' RETURN-CODE.
    DISPLAY 'DEF VARLEN : ' WS-VP-VARLEN.
*
*           **************************
*           * Assign a value for the *
*           * variable just defined *
*           **************************
    MOVE 'PUT ' TO WS-VP-FUNCTION.
    MOVE 'BLAH' TO WS-VP-VARVALUE.
*
    CALL CMNLPOOL USING WS-VP-FUNCTION
                        WS-VP-MSGAREA
                        WS-VP-CONTEXT
                        WS-VP-POOL
                        WS-VP-VARNAME
                        WS-VP-VARVALUE.
    DISPLAY 'PUT RETURN-CODE: ' RETURN-CODE.
*
*           ************************************
*           * Retrieve the value of a variable *
*           ************************************
    MOVE 'GET ' TO WS-VP-FUNCTION.
    MOVE SPACES TO WS-VP-VARVALUE.
*
    CALL CMNLPOOL USING WS-VP-FUNCTION
                        WS-VP-MSGAREA
                        WS-VP-CONTEXT
                        WS-VP-POOL
                        WS-VP-VARNAME
                        WS-VP-VARVALUE.
    DISPLAY 'GET RETURN-CODE: ' RETURN-CODE.
    DISPLAY 'GET VARVALUE : ' WS-VP-VARVALUE.
*
*           ******************************
*           * Terminate variable pool *
*           * access conversation *
*           ******************************
    MOVE 'TERM' TO WS-VP-FUNCTION.
*
    CALL CMNLPOOL USING WS-VP-FUNCTION
                        WS-VP-MSGAREA
                        WS-VP-CONTEXT.
    DISPLAY 'TERM RETURN-CODE: ' RETURN-CODE.
*

REXX execs need to call CMNRPOOL using the LINKMVS command. (CMNRPOOL relies on the parameter structure generated by LINKMVS.) Again, INIT/TERM are not strictly necessary within HLLX but are shown here for completeness. There are a number of special REXX variables returned by CMNRPOOL:

Variable Description
VPOOLMSG Contains any messages returned by the process. (Note: The standard REXX variable RC contains the return code.)
VPOOLVLN The length of the variable just defined.
VPOOLCTX Hexadecimal value representing the conversation context, generated by an INIT call. It is passed from one execution of CMNRPOOL to the next until the TERM call is made. It is purely for internal use and no good will come from tampering with it.

...

...

/* Demonstration of the use of the HLLX vpool facility      */
/* REXX execs call the CMNRPOOL front end program           */
/* LE programs use the CMNLPOOL front end program           */
/* However, after differing parameter parsing, both pass    */
/* control to the same CMNVPOOL subroutine.                 */
/*                                                          */
/* Any messages are returned in REXX variable VPOOLMSG.     */
/* The length of a defined variable is returned in          */
/* VPOOLVLN.                                                */
/* If conversational mode is setup then the context is      */
/* held in VPOOLCTX, but this is for internal use only.     */

Say " "
Say "Demonstration of HLLX variable pool services"
Say " "

function = "INIT"
address LINKMVS "CMNRPOOL function"
Say "Return Code from INIT call is: "RC
Say "Returned message is          : "VPOOLMSG
Say " "

function = "DEF"
userid = "WSER58"
varname = "TrialVariable"
varlen = "16"
address LINKMVS "CMNRPOOL function userid varname varlen"
Say "Return Code from DEF call is: "RC
Say "Returned message is         : "VPOOLMSG
Say "Variable length is          : "VPOOLVLN
Say " "

TrialVariable = "CheckThisOut"

function = "PUT"
address LINKMVS "CMNRPOOL function userid varname"
Say "Return Code from PUT call is: "RC
Say "Returned message is         : "VPOOLMSG
Say "TrialVariable after PUT     : "TrialVariable
Say " "

TrialVariable = "ChangeIt"

Say "TrialVariable before GET    : "TrialVariable
function = "GET"
userid = "WSER58"
varname = "TrialVariable"

address LINKMVS "CMNRPOOL function userid varname"
Say "Return Code from GET call is: "RC
Say "Returned message is         : "VPOOLMSG
Say "TrialVariable after GET     : "TrialVariable
Say " "

function = "TERM"
address LINKMVS "CMNRPOOL function"
Say "Return Code from TERM call is: "RC
Say "Returned message is          : "VPOOLMSG
Say " "

...

Batch Maintenance Utility - CMNHLLVP

Program CMNHLLVP is provided to help with offline maintenance of the CMNVPOOL KSDS (see sample JCL member CMNHLLVP).

It provides the following four actions:

Action Description
LIST Report on one or more records in the VPOOL KSDS—the key selection criteria can be specific or generic (using a trailing asterisk to indicate the end of significant key value).
INSERT Insert a new record using a specific key.
DELETE Delete one or more records from the VPOOL KSDS—the key selection criteria can be specific or generic.
UPDATE Update one or more records in the VPOOL KSDS—the key selection criteria can be specific or generic.

Note

Only the LIST action can be performed while the VPOOL KSDS is in use by the HLLX stc.

The execution parameter supplies the security class used to control access to this function (more about this below).

The SYSIN parameters are:

Parameter Description
ACTION= One of LIST, INSERT, DELETE, UPDATE
POOL= The variable pool name
VARNAME= The variable name
VARVALUE= The variable value to be used

This utility has the following rules and limitations:

  • An asterisk in column 1 denotes a comment and is ignored by the utility.

  • For long variable values, you can continue the value onto more than one line by putting a non-blank character in column 72. The continued line starts in column 1 on the next sysin record.

  • You may have as many ACTIONs as you wish in the same SYSIN input stream.

  • Each action must be followed by a POOL= and a VARNAME= parameter. The INSERT and UPDATE actions also require a VARVALUE= parameter.

  • Apart from INSERT, the actions may be processed against multiple records by ending either (or both) POOL= and VARNAME= values with an asterisk. The SYSPRINT output gives an indication of what was read from SYSIN and the results of each action.

Sample JCL:

...

//*
//RUNVP        EXEC PGM=CMNHLLVP,
//                 PARM=$CHGMAN,
//                 REGION=0M
//*
//SYSPRINT      DD     SYSOUT=*
//SYSUDUMP      DD     SYSOUT=*
//CMNVPOOL      DD     DISP=SHR,DSN=your.target.vpool.ksds
//SYSIN         DD *
*
* The LIST action reports all records in the VPOOL ksds which satisfy
* the selection criteria (on POOL name and VARiable NAME).
* These names can be specific or generic (by use of the trailing
* asterisk)
*
ACTION=LIST
POOL=WSER5*
VARNAME=*
*
* The INSERT action requires a specific pool and variable name.
* The variable value will be inserted into the VPOOL ksds as long
* as the key fields are unique.
*
ACTION=INSERT
POOL=WSER58
VARNAME=TestIt001
VARVALUE=This is an overlong value for a vpool variable but it's here t+
o show how continuation is used (any non-blank in column 72)
*
* The DELETE action will delete all records which match the selection
* criteria (be careful).
*

ACTION=DELETE
POOL=WSER5*
VARNAME=TestIt6*
*
* The UPDATE action replaces the variable value of all records which
* match the selection criteria.
*
ACTION=UPDATE
POOL=WSER5*
VARNAME=Test*
VARVALUE=Updated value for this group of records
/*

This facility is secured and the userid running the job will need access to the relevant security profile, namely HLLX.VPOOL, within the class supplied on the execution parameter. (It is recommended to use the same class as other ZMF functional security.)

To perform the LIST action, the userid requires READ access to this profile. All other actions require UPDATE.

Tracing

The HLLEXIT service requests are just like any other service request and, as such, can be traced using the standard CMN/Sernet tracing facilities. This trace will show you what the client is sending to the ZMF started task.

At the other end of the HLLX path, each exit can also use display/put/say facilities to show what it is being passed.

The data is reformatted by the code executing in the HLLX started task. There is a new trace facility available to show exactly what has been passed to the HLLX started task prior to this reformatting. (Note: Displays in the exit can show what the reformatted data areas look like as they are passed directly to the exit after reformatting). The trace will also show what has been passed back to the HLLX started task from the user exit after any reformatting.

Although this tracing is happening in the HLLX address space, it is controlled with modify commands to the ZMF/Sernet started task (to avoid having to work with more than one started task). The trace command is modeled after the NETTRACE Sernet command, for example: /F zmfstcname,HLXTRACE,ON,EXIT=exitmask,USER=usermask

OFF turns off all traces.

There is no sophistication to this trace command. OFF turns off the prior defined trace request. Each new ON command replaces the prior trace definition. As with NETTRACE there are shortcut synonyms for HLXTRACE (HT) and ON/OFF (Y/N). The mask fields work with an asterisk at the end of the value only.

For example: /F stcname,HT,Y,EXIT=PCRE*,USER=WSER5*

will put out trace information for all PCRExxxx user exit points and for all userids beginning with WSER5. As usual with trace commands, the more specific you can be the less trace output that you will have to examine.

The length of the data displayed is taken from the length field at the beginning of each data area. If this is found to be non-numeric then a default of 256 bytes is employed. Here is some sample trace output:

tracing example