You are on page 1of 8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)

Written by Bruce Vining


Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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

1/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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;

2/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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')

3/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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)

4/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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

5/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

:'*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

6/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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

7/8

The API Corner: Automating Recovery (or Keeping the Help Desk Out of the Loop)
Written by Bruce Vining
Wednesday, 21 July 2010 00:00 - Last Updated Wednesday, 21 July 2010 00:00

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 bvining@brucevining.com
. I'll see what I can do about answering your burning questions in future columns.

8/8

You might also like