You are on page 1of 13

ILE Refresher

by Paul Morris This article is not intended to be a complete course in ILE the Integrated Language Environment. You would need a substantial book to do that. Rather, it's meant to describe many of the features of ILE that you may have come across but not ventured to use and to encourage to you to exploit more of the facilities provided. I came to realize the need for this type of article in my travels as a freelance consultant. I have encountered many sites and programmers using just RPG IV, yet claiming to use or understand ILE. ILE is more than RPG IV. It provides a structure for running together programs of different languages and includes features such as ILE modules, service programs, different types of program calls, and activation groups. By providing you with a better understanding of what you can do with ILE, my aim is to help you develop better programs on your iSeries.

Modules
RPG IV is just one programming language in the set of languages that ILE supports. The old RPG III compiler (RPG/400) is not part of the ILE language set, although, as you will see later, ILE does accommodate these older OPM languages. (OPM is the Original Program Model that originated with the S/38 and was the only way of compiling programs in the early days of the AS/400.) The RPG IV compiler (ILE RPG/400) along with the others, such as the ILE CL compiler strictly speaking, does not create programs; it creates a *MODULE object type. Although an ILE module object has the complete executable code produced by compiling a source member, a module is not a directly callable object. Instead, a module contains one or more procedures (one of which may be the "main" procedure of a program) that must be processed by the OS/400 program binder to become part of a program or service program object. Program objects and procedures within programs and service programs are ready to be called. The CRTBNDRPG (Create Bound RPG Program) command is actually running two functions: CRT RPGMOD (Create RPG Module) to generate a module and CRTPGM (Create Program) to bind the modules into a program that can be executed. An important feature of the binder is that it can bind modules written in more than one language into a program, so RPG IV (RPGLE) and ILE CL (CLLE) modules can be bound together, along with Cobol (CBLLE) or C modules should your shop be one that uses those languages. The three basic types of modules are those with a main procedure and no subprocedures, those with a main procedure and one or more subprocedures, and those with no main procedure but one or more subprocedures. A module consisting solely of a main procedure is what we would call the normal part of the program where traditionally most of the code is placed. A main procedure can be CALLed (if this is the "entry point" or the only module of a program), CALLBed, or CALLPed. Whether you use it or not, the main procedure contains the legacy of the RPG cycle (as do all traditionally written RPG IV

Page 1 of 13

and RPG III programs). Compiling a source member to a bound program implies that you have a main procedure. In fact, the default is for a main procedure to exist. A main procedure lets the module become the entry point of a program (i.e., it is the code that begins to run when a program is called). Although a program can consist of many modules, each with a main procedure, there is only one that becomes the entry procedure of the program. Normally, the first module listed in the CRTPGM command becomes the entry point, although this default can be changed on the CRTPGM command. A module consisting of a main procedure and subprocedures is similar to the first type, but at the end of the source, you have one or more subprocedures defined. These subprocedures generally have code that provides functionality for the main procedure. Modules made up of only subprocedures are identified by the NoMain H-spec. With NoMain, you have no C-specs in the body of the source; the executable code is in the subprocedures. These subprocedures contain routines or functions that are available to other modules.

Service Programs
A service program is made up of one or more modules, each containing one or more procedures bound together into one object type, *SRVPGM. The purpose of service programs, as the name implies, is to provide services (a set of common routines) that other programs can use. A service program cannot be called directly as you would call a program. When it is activated, the service program just sits there waiting to be asked to perform a service to other programs. For example, in a multicurrency system, you may have a service program that has a procedure for converting currency values from one currency to another, and which would accept parameters of From Currency (such as USD ($)), To Currency (such as GBP ()), the value to convert, and a return value of the converted value. Programs for order processing, accounts receivable, general ledger, and so forth could then call this single procedure. The benefit of this approach is that the code for converting currency values would be defined once, tested thoroughly, and then be available for everyone to use. Hence, the business logic is "written once, used many." Writing programs within the ILE environment will give us smaller programs that are not cluttered with basic routines, and we can concentrate on developing code for the issues in hand.

Program Calls
ILE offers two different types of program calls. The first is the dynamic call that you use for calling programs and is an OPM type of call. RPG IV uses the CALL operation to call a program, and ILE CL also has a CALL command for this purpose. You use them just as you have always done to call a program. A dynamic call can call an ILE bound program or an OPM program. Variations include

an OPM program calling an OPM or ILE program

Page 2 of 13

an ILE program calling an OPM or ILE program a service program calling an OPM or ILE program

