FCPE-SPI FLAM Column Processing Exit (FCPE) Service Provider Interface (SPI)

FCPE - Service Provider Interface

This service provider interface enables the possibility to write custom pre- and post-processing exits for columns as part of the table support in FLAM. It can be used to implement arbitrary validation and transformation (e.g. tokenization, masking, encryption) tasks based on data items of a column.

The service provider interface can be invoked as one of the pre- or post-processing steps for a column. See example below:

    read.record(... table(... row(...
    col(format.fix(...)
        process.exit(library='libfcpe', function='validate_string', parameter='format=IBAN')
        process.exit(library='libfcpe', function='mask_string', parameter='offset=4 length=6')
        process.exit(library='libfcpe', function='add_checksum', parameter='method=crc')
        type.string(...)
    ))))

The different functions can have different parameter strings. All of them depend on the implementation of the custom column processing exit according to this service provider interface.

# Implementation overview

The interface that must be implemented to create a custom exit is described in the FCPE function section. This interface function must be an external function of a DLL/SO or load module (z/OS). The (`PROCESS.EXIT()`) object is used to configure how to call the custom exit routine.

First, the DLL/SO is loaded based on the specified library name or default name. Then the function address is determined based on the provided function name or default name). For load modules on z/OS, the function name is the load module name and the library name must be empty. It is also possible to use DLLs on z/OS.

If the function pointer is determined, the exit driver calls the exit function with the function code for opening (see function codes). A function-code-specific parameter structure ((see FcpeOpn struct)) contains the parameter string from the exit call specification (`PROCESS.EXIT(PARAMETER="xxx")`). Additionally, a read or write indication flag and the table and column name are provided as input. If the column processing is invertible, an inverse parameter string can be returned by the exit implementation while opening which must be suitable to configure the exit so that the changes made by this exit are reverted. For text data, the CCSID (IBM - Coded Character Set Identifier) with the combined character form ((see this section)) is provided as 32 bit integer. If the character set is changed by the exit, then the CCSID parameter must be adjusted by the exit.

If the exit only performs validation tasks or updates the input data in-place, then a flag bit must be set to indicate that the output buffer is not used (see here).

The first parameter of the exit function contains a handle. When it is first called with the function code for opening (FCPE_FUNC_OPN), the handle's value is NULL. This handle can be used to store state information that may be needed during data processing by the exit (i.e. subsequent calls with the FCPE_FUNC_RUN code). The exit function is responsible for allocating memory for the handle (if needed) when called with the function code for opening (FCPE_FUNC_OPN) and must release it (and all other resources) when called with the function code for closing the exit (FCPE_FUNC_CLS).

When called with the run function code (FCPE_FUNC_RUN), the exit function gets the input data length and a pointer to the input data as input (see FcpeRun) and writes the output data and its length to the respective fields. If the exit function set the flag FCPE_FLAG_NOCPY during the opening call to indicate that it does not write any data to the output buffer (e.g. the data is only validated), the exit driver sets the output pointer to NULL and expects that the exit pointer sets the output pointer to the input pointer in the run function.

Nonetheless, the exit function is called once more with the FCPE_FUNC_CLS function code to allow the release of all resources.

All function code specific parameter structures contain a length and a buffer of size 1024 bytes for an error message. The error message is displayed to the user if the return code differs from zero.

# Interface standards

The interface is built on one function with four parameters, a classic load module interface. All parameters of the function are call-by-reference. The function does not have a return value. The return code is the second parameter of the function. There are only two types of parameters:

    POINTER:   pointer to an address (usually 32 (PIC S9(9) COMP) or 64 bit)
    INTEGER:   pointer to a 32 bit number in two's complement (PIC S9(9) COMP)

The type INTEGER has local endianness. On mainframes, this is usually big endian and little endian on x86 platforms.

The POINTER type is used for the handle and the parameter structures. The handle is a black box to FLAM. The parameter structures are unions with the function code acting as selector (see structures section).

