Service Programs
Service programs offer a method of leveraging common code or functions.
Service Programs vs. Modules
In general modules and service programs are object designed to share common code from application to application. Sharing a procedure means the code may be maintained in a single source member and not having to maintain dozens (or hundreds) of programs that use the same code. There is a performance benefit to moving to modules and service programs instead of using subprograms. Typically, a dynamic call (as to a subprogram) is slower than a call to a bound procedure. (Less handshaking between the objects to establish the parameters exchanged means faster execution.)
Though modules are convient to maximize code re-use, a better idea is to bind modules into a service program. Though I have created both modules and programs, there is less payback in using modules as opposed to creating a service program. What’s the difference? Primarily the difference lies in the binding.
Modules are ILE building blocks—they are not executable. In order to use a module, it must be bound to a program or a service program. When a program binds a module, the process is a bind by copy event. Once the program has been created, the module may be deleted—it is no longer required, because a copy of the instructions have been added into the program object. This is essentially what happens using the CRTBNDRPG command, whether the program is bound to another module, or not. A module of the program is created in QTEMP, a program is created from the module and when the job ends the module, created in QTEMP is destroyed along with the other contents of QTEMP.
The logical question is; why create a service program instead of a module? It is a good question and the answer requires a good understanding of ILE concepts. If a module is bound to a program, or number of programs, you must keep track of all the programs that bind to the module. If the module changes, in order for programs created with the module to recognize the changes in the code, all of the programs must be recreated (re-compiled, or re-bound--remember: bind by copy). By having the module bound to a service program instead, (a bind by reference), the module source could be changed, re-created, and the service program could be updated, without having to re-compile 100 separate programs. (If the signature of the procedure did not change, or if the previous signature is still supported, there is no need for the program to change.)
The answer to the question why use a service program, is that it provides the same code re-use with a greater degree of flexibility and ease of maintenance--an often overlooked advantage to using a service program. In terms of change management, the number of source file members checked out, changed, compiled, and tested, can be reduced to 1, when utilizing service programs.
There are some performance considerations when using a service program. The first reference to a bound procedure loads the entire service program to a job. However, after that point, invoking procedures from the service program are much faster than a dynamic call. The service program stays resident within the activation group so if 5, or 6 programs use the same service program, there is no need to perform the load operation for each program—it is a one-time only penalty.
Binding Directories
The more modules or service programs a binding directory contains, the longer it may take to bind the programs. Therefore,
you should include only the necessary modules or service programs in your binding directory. Binding directories can reduce
program size because you do not specify modules or service programs that do not get used.
During binding, processing happens in this order:
1. All of the modules specified on the MODULE parameter are examined. The binder determines the list of symbols imported
and exported by the object. After being examined, modules are bound, in the order listed, into the program being created.
2. All of the service programs on the BNDSRVPGM parameter are examined in the order listed. The service programs are bound
only if needed to resolve an import.
3. All of the binding directories on the BNDDIR parameter are processed in the order listed. All the objects listed in these
binding directories are examined in the order listed, but they are bound only if needed to resolve imports. Duplicate
entries in binding directories are silently ignored.
4. Each module has a list of reference system objects. This list is comprised of binding directories. The reference system
objects from bound modules are processed sequentially—all the reference system objects from the first module are processed
first, then the objects from the second module, etc. This processing continues only as long as unresolved imports exist.
Though it may be convenient to put all modules and service programs into a common binding directory, it works against fast, efficient, object creation. A better methodology would be to create binding directories for specific functional areas, keeping the directory entries to a minimum and relevant to the applications bound to the directory.
Record Lock Service Program
STRPGMEXP PGMLVL(*CURRENT) /********************************************************************/ /* *MODULE SC0930RM SCROY 01/19/10 14:18:20 */ /********************************************************************/ EXPORT SYMBOL("DSPLCKMSG") EXPORT SYMBOL("RCDLCKMGR") EXPORT SYMBOL("SNDLCKMSG") ENDPGMEX
CRTSRVPGM SRVPGM(SCROY/SC0930SV) MODULE(SC0930RM) TEXT('Record lock services') BNDDIR(SC0930_BD) ACTGRP(QILE)
H/TITLE ** Record Lock error handlers ** H DEBUG(*YES) H OPTION(*SRCSTMT : *NODEBUGIO) H nomain ********************************************************************* * PROGRAM NAME - SC0930RM * * * * FUNCTION - This member contains modules that may be used as * * record lock monitor for interactive and batch * * programs. Interactively, if will display a * * pop-up window to the user, with lock information. * * In batch mode, the program will send a message * * to the system operator message queue. * * * * PROGRAMMER - STEVE CROY * ********************************************************************* D CF E DS EXTNAME(SCKEYSPF) qualified D PGMDS ESDS EXTNAME(SCPSTSPf) D DSPDS E DS EXTNAME(SCDSPFPF) /copy qrpglesrc,sc0000_pr /copy qrpglesrc,sc0930_pr D RCVM0100 DS qualified D BytesRtn 10I 0 D BytesAvail 10I 0 D MsgSev 10I 0 D MsgID 7A D MsgType 2A D MsgKey 4A D 7A D CCSID_status 10I 0 D CCSID 10I 0 D MsgDtaLen 10I 0 D MsgDtaAvail 10I 0 D MsgDta 8000A D ErrorCode ds qualified D BytesProv 10I 0 inz(0) D BytesAvail 10I 0 inz(0) *--------------------------------------------------------------------- * Define constants *--------------------------------------------------------------------- D Yes C CONST('Y') D No C CONST('N') ********************************************************************* * Procedure - rcdLckMgr * * * * FUNCTION - This procedure is a general purpose record lock * * manager. This may be used to route the record lock * * messasge to the batch record lock procedure or the * * interactive record lock procedure. * * * * PARAMTERS - Type NAME LEN ATR Desc * * Input -CPFmsg 80 a CPF error message * * -callingPgm 10 a calling program * * -fileName 8 a file to be locked * * -exitAllowed 1 a exit flag Y/N * * I/O -*NONE * * OUTPUT -byPassLock n bypass update '0'/'1' * * * * PROGRAMMER - STEVE CROY 01/19/2010 * ********************************************************************* ********************************************************************* * MODIFICATION LOG * * * * DATE PROGRAMMER DESCRIPTION * * * ********************************************************************* P rcdLckMgr B export D rcdLckMgr PI n D CPFMsg 80 D callingPgm 10 D filename 8 D ExitAllowed 1 *--------------------------------------------------------------------- * START of work fields *--------------------------------------------------------------------- D JobAttr S 1A D byPass S n *--------------------------------------------------------------------- * END of work fields *-------------------------------------------------------------------- /free JobAttr = GetJobAtr() ; IF JobAttr = 'I' ; byPass = dsplckMsg(cpfmsg:callingPgm:fileName:exitAllowed) ; ELSE ; byPass = sndlckMsg(cpfmsg:exitAllowed) ; ENDIF ; RETURN byPass ; /end-Free P rcdLckMgr E ********************************************************************* * Procedure - SNDLCKMSG * * * * FUNCTION - This procedure is used to send a record lock * * message to the QSYSOPR message queue. The proc * * will wait for a reply then time out and return * * to the calling program. * * * * PARAMTERS - Type NAME LEN ATR Desc * * Input -CPFmsg 80 a CPF error message * * -exitAllowed 1 a exit flag Y/N * * I/O -*NONE * * OUTPUT -byPassLock n bypass update '0'/'1' * * * * PROGRAMMER - STEVE CROY 01/19/2010 * ********************************************************************* ********************************************************************* * MODIFICATION LOG * * * * DATE PROGRAMMER DESCRIPTION * * * ********************************************************************* P sndLckMsg B export D sndLckMsg PI n D CPFmsg 80 D exitAllowed 1 *--------------------------------------------------------------------- * START of work fields *--------------------------------------------------------------------- D action1 s 25A inz('Reply R, to retry lock') D action2 s 30A inz('reply C, cancel (by-pass) lock') D NbrSecs S 15 5 inz(180) D Message s 256A varying D MsgKey s 4A D MsgQ s 20A dim(1) inz('*SYSOPR') D Reply s 100A D bypass s n *--------------------------------------------------------------------- * END of work fields *--------------------------------------------------------------------- /free //---------------------------------------------------- // Create a message to send to the system operator. // Send an *INQ message to QSYSOPR asking for a reply. //---------------------------------------------------- Reply = 'R' ; Message = %trim(cpfmsg) + ' ' + %trim(action1) ; IF exitAllowed = Yes ; Message = %trim(Message) + ', ' + %trim(action2) + '.' ; ELSE ; Message = %trim(Message) + '.' ; ENDIF ; QMHSNDM( *blanks : *blanks : Message : %len(Message) : '*INQ' : MsgQ : %elem(MsgQ) : '*PGMQ' : MsgKey : ErrorCode ) ; //---------------------------------------------------- // Wait up to 5 minutes (300 seconds) for a reply to the // above message. If you change the value of 300 below to // a value of -1, it will wait indefinitely. //---------------------------------------------------- QMHRCVPM( RCVM0100 : %size(RCVM0100) : 'RCVM0100' : '*' : 0 : '*RPY' : MsgKey : 300 : '*REMOVE' : ErrorCode ) ; //---------------------------------------------------- // The "Reply" Variable contains the operator's reply // If the reply was C (cancel) by-pass update //---------------------------------------------------- IF RCVM0100.BytesRtn > 0 AND RCVM0100.MsgDta <> *blank ; Reply = %subst(RCVM0100.MsgDta: 1: RCVM0100.MsgDtaLen) ; ENDIF ; IF Reply = *blank ; Reply = 'R' ; ENDIF ; Reply = UpperCase(Reply:%size(Reply)) ; IF %subst(Reply:1:1) = 'C' ; byPass = *ON ; ELSE ; byPass = *OFF ; ENDIF ; RETURN byPass ; /END-free P sndLckMsg E ********************************************************************* * Procedure - DSPLCKMSG * * * * FUNCTION - This procedure is used to display a record lock * * message to an interactive application. The message * * is displayed and the user may send a cancel, or an * * an attempt a retry indicator to the calling program. * * * * PARAMTERS - Type NAME LEN ATR Desc * * Input -CPFmsg 80 a CPF error message * * -thisPgm 10 a calling program * * -fileName 8 a file to be locked * * -exitAllowed 1 a exit flag Y/N * * I/O -*NONE * * OUTPUT -byPassLock n bypass update '0'/'1' * * * * PROGRAMMER - STEVE CROY 01/19/2010 * ********************************************************************* ********************************************************************* * MODIFICATION LOG * * * * DATE PROGRAMMER DESCRIPTION * * * ********************************************************************* P dspLckMsg B export D dspLckMsg PI n D CPFmessage 80 D thisPgm 10 D filename 8 D exitAllowed 1 *--------------------------------------------------------------------- * START of work fields *--------------------------------------------------------------------- D byPassLock s n D msgText s 255a D msgTitle s 27a D rtnKey s 3a D endLoop s n *--------------------------------------------------------------------- * END of work fields *--------------------------------------------------------------------- /free //---------------------------------------------------- // Format the message //---------------------------------------------------- msgTitle = 'Program: ' + thisPgm ; msgText = %trim(CPFmessage) + ' ' + 'File ' + %trim(fileName) + ' is locked for update.' ; // Determine if user allowed to use ESCAPE IF exitAllowed = Yes ; msgText = %trim(CPFmessage) + ' F12 to bypass update,' + ' or ENTER to retry the record.' ; ELSE ; msgText = %trim(CPFmessage) + ' Press ENTER to attempt' + ' to get the record again.' ; ENDIF ; //---------------------------------------------------- // Display record lock message //---------------------------------------------------- bypassLock = *OFF ; DisplayMessage(msgText:msgTitle) ; rtnKey = getJobKey() ; IF (rtnKey = 'F3 ' or rtnKey = 'F12') and exitAllowed = Yes ; bypassLock = *ON ; ENDIF ; //---------------------------------------------------- // Reset job keys and return bypass //---------------------------------------------------- setJobKey() ; RETURN bypassLock ; /END-free P dspLckMsg E
Bindiding Directory
The binding directory named on the CRTSRVPGM command incudes another service program. Binding entries may reference modules or other service programs. Procedures from the service program SC0065SV will be available to the record lock service program by naming the (SC0930_bd) binding directory on the CRTSRVPGM command.
Binding Directory: SC0930_BD Library: SCROY Type options, press Enter. 1=Add 4=Remove -------Creation------- Opt Object Type Library Activation Date Time SC0065SV *SRVPGM *LIBL *IMMED 11/11/11 08:28:59