ILE in Practice
April 7, 2004 Joel Cochran
[The code for this article is available for download.]
In previous articles I examined the various pieces of an ILE application, covering such topics as service programs, binder source, binding directories, and activation groups (see links to previous articles at the bottom of this page). This article walks through the process from start to finish. The front end application is made up for demonstration purposes, but the security service program and all of the methods demonstrated are being used in my shop today.
All of the source code, /copy members, and file objects discussed in this article are available for download via the *SAVF (Midserve.savf). You will need to be at V5R1.
To install the library you will need the ability to FTP to your AS/400 and the authority to execute the RSTLIB command.
Follow these steps:
- Create a *SAVF somewhere on your AS/400, like so:
- Download the file above to your PC (for example: c:mypath).
FTP the file to your AS/400. The file name on your PC will be MIDSERVE.savf, so when you FTP be sure to include the .savf extension:
ftp your.as400.ip.address login (user and password as prompted) bin cd MYLIB lcd c:mypath put MIDSERVE.savf
- Restore MIDSERVE from the *SAVF:
RSTLIB SAVLIB(MIDSERVE) DEV(*SAVF) SAVF(MYLIB/MIDSERVE) MBROPT(*ALL) ALWOBJDIF(*ALL)
For demonstration, I will construct a typical file maintenance program, in a library called MIDSERVE. For the sake of simplicity, I will maintain a single three-field file (Mynames.pf).
This file contains names and e-mail addresses based on a unique ID field. The maintenance application needs to have all the typical options to create, change, copy, delete, and display these records. However, this simple application has one requirement that’s not so simple: all options and function keys must be securable by the user. In other words, the application must be able to determine whether the current user profile has authority to the option or function.
In order to do this, I will build a service program that houses all of the option-level security procedures. By doing so, the same approach can implemented in any future application that needs this level of security. I will also house all of my maintenance screens in a service program and simply call those procedures from a shell or search program. So let’s get started!
Building an ILE application is like preparing a dish from a recipe. The first thing you need is a list of ingredients. Here is a list of all the components need to construct the sample application:
- A database file for storing the security parameters by user profile.
- A service program for accessing the security file and determining user options at run time.
- A display file for the individual records with the following formats: create, change, copy, delete, and display.
- A service program for the display file formats containing a procedure for each format.
- A display file with a subfile for searching names and e-mail addresses.
- A main program for handling the subfile display.
Now that you know what pieces are needed, you can begin to look at how they are put together to create the sample application. Remember that the idea of ILE is to take building blocks and arrange them just right. Here’s how each block fits into the equation.
From a process flow point of view, the user calls the program MYMAINT, which builds the display and subfile for MYDISPLAY. In the process, it needs to check every option and function key to determine whether to display them (you only want to display options the user has authority to perform). So as the format is being built, the security for each option is checked, using the check_auth() subprocedure in the OPTSECURE service program. Then, when an option or function is selected, the appropriate subprocedure is called in NAMESRV. As a precaution against programmer error (specifically an error of omission, like forgetting to run check_auth() when building the screen display), the subprocedures in NAMESRV also perform check_auth() for themselves.
Please participate in our iSeries programming survey at
What you’ll notice from the process flow is that everything filters through OPTSECURE (the security service program), so I’ll begin there.
THE SECURITY SERVICE PROGRAM
OPTSECURE relies on two files to determine user access to each option: one for user or group authority and one to link users to groups. The file objects are provided in the *SAVF, and the DDS for these files are Secfilegrp.pf and Secfileusr.pf.
Note that the SECFILEUSR file comes with several records to illustrate how the file is populated, including one group profile. To add users to a group, add a record for them in the SECFILEGRP file. Users can have mixes of group profiles and individual authorities, but keep in mind that Inclusion always overrides Exclusion. You will need to write a program or use DFU or a tool such as WRKDBF to populate these files.
OPTSECURE has three procedures:
- The secure_init() procedure initializes OPTSECURE based on the current user profile: this procedure can be called externally to reset the values, but running check_auth() the first time will invoke this procedure as well.
- The setAuthProfile() procedure is used internally by secure_init() to set user options.
- The check_auth() procedure is the primary procedure called from other programs to validate option level security.
Since this article focuses on putting the pieces together, and not on the nitty gritty details of the pieces themselves, I won’t go into detail about how the procedures work.
I will review, however, how to create this service program and how to use it in the main program. Note that most of these source members are SQLRPGLE programs, so in order to use them, you will need the SQL Development Kit. If you do not have it, you can reconstruct the logic using native file access. Also note that there are several /copy statements in the source. I discussed their use at length in “Customizing Your Development with Extensible RPG.” You will need these members in your MIDSERVE QRPGLESRC source file: Cp_hspecs.rpg, Cp_const.rpg, and Cp_protos.rpg.
Assuming that the two database files (SECFILEUSR and SECFILEGRP) exist, compile the OPTSECURE module, using option 15 in PDM or the CRTSQLRPGI command:
CRTSQLRPGI OBJ(MIDSERVE/OPTSECURE) SRCFILE(MIDSERVE/QRPGLESRC) OBJTYPE(*MODULE)
Now that you have the module, you are ready to create the service program. As I discussed in “Binder Source: The Little Language That Could,” you’ll want to use binder source language to create and manage the exported procedures for this service program. Create a source file in MIDSERVE called QSRVSRC. Add an OPTSECURE member: Optsecure.bnd.
Note that even though there are three procedures in the service program, you only want to export two: secure_init() and check_auth(). Now create the service program, like so:
CRTSRVPGM SRVPGM(MIDSERVE/OPTSECURE) ACTGRP(OPTSECURE)
Note that if the service program already exists, the service program will not be created. Examining the job log will reveal one or more instances of the following message: “Definition supplied multiple times for symbol ‘XXXX’.” As I understand it, this is because the *MODULE references its own binding directory, and so, as it is trying to compile, it is also trying to reference its own exports. This is a minor inconvenience but is easy to get around: Delete the existing *SRVPGM object, then reissue the Create command. You could include an additional option on the CRTSRVPGM command, OPTIONS(*DUPPROC), but since this opens the door for unpredictable results, I usually avoid this approach.
You are using binder source, so the command is extremely short, because it will automatically use EXPORT(*SRCFILE) and a source file in QSRVSRC with the same name as the service program. This is why the binder source member should have the same name as the service program. And since you only have one module, you do not need to specify a module list. The following command is the long version of the same command (with the addition of a description):
CRTSRVPGM SRVPGM(MIDSERVE/OPTSECURE) MODULE(MIDSERVE/OPTSECURE) SRCFILE(MIDSERVE/QSRVSRC) TEXT('MidServe OPT Security Service Program') ACTGRP(OPTSECURE)
Don’t forget the activation group! You use named activation groups in this application to improve performance. If you look at the source for OPTSECURE, you’ll see that all of the authorization storage variables are global: by using a named activation group, you only have to run the initialization routine once per job. Considering that you are going to be calling the check_auth() procedure a lot, you’ll want to limit the amount of work that the program needs to do, and this is a very effective way of handling this scenario.
THE BINDING DIRECTORY
Once you examine the cp_hspecs member, you will see a reference to a binding directory called MIDBNDDIR. Now that you have something to put in it, go ahead and create it:
CRTBNDDIR BNDDIR(MIDSERVE/MIDBNDDIR) TEXT('MidServe Binding Directory')
Since every other component in this example relies on OPTSECURE, go ahead and place it in the binding directory. My preference is to use the Work with Binding Directory Entries command:
Add the OPTSECURE *SRVPGM to the directory. Now when you insert the cp_hspecs /copy member (which contains the BNDDIR reference) to the other programs, these procedures will be automatically available.
THE SCREEN FORMATS SERVICE PROGRAM
Here are the source members for the display file and the service program: Namefm.dsp and Namesrv.rpg.
The NAME display file has five formats: NAMEU1, NAMEU2, NAMEU3, NAMEU4, and NAMEU5. Each one corresponds with the SSA option number (1=Create, 2=Change, 3=Copy, 4=Delete, and 5=Display). The NAMESRV service program has corresponding procedures: MYNAMES_create(), MYNAMES_change(), MYNAMES_copy(), MYNAMES_delete(), and MYNAMES_display(). You’ll notice that each procedure (except the create procedure) requires the ID key of the desired record.
You will also need the prototypes: Cp_mynames.rpg.
I’ve separated these from the other prototypes just to show a modular approach: you could just as easily put these into the cp_protos /copy member.
As before, you’ll want to create a binder source member for this service program: Namesrv.bnd.
Using option 15 in PDM, compile the NAMESRV module and create the service program:
I have not specified an activation group here, to show some variety, which means I’ll be using *CALLER. But you could place this in a named activation group as well. In this case, the benefit would depend on how often this service program is accessed. Also, since, unlike the security service program, there is no particular information that needs to be shared from program to program within the job, the benefit is decreased.
Once the service program is created, you add it to the binding directory as well. Now you are ready to begin the main program, which will have access to all of the procedures (as will any future program) by virtue of /copy’ing the prototypes and binding directory statements.
THE MAIN EVENT
Now that the background is set, it is time to focus on the main program. At this point, all that is left to do is to create a subfile program so that you can select records. I have put together some subfile templates (again, they are SQL-based) and have included them in the *SAVF download. I used these templates as the basis for the display and main program.
The source members for the display file and the main program are Mymaintdsp.dsp and Mymaint.rpg. Using option 15 in PDM, compile the MYMAINT module.
There are a few things to look at. First, I have an internal procedure called setOptions(), which uses the check_auth() procedures from OPTSECURE. Before you can use it, you will need to add some entries into the security file, SECFILEUSR. Specifically, you will need to add one for each option on the screen and function key that you want to have security control over. You can follow the examples in the *SAVF download, but you will need to copy or change the records to match your user profile.
Second, you’ll see that handling the subfile options couldn’t be easier. If an option is selected, simply call the appropriate procedure with the subfile ID. By modularizing as much as possible, you limit the amount of work for the main program. As a nice aside, you can now use the security service program in other displays, as well by simply including the prototypes and binding directory references.
You can also use the screen display service program in other applications. For example, if you had an accounting package that needed to see details of an inventory record, rather than having the accounting package program execute a chain and format a screen, etc., the program could simply call the INVENTORY_display() procedure.
TYING UP LOOSE ENDS
There is one thing left to do: to create the program, using the CRTPGM command. What’s great about this approach is that, having done all of the work up front, creating the program itself couldn’t be easier:
Again, as far as activation groups go, I haven’t specified one, which for a program means that *NEW is used. Again, I’ve done this for the sake of variety, and there is no real reason not to use a named activation group. Depending on the frequency of use, you may not notice a big difference between the two, but if the program is accessed a lot, you should see a performance boost by using named activation groups.
Now a simple CALL from the command line, and you are off and running. If the program won’t compile or does not give expected results, check the following things: Did you create both the service programs? Did you add them both as binding directory entries? Is your library list set? Did you add records to the SECFILEUSR? This program is not perfect (what subfile program is?), but it should be a functional example.
This ends my series on ILE. I hope you’ve found something worth your while, I know I learned alot writing about it. So until next time, happy coding!
Joel Cochran is the director of research and development for a small software firm in Staunton, Virginia, and is the author and publisher of www.RPGNext.com. E-mail: firstname.lastname@example.org
Editor’s Note: The FUNCKEYS.RPG /copy member referenced in this article was provided with the permission of Bob Cozzi and the RPG IV Developer Network at www.rpgiv.com.