The second type is a static or bound call that you use for calling procedures in a program or service program. RPG IV uses the CALLP operation (or the older CALLB) for this purpose, while ILE CL uses the CALLPRC (Call Procedure) command. With bound calls, the work of finding the module or procedure to run has been done before the call. Bound calls come in two flavors, although you do not make the distinction when you write the code. The first type is bound by copy, where the modules are copied into a program. The second is bound by reference, where the modules are in a service program external to the program. Bound procedure calls (remember, only an ILE program or service program can do this, not OPM) include

a procedure in the same module a procedure in a separate module in the same program/service program a procedure in a separate service program

For both dynamic and bound calls, the RPG IV CALLP operation for prototyped calls is a feature of the language that provides a consistent way of making a call. It replaces both the dynamic CALL for calling programs and the older bound CALLB for calling procedures. It also eliminates PLIST/PARM, and with an associated procedure interface (PI) definition, can eliminate the *ENTRY PLIST for the called program. A PI definition is how a procedure gets its parameters, and a prototype specification (PR) defines the parameters passed to it. An example of a procedure call could look like this:
Callp GetOEP(cono05: cusn05: dseq05: corpaa: brchaa)

or implicitly as part of a function call, like this:


Eval wrk8 = soundalike(wrk8: cono05: cocd05)

The latter is the call for the procedure in Figure 1. Figure 2 shows the prototype for this procedure.

Figure 1: SoundAlike procedure


*===================================================================== * Module Name - WAJ006RM Version 1.0 *===================================================================== H Debug Datedit(*ymd) Datfmt(*iso) Nomain *===================================================================== * prototypes

Page 3 of 13

/COPY WPGMCPY,WAJ1PROTO *===================================================================== p SoundAlike b Export d d d d SoundAlike Text Company Country pi 8 8 2 3 Value Value Value

d Soundit s 8 *--------------------------------------------------------------------* work fields *===================================================================== c Exsr Main c Exsr Exit *===================================================================== * Main Processing *--------------------------------------------------------------------c Main Begsr c * * * c c Eval :: :: :: Movea wb Soundit Soundit = text

Endsr *===================================================================== * Return with the parameter *--------------------------------------------------------------------c Exit Begsr c c Return Soundit

Endsr *--------------------------------------------------------------------p e

Figure 2: Prototype specification (PR) for SoundAlike


* This is an extract from a copy source member with multiple prototypes *===================================================================== * Sound like a word Module WAJ006RM *--------------------------------------------------------------------d SoundAlike pr 8 d Text 8 Value d Company 2 Value d Country 3 Value *=====================================================================

Page 4 of 13

Calling an ILE program loads and initializes the program first, then any bound service programs it uses, then any bound service programs they may use, and so on. This chain of events could take awhile, but this delay happens only for the first call. If the program being called needs to set up a different activation group, the system also needs to spend time setting this up (see the section Activation Groups on page ProVIP 33).

Binding
As mentioned earlier, there are two types of binding. The first, bound by copy, is where the binder, at compile time (using CRTPGM), copies the modules into the program object to make an executable program. The module objects could be deleted at this point, and the program would run. The binder uses modules listed on the module parameter of the CRT command, and then any modules from the binding directory that provide the required procedures. A binding directory (object *BNDDIR) is a convenient tool for listing modules or service programs that a program may require. The CRTPGM and CRTSRV PGM (Create Service Program) commands use a binding directory when they cannot find all the procedures they need in the MODULE or BNDSRVPGM parameter entries. In that case, the commands will search any binding directories in the BNDDIR parameter to find the modules or service programs they need to complete the binding process. All the objects named in the MOD ULE and BNDSRVPGM parameters will be bound, but only the needed objects from the BNDDIR entries will be bound into the program or service program. The benefit of bound by copy is that the code is together in one place with the links between modules established before the program runs, which gives fast calls between modules. One example of how this might be used is an RPG IV module calling an ILE CL module that performs some system functions. As the CL coding is specific to the one RPG module, it's bound to the one program rather than placed in a service program. A downside to this type of binding is that your program objects are larger. You use the second type of binding, bound by reference, when programs or service programs refer to procedures in a service program. With this type of call, the program binder (CRTPGM) checks the links made by the calls to procedures in the service program (and saves them in the program object), but the links are not established (i.e., converted to physical address locations) until the program is activated. The calls may be slower, and the program takes a one-time hit at initialization because the linkages get established at that point. The benefit, though, is that the program object is smaller and, more important, it refers to common, previously tested procedures. Therefore, the calling program is easier to write and test. Take the previous example of the currency conversion routine. Should the business decide to alter the logic of the routine, it needs to be changed in one place only, the module recompiled, and the service program updated to reflect the changed module. No more trawling through hundreds of programs looking for code to change!

