Recover from, and remove, error messages when using the QCMDEXC API to run CL commands.
In last month's column, "The API Corner: Running CL Commands from RPG," you saw that an RPG program can easily run a CL command using any one of the three APIs: QCMDEXC, system, or QCAPCMD. Each of the APIs also provides the ability for the RPG program to detect when an error is encountered in the running of the CL command. The RPG programs found in the previous column, however, simply logged the fact that an error was encountered and then ended.
Just as a CL program running a CL command can use the Monitor Message (MONMSG) command to detect error conditions and then recover from the error, so too can your RPG programs. Today's column will look at one way that you can provide this type of error recovery.
Determining That an Error Has Occurred
When your RPG program calls an API (or any program for that matter), you have a choice in how you might determine that an error has occurred. The programs provided in the earlier article utilized the MONITOR feature of RPG introduced in V5R1. Other methods to detect an error in running a CL command include these:
- You can specify an error indicator (ER) with the CALL operation:
c call 'QCMDEXC' 99
c parm Cmd
c parm Len
c 99'Error' dsply
c n99'OK' dsply
- You can specify the 'E' operation code extender with the CALL operation:
c call(e) 'QCMDEXC'
c parm Cmd
c parm Len
c if %error
c 'Error' dsply
c else
c 'OK' dsply
c endif
or
/free
callp(e) RunCmd(Cmd :Len);
if %error;
dsply 'Error';
else;
dsply 'OK';
endif;
/end-free
- You can specify a program exception/error subroutine for the program:
/free
callp RunCmd(Cmd :Len);
dsply 'OK';
// various processing
begsr *PSSR;
dsply 'Error';
*inlr = *on;
return;
endsr;
/end-free
As the MONITOR support provides one coding style that can replace indicators, 'E' operation code extenders, and *PSSR subroutines, my preference is to use MONITOR groups exclusively, which is reflected in the following program samples. You can, however, replace the use of MONITOR with the above programming styles if you desire. The key point is recognizing that an error has occurred in the calling of the API. There are additional reasons for my preferring MONITOR over the other approaches, but these reasons could easily be an article in their own right.
Determining What Error Has Occurred
Having determined that an error condition exists, we need to determine specifically which of several possible error situations the program is in as program recovery actions will vary based on the actual error encountered.
In the previous article, we used the program status data structure (PSDS) to determine the name of the user profile running the program. You can also use the PSDS to access the exception ID, or message ID, of the escape message causing an error condition. This information is available in positions 40 through 46 of the PSDS and can be defined as shown below.
dPSDS sds 429 qualified
d MsgID 40 46
d JobUsr 254 263
In the error detection examples shown above, you could replace the "DSPLY 'Error'" operations with "DSPLY PSDS.MsgID" to display the error message ID. Having access to the underlying cause of the CL command not running successfully, we can enhance the CMDEXC0 program as shown here.
h dftactgrp(*no)
dRunCmd pr extpgm('QCMDEXC')
d Cmd 512 const options(*varsize)
d LenCmd 15p 5 const
d IGC 3 const options(*nopass)
dSndMsg pr extpgm('QSYS/QMHSNDPM')
d MsgID 7 const
d QualMsgF 20 const
d MsgDta 65535 const options(*varsize)
d LenMsgDta 10i 0 const
d MsgType 10 const
d CSE 65535 const options(*varsize)
d CSECtr 10i 0 const
d MsgKey 4
d QUSEC likeds(QUSEC)
d LenCSE 10i 0 const options(*nopass)
d CSEQual 20 const options(*nopass)
d DSPWaitTime 10i 0 const options(*nopass)
d CSEType 10 const options(*nopass)
d CCSID 10i 0 const options(*nopass)
dRmvMsg pr extpgm('QSYS/QMHRMVPM')
d CallStackEntry 65535 const options(*varsize)
d CallStackCntr 10i 0 const
d MsgKey 4 const
d MsgType 10 const
d QUSEC likeds(QUSEC)
d LenCSE 10i 0 const options(*nopass)
d CSEQual 20 const options(*nopass)
d RmvUnhExp 10 const options(*nopass)
d CSEType 10 const options(*nopass)
d AlwDftRpy 10 const options(*nopass)
/copy qsysinc/qrpglesrc,qusec
dPSDS sds 429 qualified
d MsgID 40 46
d JobUsr 254 263
dCmd s 512
dMsgKey s 4
/free
QUSBPRV = 0;
monitor;
Cmd = 'CLRPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';
RunCmd(Cmd :%len(%trim(Cmd)));
on-error 00202;
select;
when PSDS.MsgID = 'CPF3141';
monitor;
RmvMsg('*' :0 :' ' :'*ALL' :QUSEC);
Cmd = 'ADDPFM FILE(SOMEFILE) MBR(' + PSDS.JobUsr + ')';
RunCmd(Cmd :%len(%trim(Cmd)));
on-error;
SndMsg('ESC0001' :'OURMSGS *LIBL' :' ' :0
:'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);
endmon;
when PSDS.MsgID = 'CPF3142';
monitor;
RmvMsg('*' :0 :' ' :'*ALL' :QUSEC);
Cmd = 'CRTPF FILE(SOMEFILE) MBR(' + PSDS.JobUsr +
') MAXMBRS(*NOMAX) OPTION(*NOSRC *NOLIST)';
RunCmd(Cmd :%len(%trim(Cmd)));
on-error;
SndMsg('ESC0001' :'OURMSGS *LIBL' :' ' :0
:'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);
endmon;
other;
SndMsg('ESC0001' :'OURMSGS *LIBL' :' ' :0
:'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);
endsl;
on-error;
SndMsg('ESC0001' :'OURMSGS *LIBL' :' ' :0
:'*ESCAPE' :'*PGMBDY' :1 :MsgKey :QUSEC);
endmon;
// Do further processing
*inlr = *on;
return;
/end-free
The actual changes to CMDEXC0 are fairly small, but we now have a program that can recover from some error situations without needing to call in the help desk. The program changes include the following:
Remove Program Messages (QMHRMVPM) API
Prototyping of the Remove Program Messages (QMHRMVPM) API, which is documented here, has been added. Strictly speaking, we do not need to use this API. But as you will shortly see, the program is now handling error conditions such as CPF3142 – File not found and CPF3141 – Member not found. My preference is to only leave messages in the job log that indicate a problem, not messages that the program has anticipated and handled. For this reason, the program uses the Remove Program Messages API to selectively remove handled exceptions from the job log.
MsgID
We added MsgID to the PSDS to determine why the call to QCMDEXC failed when running the CLRPFM command.
ON-ERROR Check for Status Value of 00202
The program now includes an ON-ERROR check for a status value of 00202 - Called program or procedure failed.
If the failure is related to message ID CPF3141 (Member not found), the program removes the CPF3141 message from the job log using the QMHRMVPM API and adds the member to the SOMEFILE file using the QCMDEXC API to run the CL command Add Physical File Member (ADDPFM). If a failure occurs on the ADDPFM, the program ends by sending user escape message ESC0001 created in the previous article (an unexpected error condition and instructions to see the job log for additional information). In other words, the program gives up in this situation. If the ADDPFM is successful, the program resumes operation following the ENDMON operation associated with the initial MONITOR
If the failure is related to message ID CPF3142 (File not found), the program removes the CPF3142 message from the job log using the QMHRMVPM API and creates the file SOMEFILE file, with the necessary member, using the QCMDEXC API to run the CL command Create Physical File Member (CRTPF). If a failure occurs on the CRTPF, the program ends by again sending user escape message ESC0001. If the CRTPF is successful, the program resumes operation following the ENDMON operation associated with the initial MONITOR. If the failure is related to any other message ID, the program sends user escape message ESC0001.
Done!
And that's it! The RPG program is now effectively monitoring for error conditions CPF3141 and CPF3142, in a manner quite similar to how a CL program might use MONMSG to handle these types of situations, and also removing error messages from the job log that represent handled "errors." Error messages remaining in the job log are truly error conditions.
In the next column, we will look at how we can provide similar error-handling when using the QCAPCMD API. You'll also see how the QCAPCMD API can even further streamline error recovery and why I prefer it over the QCMDEXC API. Note that I am intentionally deferring further discussion of the API system. I believe that first understanding what the QCMDEXC and QCAPCMD APIs can do will better enable you to see some of the limitations found with the API system.
Questions?
In the meantime, if you have any API questions, send them to me at
LATEST COMMENTS
MC Press Online