RPG Exception Handling
Trapping errors in code requires understand the hierarchy of exception handling.
Fixed-format RPG code tends to be predictable. In fact without the use of built-in-functions (BIF’s), straight-line RPG code tends to be very predictable, and consistent. With additional op codes, and BIF’s, predictability is not quite as certain and consistency becomes a question of point-of-view instead of immutable fact.
0042.00 C ERPARM PLIST 0043.00 C PARM MSGTXT 0044.00 C PARM PRGNAM 0045.00 C PARM ERREXT 0046.00 C PARM ERRCMD 0047.00 C PARM ERRNAM 0049.00 C DOU *IN12 = *OFF 0050.00 C 1 CHAIN MYNAMES 1112 0051.00 *--------------------------------------------------------------------- 0052.00 * 11 on, no record found 0053.00 * 12 on, record locked, the MSGID will be 'CPF5027' 0054.00 * 11 + 12 off, record found and locked by this program 0055.00 * If error in getting record, call error handling program 0056.00 * If the user is allowed to bypass the update, set the 0057.00 * ERREXT to 'Y' to allow an exit to the loop. 0058.00 *--------------------------------------------------------------------- 0059.00 C IF *IN12 = *ON 0060.00 C MOVE 'Y' ERREXT 0061.00 C MOVE ERRFIL ERRNAM 0062.00 C CALL 'SWRCLKRI' ERPARM 0063.00 *-------------------------------------------------------------------- 0064.00 * If user requested EXIT, do not retry access to record and 0065.00 * record again. 0066.00 *-------------------------------------------------------------------- 0067.00 C IF ERRCMD = #TRUE 0068.00 C MOVE *ON #ERR 0069.00 C EVAL *IN12 = *OFF 0070.00 C EVAL *IN11 = *ON 0071.00 C ENDIF 0072.00 C ENDIF 0073.00 C ENDDO 0075.00 C IF *IN11 = *OFF 0076.00 C MOVEL #YES #LOCK 0077.00 C ELSE 0078.00 C MOVEL #NO #LOCK 0079.00 C ENDIF Fig. 1
Consider the code sample in Fig. 1. This is a block of code designed to trap a potential record lock situation. Yes, it is rather simplistic, but it does serve the purpose. A chain (in this case using RRN, not a key) to MYNAMES has been conditioned with indicators 11, to signal a not found condition, and indicator 12 to signal a file error. The DO logic loop will execute, until *IN12 is *OFF signaling no record lock (or file error) has been found on the requested record. If there is a record lock encountered, the code will display a message to the effect that the record is in use by another job. The text for the message comes from the program status data structure. It provides a fairly easy method of managing a record lock condition, putting a remedy in the hands of the user.
............................................ : RCLKR001 Record Access Error Screen . . : : : : A record is in use by another job on the : : system. This denies access to a critical : : data file your program needs. : : : : The job which controls the above file: : : Record 1 in use by job 477478/SCROY/QPAD : : EV0028. : : : : Program requesting data: RPGLOCK2 : : File name..............: MYNAMES : : : : Press ENTER to attempt to get the record : : again. If the problem persists, call the : : user that has the record you need to : : determine when you can get access. : : : : F3=Exit and bypass record update. : :..........................................: Fig. 1a
Re-writing the simple logic loop above in free-format RPG poses a question of how to produce the same results. There are several different options available when considering BIF’s and new elements such as MONITOR are used. Assuming that the subprogram is to remain the same (in this case, it does). The inclination to assume the program status data structure contains the same information when the file operation is the same, regardless of the program defined error handling, is a mistake. In the sample code below, Fig. 2, the logic loop has been re-written. Since free-format RPG does not support resulting indicators, a MONITOR statement has been inserted in the loop. Note in this case the monitor has a simple generic error trap, *FILE, which implicitly instructs the ON-ERROR clause to react to any file error. Notice also, the CHAIN op code does not use an operation extender (E).
0011.00 D DisplayLock PR Extpgm(‘SWRCLKRI’) 0012.00 D CPFMessage 80 0013.00 D CallingPgm 10 0014.00 D ExitAllowed 1 0015.00 D BypassLock 1 0016.00 D FileLocked 8 0055.00 DOU NoErrorFound 0056.00 MONITOR 0057.00 CHAIN 1 MYNAMES 0058.00 IF %found(MYNAMES) 0059.00 RecordLocked = *ON 0060.00 NoErrorFound = *ON 0061.00 ELSE 0062.00 RecordLocked = *OFF 0063.00 NoErrorFound = *ON 0064.00 ENDIF 0065.00 ON-ERROR *FILE 0066.00 NoErrorFound = *OFF 0067.00 CPFMessage = %trim(WQMSGD) 0068.00 ExitAllowed= #YES 0069.00 CallingPgm = WQPGMN 0070.00 FileLocked = WQLSTF 0071.00 DisplayLock( CPFMessage 0072.00 CallingPgm 0073.00 ExitAllowed 0074.00 BypassLock 0075.00 FileLocked ) 0076.00 NoErrorFound = BypassLock 0077.00 ENDMON 0078.00 ENDDO Fig. 2
If the operation extender is used on the CHAIN op code, CHAIN(E), the error will be intercepted prior to the MONITOR and the ON-ERROR clause has nothing to react to, the only condition to report will be based on the BIF %FOUND test, the ON-ERROR clause would not be invoked. So, leaving aside the issue of the operation extender the expectation is that the code will react in the same manner as the fixed-format code. But that does not hold true. The monitor operation takes place at a different level than the simple test of the indicator in the sample of Fig. 1. Though monitoring the same file, and calling the same subprogram, and using the program status data structure a completely different message is displayed. The monitor block is relaying the text of RNX1218, not the text of CPF5027. Look at the pop-up record access error screen below.
............................................ : RCLKR001 Record Access Error Screen . . : : : : A record is in use by another job on the : : system. This denies access to a critical : : data file your program needs. : : : : The job which controls the above file: : : Unable to allocate a record in file MYNA : : MES. : : : : Program requesting data: RPGLOCKF3 : : File name…...........: MYNAMES : : : : Press ENTER to attempt to get the record : : again. If the problem persists, call the : : user that has the record you need to : : determine when you can get access. : : : : F3=Exit and bypass record update. : :..........................................: Fig. 2a
Both processes of course accomplish the same function; that is to put the error message and a remedy in the hands of the application user. But though they seem to be functionally equivalent, they are not identical. In order to mimic the original code and produce the same resulting output, a different approach to the code is necessary. The monitor block needs to be removed, and the operation extender is added to the code. The code snippet in Fig. 3 illustrates the different technique to handling the record lock condition. This code comes closer to approximating the original fixed-format code.
0055.00 DOU NoErrorFound 0056.00 CHAIN(e) 1 MYNAMES 0057.00 If %status = 1218 0058.00 NoErrorFound = *OFF 0059.00 CPFMessage = %trim(WQMSGD) 0060.00 ExitAllowed= #YES 0061.00 CallingPgm = WQPGMN 0062.00 FileLocked = WQLSTF 0063.00 DisplayLock( CPFMessage 0064.00 CallingPgm 0065.00 ExitAllowed 0066.00 BypassLock 0067.00 FileLocked ) 0068.00 NoErrorFound = BypassLock 0069.00 ELSE 0070.00 IF %found(MYNAMES) 0071.00 RecordLocked = *ON 0072.00 NoErrorFound = *ON 0073.00 ELSE 0074.00 RecordLocked = *OFF 0075.00 NoErrorFound = *ON 0076.00 ENDIF 0077.00 ENDIF 0078.00 ENDDO Fig. 3
In this instance, the BIF %STATUS is used to examine the file status code after the requested file operation. The status 01218 indicates an attempt to lock a record already held. The resulting display (Fig. 3a) matches the result of the original fixed-format code example.
........................................... : RCLKR001 Record Access Error Screen . . : : : : A record is in use by another job on the : : system. This denies access to a critical : : data file your program needs. : : : : The job which controls the above file: : : Record 1 in use by job 477478/SCROY/QPAD : : EV0028. : : : : Program requesting data: RPGLOCKF2 : : File name…...........: MYNAMES : : : : Press ENTER to attempt to get the record : : again. If the problem persists, call the : : user that has the record you need to : : determine when you can get access. : : : : F3=Exit and bypass record update. : :..........................................: Fig. 3a
The resulting display (Fig. 3a) now shows the same message text of the original fixed-format (Fig. 1a) display, the message data from CPF5027. An additional consideration for managing errors within an application—there is a specific hierarchy to the error management process. The given priority of the message handling functions is listed under the heading of hierarchy.
Status Codes
Understanding the %STATUS BIF improves exception management in programs. The BIF returns the most recent value set for the program or file status. %STATUS is set whenever the program status or any file status changes, generally when an error occurs.
A DSPSIZ(24 80 *DS3) A INVITE A PRINT A INDARA . . Fig. 1 (DDS) . . . FSWRCLKDI CF E WORKSTN USROPN F MAXDEV(*FILE) F INFDS(DSPDS) . . . DOU Function = 'EXIT' WRITE RCLKR002 READ(E)SWRCLKDI IF %ERROR SELECT WHEN %STATUS = 01331 Function = 'EXIT' OTHER REPORTERROR(%STATUS) ENDSL ELSE PROCESSREQUEST() ENDIF ENDDO . . Fig. 2 (RPG)
It is realively easy to provide a display timeout in RPG; treat the workstation file as if the file was a communications file.
When the display file is created, set the wait record time to the desired timeout length:
Maximum file wait time . . . . . . . . . . : WAITFILE *IMMED
Maximum record wait time . . . . . . . . . : WAITRCD 30
In your application program, instead of using the EXFMT op code use the Write/Read technique of managing the device file. Write the record format, the program will wait for a response from the user. If the user has not responded within the specified time period then a timeout will occur and the program will read the file as if a response had been entered. You can use %ERROR with the %STATUS BIF to capture and manage the file exception, or timeout logic and programmatically determine what action to perform. In the snippet above (Fig. 2), the file error (a timeout) will cause the program to exit the display loop (exit the application).