Most OS/400 APIs accept an error parameter that allows you to control what happens when an error occurs while executing the API. Usually, either you can let the system handle the error for you, or you can handle the error condition within your program. If you let the system handle the error, chances are it won't do it gracefully. In this article, I'll show you a technique you can use to trap API errors and send a message back to the user advising him what went wrong.
In general, the error parameter that most APIs accept has the same characteristics for all APIs. Once you learn to code this parameter, you can apply the same knowledge to other APIs. The technique I'm going to discuss applies to the majority of OS/400 APIs. However, a few newer types, such as the bindable APIs and sockets APIs, require different error handling techniques. I won't cover those in this article. For information on how to trap for errors with those types of APIs, see the OS/400 System API Reference V3R1 manual.
The standard API error parameter lets you trap for errors and take appropriate action when they are encountered. For example, in an interactive program, you may have the user supply a value for a parameter to a user-written command. You might then pass that value to an OS/400 API. If the API rejects the value, you can send a message to the user to let him know that the value he supplied was invalid and request that he try again. This rejection could continue until the user supplies a value the API can use. In this case, by using the error parameter, the API can be used to perform validation.
With some APIs, the error parameter is optional. With others, it is required. When the error parameter is optional, you may decide not to code it. If you don't code it and an error occurs while executing the API, the error will be handled by the system in the form of an exception error.
Even if the error parameter is required, you can still decide to let the system handle the error for you. You can do that by passing a four-byte binary field containing the value zero. In this case, you're telling the API to ignore the error parameter even though it's required. It's as if the error code were optional, and you decided not to code it. In both cases, if an error occurs during the execution of the API, the system generates an exception message.
A better approach is to code the error parameter in a way that lets the system know you want to trap for errors within your program. You do that by coding the error parameter as a data structure consisting of five subfields. 1 shows the structure of the error parameter.
A better approach is to code the error parameter in a way that lets the system know you want to trap for errors within your program. You do that by coding the error parameter as a data structure consisting of five subfields. Figure 1 shows the structure of the error parameter.
The first subfield, Bytes provided, is a four-byte binary field. This is an input field, so it must be loaded with a value before the API is executed. The value you place in this field must be either zero or a number greater than or equal to eight. If you pass a value of zero, you're telling the API to let the system handle errors for you. Otherwise, the value should be equal to the length of the data structure used to describe the error parameter.
The second subfield, Bytes available, is an output field that the API loads when it executes. Your program can use this four-byte binary field to detect if an error occurs. If the value of this field is zero after the call to the API, no error has occurred. If the value of this field is greater than zero, an error has occurred. When an error occurs, this field contains the length of the error information returned to your program. Normally, your program contains code that checks this field immediately after calling an API to see if an error has occurred.
The API loads the third subfield, Message identifier, when errors occur. This is a seven-byte character field in which the API will place a message identifier for the error condition. For example, the Retrieve Object Description (QUSROBJD) API might return a value of CPF2101 to specify that the object type parameter was loaded with an invalid value.
The fourth subfield is reserved by IBM, presumably for future use. Your program should not attempt to use this field. Any value that might be returned to your program would be meaningless and should, therefore, be ignored.
The last field, Message data, contains the data for the message identifier returned in the Message identifier field. This data does not contain the message text; it contains the substitution variables within the message text. For example, if the message text is Object type &1 not valid, the value in the Message data field contains the data that is substituted for the &1 variable.
The Message data field is a variable-length field, so you can define it any length you want. The only restrictions are those of the language you're using. For example, in RPG III, a subfield can have a maximum length of 256 bytes. I've found that 100 bytes is usually adequate. Although in extreme cases an error may require a larger message data field, 100 bytes should be large enough for most errors. If the message data is longer than the length of the message data field, it is simply truncated on the right. Even if this happens, all other aspects of the error parameter should still work normally.
After you call an API for which you have coded an error parameter, the first thing you should do in your program is to check to see if an error occurred. The Bytes available field will contain a value greater than zero if there is an error. When an error occurs, you will probably want to let the user of the program know what went wrong. You can do this by passing the message identifier and message data fields to a CL program. The CL program can then use these values to execute the Send Program Message (SNDPGMMSG) command.
The following programs illustrate API error handling. This example uses the Create User Space (QUSCRTUS) API, which uses the error parameter. It attempts to create a user space in library QTEMP. However, the call to the API has been purposely rigged to fail by passing it an invalid user space name. Although the API fails, the program does not. Because the appropriate error handling technique was used, the user of the program gets a simple error message on the bottom of the screen that tells him what went wrong. Take a look at the example programs in Figures 2 and 3.
In this example, a CL program (API001CL) calls an RPG program (API001RG). Program API001CL passes three parameters to API001RG. The first parameter is an error flag. This error flag will be returned with a Y if an error occurs. If no error occurs, it will contain an N. The second and third parameters are used to pass a message identifier and message data if an error occurs.
The program API001RG accepts these three parameters. It then attempts to create a user space using the QUSCRTUS API. However, the user space name has been deliberately loaded with an invalid value of BAD!NAME. The exclamation point character is not allowed in an object name, so the API will not be able to create the user space.
When this API is executed, the user space is not created. Instead, fields within the ERROR data structure are loaded with values to signal that an error has occurred. The program checks the Bytes available (BYTAVA) field to test for the error condition. Because this field contains a value greater than zero, the program knows that an error has occurred and loads the parameter fields ERR, MSG, and DTA. The ERR parameter is loaded with a Y to let the API001CL program know that an error has occurred. The MSG parameter is loaded with the MSGID field from the ERROR data structure, which contains the message identifier associated with the message. The DTA parameter is loaded with the MSGDTA field from the ERROR data structure, which contains the data for the message.
When control is passed back to the API001CL program, it checks the &ERR parameter. If it finds a value of Y in this field, it knows that an error has occurred, and it uses the SNDPGMMSG command to send a message back to the user to let him know. The SNDPGMMSG command uses the &MSG and &DTA parameters that were passed back from the API001RG program. The user sees a message on the bottom of his screen that says Object BAD!NAME is not valid. This not only lets the user know that an error occurred, but it also tells him why.
As you can see, this method is a good way to handle error conditions. When a problem occurs during the execution of an API, the user can be notified in a friendly way that an error occurred. It also gives users specific information about what went wrong so that they can take corrective measures without necessarily having to search through a job log. Even though the error parameter is often optional, you should consider taking advantage of it whenever possible.
Robin Klima is a senior technical editor for Midrange Computing. He can be reached by E-mail at
REFERENCE
OS/400 System API Reference V3R1 (SC41-3801, CD-ROM QBKAVD00).
API Error Handling
Figure 1: The Structure of the Error Parameter
API Error Handling
Figure 2: Example Program API001CL
/*===============================================================*/ /* To Compile: */ /* */ /* CRTCLPGM PGM(XXX/API001CL) SRCFILE(XXX/QCLSRC) */ /* */ /*===============================================================*/ PGM DCL VAR(&ERR) TYPE(*CHAR) LEN(1) DCL VAR(&MSG) TYPE(*CHAR) LEN(7) DCL VAR(&DTA) TYPE(*CHAR) LEN(100) /* CALL PROGRAM TO CREATE A USER SPACE */ CALL PGM(API001RG) PARM(&ERR &MSG &DTA) /* CHECK FOR AND PROCESS ERROR MESSAGES */ IF COND(&ERR *EQ 'Y') THEN(SNDPGMMSG + MSGID(&MSG) MSGF(QCPFMSG) MSGDTA(&DTA) + MSGTYPE(*ESCAPE)) ENDPGM
API Error Handling
Figure 3: Example Program API001RG
*=============================================================== * To compile: * * CRTRPGPGM PGM(XXX/API001RG) SRCFILE(XXX/QRPGSRC) * *=============================================================== *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 * * API Error Parameter IERROR DS I I 116 B 1 40BYTPRV I B 5 80BYTAVA I 9 15 MSGID I 16 16 RESRVD I 17 116 MSGDTA I DS I B 1 40LENSPC * C *ENTRY PLIST C PARM ERR 1 C PARM MSG 7 C PARM DTA 100 * * Intentionally load an invalid name to force an error C MOVEL'BAD!NAME'USRSPC 20 C MOVEL'QTEMP' SPCLIB 10 C MOVE SPCLIB USRSPC C MOVE 'N' ERR * * Attempt to create the user space C CALL 'QUSCRTUS' C PARM USRSPC C PARM *BLANKS ATRSPC 10 C PARM 1024 LENSPC C PARM *BLANKS VALSPC 1 C PARM '*CHANGE' AUTSPC 10 C PARM *BLANKS TXTSPC 50 C PARM '*YES' RPLSPC 10 C PARM ERROR * * If an error occurred then load error parameters C BYTAVA IFGT 0 C MOVEL'Y' ERR C MOVELMSGID MSG C MOVELMSGDTA DTA C END * C MOVE *ON *INLR *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7
LATEST COMMENTS
MC Press Online