Page 5 of 13

In RPG IV, both types of binding use the same procedure call. Let's say you have two modules created with CRTRPGMOD MYMOD1 and CRTRPGMOD MYMOD2. To bind these modules by copy, use
CRTPGM MYMAIN MODULE(MYMOD1 MYMOD2)

To bind them by reference (with MYMOD2 using a service program), you first create the service program
CRTSRVPGM MYSRVPGM1 MODULE(MYMOD2) EXPORT(*ALL)

And then bind both modules to program MYMAIN with


CRTPGM MYMAIN MODULE(MYMOD1) BND SRVPGM(MYSRVPGM1)

Procedures
Procedures (often called subprocedures) are coded after the end of a main procedure and its C-specs (and O-specs should you persist in using these!). If you have no main procedure, they are coded after any global definitions at the top of the source member. A simplistic view of procedures in a module is that they are similar to subroutines, but with some important differences: 1. Procedures can have their own D-specs. These variables are local to the procedure and cannot be seen by other procedures (i.e., they define their own local variables). 2. They can have their own subroutines. Again, these are local to the procedure and cannot be seen outside it. 3. They can receive and pass back parameters, which can be passed by value and can provide parameter checking. Parameters can be optional or omitted. 4. They can return a value. This makes the procedure a user-defined function (your own BIFs!). 5. They can be called by other modules. 6. They are always invoked by a bound call. 7. They don't have a built-in RPG logic cycle. 8. They cannot have *INZSR nor a *PSSR coded on the F-spec of a file used by a procedure because they cannot access subroutines in other procedures, including the main procedure.

Page 6 of 13

9. They cannot access subroutines in the main procedure or any other procedure. 10. They don't have F-specs (although a subprocedure can use F-specs coded at the beginning of the source). 11. They can provide multiple entry points within the same module. 12. They support recursion; a procedure can call itself. Coding a procedure requires