To support 64 bit RISC architectures, a strong alignment to 64 bit in the parameter structures is required. This requires a 32 bit dummy field behind each 32 bit length field. The trick with the dummy fields makes the parameter structures compatible between the different supported architectures of FLAM and prevents bus errors on certain platforms.

All strings passed to the exit function are null-terminated and the corresponding lengths are set to the string length in bytes without the null character. The exit driver does not expect null-termination from the exit function, but the length fields must be set correctly. For the error message or the inverse string, the length can be set to the size of the error message buffer (1024) or inverse string buffer, respectively, if a null-terminated string is returned by the exit function.

# Return code handling

The return code of the function must be one of the defined FLAM return codes (see module return codes). If the exit function is called with the funtion code FCPE_FUNC_RUN, then FLMRTC_LEN must be returned if the output buffer provided by the exit driver is too small to write the result and set the output size to the minimum required buffer size. If the input is not formatted correctly, FLMRTC_FMT must be returned to support the format detection possibilities of FLAM. All other return codes will result in an error and FLAM aborts the remaining processing.

# Coding example for an FCPE

The example below uses the memory to memory interface to implement a simple exit, where the from-to conversion string is used as parameter for the service provider. The example is written in C and it implements the three function codes as switch block. The service provider interface specification (FCPE.h) and the FLUC byte interface header (FLCBYT.h) must be included to implement the interface and use the byte interface for conversion, respectively. The exit function FCPETST implements the interface as specified here, using the FLUC memory to memory interface.

    #include <stdio.h>

    #include "FCPE.h"
    #include "FLCBYT.h"

    extern void FCPETST(
       void**         ppHdl,
       int*           piRetco,
       const int*     piFunc,
       TuFcpePar*     puPara)
    {
       size_t         out;
       *piRetco=FLMRTC_OK;
       switch(*piFunc){
       case FCPE_FUNC_OPN:
          *ppHdl=fcbopenv(NULL,puPara->stOpn.pcPar);
          if(*ppHdl==NULL){
             *piRetco=fcberrno;
             snprintf(puPara->stOpn.acMsg,sizeof(puPara->stOpn.acMsg),"%s",fcberrtr());
             puPara->stOpn.uiMsg = strlen(puPara->stOpn.acMsg);
          }
          puPara->stOpn.uiIvr=0;
          puPara->stOpn.uiCcs=0;
          break;
       case FCPE_FUNC_RUN:
          out=puPara->stRun.uiOut;
          *piRetco=fcbconv(*ppHdl,puPara->stRun.uiInp,puPara->stRun.pcInp,&out,puPara->stRun.pcOut);
          puPara->stRun.uiOut=out;
          if(*piRetco){
             snprintf(puPara->stRun.acMsg,sizeof(puPara->stRun.acMsg),"%s",fcberrtr());
             puPara->stRun.uiMsg=strlen(puPara->stRun.acMsg);
          }
          break;
       case FCPE_FUNC_CLS:
          *piRetco = fcbclosev(*ppHdl);
          if(*piRetco){
             *piRetco=fcberrno;
             snprintf(puPara->stCls.acMsg,sizeof(puPara->stCls.acMsg),"%s",fcberrtr());
             puPara->stCls.uiMsg=strlen(puPara->stCls.acMsg);
          }
          break;
       default:
          *piRetco=FLMRTC_ITF;
          break;
       }
    }

For simplicity of the example, it the CCSID is set to zero and no inverse string is returned. This simple implementation of the SPI is quite powerful, because it provides the complete conversion functionality of FLUC for each data item of a column.

This test implementation is also deployed as sample C source (FCPETST.c) in the installation package (SRCLIBC/samples). It can be used as role model for other implementations.

The implementation of an exit does not have to be done in C, but the binary interface of the compiled and linked DLL/SO or load module must be compatible to the C calling convention.