Read an Excerpt
Evolve Your RPG Coding
Move from OPM to ILE ... and Beyond
By Rafael Victória-Pereira MC Press
Copyright © 2015 Rafael Victória-Pereira
All rights reserved.
ISBN: 978-1-58347-426-6
CHAPTER 1
Modules, Programs, and Service Programs
How do you create an OPM RPG program? You write your source, compile it with PDM's option 14, and (after you've squashed all those annoying little bugs) you get a *PGM object that you can execute.
ILE uses a similar process, but includes an intermediary step. To create an ILE program, you compile your source not into a program as you would in OPM, but into a module, and then you create your program.
Why the additional step of creating a module for the same source code?
The extra step actually introduces some advantages. Breaking your code into smaller pieces (modules) assures faster compile times and reusability of code. (You'll see a practical example of this later in this chapter.) You can also copy modules, moving them from one system to another. This ability to distribute and reuse modules opens the possibility that third parties could sell specialized modules or groups of modules to provide functions that you might not be willing or able to write on your own.
Since RPG has an interestingly active community, you can get many specialized modules for free. Websites like MC Press Online (www.mcpressonline.com/) often present procedures and functions (explained in chapter 3) custom-built but easily adapted for certain goals. For example, you could buy (or obtain for free) a set of financial procedures/functions written in C, and then piece them together into usable programs, even if you don't have a C compiler — or maybe any compiler at all. Even if you never learn a single line of C, you can still take advantage of C's ability to handle complex math expressions by linking an ILE C module with your ILE RPG modules. (Again, I'll explain this concept of "linking" or "binding" later.)
Yes, you read it right: an ILE C module can work seamlessly side by side an ILE RPG module. That's the beauty of ILE!
A Typical OPM Scenario
Back to the topic at hand: the typical OPM RPG program consists of an all-in-one block of code. This code contains a group of subroutines to handle the screen (if the program has one), another group to handle the necessary validations, and in really well- organized code, a subroutine or two to handle the database interactions. If the code is not that well-organized, or if it's a relic from the 1980s, it might contain a single huge block of code, loaded with GOTOs, labels, and a primary cycle.
This code, or at least part of it, probably exists in some other program also. Even if the other program serves a different purpose, it contains the same, or at least very similar, validations (business rules, for instance) and database operations. We've all seen this — code copied from one program to another and modified slightly (or not at all) to get the required functionality. This creates a problem: with the same code in many programs, when you find a bug in one copy, you have to fix all versions of the code in order to completely squash that bug.
Let's consider an example. Figure 1.1 shows a typical OPM scenario. PGM A enters items into the company's inventory automatically, using the cargo manifest of an inbound shipment. This fixed-format text file contains all the relevant information for the inventory. PGM A might contain subroutines for basic screen handling, subroutines guiding the program flow (perhaps even an old-fashioned primary cycle), a set of business-rule subroutines common to all inventory-handling programs, and another group of subroutines to handle database interaction — again, common to several programs. PGM B manages the inventory interactively, allowing the user to add, change, and remove items.
Both PGM A and PGM B contain similar code to handle the business rules related to inventory management. In fact, the middle group of subroutines (the Business Rules Validation Subroutines group) is exactly the same in both programs. Where the programs' functionalities differ, in the bottom group (Database Operations Subroutines), the subroutines still probably have a lot in common.
A Basic ILE Approach to This Problem
This is where ILE's modularity comes into play. By isolating similar sets of code (hopefully, already self-contained in subroutines) into procedures and grouping those procedures in modules, we can easily reuse the modules in different programs. I'll detail this concept in a couple of chapters; for the moment, think of the modules as subroutines with parameters. Figure 1.2 shows the basic ILE approach to the inventory programs scenario.
The ILE solution requires creating four modules:
PGM A's loop through the cargo manifest file becomes module M1, and PGM B's loop through user interaction becomes M2. These modules contain the specific functionality of each program. Because the cargo manifest guides PGM A, the screen interaction is probably minimal, limited to the input of the filename and location. PGM B includes more complex user interface (UI) code, requiring richer screen interaction to support options for adding, changing, and removing items from the inventory.
Module M3 incorporates all the code related to business rules. This module concentrates all the previously duplicated functionality from both programs in a single place. Whenever the code requires a change, you only have to do it once.
A fourth module, M4, contains all the database operations related to inventory files. Again, concentrating all the operations in one place reduces the maintenance cost of the code. It also enables the programmer to add new functionality and have it (almost) immediately available to all the programs that use this module.
Note that the "main flow" modules (M1 and M2) only have the code that acts as the backbone of the programs, guiding their flow. Everything else, even if it was not duplicated in the original programs, is isolated into procedures and placed in M3 or M4, depending on its nature. By doing this segregation, you assure that future programs related to inventory can reuse these blocks of code.
If you needed to create a PGM C for some other inventory-related set of operations, you could simply write a module for the program flow code (the part that is unique to that program, conceptually similar to the M1 and M2 modules). You would reuse the modules for business rules and database operations like building blocks, to perform their specific functions, without duplicating the code and keeping a single point of maintenance.
After developing the individual modules, you create the program, which includes all the modules with the proper command (more on that later), because *MODULE objects cannot be executed. A module contains all the compiled and translated code from a program source member, but it does not include any mechanism to call or execute the code. Note that you can code the modules using one of the several ILE languages available; a *PGM object can contain RPG, C, C++, or COBOL modules. The procedures contained in them "talk" to one another via their parameter lists. Think of modules as the ILE equivalent to Window's dynamic-link library (DLL) files: they contain tools that you can reuse at will, but you cannot "run" them.
This reusability likens RPG to more "modern" languages like Java or PHP, in more ways than you might imagine. I've mentioned the different languages you can write the modules in, and the plug-and-play ease of use. Additionally, you can use code written in non-ILE languages such as Java just like an RPG module, if (and this is a big "if") a proper procedure interface exists. This means that the parameters used by the Java function you want to use must be "translatable" to something RPG can handle. The details of this topic are outside the scope of this book, but if you want to know more, here are a few books that might help (all published by MC Press):
Advanced Integrated RPG, by Thomas Snyder
The Modern RPG IV Language, by Robert Cozzi, Jr.
My own Flexible Input, Dazzling Output with IBM i
Why Some Detractors Say ILE Means "It's a Link Editor"
What do we know so far? A *PGM object includes one or more *MODULE objects. Modules are not executable; programs are.
How are programs and modules linked? Well, when you create a program, you need to indicate which modules are going to be part of that program and, of those modules, which one will be the entry point, or the first to be executed, to get the program running. Going back to our inventory scenario, PGM A would have three modules:
The cargo manifest file flow (M1) — the entry point module
Business validations (M3)
Database operations (M4)
The same goes for PGM B, replacing M1 with M2.
To make the transition to ILE smoother, think of this entry point module as your OPM program. Its input parameters will be mentioned in the *ENTRY PLIST instead of a procedure interface, and it won't have procedures or functions, just a set of subroutines that guide the program flow. This module's code will then call the necessary procedures/functions from the other modules as needed. Building a program becomes simply linking modules together; hence, some detractors say that "ILE" stands for "It's a Link Editor."
Anatomy of a Module Object
Let's get back to the module object. You need to be aware of a few other characteristics of a module. I've mentioned that modules consist of procedures and functions. You can make these building blocks available to other ILE objects by having the module export them, or they can be built just for supporting other procedures within the module. In the latter case, they are private to the module, a bit like the way a subroutine is not visible outside an OPM program. Since reusability is one of the pillars of ILE, the code of these procedures/functions in turn can use procedures/functions from other modules, as I mentioned previously. These are called the module imports or references.
Everything I said for procedures/functions also applies for data: a module can refer to a data item from another module (import data) or make a data item available to the "outside world" (export data). But that's not all. I've briefly mentioned the entry point module — if you know a little C, you'll know that a function called main() is the entry point. RPG doesn't work quite the same way, because you can write a module without procedures and with an *ENTRY PLIST — the closest approach to an OPM program. (I'll discuss modules, procedures, and functions in more detail in chapter 3.)
An Even Better Way: In Comes the Service Program
You've seen so far that you create a program by piecing copies of modules together. This is known as "bind by copy." Going back to our example, you'd be duplicating the business rules and DB operations modules into each of the programs. Note that you won't actually see this duplication; it happens on the *PGM object's creation, increasing each program's size. This approach also has another problem: any change to the business rules or DB operations modules requires recreating all the programs that used them! This doesn't seem very practical, right?
Wouldn't it be better if you could use the procedures/functions in the modules without having to copy them to your program, but just by referencing them, like you do with opcodes and built-in functions? That's where the service program concept comes into play. A*SRVPGM object provides another way to group *MODULE objects. It packages together frequently used modules that many programs share, without actually copying the modules into each individual program. This addresses the size problem of the previously mentioned scenario. Service programs let you build smaller ILE programs and centralize the organization and reuse of common code, thus solving the recompilation issues also previously mentioned.
Common editing routines, code related to database operations, business rules, and other policies that are common to the whole application, or even application-specific calculations, are good candidates for service programs. As you can see in Figure 1.3, by linking the modules to a service program instead of linking them directly to each of the programs and then linking the service program to PGM A and PGM B, you get the same functionality and code separation without the duplication of code. Up to a certain extent, you can also eliminate the need to recreate the program every time a line of code changes in the modules, because the service program binds by reference (not by copy), to the programs. (I'll explain this "certain extent" in a couple of chapters, I promise — it's related to the procedures' parameters.)
Of course, changes to M1 or M2 will still require recreating PGM A or PGM B because those modules still bind by copy to their respective programs. A service program serves as a collection of runnable procedures, ready for use in programs or other service programs.
A service program, like the modules bound to it, cannot be executed. The great advantage here is, as long as you don't change certain characteristics of the service program, you can freely change the code of its modules without recreating all the programs bound to that service program. However, whenever you change a module that's bound to a service program, it's a good idea to recreate the service program; even though it might not produce an error, it can lead to baffling decimal data errors and other strange problems that are difficult to trace in the programs that use that service program.
Summary
Let's finish with a recap of this chapter's key concepts:
*MODULE — This object contains one or more procedures; it cannot be executed. It contains imports (references to data, procedures, and/or functions from other modules) and exports (data and/or procedures/functions made available to other modules/programs/service programs, also known as definitions). It may contain a user entry point.
*PGM — This object is composed of one or more modules that can be written in different languages. *PGM objects can be executed. The simplest form of an ILE *PGM (and the one most similar to OPM) includes just one module that contains all the necessary code for the program's functionality. (Although this is possible, it's not advisable, because it doesn't take advantage of ILE's modularity and reusability.)
*SRVPGM — This object is composed of one or more modules. It cannot be executed. It allows for the reuse of modules without duplicating the code into each program. Certain changes to a module can occur without changing the programs that use the service program; however, it's advisable to recreate the service program every time one of its modules changes.
In the next chapter, you'll learn how "binding" between modules, service programs, and programs works, and get a quick tour of the commands to create each of these ILE objects.
CHAPTER 2
Binding It All Together
Creating an OPM RPG program is an easy, one-step operation: just type "14" in the respective source member line of the Work with Members Using PDM screen, and you're done. Creating an ILE RPG program requires additional work, but it doesn't have to be hard. It's just an operation with a few more steps.
Before We Begin, Let's Review the Journey
Instead of dabbling in theory, let's go back to the service program scenario from the previous chapter, shown in Figure 2.1.
Here, the modules M1 and M2 link (or bind) by copy to PGM A and PGM B, respectively. This means that when you create the programs, the system creates a copy of the modules and stores them inside the programs. The same thing happens with modules M3 and M4: they are copied into SRVPGM C, because they are also bound by copy to this service program. Both PGM A and PGM B use these modules, but the link is different: they reference M3 and M4, via SRVPGM C. In practice, this means that the programs won't contain a copy of M3 and M4, or not even a copy of SRVPGM C. They just "point" to SRVPGM C to use the M3 and M4 modules.
In an OPM environment, compilation between programs is not usually an issue. In ILE, because of the way it's structured, the compilation order of the objects might cause some issues. It's important, therefore, to mention a few basic rules to avoid wasting time with strange compilation and runtime errors:
1. Always compile all the modules first.
2. Compile the service programs next.
3. If you have service programs that use other service programs, start with the ones that don't.
4. Then, and only then, compile the programs, starting with those that don't use other programs.
(Continues...)
Excerpted from Evolve Your RPG Coding by Rafael Victória-Pereira. Copyright © 2015 Rafael Victória-Pereira. Excerpted by permission of MC Press.
All rights reserved. No part of this excerpt may be reproduced or reprinted without permission in writing from the publisher.
Excerpts are provided by Dial-A-Book Inc. solely for the personal use of visitors to this web site.