IBM i e-Book
A Developer’s Guide to Mastering IBM i Concepts
IBM i Index
IBM i History and Overview
System Architecture
Development Tools
Deep Dive into DDS and DDL
Control Language (CL)
Report Program Generator (RPG)
Integrated Language Environment
SQL on IBM i
Jobs & Logs
RPG Built in Functions
- Char
- Check
- Date
- Diff
- Div
- Elem
- Eof
- Error
- Found
- List
- Lower
- Max
- Minutes
- Months
- Occur
- Open
- Parms
- Range
- Rem
- Scanrpl
- Sqrt
- Abs
- Checkr
- Days
- Dec
- Decpos
- Editc
- Equal
- Hours
- Inth
- Len
- Lookup
- Maxarr
- Min
- Minarr
- Replace
- Scan
- Scanr
- Seconds
- Size
- Split
- Status
- Subdt
- Subst
- Timestamp
- Trim
- Triml
- Trimr
- Uns
- Upper
- Xfoot
- Xlate
- Years
RPGLE Opcodes
- Z-add(H)
- Unlock
- Scan(E)
- Readpe
- Read
- Open
- Mult & Mult(H)
- Monitor
- Lookup
- LeaveSr
- Leave
- Exsr
- Do
- Cat
- Callp
- Callb
- Call
- BegSr
- Z-sub(H)
- Time
- Z-sub
- Z-add
- Xlate(E P)
- Xlate
- Xfoot
- Write
- When
- Update
- Subst(E P)
- Subdur
- Sorta
- Seton
- Setoff
- Setll
- Setgt
- Select
- Return
- Readp
- Reade
- Plist
- Parm
- Other
- Opcode Extender for File Operations
- On-Error
- Occur
- Mvr
- Movel
- Klist
- Kfld
- Iter
- In & Out
- IfXX
- If
- For
- Extrct
- Exfmt
- Except(Rpgle)/Excpt(Rpg)
- Eval(R)
- Eval (M)
- Eval
- Dump(A)
- Dsply
- DoW
- DoU
- Div
- Delete
- Define
- Comp
- Close
- Check(E)
- Chain
- Cat(P)
- Adddur
- Add(H)
- Add
Integrated Language Environment
Introduction to ILE
ILE, or Integrated Language Environment, is a programming environment on AS/400 (now known as IBM i) designed to enhance flexibility and modularity in software development. Introduced to IBM systems in the 1990s, ILE supports multiple programming languages like RPG, COBOL, and C, allowing developers to create modular, reusable components. This environment promotes a structured approach to programming, fostering better organization and maintenance of applications on the IBM i platform.
Key Features
- Multi-Language Support: ILE accommodates various programming languages such as RPG (Report Program Generator), COBOL, and C. It empowers developers to choose the language that best suits their application requirements while maintaining inter-operability.
- Modular Design: ILE has its emphasis on modularity. Developers can create reusable modules, and integrate them as service programs, which contribute to a more efficient and maintainable codebase. This modular approach facilitates code sharing and enhances collaboration among developers.
- Service Programs and Binding: ILE’s service programs encapsulate logical units of code, fostering a modular structure. The binding process connects these service programs to form a cohesive application. This binding mechanism ensures that changes in one module do not necessitate a complete recompilation of the entire application.
- Procedure-Oriented Programming: ILE promotes a procedural programming paradigm, allowing developers to break down complex tasks into manageable procedures. This granularity enhances code readability and maintainability, crucial for the long-term success of software projects.
Conclusion:
Integrated Language Environment (ILE) has emerged as a cornerstone in IBM i programming, providing a versatile and powerful framework for developers. Its multi-language support, modular design, and procedural programming paradigm contribute to the creation of scalable, maintainable, and efficient applications on the IBM i platform. ILE stands as a testament to IBM’s commitment to innovation and adaptability in the ever-evolving landscape of software development.
Advantages of ILE
Program development is improved with the help of ILE, which is a set of tools associated with system support. The capabilities of this model can be used only by programs produced by the ILE family of compilers. It includes ILE RPG, ILE COBOL,ILE SQLRPG, ILE C, ILE C++, and ILE CL.
The Integrated Language Environment (ILE) is preferable to earlier program models in several ways. Let’s explore the following advantages of ILE:
- Binding:
Static binding is supported by ILE. This indicates that rather than being resolved at runtime, external operations (subroutines, functions, etc.) are resolved during compilation. Better performance is achieved and dynamic binding’s overhead is avoided.
- Modularity:
By enabling you to design distinct modules (programs, service programs, procedures) that can be separately compiled and maintained, ILE encourages modularity.
The reusability of modules across many applications improves the organization and maintainability of the code. - Reusable Components:
You can use ILE to design reusable parts that encapsulate certain functionality, like procedures or service programs. By sharing these parts across several programs, redundancies can be avoided and development efficiency can be increased.
- Common Runtime Services:
ILE offers a collection of standard runtime functions that are compatible with several programming languages (RPG, COBOL, C, etc.), including memory management, error handling, and file input/output. This assures uniform behavior and streamlines cross-language integration.
- Coexistence:
Programs written in various languages can co-exist within a single application because of ILE. The ability to call C functions from RPG programs and vice versa allows for the development of mixed languages.
- Source Debugger:
Developers can find and address problems during program execution with the help of ILE’s powerful source-level debugger. The ability to debug is necessary for effective development and maintenance.
- Better Control over Resources:
ILE gives users more precise control over resources including files, memory, and database connections. Based on particular requirements, developers can optimize resource use.
- Better Control over Language Interactions:
Interaction between languages is made easier with the help of ILE.
Let’s take an example, an RPG program can call a C function directly, enhancing flexibility. - Better Code Optimization:
Advanced code optimization is carried out by ILE compilers, making programs run faster and more effectively. Performance-critical applications especially benefit from this enhancement.
- Foundation for the Future:
ILE provides a strong base upon which to modernize legacy applications and integrate them with new technologies. It helps organizations modernize their systems without compromising their current investments.
ILE Program Concept
Activation Group:
An activation group is a job sub-structure that includes all ILE and service programs. These sub-structures contain the resources necessary to make the programs execute. These resources can be broadly classified into the following categories:
- Dynamic storage
- Static program variables
- Temporary resources for maintaining data;
- Certain kinds of exception handlers and ending procedures;
Activation Group Creation:
When you construct your program or service program, you can define an activation group attribute that will control the development of a non-default activation group at runtime. The ACTGRP parameter on the CRTPGM or CRTSRVPGM commands is used to specify the attribute. There is no Create Activation Group command.
One of the following activation group characteristics are used by all ILE programs:
A user-named activation group:
Specified with the ACTGRP(name) parameter. With the help of this feature, you can operate a group of ILE programs and service programs as a single application. When it is initially required, the activation group is formed. Then, every application and service program that uses the same activation group name makes use of it.
A system-named activation group:
Specified using the CRTPGM command’s ACTGRP(*NEW) option. With this feature, each time the program is called, a new activation group can be created. The name of this activation group is chosen by ILE. The name that ILE gave you is unique to your job. The name you select for a user-named activation group doesn’t match with the name assigned to a system-named activation group. Service programs do not support this attribute.
An attribute to use the activation group of the calling program:
specified with the option ACTGRP(*CALLER). With the use of this feature, you can create an ILE program or service program that will be executed within the caller program’s activation group. When a program or service program is activated using this feature, new activation group is never created.
An attribute to choose the activation group appropriate to the programming language and storage model:
Specified with the CRTPGM command’s ACTGRP(*ENTMOD) option. The program entry procedure module specified by the ENTMOD argument is examined when ACTGRP(*ENTMOD) is given. One of the following may occur:
- If the module attribute is RPGLE, CBLLE, or CLLE, and
if STGMDL(*SNGLVL) is specified, then QILE is used as the activation group. - if STGMDL(*TERASPACE) is specified, then QILETS is used as the activation group.
- If the module attribute is not RPGLE, CBLLE, or CLLE, then *NEW is used as the activation group.
- ACTGRP(*ENTMOD) is the default value for this parameter of the CRTPGM command.
Each activation group in a work has a unique name. Once an activation group exists within a job, it is used to activate programs and service programs which has its name specified. Duplicate activation group names are not allowed within a single project due to this architecture.
The ACTGRP parameter on the UPDPGM and UPDSRVPGM commands can be used to change the activation group into which the program or service program is activated.
Default Activation Groups:
The system creates two activation groups that are used by all OPM programs when a job is initiated. Application programs use one of these activation groups. The other is used for operating system programs.
Static program variables are stored in single-level storage through these OPM default activation groups. The OPM default activation groups cannot be removed. They are deleted by the system when your job ends.
If the following requirements are met, ILE programs and service programs can be activated in the OPM default activation groups:
- The ILE programs or service programs were created with the activation group *CALLER option or with the DFTACTGRP(*YES) option.
- The call to the ILE programs or service programs originates in the OPM default activation groups.
- The ILE program or service program does not use the teraspace storage model.
The operating system will also create a teraspace default activation group when it determines one is needed.
Static program variables are stored in teraspace by the teraspace default activation group.
You cannot delete the teraspace default activation group. When your job is terminated, the system will remove it. If the following requirements are met, ILE programs and service programs may be activated in the Teraspace default activation group:
- The ILE program or service program was created with the activation group *CALLER option.
- The state of the ILE program or service program is *USER.
For the ILE program or service program to be activated into the teraspace default activation group, one of the following criteria must also be satisfied:
- The call to the ILE program or service program originates in the teraspace default activation group and the ILE program or service program was created with either the storage model *INHERIT or the storage model *TERASPACE option.
- The ILE program or service program was created with the storage model *INHERIT option, there are no application entries on the call stack associated with a different activation group and the activation occurs in preparation for one of these invocations:
- SQL stored procedure
- SQL function
- SQL trigger
- The ILE program or service program was created with the storage model *TERASPACE option and there are no call stack entries associated with a teraspace storage model activation group. See Selecting a Compatible Activation Group for additional information.
Non-Default Activation Group Deletion
Activation groups require resources to be created within a job. If an application can reuse an activation group, processing time could be reduced.
To enable you to exit an invocation without terminating or erasing the related activation group, ILE offers various options.
Whether the activation group is deleted depends on the type of activation group and the method in which the application ended.
The following are various ways for an application to go back to a call stack entry linked to a different activation group:
- HLL end verbs: For example, STOP RUN in COBOL or exit() in C.
- Call to API CEETREC
- Unhandled exceptions:Unhandled exceptions can be moved by the system to a call stack entry in another activation group.
- Language-specific HLL return statements:For example, a return statement in C, an EXIT PROGRAM statement in COBOL, or a RETURN statement in RPG or RETURN command in CL.
- Skip operations:For example, sending an exception message or branching to a call stack entry that is not associated with your activation group.
Activation groups can be removed from an application by executing API CEETREC or by utilizing HLL end verbs. Moreover, deletion of your activation group may result from an unhandled exception.
As long as the closest control boundary is the oldest call stack entry connected to the activation group, these actions will always remove your activation group. Control moves to the call stack entry that comes before the control boundary if the closest control boundary is not the oldest call stack item. The activation group isn’t eliminated, though.
Example: ILEACT(Main PGM)
ILEACTPGM1:
ILEACTPGM2:
Output :
WRKJOB (Take Option 18 to see Activation Group)
Before Calling Main Program
After Calling Main Program
Module
A module is a non-runnable object (type *MODULE) which is the output of an ILE compiler. It is the basic building block for creating runnable ILE Objects. This Module object acts as a significant difference between ILE and OPM programs where the output of an OPM compiler is a runnable program.
Below Diagram shows about Modules which are non-executable object that holds procedures and then the same can be bind to *PGM and or *SRVPGM.
Common concepts in ILE RPG, ILE COBOL, ILE C & ILE C++
- Exports
An export is the method of a procedure or data item, coded in a module object, that is available for use by other ILE objects. The export is identified by its name and its associated type, either procedure or data. An export can also be called a definition.
- Imports
An import is the use of, or reference to the name of a procedure or data item not defined in the current module object. The import is identified by its name and its associated type, either procedure or data. An import can also be called a reference.
Since, module object is the basic building block of an ILE runnable object. Hence, when a module object is created, the following may also be generated.
- Debug Data
This is the data necessary for debugging a runnable ILE object.
- Program Entry Procedure (PEP)
This is the compiler-generated code that is the entry point for an ILE program on a dynamic program call. It is similar to the code provided for the entry point in an OPM program.
- User Entry Procedure (UEP)
A user entry procedure, written by a programmer, is the target of the dynamic program call. It is the procedure that gets control from the PEP. The main() function of a C program becomes the UEP of that program in ILE.
Conceptual View of a Module
The below example shows Module object M1 exports 2 procedures (Get_Employee and Upd_Employee) and a data item (rtn_code). The same Module object M1 imports a procedure called Del_Employee. It contains PEP, a corresponding UEP and debug data as well.
Creating a Program with the CRTRPGMOD and CRTPGM Commands
The two-step process of program creation consists of compiling source into modules using CRTRPGMOD and then binding one or more module objects into a program using CRTPGM. This process helps to create permanent modules.
Key Features
- I. Allows to modularize an application without recompiling the whole application.
- II. To reuse the same module in different applications.
Creating a Module Object
An ILE RPG module consists of one or more procedures, data item specifications, and static storage used by all the procedures in the module. It is possible to directly access the procedures or data items in one module from another ILE object.
Following are the procedures that can make up an ILE RPG module,
- Cycle-Main Procedure:
An optional procedure which consists of the set of H, F, D, I, C, and O specifications that begin the source. The cycle-main procedure has its own LR semantics and logic cycle, neither of which is affected by those of other ILE RPG modules in the program.
- Sub-Procedure:
Zero or more procedures, which are coded on P, D, and C specifications which do not use the RPG cycle. A sub procedure may have local storage that is available for use only by the sub procedure itself. One of the sub procedures may be designated as a linear-main procedure, if a cycle-main procedure is not coded.
The main procedure (if coded) can always be called by other modules in the program. Sub-procedures may be local to the module or exported. If they are local, they can only be called by other procedures in the module; if they are exported from the module, they can be called by any procedure in the program.
Module creation consists of compiling a source member, and, if that is successful, creating a *MODULE object. The *MODULE object includes a list of imports and exports referenced within the module.
Note:
- I. A module cannot be run by itself. We must bind one or more modules together to create a program object (type *PGM) or a service program object (type *SRVPGM)
- II. Then the procedures can be accessed within the bound modules through static procedure calls.
Advantages of combining modules into a PGM or a Service PGM:
- I. Allows to reuse the pieces of code which generally results in smaller programs. Smaller programs provide better performance and easier debugging capabilities.
- II. Allows to maintain shared code with less chance of introducing errors to other parts of the overall program.
- III. Manage large programs more effectively. It allows to divide the old program into parts that can be handled separately. If the program needs to be enhanced, only recompiling of the changed modules is sufficient.
Module Creation using CRTRPGMOD Command
Using a standard ILE RPG Compiler Command – CRTRPGMOD, we can create the *MODULE Object. This command can be used interactively, as part of a batch input stream, or from a Command Language (CL) Program.
Example of CRTRPGMOD
CRTRPGMOD MODULE (Module Lib / Module Name) SRCFILE (Source Lib / SRCPF)
Binding Directory
Introduction
The concept of the Binding Directory is crucial to understand while working with ILE programs. Binding Directory in IBM i is an object of type *BNDDIR.
It contains the entries of modules and service programs to be used in an ILE program. So, if we specify a binding directory in an ILE program, it can utilize the procedures present in modules & service programs bound.
Usage
Binding Directory is an important ILE concept & using it in the ILE program provides more efficiency by binding all the required procedures present in multiple modules and service programs in a single binding directory. There are multiple commands available to handle binding directories in IBM i, which then can be used in multiple ILE programs. Thus, reducing the need to write similar code again & again in different programs.
Useful Commands
The following commands are important to understand the concept of binding directory:
CRTBNDDIR (Create binding directory):
This command creates a binding directory object in the particular library. The command syntax can be expressed as:
CRTBNDDIR BNDDIR(*CURLIB/PIOBNDDIR) AUT(*ALL) TEXT('Test Binding Directory')
The noticeable parameters of this command are:
- BNDDIR: In this parameter, we have to specify the name of the library & binding directory object to be created. If we specify *CURLIB, the binding directory will be created in the current library.
- AUT: In this parameter, we have to specify the authority available to the users of the binding directory being created.
- TEXT:In this parameter, we can specify any text description for the *BNDDIR object being created.
If the user will do F4 on the CRTBNDDIR command, the below-mentioned prompt will be available:
ADDBNDDIRE (Add entry to binding directory):
This command adds entries of either modules or service programs to an existing binding directory. The command syntax can be expressed as:
ADDBNDDIRE BNDDIR(PIOLIB/PIOBNDDIR) OBJ((PIOLIB/PIOMOD1 *MODULE) (PIOLIB/PIOSRVPGM1 *SRVPGM *IMMED)) POSITION(*LAST)
The noticeable parameters of this command are:
- BNDDIR: In this parameter, we have to specify the name of the library & binding directory to which we are adding the entry.
- If we specify *CURLIB, the binding directory from the current library is picked.
- If we specify *LIBL, the binding directory will be picked from the library mentioned first in the library list.
- OBJ: In this parameter, we mention the details of all object entries (either *MODULE or *SRVPGM) we want to add in the binding directory.
- POSITION: In this parameter, we mention the level at which the object entry will be located in the BNDDIR.
If the user will do F4 on the ADDBNDDIRE command, the below-mentioned prompt will be available:
DSPBNDDIR (Display Binding Directory):
This command is utilized to display the details of the objects bound in an existing binding directory. The command syntax can be expressed as:
DSPBNDDIR BNDDIR(PIOLIB/PIOBNDDIR) OUTPUT(*)
The noticeable parameters of this command are:
BNDDIR: In this parameter, we have to specify the name of the library and binding directory that we want to be displayed.
OUTPUT: In this parameter, if we will mention *PRINT value then output from the command will be printed to the job’s spooled output. If *OUTFILE is mentioned then we can store the output of the command in the database file mentioned.
If the user will do F4 on the DSPBNDDIR command, the below-mentioned prompt will be available:
If we run this command for the binding directory PIOBNDDIR, the following result will be visible:
WRKBNDDIR (Work with Binding Directory):
Through this command, we can do multiple operations with a binding directory like create, delete, and display. Also, we can do operations with the entries of the binding directory like add an entry and remove an entry. The command syntax can be expressed as:
WRKBNDDIR BNDDIR(PIOLIB/PIOBNDDIR)
The noticeable parameter of this command is:
- BNDDIR: In this parameter, we have to specify the name of the library and binding directory that we want to work with.
If the user will do F4 on the WRKBNDDIR command, the below-mentioned prompt will be available:
If we run this command for the binding directory PIOBNDDIR, the following result will be visible:
DLTBNDDIR (Delete Binding directory)
This command is used to delete an existing binding directory object from a particular library. The command syntax can be expressed as:
DLTBNDDIR BNDDIR(PIOLIB/PIOBNDDIR)
The noticeable parameter of this command is:
- BNDDIR: In this parameter, we have to specify the library name and binding directory we want to delete.
If the user will do F4 on the DLTBNDDIR command, the below-mentioned prompt will be available:
Restrictions
The restrictions a user can have on a binding directory depend on the authority level provided while executing CRTBNDDIR command.
- *LIBCRTAUT: If we specify this AUT value, the binding directory will be created with the authority level same as specified in the CRTAUT parameter while creating the library (using the CRTLIB command) in which the BNDDIR object is being created now.
- *CHANGE: If we specify this AUT value, the user can do all basic operations & also be able to change the BNDDIR.
- *ALL: If we specify this AUT value, the user can do all the operations.
- *USE: If we specify this AUT value, the user can do all basic operations but cannot change the BNDDIR.
Procedure
The main procedure is specified by everything that comes before the first procedure specification, so no special coding is required. The global Definition specifications allow for the coding of the main procedure’s parameters using either a prototype and procedure interface or a *ENTRY PLIST in the main procedure’s calculations.
It is assumed that the procedure interface for the main procedure is any interface for procedures found in the global definitions. The prototype with the same name must come before the procedure interface in the source, and the name is required for the procedure interface for the main procedure.
The name of the module that is being created and the main procedure must match. Either use this name for the prototype and procedure interface or include it in the prototype’s EXTPROC keyword.
Sample code:
Ctl-Opt DftActGrp(*No) Main(MainProc); Dcl-proc MainProc; Dcl-s String char(30); String = 'This is MainProc'; *Inlr = *On; End-proc;
To define the main procedure as a program, you can also use a prototype and procedure interface. In this case, the prototype’s EXTPGM keyword would be specified.
Sample code:
Dcl-Pr CheckObj extpgm('CHECKOBJ'); Object char(10); Library char(10); Found ind; End-pr; CheckObj(ObjectName:Library:Found); Dcl-Pi CheckObj; Object char(10); Library char(10); Found ind; End-Pi; If Found = '1'; Dsply 'Object found'; Else; Dsply 'Object not found'; Endif;
Subprocedure:
A procedure that follows the main source section is called a subprocedure. subprocedure differs from a main procedure primarily in that:
- Names that are defined within subprocedure are not accessible outside the subprocedure.
- No cycle code is generated for the subprocedure.
- The call interface must be prototyped.
- Calls to subprocedures must be bound procedure calls.
- Only P, F, D, and C specifications can be used.
- Other than being called through a program call rather than a bound call, a linear-main procedure is just like any other subprocedure.
Because the data items in subprocedures are local, they can offer independence from other procedures. Typically, local data items are kept in automatic storage, which means that the value of a local variable is not preserved between calls to the procedure.
Another feature provided by subprocedures. A subprocedure can be called in an expression to return a value, and parameters can be passed to it by value.
Below figure illustrates the possible layout of a module with multiple procedures:
Subprocedure Definition:
Sample code:
Dcl-Proc Calculator Export; Dcl-Pi Calculator Zoned(10:0); Num1 Zoned(4:0); Num2 Zoned(4:0); Num3 Zoned(4:0); End-Pi; Dcl-S Result Zoned(10:0); Result = Num1 * 10 + Num2 + Num3 - 15; Return Result; End-Proc;
Dcl-s Number1 Zoned(4:0) Inz(20); Dcl-s Number2 Zoned(4:0) Inz(30); Dcl-s Number3 Zoned(4:0) Inz(40); Dcl-s Result Zoned(10:0); Dcl-pr Calculator Zoned(10:0); Num1 Zoned(4:0); Num2 Zoned(4:0); Num3 Zoned(4:0); End-Pr;
Result = Calculator(Number1:Number2:Number3);
- A prototype that includes the name, any parameters, and any return value.
- “Dcl-Proc” keyword will begin a procedure.
- A definition of a Procedure-Interface which defines any parameters and the return value. The corresponding prototype and the procedure interface must match. If the subprocedure does not return a value and receives no parameters, the procedure-interface definition is not required.
- Additional definition specifications for prototypes, constants, and variables required by the subprocedure. These definitions are local definitions.
- Any standard or free form calculation specifications required to complete the procedure’s task. Both local and global definitions may be used in the calculations. The subprocedure contains any local subroutines. They are only useful within the subprocedure. A RETURN operation must be included in the subprocedure if it returns a value.
- The “End-Proc” Keyword indicates the end of a procedure.
Subprocedure Scope:
A subprocedures defined items are all local. When a local item and a global data item is defined with the same name, the local definition is used for all references to that name within the subprocedure.
- Subroutine names and tag names are known only to the procedure, even those defined in the main procedure, in which they are defined.
- Every field specified in the specifications for the input and output is global. Even if there is a local variable with the same name, the global name is used when a subprocedure uses input or output specifications (for example, while processing a read operation).
Subprocedure calculations:
A subprocedure does not have its own RPG cycle code, so it needs to be coded differently than a main procedure. When one of the following happens, the subprocedure ends:
- A RETURN operation is processed.
- The final computation within the subprocedure is processed.
Service Program
Introduction
A service program is a collection of available data items and runnable procedures that other ILE programs or service programs can directly and easily access. A service program is similar to a subroutine library or procedure library in many aspects.
The name “service program” refers to the common services that these programs offer that other ILE objects might need.
A service program’s public interface is consisting of the names of the exported procedures and data items that other ILE objects can access. A service program can only export items that are exported from the module objects that make up the service program.
Which processes or data items are known to other ILE objects can be specified by the programmer. Therefore, private, or hidden processes and data within a service program can exist and become unavailable to other ILE objects.
A service program can be updated without requiring the other ILE programs or service programs that use the updated service program. Whether a change is compatible with the current support is up to the programmer who is making the changes to the service program.
Characteristics of an ILE *SRVPGM object:
- To create the *SRVPGM object, one or more modules are copied from any ILE language.
- There is no PEP connected to the service program. A dynamic program call to a service program is not valid as there is no PEP. The PEP of a module is ignored.
- The public interface identifies this service program’s exports, which are available for use by other ILE programs or service programs.
- The procedure and data item names that are exported from the service program are used to generate signatures.
- As long as previous signatures are still supported, service programs can be changed without impacting ILE programs or service programs that utilize them.
- Modules can have debug data.
- It is only possible to export weak data to an activation group. It cannot be included in the exported public interface from the service program.
Create and use the service program:
MODULECALL1:
**Free Dcl-Pr Addition Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pr; Dcl-Pr Subtraction Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pr; Dcl-S Number1 Zoned(2:0) Inz(60); Dcl-S Number2 Zoned(2:0) Inz(20); Dcl-S Output Zoned(5:0); Output = Addition(Number1:Number2); Dsply Output; Output = Subtraction(Number1:Number2); Dsply Output; *Inlr = *On;
MODULE1:
**Free Dcl-Proc Addition Export; Dcl-Pi Addition Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pi; Dcl-S Result Zoned(5:0); Result = Num1 + Num2; Return Result; *Inlr = *On; End-Proc;
MODULE2:
**Free Dcl-Proc Subtraction Export; Dcl-Pi Subtraction Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pi; Dcl-S Result Zoned(5:0); Result = Num1 - Num2; Return Result; *Inlr = *On; End-Proc;
- Create Module MODULECALL1, MODULE1, and MODULE2.
CRTRPGMOD MODULE(DEMOLIB/MODULECALL) SRCFILE(DEMOLIB/QRPGLESRC) CRTRPGMOD MODULE(DEMOLIB/MODULE1) SRCFILE(DEMOLIB/QRPGLESRC) CRTRPGMOD MODULE(DEMOLIB/MODULE2) SRCFILE(DEMOLIB/QRPGLESRC)
- To create a service program SRVPGM1, use the CRTSRVPGM command.
CRTSRVPGM SRVPGM(DEMOLIB/SRVPGM1) MODULE(DEMOLIB/MODULE1 DEMOLIB/MODULE2) EXPORT(*ALL)
- To display a service program, use the DSPSRVPGM command.
DSPSRVPGM SRVPGM(DEMOLIB/SRVPGM1)
- To update a service program, use the UPDSRVPGM command.
- Now bind the service program SRVPGM1 to the calling program CALLPGM1.
CRTPGM PGM(DEMOLIB/CALLPGM1) MODULE(DEMOLIB/MODULECALL) BNDSRVPGM((DEMOLIB/SRVPGM1))
Signature
What is a Service Program Signature?
Every service program has at least one signature. The signature is generated by the system when a Create Service Program (CRTSRVPGM) command specifying Export(*ALL) or Export(*SRCFILE) is issued.
The signature value is generated by an algorithm that uses as input the names of all of the service programs “exports” and their sequence. Here, “exports” are nothing but external procedures and external variables, constants etc. that are exported so that those can be used/called in other programs.
In the above screenshot, a Service Program ‘CUSTSTUFF’ is created by binding 2 modules -Module1 and Module2.
What is the use of a Signature?
A signature is a value that provides a similar check for service programs that a level check does for files. It helps ensure that changes made to the service program are done in such a way that the programs using them can still function properly.
Where can we see the Signature of a Service Program?
You can see the current signature value for a service program by using the Display Service Program (DSPSRVPGM) command. The current signature appears on the first screen of the command output.
We can see the Signature of the Service Program ‘CUSTSTUFF’ in the last row.
If you continue to press Enter on each screen displayed by the command, screen 9 (of 10)
shows all the valid signatures, including any valid previous signatures.
In addition to these, we can also see the modules and the list of external procedures that are bound to the Service Program.
What causes Signature to Change?
A signature changes when the list of exports for the service program changes. The most common cause of a signature change is adding a new procedure to the service program.
By using ‘UPDSRVPGM’ we are updating the Service Program ‘CUSTSTUFF’ as a new external procedure is added in one of the modules.
In the above screenshot, we can see a new Export is added in the Service Program. That causes the change in Signature as in the below screenshot.
Note: A change in a procedures’ parameters doesn’t change the service program’s signature.
What happens when Signature changes?
When a program is called, it immediately checks the signature value of any service programs that it uses and produces a signature violation message if the signatures don’t match. This happens at program start up not when you call a service program procedure.
The program ‘CUSTPGM’ is created by binding the Service Program ‘CUSTSTUFF’ when it had only 4 external procedures.
As you can see, the Signature of ‘CUSTSTUFF’, the ‘CUSTPGM’ is referring to is the older one. (The command used to display this is DSPPGM against the program ‘CUSTPGM’.)
Now that the Signature of the Service Program is changed after the addition of a new Procedure, a call to ‘CUSTPGM’ results in an error.
When we see the job log, we realise it is a Signature Violation error.
How to correct a Signature violation?
Use the Update Program (UPDPGM) command. If any signatures have changed, it also updates the program’s signature values.
Note: You don’t need to specify the service program because the UPDPGM command automatically re-checks the signatures of any bound service programs.
As you can see, the Signature to which ‘CUSTPGM’ refers to is matching with the current Signature of ‘CUSTSTUFF’.
Now, a call to ‘CUSTPGM’ does not result in any error.
Conclusion
In our example, we have only one program that uses the Service Program ‘CUSTSTUFF’. What if there are some 20 programs that use a Service Program, and we have to find these 20 among 100 programs?.
In this case, to resolve the ‘Signature Violation’ error by, finding out which 20 are using the concerned Service Program –
- We must execute DSPPGM against 100s of programs.
- We can use SQL to qsys2.bound_module_info (v7r3 and above) or use APIs QBNLPGMI/QBNLSPGM to get the list of programs that use the service program.
to find out which 20 are actually using the concerned Service Program and And, do execute UPDPGM on those 20 programs manually.
Needless to say, it is a cumbersome process, unless, you have an appropriate change management tool which would do all the updates/recompilations automatically.
The solution to this problem is ‘Binder Language’.
Binder Language
Binder language commands & parameters
Below are the instructions which are typically used to write the export source file in binder language:
- STRPGMEXP & ENDPGMEXP commands
- STRPGMEXP command specifies the start of the list of exported symbols (or procedures) and it is paired by ENDPGMEXP command to specify the end of this export list.
- There can be multiple blocks of STRPGMEXP-ENDPGMEXP inside a binder language.
- Every block specifies different export list & corresponding signature of the service program.
- PGMLVL parameter
- This parameter is used with STRPGMEXP command used in binder language; and it specifies which STRPGMEXP-ENDPGMEXP block export list should be used to create the latest/current signature of the service program.
- “*CURRENT” & “*PRV” are the values which are used with PGMLVL command.
- Only one PGMLVL parameter can have *CURRENT value (It shows that the STRPGMEXP-ENDPGMEXP block used with PGMLVL (*CURRENT) parameter is the current signature of the service program).
- Apart from one PGMLVL having *CURRENT value, rest all PGMLVL parameters in an export source file must have PGMLVL values as *PRV.
- SIGNATURE parameter
- This parameter is also provided with STRPGMEXP command but it’s not mandatory. It is used to explicitly provide a signature for the export list.
- *GEN (Default) and explicit name provided by programmers are possible values which are used with SIGNATURE parameter.
- A possible situation to provide an explicit name for signature is that there is a change in parameters of an existing procedure of the service program (and no other change), even after recompiling the module and updating the service program the system can generate the same signature (because the signature depends on the list of exported procedures and their order in the export list and not on the parameters of procedures).For this case we can provide a different signature name while updating the service program to force all programs to recompile.
- EXPORT command:
- The EXPORT command is used to provide the name of the procedure to be exported from service program using SYMBOL parameter.
If the symbol name contains lower case character, use apostrophe while mentioning the name in SYMBOL parameter.
- The EXPORT command is used to provide the name of the procedure to be exported from service program using SYMBOL parameter.
Binder Language examples
Below are some samples of binder language sources:
- Example (1) : How to use binder language source in service program creation
There is a module named NUMERICOPS with below procedures:To create a service program named NUMERICOPS, while providing only the functionality of adding numbers (export only “AddNumbers” procedure), we can create below binder language source file.
While creating the service program we can provide the binder language source file as below:
- Example (2): Maintaining multiple signatures for the service program
For the service program created in example 1, below is the signature which has been created (we can see this signature using DSPSRVPGM SRVPGM(NUMERICOPS) DETAIL(*SIGNATURE) command):
Now to start exporting “SUBTRACTNUMBERS” procedure, we modify the binder language as below:
Create the service program again using the binder language export list file (through CRTSRVPGM command given in example 1). After re-creating the service program, we see below signatures available for service program:
As the older signature is still maintained with service program, we will not need to recompile older modules bound with service program.
- Example (3): Impact of “SIGNATURE” parameter in binder language
As we can see, in example 2, the SIGNATURE parameter has been used with STRPGMEXP command.
Now, let’s say we change the “ADDNUMBERS” procedure to accept three numbers instead of two and modify the returning parameter length from 8 to 10 numeric as below (compare with example 1 source to see the modifications):
If we re-create the program using the same binder language source (no modifications made in SIGNATURE parameter), there will be no impact on the signatures of the service program because no new procedure added/no existing procedure removed AND no change done in the order of the procedures being exported earlier.
Below are the signatures after recompiling the module and re-creating the service program:We can observe that these are same signatures which were generated in example 2 (Given above again for quick reference/comparison).
To resolve this issue (to generate a new signature when there is a change in the parameters of the procedure), we can modify the SIGNATURE parameter (for this example, modified it from “ADD2_SUB2” to “ADD3_SUB3”):
Now, if we re-create the service program, we find below signatures and we can see that new signature has been generated which will force the recompilation of existing programs.
Additional tips
Tip 1: To retrieve the current binder language source file for the service program, use RTVBNDSRC command.
Tip 2: While creating the service program, to see the errors in compilation listing related to binder language, use parameter DETAIL(*EXTENDED) or DETAIL(*FULL).
Tip 3: There should be no change in the order of the symbols exported earlier while providing a new STRPGMEXP-ENDPGMEXP block, otherwise it will create instability in service program processing.
Bind By Reference
Signature Violation
The signature violation error occurs when the signature IDs of a program and its associated service program don’t match.
Signature IDs
A signature ID is a special identification associated with a program or service program in the context of service programs. It guarantees that the interfaces of the caller and the callee are compatible. When a program calls for a procedure through a service program, the system verifies if the signature IDs match. A signature violation error happens if they don’t.
Common Causes
Service Program Changes: A service program’s signature ID changes when it is modified, such as when new modules are added.
Existing Callers: It’s possible that older signature IDs are still cached in programs that refers the service program.
Example:
MODULECALL1:
**Free Dcl-Pr Addition Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pr; Dcl-S Number1 Zoned(2:0) Inz(60); Dcl-S Number2 Zoned(2:0) Inz(20); Dcl-S Output Zoned(5:0); Output = Addition(Number1:Number2); Dsply Output; *Inlr = *On;
MODULE1:
**Free Dcl-Proc Addition Export; Dcl-Pi Addition Zoned(5:0); Num1 Zoned(2:0); Num2 Zoned(2:0); End-Pi; Dcl-S Result Zoned(5:0); Result = Num1 + Num2; Return Result; *Inlr = *On; End-Proc;
- Create Module MODULECALL1, MODULE1
CRTRPGMOD MODULE(DEMOLIB/MODULECALL) SRCFILE(DEMOLIB/QRPGLESRC) CRTRPGMOD MODULE(DEMOLIB/MODULE1) SRCFILE(DEMOLIB/QRPGLESRC)
- To create a service program SRVPGM1, use the CRTSRVPGM command.
CRTSRVPGM SRVPGM(DEMOLIB/SRVPGM1) MODULE(DEMOLIB/MODULE1) EXPORT(*ALL)
- Now bind the service program SRVPGM1 to the calling program CALLPGM1 and call the CALLPGM1 program.
CRTPGM PGM(DEMOLIB/CALLPGM1) MODULE(DEMOLIB/MODULECALL) BNDSRVPGM((DEMOLIB/SRVPGM1))
Result:
- Now adding the SUBTRACTION procedure to MODULE1 module and updating the SRVPGM1 service program using UPDSRVPGM command. Signature ID of associated service program will change.
- After updating the SRVPGM1 service program and calling the CALLPGM1 program it will get the Program signature violation error.
Avoiding Signature Violation error
Rebind Programs: Rebind all programs that refer the modified service program. By doing this, you can be sure that their signature IDs have been updated with the new program signature.
Manage Signatures: As an alternative, you can manage service program signatures by creating binder language, which is used during program compilation. By doing this, you can manage the handling of signature IDs without having to recompile every caller.
Binder Language
A service program’s exports are defined by a small set of nonrunnable commands known as the binder language. When a BND source type is specified, the binder language allows the source entry utility (SEU) syntax checker to prompt and verify the input.
The binder language consists of a list of the following commands:
- Start Program Export (STRPGMEXP) command, which identifies the beginning of a list of exports from a service program
- Export Symbol (EXPORT) commands, each of which identifies a symbol name available to be exported from a service program
- End Program Export (ENDPGMEXP) command, which identifies the end of a list of exports from a service program
The public interface to a service program is defined by the symbols found between a pair of STRPGMEXP PGMLVL(*CURRENT) and ENDPGMEXP symbols. A signature serves as a representation of that public interface. A value known as a signature identifies the interface that a service program supports.
You can add new exports to the end of the list of exports, so your binder language source only needs one export block if you choose to specify an explicit signature.
If you choose not to specify an explicit signature, the binder generates a signature from the list of procedure and data item names to be exported and from the order in which they are specified.
Each time you add a new export to your service program, you have to create a new export block in your binder source.
The first entry in a list of exports from a service program is indicated by the Start Program Export (STRPGMEXP) command. A service program’s list of exports can be ended with the End Program Export (ENDPGMEXP) command.
Multiple signatures are produced when a source file contains multiple STRPGMEXP and ENDPGMEXP pairs. There is no significance to the order in which the STRPGMEXP and ENDPGMEXP pairs occur.
PGMLVL(*CURRENT) can only be specified by one STRPGMEXP command, though it need not be the first one. A source file’s other STRPGMEXP commands must all include PGMLVL(*PRV). PGMLVL(*CURRENT)-specified STRPGMEXP commands are represented by the current signature.
You can explicitly specify a signature for a service program using the signature (SIGNATURE) parameter.
A character string or a hexadecimal string can be used as the explicit signature.
The binder generates a signature from exported symbols when the signature parameter’s default value, *GEN is used.
The STRPGMEXP command’s level check (LVLCHK) parameter indicates whether the binder will automatically check a service program’s public interface.
A symbol name that can be exported from a service program is identified by the Export Symbol (EXPORT) command.
Example:
- You can create binder source file using STRSEU command with BND type.
- Exported the procedure ADDITION by Export symbol (EXPORT) command.
- Create service program using CRTSRVPGM command and export the BNDSRC binding source file.
- See the Signature ID of recently created service program SRVPGM2.
- After binding the SRVPGM2 service program to the CALLPGM2 and call the CALLPGM2.
- Specify another STRPGMEXP and ENDPGMEXP pair with PGLVAL(*CURRENT) parameter. And exported the new procedure SUBTRACTION.
- Updating the existing service program SRVPGM2 with BNDSRC source file. Also, one more signature will be added with old signature.
- Call the CALLPGM2 program and this time you will not get signature violation error.
Bind By Copy
Bind by copy is nothing but static binding. Binding a module with "Bind by copy" means entire module object definition is copied into the bounded program. Once you create a program object with module using bind by copy, you can delete the module object, but the program still gets executed.
The modules specified on the MODULE parameter of commands like CRTPGM, CRTSRVPGM etc. are always bound by copy. Refer below screenshots for more info.
Module parameter from CRTSRVPGM
Even if you add modules to a binding directory and bind them to program using binding directory, then it is bind by copy.
Advantages of Bind by Copy
If you bind a module to a program using bind by copy, there will not be any overhead in loading and executing the program. So, the program execution gets faster.
Disadvantages of Bind by copy
If you have simple piece code in module and bind it in multiple programs using bind by copy, each program will have the copy of the code and that increases the program size.
Also, if you need to make any modifications to that piece of code, you need to modify the module and need to recompile all the programs that are bounded the module. In such scenarios, maintaining list of bounded programs is another overhead.
Below is a simple code to print some patterns:
Module 1
CRTRPGMOD MODULE(QTEMP/PATTERN) SRCFILE(DEMOLIB/QRPGLESRC)You can create the program using the command below.
CRTPGM PGM(QTEMP/PATTERN) MODULE(QTEMP/PATTERN)Check the objects using the below command.
WRKOBJ QTEMP/PATTERN
Now delete the module by taking option 4
Now call the program