Getting to the heart of ILE objects
As we discussed in Part I, the first step to learning ILE is to understand all the new object types involved in ILE. This is important not only for programmers, but also for administrators who will be managing ILE objects. In Part I we used a chart that summarized the ILE object types. Let's now discuss these ILE objects in more detail.
ILE programs are significantly different from OPM programs. Although they do not represent a new object type -- they are still just *PGM objects -- they are a different type of program object. (An ILE program will have a Program Type attribute of ILE, whereas an OPM program will have a Program Type attribute of OPM.) The main advantage ILE programs enjoy over OPM programs is their ability to access all the new features that were introduced with ILE (e.g., ILE-specific language enhancements, ILE-specific APIs, etc.). But the real difference between program objects of type OPM and type ILE is the way they execute. (See "Activation groups" below.)
With the introduction of ILE came activation groups -- a new type of process object. A process object is a temporary object used by a process (i.e. a job) during the life span of the process. It's not a persistent, storable type of object such as a program or a file; when a job ends, its associated process objects are, for all intents and purposes, deleted.
Programs do not run in program activation groups (PAGs) any more; they run in activation groups, which can be thought of as "mini-PAGs." Even OPM programs run in an activation group -- a special activation group called the default activation group, which every job automatically has. The default activation group is an ILE activation group, but it has the ability to emulate the OPM environment hence allowing OPM program to run on an ILE-based machine. However, as with any type of emulation, performance is slowed by the extra processing necessary to simulate the OPM environment.
In addition to the default activation group, there are non-default activation groups. Non-default activation groups do not have any of the overhead of the default activation group that is necessary to run OPM programs. You can give your own names to activation groups or let the system name activation groups for you.
Unlike OPM programs, which must run in the default activation group, ILE programs can run in any activation group you choose. Use this to your advantage to isolate applications in their own activation group.
You can even specify that a program should run in the same activation group as the program that calls it. This is done by specifying the value of *CALLER for the activation group attribute when creating the program.
Tip: Be wary when using the *CALLER activation group attribute, especially with service programs (see below). If such a service program is shared among a number of applications that run in different activation groups, a copy of the service program will be activated in each activation group from which it is called. This can add up quickly and waste memory. Instead, define service programs that are shared among many programs running in different activation groups to run in their own named activation group.
These new objects, object type *MODULE, are the building blocks of all ILE programs, including service programs. They are intermediate objects between source code and programs. They represent the result of compiling source members (with one of the CRTxxxMOD commands) before they are ultimately incorporated, or bound, into programs with either the CRTPGM or CRTSRVPGM command. Even the CRTBNDxxx commands create a module object -- albeit a temporary one in QTEMP -- before finally creating a program out of that temporary module.
Modules are reusable. The word module is derived from the term modularize, which we should all know by now is the most advantageous way to build reusable code. With OPM RPG, for example, the only ways we had to modularize were the following:
- Create subroutines in copy-members and include them in programs with the /COPY compiler directive. This provides a fast calling interface, but parameters have to be simulated using global variables. It also increases the size of program objects.
- Create stand-alone programs and call them from other programs. This provides for true parameters, but it is a slow calling interface.
Modules combine the advantages of these two methods, giving us true parameter support and a fast call interface.
Modules have one disadvantage, though. Like the first OPM method, they increase program size. Service programs, on the other hand, give us a way to use modules without increasing program size, but at the expense of a slightly slower calling interface.
Tip: As a general rule, a module that will be called by many programs should be put into a service program, while a module called by just a couple of programs should be bound directly into the calling program. (Reminder, since a module is a reusable object, it can be bound into both a service program and a regular program.)
Service programs are to the iSeries what DLLs are to PCs. They provide a fast calling interface without having to include code in your program. For example, in RPG the fastest calling interface is the EXSR op-code, but the subroutine that is called must be part of the calling program, adding size to both the source and executable program.
A procedure in a service program (a procedure is a kind of self-contained subroutine or a mini-program) can be called with almost the same speed, but the source and executable code of the procedure are not part of the calling program. This is especially advantageous when many programs call the same procedure.
Procedures in a service program are called using the CALLB or CALLP op-code (or in-line as part of an expression) in RPG, the CALLPRC command in CL, or the CALL PROCEDURE statement in COBOL. Service programs used by programs must be bound to the program using the CRTPGM or CRTBNDxxx command.
A service program has an object type of *SRVPGM. It is created similarly to how programs are created, except the CRTSRVPGM command is used instead of the CRTPGM command or one of the CRTBNDxxx commands. Service programs can be bound to (i.e. used by) other service programs.
ILE programs and service programs are created by binding one or more modules and/or service programs together using the CRTPGM or CRTSRVPGM command. As programs become more modular, the complexity of the CRTPGM (or CRTSRVPGM) command becomes burdensome.
For example in OPM, the create program command was fairly simple:
CRTRPGPGM PGM(MYLIB/MYPGM) SRCFILE(MYLIB/QRPGSRC) SRCMBR(*PGM)
However, in ILE the complexity of the create program command grows proportionately to the number of modules and service programs that compose the program. For example, without the use of a binding directory, a program that is made up of four modules (MYPGM, MOD1, MOD2 and MOD3) and three service programs (SRVPGM1, SRVPGM2 and SRVPGM3) would require a CRTPGM like following at a minimum:
CRTPGM PGM(MYLIB/MYPGM) MODULE(MYLIB/MYPGM MYLIB/MOD1 MYLIB/MOD2 MYLIB/MOD3) BNDSRVPGM(MYLIB/SRVPGM1 MYLIB/SRVPGM2 MYLIB/SRVPGM3)
The solution is to use a binding directory. A binding directory, object type *BNDDIR, is nothing more than a stored list of module and service program names. When a binding directory is specified on the CRTPGM (or other command), as in
CRTPGM PGM(MYLIB/MYPGM) MODULE(MYLIB/MYPGM) BNDDIR(MYLIB/MYPGM)
the system uses the list of modules and service programs in the binding directory as if they had been specified individually on the create command. In the above command, we named the binding directory MYPGM -- the same name as the program -- so we know it is the list of modules and service programs that make up the program (i.e. it contains the all the modules and service programs listed in the first CRTPGM command above except module MYPGM, which contains the program entry point procedure.)
Binding directories can also be used to group modules and/or service programs into logical groups. You can even create a "super" binding directory that contains the names of all the modules and service programs on your system, but remember as the number of entries in a binding directory goes up, so does the program creation time.
About the author: Ron Turull is editor of Inside Version 5. He has more than 20 years' experience programming for and managing AS/400-iSeries systems.
This was first published in May 2005