Learn how to set up and compile service programs, a key feature of ILE RPG on IBM i
Editor’s note: This article is excerpted from chapter 9 of 21st Century RPG: /Free, ILE, and MVC.
There is one more type of program structure that I want to introduce you to. And that is a service program: a program that can be called by many other programs and which basically serves as a holder for a variety of sub-procedures.
Service programs are a natural part of ILE and the culmination of what it promises.
And the drawback? Well, I guess it would be that just the idea of service programs scares some people, but there is no reason for that. To be perfectly honest, service programs are just a hair more complicated than either of the previous two options. And I am not being tongue in cheek when I say “just a hair.”
The truth is, in some ways, service programs are like one of those haunted Halloween houses, all smoke and mirrors. Once you get into it and understand the basics of prototyping (which you do by now), service programs are no big deal, and even someone like you can use them extensively. And people say I have no tact. Ha!
So let’s stop talking and get started.
The Service Program Scenario
This scenario consists of two programs, just like it did with the CALLP example in chapter 8.
There is the calling program: the program that would like to use the services (sub-procedures) contained in the service program.
And there is the service program itself, standing alone, cold, and aloof.
The difference between this scenario and the standard CALLP model is that we are going to actually bind the two programs together in a tighter bind than is possible with just the CALLP. This will make a call to a service program very fast. And it will also produce a bind that is more resistant to changes in the called module than if we had used the CRTRPGMOD/CRTPGM combo.
The end result will be a connection that is very fast and uses very little resources as it is processed. And that is why “time travel is not only possible but has probably already happened.” Or in this case, that is why “service programs are a big deal.”
The Service Program: DWS0260SRV
So far we have started with the calling program, but this time let’s start with our old friend the product number validation program and set it up as a sub-procedure in a service program. Are you listening to me? This program is not the program that calls the service program. The program below is the service program. Got that? You always create the service program first, although many times it already exists when you go to use it. OK, good, here we go. Oh, and I am naming this DWS0260SRV. We will need that name when we go to bind everything together.
H********************************************************
H* DWS0260SRV – Product Master Service Program
H********************************************************
H NOMAIN
H********************************************************
F********************************************************
F* Product Master
FMSPMP100 UF E K DISK PREFIX(PM_)
F********************************************************
P********************************************************
P**** Beginning of Procedure VAL_PRDNO ****************
P********************************************************
P VAL_PRDNO B EXPORT
P*
D********************************************************
D*/COPY DSHIREY/QCPYLESRC,DWS0260SRV
D*
D VAL_PRDNO PR
D PRDNO 15
D MSG 60
D ERROR_FLAG 1
D VAL_PRDNO PI
D PRDNO 15
D MSG 60
D ERROR_FLAG 1
D*
/FREE
//Verify Product Number
MSG = *BLANKS;
CHAIN (PRDNO) MSPMP100;
IF NOT %FOUND;
MSG = 'R U KIDDING? PRODUCT NUMBER +
NOT VALID (DWS0260SRV.01)';
ERROR_FLAG = 'Y';
ENDIF;
CLOSE MSPMP100;
/END-FREE
P VAL_PRDNO E
P********************************************************
Not really too bad. That’s all there is to a service program with a single sub-procedure. Of course, most self-respecting service programs will contain more than one sub-procedure, sometimes many more than one, but this will give you the idea. In reality, for all their street rep, service programs are really just a sheath, something that goes around a group of procedures.
Where should you keep your service program source? Well, you could create another source file to put your service programs into, but that’s up to you. They don’t have a special source type; they are just RPGLE like everything else. The object type is different, of course: it’s *SRVPGM instead of plain old *PGM. Just don’t put them in QSRVSRC. You will want to keep this available for the binding language source that we will talk about later (as in chapter 20).
Now let’s play.
H********************************************************
H* DWS0260SRV – Product Master Service Program
H*******************************************************
H NOMAIN
H*******************************************************
We start with another H-spec, and this one is required. What it basically says is that there will be no main logic to this program. This wipes out the RPG cycle, but who cares. It clears the way to have a program that consists of just (gasp) sub-procedures.
Remember before when we had the sub-procedure embedded in a program, we also had mainline logic. This is not the way it works in service programs: all you have is sub-procedures, and to make this legal, you need the NOMAIN spec.
Service programs are not created through the CRTBNDRPG command and cannot be created in an OPM environment. We will look at just how we create the service program in a minute, but for now, just know that because of this we do not need the DFTACTGRP H-spec.
F********************************************************
F* Product Master
FMSPMP100 UF E K DISK PREFIX(PM_)
F********************************************************
Next, we have our F-spec for the file we will use in the sub-procedure.
If we were at a newer level of the operating system (V6.1 or above), we could just put this in the sub-procedure that is coming up, but with antiquated 5.4 we don’t have that option. Not a big deal. The advantage of putting it in the sub-procedure is that if you have several files defined in your service program, then you don’t open them all when the service program starts. You will wait till you get into the sub-procedure and then open just the ones required for that.
P********************************************************
P**** Beginning of Procedure VAL_PRDNO ****************
P********************************************************
P VAL_PRDNO B EXPORT
P*
Then, without so much as a “have an apple,” we start the first sub-procedure by having the B P-spec. Remember that everything that comes after this is part of the sub-procedure. If the service program contained several procedures (which it almost always will), then you list them one right after the other. And remember that this is not a D-spec. It is a new type of spec, the P-spec, that defines when a sub-procedure begins and ends.
We are not going to talk about exporting now (that will happen when we talk about binding language in chapter 20), but we need to put EXPORT as a keyword on the beginning P-spec. Trust me. This is very important, and nothing will work in this example if you don’t do that. You’ll thank me later.
D*******************************************************
D*/COPY DSHIREY/QCPYLESRC,DWS0260SRV
D*
D VAL_PRDNO PR
D PRDNO 15
D MSG 60
D ERROR_FLAG 1
D VAL_PRDNO PI
D PRDNO 15
D MSG 60
D ERROR_FLAG 1
D*
Next we have D-specs that define the prototype.
Technically you can put the PR up above in the global part of the program (that is, before the first P-spec), but I see no reason to split them up, so I put it in the sub-procedure with the PI.
The PR and the PI must be identical, and the name on the PR D-spec must be the same as the name on the associated PI D-spec. So, it’s not like you can get away with one global PR, even if the subfields on the different PIs were identical. But it’s up to you where you put it. I don’t really care.
Often, people will use a copybook to bring the PR or PI in, and it is up to you if you like that kind of action. In fact, that is probably the default with all the cool kids. Not the first time I have bucked the trend. I have included a commented-out Copy statement, just to show you how it would replace the PR D-spec. If you use the Copy statement, then you don’t need the D-spec.
/FREE
//Verify Product Number
MSG = *BLANKS;
CHAIN (PRDNO) MSPMP100;
IF NOT %FOUND;
MSG = 'R U KIDDING? PRODUCT NUMBER NOT +
VALID (DWS0260SRV.01)';
ERROR_FLAG = 'Y';
ENDIF;
CLOSE MSPMP100;
/END-FREE
This is followed by the logic statements, which you should be quite familiar with now. The key for the read was passed in via the prototype, and the message is set in this code.
Also, please note that there is no *INLR logic. No need for it. There is no main procedure in the service program. I’m serious. Can you dig it?
P VAL_PRDNO E
P********************************************************
Finally, here is the end P-spec to close the sub-procedure. And that’s it. If there were more sub-procedures here, then we would start with another B P-spec. If this were the end of the service program, which it is in this case, then this would be the end. There is no *INLR or anything else. We don’t need the *INLR because there is no main procedure to end. Neat, eh?
Compiling the Service Program
Now before we go any further, that is, before we create the program that calls the service program, we need to look at how to compile the service program because it’s just a tad different from what we did before.
And this must be done before we compile the calling program, so we might as well get it out of the way now. The reason for this is that when we compile the calling program, we want to actually bind the object for the service program into the object for the calling program. So, the service program object must exist before we compile the calling program.
To create the service program object, issue the following two commands:
CRTRPGMOD MODULE(MyLib/'service program') SRCFILE(MyLib/QRPGLESRC)
CRTSRVPGM SRVPGM(MyLib/'service program') EXPORT(*ALL)
Note: specifying EXPORT(*ALL) is important, and it is not the default. There are other values for the EXPORT parm, but right now just use *ALL and don’t worry about it. We will talk about it later. And yes, that was a threat.
And that’s all you need to do to create your service program object. I know it’s two steps and doesn’t use option 14 (for the PDM crowd), but heck fire, boy, it’s not like you are going to be doing this every day. Once you get them set, you probably won’t touch them. Maybe. Actually, it’s pretty simple, eh? A lot of it is just what we did when we created the program with the sub-procedure. You see, it all fits together once you get your head out ... well, once you start getting into it.
Debugging? If you want to debug a service program, you have to set the DBG parm in the CRTRPGMOD command to *SOURCE when you do the CRTRPGMOD for the service program. You do not, however, do a STRDBG command for the service program module. Instead, you can do it for the program that calls the service program and then use F22 instead of F10 to step into the service program.
LATEST COMMENTS
MC Press Online