|
ILE Static Binding
by Kevin Vandever
[The code for this article is available for download.]
ILE has been around for about eight years, yet many iSeries shops still considered it new technology. The
primary reason is that the Integrated Language Environment is so different from what we are used to.
Things like service programs, subprocedures, activation groups, and static binding are all foreign concepts
to our midrange-programming minds. In this article, I am going to discuss the ILE concept of static binding
and explain why you might want to use it and when to employ which static binding technique.
What Is Static Binding?
Binding is simply a method of combining two or more iSeries objects into a single program or service
program. Binding by itself is not a new concept; all of use have bound programs together since we were
little midrange programmers. Whenever you call one program from another using the CALL operation, you
are dynamically binding those programs. Dynamic binding means that neither program knows about the
other until the call takes place. In that sense it is dynamic, because all the binding is happening right at the
time the program is called, and subsequent calls to the same program might be different, depending on what
has been changed in the program since the last time it was called. It's hip, it's unattached, but, as you'll see,
it comes with a cost. Static binding, on the other hand, is the method of binding two objects together well
before they ever use each other. This type of binding is static, in that the objects are bound together and the
relationship between those bound objects remains consistent until one of the objects is changed and
everything is statically bound again.
Why Do It?
The primary reason for statically binding, as opposed to dynamically binding or not binding at all, is that it
removes the performance penalty that comes with breaking code into smaller, more manageable pieces to
perform specific tasks. Dynamic binding is expensive because every call to a program must go through
program call resolution and program startup tasks. Not binding at all removes this performance hit but
causes monolithic programs that are next to impossible to maintain and do not promote code reuse. Static
binding allows you to code in a more modular fashion without incurring the performance hit associated
with dynamic binding. This is accomplished differently, depending on the type of static binding you
employ.
Binding Building Blocks
Now that I have explained static binding and why you should use it, I want to discuss the ILE building
blocks in a little more detail. Before ILE, we only concerned ourselves with program objects (*PGM). With
the introduction of ILE, there came along some new object types that are the building blocks to static
binding. Below is a description of each:
Program (*PGM) is still around but is now created using the CRTBNDxxx command (where
xxx represents the language, such as RPG, CL, or CBL) and the CRTPGM command, which allows
you to statically bind two or more modules together to form a program. *PGMs can be called using the
dynamic CALL operation or, if created by binding two or modules, those modules can be called using a
bound call (CALLB in RPG) or a prototyped call (CALLP in RPG).
Module (*MODULE) is the basic building block of static binding. Modules allow you to organize related
tasks into smaller units of code, which can then be bound together into programs or service programs. They
cannot be called using the dynamic CALL operation but are, instead, called with either the bound call or
the prototyped call. Modules are created using the CRTxxxMOD command (where xxx
represents the language used). NOMAIN modules cannot be called directly. Instead, the subprocedures
contained inside the module are called using prototyped call (CALLP in RPG).
Service Program (*SRVPGM) can be viewed as a collection of subroutines packaged together and
accessible to the outside world. Service programs are the ILE packages used for bind by reference, meaning
that the exports (subprocedures you exported during the creation of your service program) are loaded into
memory and shared with other programs and service programs that need them. You use the CRTSRVPGM
command to create a service program.
Procedures (also known as subprocedures) are the smallest component of static binding and are self-
contained high-level-language statements used to perform a specific task. There is no related object type to
describe the subprocedure; it is only used as a building block to create a *MODULE. One or more
subprocedures are created inside a module and then compiled into a program or service program.
Two Flavors
There are two types of static binding: Bind by copy and bind by reference. Bind by copy is accomplished
by copying two or more modules into a program. In this way, the finished product performs just a like a
program with multiple subroutines, except that each module can be called from the other modules within
the program. I've included three modules (module1, module2,
and callmods) that you can compile together to form a program. In my example, I will create
a program, PROGRAM1, that will be comprised of my three modules. The modules are created first, then
all the modules are compiled together as a *PGM, using the CRTPGM command. In this way all the
modules are copied into the program. The following is the sequence used to create the program:
CRTRPGMOD MODULE(YourLib/MODULE1) SRCFILE(YourLib/QRPGLESRC)
CRTRPGMOD MODULE(YourLib/MODULE2) SRCFILE(YourLib/QRPGLESRC)
CRTRPGMOD MODULE(YourLib/CALLMODS) SRCFILE(YourLib/QRPGLESRC)
CRTPGM PGM(YourLib/PROGRAM1) MODULE(CALLMODS MODULE1 MODULE2)
ENTMOD(CALLMODS)
Now MODULE1 and MODULE2 can be called from within PROGRAM1. If we had bound other modules,
by copy, into PROGRAM1, we could have called them, too. Notice that CALLMODS actually calls the
other two modules, using the CALLB operation. That's because the CALLMODS module was made the
program entry procedure (PEP) by the ENTMOD parameter in the CRTPGM command. That doesn't mean
it is the only module from which you can call other modules; it simply means that when your program is
called, this module is the one it will start in.
Bind by reference works a little differently. Modules are still bound together to form an object, but this
time the object is a service program (*SRVPGM), not a program. (For more information on service
programs, check out my article "The Basics of ILE Service Programs.") The service program is then loaded into the
memory of the calling program, at runtime. There exists only one copy of the code, but that copy is loaded
at runtime and shared with other programs or service programs that might need it. A service program is like
an RPG program with a bunch of subroutines that can be called from the outside world or, in java speak, as
a class with multiple methods. A service program cannot be called directly; rather, each component
(subprocedure) is called directly. This adds a new spin to RPG, and that spin is multiple entry points. I've
included two modules from my service program article (CstOps and CstData),
along with their corresponding prototypes (CstOpsPR and CstDataPR). I will use these modules to build my service program. A service program is
not all that different from a program, except that each module contains one or more subprocedures and
those subprocedures, not the modules, are the entry points into the service program. Another difference is
that the modules contained in service programs contain no program entry procedure (PEP), as our program
did. This is accomplished by adding an H-spec (NOMAIN) entry in each module. NOMAIN states that
there is no PEP, and therefore much of the tasks done at program startup, including the loading of the logic
cycle, are not done. This lightens the object significantly. It also means that in addition to the service
program not being able to be called directly, the modules can't be called, either. The only way to access the
service program is through the subprocedures. The module becomes little more than a container for its
subprocedures. (This is discussed further in "The Basics of ILE Service Programs." For more information
on subprocedures, check out Ted Holt's article "Subprocedures: Better than
Subroutines.")
These are the steps necessary to build the service program from the given modules:
CRTRPGMOD MODULE(YourLib/MODULE1) SRCFILE(YourLib/QRPGLESRC)
CRTRPGMOD MODULE(YourLib/MODULE2) SRCFILE(YourLib/QRPGLESRC)
CRTSRVPGM SRVPGM(YourLib/SRVPGM1) MODULE(MODULE1 MODULE2) EXPORT(*ALL)
The EXPORT(*ALL) parameter tells which subprocedures you want to make available to the outside
world. In my service program article, I included a program you can use to test out the calls to the
subprocedures inside your service program. You will also find an explanation of a binding directory, which
is a way to organize service programs and modules to make it easier to bind them by reference to other
programs and service programs. Please refer to that article for detailed explanation on testing your service
program.
So bind by copy is achieved when everything, caller and callee, are bound together in a program (*PGM).
Bind by reference is achieved when one or more subprocedures are combined to create a NOMAIN module
and one or more NOMAIN modules are combined to create a service program (*SRVPGM). Let's look
closer at the building blocks.
Vanilla or Chocolate?
Great. So you can bind by copy or bind by reference. Or you can still dynamically bind with a traditional
CALL operation. So when do you do which? Well, there is no hard and fast rule. If your shop was really
comfortable with bind by copy and just hated bind by reference, I would not try to force you into bind by
reference; however, there are some guidelines that you can follow.
If a module is going to be called by one program, and one program only, use bind by copy. It is the best
overall performer in this case, because the code is copied right into the program at compile time. However,
this becomes a potential maintenance and compile nightmare if you decide you are going to use bind by
copy and the module is going to be called by many programs.
If you have code that is going to be called by many different programs, create subprocedures and compile
them into modules then further into service programs. You will take a performance hit the first time the
subprocedure is called for each job, but subsequent calls will perform as fast as bind by copy. Because of
the first-call performance hit, if the code is going to be called infrequently, you may want to choose another
binding strategy.
That other binding strategy is dynamic binding. Yes, it's still supported and still has value. If you have code
that is going to be called by multiple programs, but infrequently, this is still the way to go. The advantage
here is that you don't load the program unless you actually use it. The performance hit will be high each
time you call it, so use this strategy only for multiple, infrequently called programs.
Time to Take a Nap
There you have it. I know I've thrown a lot at you, but take the information in this article along with details
from the referenced articles and give it a try. It's really not that bad once you get used to it. Besides, you
will need to have the basics down in order to move onto the really fun stuff such as activation groups,
signatures, and more complex real-world applications.
|