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

  • 'E' Operation extender (or Error Indicators)
  • Monitor groups
  • File Exception error subroutine
  • ILE Condition handler
  • Program Exception Error Subroutine (*PSSR)
  • RPG default error handler

The operation extender is meant to allow the exception to be managed by BIF operations such as %ERROR, or %STATUS. %ERROR returns a Boolean true, or false. The program can be coded to manage the exception based on testing the BIF condition. Whereas %STATUS returns the program status, and managing the exception depends on code reacting to the status returned.ON-ERROR groups can be used to handle exceptions for all statements processed within a MONITOR block. If an error occurs when a statement is processed, program control passes to the appropriate ON-ERROR group.Within the ILE environment, writing a separate module to manage exceptions provides a greater degree of flexibility than the MONITOR block. However, the principal is the same; creating an instruction set which will receive control when an exception occurs. *PSSR operations are widely used and may be used to monitor and trap program and file errors. The following return-point operands can be specified on the ENDSR operation for a *PSSR subroutine


    *DETL. Continue at the beginning of detail output lines
    *GETIN. Input Record Routine
    *TOTC. Total Time Calculations
    *TOTL. Continue at the beginning of total output lines
    *OFL. Continue at the beginning of overflow lines
    *DETC. Continue at the beginning of detail calculations.
    *CANCL. Cancel the Execution of the program
 

A blank for the ENDSR will return control to the RPG default error handler. If the subroutine was called by the EXSR operation and factor 2 is blank, control returns to the next sequential instruction. But, when the *PSSR is invoked by an error, it will not return to the statement in error to retry the operation. Generally, it should be used as a controlled exit from the program. The default error handler should be avoided, (help, my program has fallen over and can't get up!), since that is the mechanism which allows the user to see (and answer) the ubiquitous ‘CPF’ error messages.