Guru: Open Access To The Rescue – The Next Step
May 6, 2019 Jon Paris
I recently wrote a tip on using an Open Access handler to enable the creation of flat files in the IFS. In that version of the handler I generated a generic file name and used hard-coded record delimiters and code page settings. I mentioned at the time that I would normally supply these values at run time. In this follow-on tip I describe how the handler can be modified to use the Open Access User Parameter to facilitate this.
The User Parameter is simply specified as a second parameter to the HANDLER keyword. It can be a simple variable or, as in the case of my example, a data structure. Its purpose is to pass additional information from the user program to the handler in cases where you need to supplement all the file/record info that Open Access supplies.
This article contains code, which you can download here.
The handler and the user program must both access the same data, so the definitions are placed in a /COPY source member so that they can easily be included in all programs that need them. The code below shows the definition I am using in my example. I’ve defined it as a Template and have also specified initialization values for the fields. More on this later. Note that the sizes of the filename and record terminator fields are just suggestions and can be changed as required for your own needs.
Dcl-ds userParms_T Template; filename varchar(128) Inz; // Full path name of file codepage int(10) Inz; // Code page for file recordTerminator varchar(2) Inz; // Record terminator End-Ds;
Changes To The Handler
The changes in the handler begin at (A) which shows the definition that allows the handler to accesses the user parameter data. If a User Parameter was supplied, then the pointer info.userArea will have been set by the RPG run time to the correct address for the file currently being processed. If no User Parameter was provided, then the pointer will be null.
(B) Begins the logic for processing the user parameter. If the User Parameter is present (i.e. the pointer info.userArea is not *Null) then the handler tests to see if a value was supplied. If it was (e.g. the file name is not an empty string) the handler uses the value supplied. If not, as with the original version, the handler supplies the default value for the field.
In each case, the values that will subsequently be used by the handler are placed in the state information area and all subsequent references will be to that data. This ensures that should the user program inadvertently change one of the parameters that it will not impact the operation of the handler.
... (A) Dcl-Ds userData Based( info.userArea ) LikeDS(userParms_T); ... // Check if user has supplied file name etc. parms and if present copy // the into the state information. // If no user data supplied for a field then set up default value. (B) // Use user supplied file name or set up default value If info.userArea <> *Null and userData.fileName <> ''; stateInfo.userParms.fileName = userData.fileName; // Use users name Else; // Otherwise build the default name. stateInfo.userParms.fileName = %Trim(info.externalFile.name) + '_' + %Char(%Date()) + '_' + %Char(%Time()) + '.txt'; EndIf; // Set up Code page If info.userArea <> *Null and userData.codePage <> 0; stateInfo.userParms.codePage = userData.codePage; // Use user value Else; stateInfo.userParms.codePage = 819; // Use default of page 819 EndIf; // Set up record terminator If info.userArea <> *Null and userData.recordTerminator <> ''; stateInfo.userParms.recordTerminator = userData.recordTerminator; Else; stateInfo.userParms.recordTerminator = CRLF; EndIf; ...
The User Program
As I mentioned in the original tip, this handler can be used with PRINTER or DISK files – so in the example here I have used both types of file. This has the advantage of allowing me to demonstrate that the same handler can be used for multiple files in a single program and that different parameters can be used for each. In fact the sample program generates data in a different format based on the associated O specs.
At (C) you can see how the User Parameter is passed to the handler via the HANDLER keyword. Note also that both files are specified as being User Open (USROPN) – the reason for this I will discuss in a moment.
The User Parameter template is incorporated into the user program via the /COPY at (D). The definition is subsequently referenced (E) via LikeDS(userParms_T) to define the parameter for the file. Notice that I also use Inz(*LikeDS) to ensure that the structure is initialized to the “no value supplied” value assumed by the handler.
// This program demonstrates the use of the IFSWRTREC2 handler. // Two IFS files will be created both defined by O-specs. (C) Dcl-f IFSOut1 Printer(50) UsrOpn Handler('IFSWRTREC2' : fileParms1 ); Dcl-f IFSOut2 Disk(60) UsrOpn Usage(*Output) Handler('IFSWRTREC2' : fileParms2); Dcl-F CustMast Keyed; // Include user parameter template (D)/Copy OASampSrc,IFSUSRPARM // User parameters for IFS output files (E) Dcl-ds fileParms1 LikeDS(userParms_T) Inz(*LikeDS); Dcl-ds fileParms2 LikeDS(userParms_T) Inz(*LikeDS); // Set up parameters for file name etc. // For IFSOut1 default just the file name (F) fileParms1.codepage = 1208; // Set codepage to UTF-8 fileParms1.recordTerminator = '!'; // Set line end to ! // For IFSOut2 supply file name // but default code page and record terminator fileParms2.fileName = '/home/PARIS/ITJungle/DiskOut.txt'; // Now that file name etc. is set we can open the files (G) Open IFSOut1; Open IFSOut2;
At (F) you can see the logic that sets specific file parameters. For the “Printer” file IFSOut1, I defaulted the file name but specified values for the code page and line terminator. For the “Disk” file IFSOut2, I specified only the filename and allowed the codepage and line terminator to default. In both cases, the default values will be supplied for the other fields by the handler.
Once all the parameter values are in place the files can be opened (G). Had I not specified the files as being User Open, the RPG cycle logic would have opened the files before I had a chance to put these values in place. If you want to avoid having to use User Open, then the required values must be passed into the program as parameters. I have included an example of doing this in the code package that accompanies this tip.
If you wish to explore using this updated handler on your own system you can download the code bundle here.
Hopefully these two tips have given you some food for thought on the types of tasks that can be simplified by using RPG’s Open Access. If you come up with any ideas of your own, or have specific requirements that you would like me to address in a future tip, please let me know.
Jon Paris is one of the world’s foremost experts on programming on the IBM i platform. A frequent author, forum contributor, and speaker at User Groups and technical conferences around the world, he is also an IBM Champion and a partner at Partner400 and System i Developer. He hosts the RPG & DB2 Summit twice per year with partners Susan Gantner and Paul Tuohy.