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