a prototype (PR specification) used by both the caller(s) and the procedure (it makes sense to procedure Begin and End specifications a procedure-interface (PI) definition, which should match the prototype (a PI definition is basically a repeat of the prototype information within the definition of a procedure; the PI definition in the module's main procedure serves as a replacement for the *ENTRY PLIST) definition specifications of local variables (optional) a calculation section, which incorporates the optional return value

Procedures use prototyped calls and may return a value. These kinds of calls are known as function calls. Procedures can be passed zero or more parameters, so you could have a function call like this:
Eval InvoiceNum = GetNextInvNum

where GetNextInvNum is a procedure in a service program, which is passed no parameters but returns a value. Parameters may or may not be updated. A global variable is one that is defined in the main procedure's D-, I-, or O-specs or, if the module is NOMAIN, before any procedure specifications. Remember that the compiler generates I- and O-specs as part of a file's external definition. Any field from a database or other externally defined file is global, which means that all procedures in a module can access it. If a name conflict occurs between global variables and a local variable in a procedure, the procedure will use the local version. However, when you have a name conflict, the file I/O action will update the global fields, not the local fields even if you do the I/O from the procedure. In other words, the local field would not be referenced. I strongly recommend that you use suitable naming conventions to avoid having fieldname conflicts between global and local variables. You have two ways to make a free-form call: 1. If there is no return value or you do not want the returned value, use the CALLP operation.

Page 7 of 13

2. If there is a return value, place the prototyped procedure within an expression as in the Get NextInvNum example above. There are three methods in which a parameter value gets passed: 1. By reference, the default that is also used by non-prototyped calls (the variable in the calling program is available to the called program. This is the classical way OPM passes values in parameters). 2. By value (keyword VALUE), in which the system makes a copy of the parameters and passes it to the procedure. This method lets you change data formats and use expressions. For example, you could have
3. MyTotal = InvTotal (LineTotal: LineTotal * 4. TaxRate: Carriage * TaxRate)

Note: you can't pass a parameter by value to a *PGM object. 5. By read-only reference (keyword CONST), which lets you pass fixed values and expressions to a program. When calling a program, parameters can be passed only by reference and by read-only reference, not by value. Passing by value lets you pass literals and expressions as parameters, pass parameters that do not match exactly, and pass a variable that, from the caller's perspective, will not be modified. For APIs, you use the keyword CONST. Note that CONST is not always read only, unless it is also defined in PI spec as CONST, or there is a different length/type between caller and the called procedure. It's best to ensure that the PI and PR specs match.

Create Considerations
The CRTBNDRPG command compiles a module and binds it into an ILE program. You use binding directories specified within either the H-spec or the command itself to bind other modules or service programs. If you specify DFTACTGRP(*YES), the CRTBNDRPG command will create the program as an OPM compatible program. But note that OPM programs cannot use procedure calls (i.e., bound calls). If you specify DFTACTGRP(*NO), the CRTBNDRPG command creates an ILE program, which can use procedure calls. UPDPGM and UPDSRVPGM let you replace modules in bound programs or service programs, provided you allow it on the create command. You can also change the named activation group. OPTION(*TRIM) removes isolated modules (those no longer resolved). UPDPGM requires replacement modules to be in same library as was the original.

Binder Source

Page 8 of 13

As mentioned previously, a service program gets bound (by reference) to a program as part of the CRTPGM or CRTBNDRPG command. The binding process checks issues such as "Does the procedure exist?" and "Do the parameters match?" When the calling program is itself called for the first time, as part of that program's initialization, the links to the service program are established. A program may refer to several service programs, and these may refer to yet other service programs leading to a small, but possibly significant, performance hit at program-call time. The benefit is that subsequent calls become a lot faster. Initialization checks the signature of a service program, a bit like a file level check. These signatures are developed when the service program is created or updated. Unlike file level checks, you can have previous versions of the signatures. These signatures are based on the modules in the service program. Therefore, adding a module to a service program will change its signature. Unless you take the appropriate steps, the calling programs would need to be rebound to get the new signature. The simplest way to create a service program is to use the default command value of EXPORT(*ALL), which creates a new signature. However, if you then add another module to it, you get signature violation errors in the calling programs unless you rebind all the programs that use this service program. You get over this issue by using binder source. Binder source is a simple language used in creating service programs that enables you to specify not only the procedures that you want to make available, but also the previous versions of the signature so that rebinding of programs can be eliminated. You enable this by using the command parameter EXPORT(*SRCFILE) and by specifying the binder source file. To make things easier to start with, you can use RTVBNDSRC to create an initial source file from an existing set of modules and then modify this to move forward with updates to the service program. Binder source is not required when export *ALL is on the CRTSRVPGM command. It defines which procedures are visible and consists of three statements similar to this:
STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) EXPORT SYMBOL('MYRTN01') ENDPGMEXP

You can use PGMLVL(*PRV) to preserve the previous version of the public interface without recompiling all programs calling it. This is a bit like having multiple level checks. To illustrate this, Figure 3 shows a source file with previous and current signatures. You will see that module WAJ010RM has been added to the list.

Activation Groups
The system uses activation groups to isolate different activities. They are like a kind of subjob with their own overrides, file opens, and commit status. An activation group puts boundaries around applications to provide this isolation. This overhead implies that there are performance issues when activation groups are created or destroyed, so you need to consider how they are used.
Page 9 of 13

A job starts with two activation groups: The first is for the system in which it runs its own tasks, and the second is the default, which is the OPM activation group. All your OPM programs run in this second group, and the system starts your applications running from this group. These two groups are never deleted. You define activation groups in the CRT commands (such as CRTPGM or CRTBNDRPG) and specify them as follows:

The default (OPM): In addition to your OPM programs, an ILE program runs in this group if it is created with ACTGRP(*CALLER) and its caller is already running in the default activation group (e.g., its caller may be an OPM program), or if you use the CRTBNDRPG command to create the program and specify DFTACTGRP(*YES). *NEW: The system generates a name for this activation group, creates it each time the program is called, and ends it when that program returns to the caller, which obviously has performance implications. Named: You give the activation group a name (such as Sales). Expect a slight performance impact at the time of creation. These groups are persistent: They stay until they are explicitly deleted or the job ends. QILE is a default name for a named activation group. It is not the default activation group (this is a common misconception). In other words, if you choose a named activation group, the command prompter supplies QILE as a name for you (which you can change). *CALLER: The activation group of the program calling this one is the activation group to be used. Note that service programs default to caller. *ENTMOD: This new V5R3 default option lets the binder choose whether the program will use *NEW or a named activation group, depending upon the language in which the entry module was written. For RPG IV entry modules, the binder usually uses a named activation group, QILE.

I would consider using a named activation group for a service program if you are isolating applications. A service program gets loaded only once per activation group. If a program is running in a named activation group, it needs more than just ending with LR to end the activation group. The activation group will stay there ready for use unless you explicitly remove it (or the job ends). If you need to end an activation group, the RCLACTGRP (Reclaim Activation Group) command in a CL or ILE CL program or module (or the equivalent API) will remove it. The exception is a program running in a *NEW activation group, which is deleted when program is not in the call stack (it has returned irrespective of any LR setting). Usually, you will not need to remove an activation group once it is created. The system has to do work to create an activation group, resulting in a slower call than when a program activates in an already existing activation group. Also, a call to a program in a different activation group is twice as slow as a call to a program in the same activation group.

Page 10 of 13

When an activation group is deleted, its resources are reclaimed. Note that an ILE program created for the default activation group will release storage at LR or at an abnormal end, but an ILE program created with ACTGRP(*CALLER) running in default activation group doesn't release its storage until the job ends. This behavior is different from what you see with OPM! When an ILE program running the default activation group ends with LR on, files will be closed and storage initialized but not released. Service programs are not affected. RCLRSC does not work in a named activation group. It works only in the default activation group. Use RCLACTGRP to clean up. Error handling is also a consideration with activation groups. In the OPM, if an exception is not handled, a system-generated function check (escape message CPF9999) is sent to the program in error. If it is not handled, the system removes the call-stack entry and passes it to the previous call-stack entry. With ILE, each call-stack entry within an activation group is given the chance to handle the message. If the error does not get handled, it gets converted to a function check (CEE9901) that then starts with the caller of the procedure in error to see whether it is handled. Therefore, you have two chances of handling a message.

Override Considerations
Overrides behave differently in ILE. With ILE, you can relate overrides to a call level, activation group, or resource/job. Call level is the normal override setting for the default (OPM) activation group. In other words, it is the default behavior for OPM programs or ILE programs running in the default activation group. This is the classic legacy way overrides work. Overrides are deleted when the corresponding call stack entry returns. Activation group is the default override behavior for ILE programs not running in the default activation group. When the activation group is deleted, all resources are closed. Only programs running in that activation group see the override. Job level is available for both OPM and ILE, and all programs can see the override regardless of the call level. If not explicitly closed, these overrides remain open until the job ends. You can scope commit control to the job or activation group. SQL cursors can be scoped to the module or activation group, determined by the parameter ENDSQL on the CRTxxx command.

End Considerations
You end a program (main procedure) by issuing the RETURN operation without turning on the LR indicator or by setting the LR indicator on and then returning. With LR off, all files remain open (open data paths are still available) and static variables remain untouched. With LR on, the files are closed, and static variables remain allocated but are marked for future initialization. If the main procedure is called again later, the static storage is initialized without a new allocation.

Page 11 of 13

A subprocedure ends with the RETURN operation or by reaching its last statement. All local (automatic) variables are removed from the stack except the static variables (those with the STATIC keyword), which are retained. An activation group is ended (deleted) according to its type. The default activation group is never ended before the job ends. A *NEW activation group is completely deleted when the program ends abnormally or when it returns to its caller (with or without LR on). This is important if calling a program recursively. When you test a new program with *NEW, you do not need to sign off or use an RCLACTGRP command to end it, as you would have to with a named activation group. A named activation group exists during all the life of the active job. To end it, you use the RCLACTGRP (Reclaim Activation Group) command. RCLACTGRP may specify a named activation group or *ELIGIBLE. Those eligible are where no programs or procedures are currently in the call stack for the job. A named activation group is deleted automatically if the only program active in it ends abnormally.

How to Proceed
If you are not currently using ILE, keep the KISS principle in mind and keep it simple. I have seen examples where the only person who understood the setup was the originator. Ensure also your development environment matches the production one. It's most embarrassing when a working program is promoted only for it to crash on the first live run with a signature violation. Review your naming conventions. Consider using SQL, not OPNQRYF. Do not write programs; write modules. Decide whether there should be one procedure in one module or many procedures in one module. I suggest one for one, unless the procedures are closely related or are "private" to another procedure. Consider using modules to break a complex program into simpler modules, even if these are not used elsewhere. This is possibly the only place I would consider export/import variables. Variables may be exported or imported, which makes one variable available to many modules, but style guides warn against doing this as it becomes a "hidden interface." A variable should be exported only once but can be imported many times. Write small, tight modules for service programs, and then bind them at compile time with binding directory compiler options. If a module is used in two or more programs, consider whether it should be a service program. Think about using activation groups, but beware of overhead in crossing boundaries. Use activation groups to separate business functions or to divide a package. Try to run an application in a single named activation group. Avoid creating unnecessary activation groups, and avoid *NEW. Avoid crossing activation group boundaries too frequently.

Page 12 of 13

Try different techniques on low-use, noncritical systems, but document what you have done for whoever has to maintain them. Finally, read and learn! Further Reading at iSeries Networkcom on page ProVIP 29 lists a number of ILE articles delving into the concepts that are just briefly touched on here.

Page 13 of 13

You might also like