Errors are an unfortunate fact of life for programmers. When they occur, they are usually followed immediately by screaming and hollering (some directed at programmers, some produced by programmers). Knowing how to deal with them may reduce the stress level of all parties concerned.
Quick! All programmers who have never had a “bug” occur in a program they have written, raise your hand! For those of you liars out there who actually raised your hands, do you know how silly you look to anyone who happens to be passing your cubicle right now?
Obviously, bugs are both an unfortunate and inevitable byproduct of programming in any language, but this reality does not mean that the users of your programs have to encounter them. There are preventative steps you can take to be prepared for them.
What Is a Bug?
If we left this definition up to our illustrious president, we would all be in trouble. Anyone who is unable to define the word is would surely be lost when it came to finding a definition for the term bug as it relates to programming.
The term bug actually originated back in the days of the dinosaur computers (the ones used when we were young). You know the ones we are referring to—those behemoth-sized computers with huge vacuum tubes instead of microchips. When insects would get caught in the machinery, they would cause the switches to malfunction. The outcome of this particular situation resulted in a definition of the term bug that even Bill Clinton could understand. The term bug has since evolved to equate with any unintended computer function.
Some might argue that, since computers can do only what they are told to do, there is no such thing as a bug. These are generally the same people who raised their hands while reading the first paragraph of this article. They reason, “If your program doesn’t account for an error, the operating system takes over and does what it is programmed to do; therefore, there are technically no ‘unintended’ computer functions.” Use this argument at your own peril because users can sometimes be hostile. This is especially true if the user
happens to be staring at some unfamiliar error screen while running a month-end in the middle of the night. Humor is generally not a viable option at these unplanned and unappreciated moments.
But the key point in the preceding statement is that your program may be designed to account for almost all conceivable errors. When you write a new program, you decide what course of action is to be taken when an error occurs. The user (or customer, in our case) should not be the one to decide what to do in the middle of a highly stressful situation. The best time to account for errors is before they occur (too bad President Clinton didn’t think about that!).
The Basics
Errors in RPG programs are generally divided into two classifications: file and program. Likewise, there are generally two simple ways of handling errors in RPG programs. For the former type, you can use error indicators on all file access operation codes, and, for the latter, you can code an error subroutine. Let’s examine each of these methods in some detail.
Using error indicators on file access operation codes is your way of telling the operating system not to take control when some problem develops while trying to access a file. Instead of turning control over to the operating system, the error indicator is turned on so that your program can handle exceptions. For those of you who dislike indicators, RPG IV now allows the use of an extender (E) for a lot of operation codes in place of an error
indicator. The extender enables the built-in-function (BIF) %ERROR to return a value of 1 (on) if an error has occurred or 0 (off) if the operation finished successfully. Using the extender and the %ERROR function together allows you to code without using an indicator.
Program Subroutines
A good method of handling program errors is to use a *PSSR subroutine in your program. Figure 1 shows an example of a program with a *PSSR subroutine. It also shows our preferred method of calling that subroutine when an error occurs (as opposed to allowing the system to call the subroutine automatically). The program shown displays the name of the job that has a lock on the record it is trying to access. Indicator 99 comes on after the maximum record wait time has expired, indicating that an error has occurred within the file. If indicator 99 is turned on, we then execute the *PSSR subroutine. We don’t have to code a call to the *PSSR subroutine. If we code an INFSR(*PSSR) keyword in the File Specification and leave the error indicator off of the Chain statement, the system will recognize that a *PSSR subroutine exists to handle the error and will automatically execute the routine.
So why do we bother coding the Invoke Subroutine (EXSR) statement for our *PSSR subroutine? The reason has to do with what happens after the subroutine executes. If we execute the subroutine by coding a call to it, control can return to the next statement following the EXSR statement. If we let the system call the subroutine, then we have less control over the return point. We tell the system where to return after executing the subroutine by entering a keyword in factor two of the End of Subroutine (ENDSR) statement. Figure 2 shows the valid keywords and their corresponding return points. You should note that all of these return points are areas within the RPG cycle, except the value of blanks. If we use blanks (or don’t code a return point) and we use EXSR to call the subroutine, control returns to the next line of code.
Let’s take a look inside the *PSSR subroutine itself. Because the subroutine is called anytime an unexpected error occurs in the program, what happens if an error occurs from within the subroutine? The subroutine is called again, which causes the error to occur again, which calls the subroutine again, which...well, you get the picture.
To prevent this occurrence, we use a field named InSub to tell us that we have been inside the subroutine. The possible values for InSub are either *ON or *OFF. If InSub is
File It!
on, then the error we are handling must have come from inside the *PSSR subroutine, since that is the only place where we set the value of InSub to *ON. In this case, we set the return point to *CANCL and let the RPG default exception handler issue its nasty error message. If InSub is not on, we set it on and check the field STATUS to determine the exact nature of the error that has occurred. In this example, if it’s a “Record in Use” error, we use the DSPLY operation code to display the name of the job that has the record locked.
File Subroutines
Another type of error-handling subroutine that you should be aware of and code into your programs is the File Error Subroutine. This subroutine takes control when an error occurs during an explicit file operation that does not have an error indicator specified, such as Chain or Read.
To indicate that you want a file error subroutine to execute automatically when an error occurs while trying to access data in a file, specify the Information Subroutine (INFSR) keyword. The parameter for this keyword is the name of the subroutine to be executed. You can see an example of this in Figure 3.
The INFSR tells the system the name of the subroutine you have written to handle errors for that file. In the example in Figure 3, the subroutine is named FILESR. The INFDS code is used to indicate the name of a data structure (INFO) that the system will load with information about the file. One of the key pieces of information about the file is the STATUS field, which contains an error code indicating the exact problem with the file.
In the example shown in Figure 3, the program attempts to read the file FAMLY before opening it. The keyword USROPN on the file definition keeps the system from opening it automatically; therefore, the program should “blow up” on the first read of the file. However, there is a file information subroutine (FILESR) defined for the file, so the system passes control to it. This subroutine checks the status code, and if the message is I/O to a closed file, it opens the file and returns control to the system at the beginning of the detail calculations. We really should include a disclaimer here about opening your files. This example is meant to show you how to code programs that prevent errors from reaching the end user. Opening your files by using an exception error is obviously not the best way to code your programs.
If you want, you can code a different subroutine for each file, or you can have each file call the same subroutine. You can even have the file information subroutine be the *PSSR subroutine. It’s a flexible system and one that is simple to exploit.
You should be aware that if the problem with the file occurs during the implicit opening of the files, your subroutine does not get control. The default exception handler (the system) takes control, no matter what. This is one good example of why you should employ user-controlled file opens rather than let the system do it for you.
Make It So, Number One
Yet another error handling technique that we like is the system reply list. We love this technique because you tell the system what to do before the error occurs, and the system takes that action when the error occurs. The system actually enters your response to an error message for you without interrupting the job!
The system reply list is a list of error messages. Figure 4 shows a couple of entries on the list. To see your list, use the Work with Reply List Entries (WRKRPYLE) command. To add a new entry to the list, use the F6 key. You can see the result of this operation in Figure 5. Let’s examine the parameters of this command in detail.
• The sequence number is assigned by you and is used to order the messages. The system searches the list in sequence number order.
• The message identifier is the ID of the message to which you want the system to respond automatically.
• The Compare data parameter allows you to get very specific about when the system will answer the message. The string you enter here is compared to the message data specified for the inquiry message. The second part of this parameter allows you to specify
where in the message data the comparison should start. If your string and the data match, the system answers the message. If they don’t, the system continues the search with the next entry in the list.
• The Message reply parameter is used to specify the answer that the system is to give to the message it issues.
Now that you have the list created, you need to tell the system that you want the job to use the list. For an interactive job, you can use the Change Job (CHGJOB) command to change the Inquiry message reply (INQMSGRPY) parameter from *RQD (response is required) to *SYSRPYL (use the system reply list). One other option for this parameter is *DFT (use the default reply for the message).
For submitted jobs, the same INQMSGRPY parameter exists on the Submit Job (SBMJOB) command, with one possible variation: You can specify *JOBD, which means, “Go get the value for this parameter from the job description” (which has the exact same INQMSGRPY parameter).
If All Else Fails, Call ‘Em Something Else
We have given you a few of our favorite tips that you can use to prevent the programs in your system from terrorizing your users.
Remember, errors need not be feared if you handle them properly. If all else fails, you can put some presidential spin on them and call them “features” instead.
FFamly uf e k disk infds(info)
dInfo ds
dStatus *STATUS
d sds
d MsgDta 91 170
dFldReturn s 6
dInSub s 1
dKey s 9 0
dmsgHld s 52
* Attempt to get record (if locked by another job, *in99 will
* come on after the default record wait parameter expires)
c eval Key = 1
c eval Insub = *off
c Key chain Famly 6899
c if *in99 = *on
c exsr *pssr
c endif
c eval *inlr = *on
* Error handling routine
c *PSSR begsr
c If InSub = *on
c eval FldReturn = '*CANCL'
c else
c eval InSub = *on
c eval FldReturn = *blanks
c if Status = 01218
c movel MsgDta MsgHld
c msghld dsply
c endif
c endif
c endsr Fldreturn
Figure 1: Using a *PSSR subroutine is good way to handle errors.
Keyword Return Point *DETL Beginning of detail lines *GETIN Get input record routine *TOTC Beginning of total calculations *TOTL Beginning of total lines
*OFL Beginning of overflow lines *DETC Beginning of detail calculations *CANCL Cancel the processing of the program Blanks Next sequential instruction after the EXSR statement.
If no EXSR statement, return to ILE RPG default exception handler
Figure 2: Control return points via these keywords.
FFamly if e k disk infds(info) infsr(FILESR) usropn
dInfo ds
dStatus *STATUS
dFldReturn s 6
dInSub s 1
c read Famly 9968
c eval *inlr = *on
* file error handling
c FileSr begsr
c If InSub = *on
c eval FldReturn = '*CANCL'
c else
c eval InSub = *on
c eval FldReturn = *blanks
c if Status = 01211
c open famly
c eval fldreturn = '*DETC'
c endif
c endif
c endsr Fldreturn
Figure 3: This is a file Information Subroutine (INFSR) example.
Work with System Reply List Entries
System: CPU35
Type options, press Enter.
2=Change 4=Delete
Sequence Message Compare
Opt Number ID Reply Compare Value Start
_ 8888 RPG8888 C *NONE
_ 9999 CPF4058 I *NONE
Bottom
Figure 4: The system reply list offers another method to handle errors.
Add Reply List Entry (ADDRPYLE)
Type choices, press Enter.
Sequence number . . . . . . . . 1-9999
Message identifier . . . . . . . *ANY Character value, *ANY
Compare data:
Comparison data . . . . . . . *NONE
Message data start position . *NONE 1-999, *NONE
Message reply . . . . . . . . . *DFT
Bottom
F3=Exit F4=Prompt F5=Refresh F10=Additional parameters F12=Cancel
F13=How to use this display F24=More keys
Figure 5: Use the Add Reply List Entry display to add entries to the system reply list.
LATEST COMMENTS
MC Press Online