|
|
Home » Starter Kit » TOC » Chapter 31
Chapter 31 - Hello, Any Messages? On the AS/400, sending and receiving program messages functions much like phone mail. Within a job, each program, as well as each job, has its own "mailbox." One program within the job can leave a message for another program or for the job; each program or job can "listen" to messages in its mailbox; and programs can remove old messages from the mailbox. In Chapter 30, I explained how programs can send messages to other program message queues or to the job's external message queue. In this chapter, we look at the "listening" side of the equation -- the RCVMSG (Receive Message) and MONMSG (Monitor Message) commands. Receiving the Right MessageYou can use the RCVMSG command in a CL program to receive a message from a message queue and copy the message contents and attributes into CL variables. Why would you want to do this? You may want to look for a particular message in a message queue to trigger an event on your system. Or you may want to look for messages that would normally require an operator reply, and instead, have your program supply the reply. Or you may want to log specific messages received at a message queue. Whatever the reason, the place to begin is the RCVMSG command. Figure 31.1 lists the RCVMSG command parameters. The first six parameters -- PGMQ (program queue), MSGQ (message queue), MSGTYPE (message type), MSGKEY (message key), WAIT (time to wait), and RMV (remove message) -- determine which message your program will receive from which message queue and how your program processes a message. Figure 31.2 illustrates a job message queue comprised of the job's external message queue and five program message queues. For our purposes, each message queue contains one message. Let's suppose that PGM_D is the active program and that it issues the following command:
RCVMSG
RCVMSG PGMQ(*SAME *) +
MSGQ(*PGMQ) +
MSGTYPE(*ANY) +
MSGKEY(*NONE) +
WAIT(0) +
RMV(*YES)
The PGMQ parameter of the pre-V2R3 RCVMSG Command, which consists of two values -- relationship and program -- lets you receive a message from any program queue active within the same job or from the job's external message queue (see Figure 31.3). The first value specifies the relationship between the program named in the second value and the receiving program. You can specify one of three values:
The value for the second element of the PGMQ parameter can be either the name of a program within the current program's job or the special value *, which tells OS/400 to use the current program's name. In the example above, because the PGMQ value is (*SAME *), PGM_D would receive a message from the PGM_D message queue. According to Figure 31.2, there is only one message to receive -- "First message on PGM_D queue." In our example, the value MSGTYPE(*ANY), combined with the value MSGKEY(*NONE), instructs the program to receive the first message of any message type found on the queue regardless of the key value (for more information about the MSGTYPE and MSGKEY parameters see "RCVMSG and the MSGTYPE and MSGKEY Parameters,"). The value WAIT(0) in the example tells the program to wait 0 seconds for a message to arrive on the message queue. You can use the WAIT parameter to specify a length of time in seconds (0 to 9999) that RCVMSG will wait for the arrival of a message. (You can also specify *MAX, which means the program will wait indefinitely to receive a message.) If RCVMSG finds a message immediately, or before the number of seconds specified in the WAIT value elapses, RCVMSG receives the message. If RCVMSG finds no message on the queue during the WAIT period, it returns blanks or zeroed values for any return variables. The last parameter value in the sample command, RMV(*YES), tells the program to delete the message from the queue after processing the command. You can use RMV(*NO) to instruct OS/400 to leave the message on the queue after RCVMSG receives the message. OS/400 then marks the message as an "old" message on the queue. A program can receive an old message again only by using the specific message key value to receive the message or by using the value *FIRST, *LAST, *NEXT, or *PRV for the MSGTYPE parameter. Note on the V2R3 RCVMSG Command Parameter Changes As with the SNDPGMMSG command, the RCVMSG command parameters also changed in V2R3 to accommodate ILE. For more information about the new parameter items you see for the PGMQ parameter in Figure 31.3, see Chapter 30. Because this book is at an introductory level and many of you will not use ILE until ILE RPG and/or ILE COBOL is available, I will not discuss these parameter changes in detail. Receiving the Right Values
All the remaining RCVMSG parameters listed in Figure 31.1 provide CL return variables to hold copies of the actual message data or message attributes. You normally use the RCVMSG command to retrieve the actual message text or attributes to evaluate that message and then take appropriate actions. For example, the following command:
RCVMSG MSGQ(MYMSGQ) +
MSGTYPE(*COMP) +
RMV(*NO) +
MSG(&MSG) +
MSGDTA(&MSGDTA) +
MSGID(&MSGID) +
SENDER(&SENDER)
retrieves the actual message text, the message data, the message identifier, and the message sender data into the return variables &MSG, &MSGDTA, &MSGID, and &SENDER, respectively. After RCVMSG is processed, the program can use these return variables. The program may receive messages looking for a particular message identifier. In this particular example, the current program might be looking for a particular completion message on a nonprogram message queue (MYMSGQ) to determine whether or not a job has completed before starting another job. Notice the SENDER parameter used in this example. When you create a return variable for the SENDER parameter, the variable must be at least 80 characters long and will return the following information: Positions 1 through 26 identify the sending job:
1-10 = job name Positions 27 through 42 identify the sending program:
27-38 = program name Positions 43 through 55 provide the date and time stamp of the message:
43-49 = date (Cyymmdd) Positions 56 through 69 identify the receiving program (when the message is sent to a program message queue):
56-65 = program name Positions 70 through 80 are reserved for future use. The SENDER return variable can be extremely helpful when processing messages. For example, during the execution of certain programs, it is helpful to know the name of the calling program without having to pass this information as a parameter or hardcoding the name of the program into the current program. You can use the technique in Figure 31.4 to retrieve that information. The current program sends a message to the calling program. The current program then immediately uses RCVMSG to receive that message from the *PRV message queue. Positions 56 through 65 of the &SENDER return value contain the name of the program that received the original message; thus, you have the name of the calling program.
Another RCVMSG command parameter that you will use frequently is RTNTYPE (return message type). When you use RCVMSG to receive messages with MSGTYPE(*ANY), your program can use a return variable to capture and interrogate the message type value. For instance, in the following command:
RCVMSG PGMQ(*SAME *) +
MSGTYPE(*ANY) +
MSG(&MSG) +
RTNTYPE(&RTNTYPE)
the variable &RTNTYPE returns a code that provides the type of the message that RCVMSG is receiving. The possible codes that are returned are:
01 Completion
As you can see, IBM did not choose to return the "word" values (e.g., *ESCAPE, *DIAG, *NOTIFY) that are used with the MSGTYPE parameter on the SNDPGMMSG (Send Program Message) command but instead chose to use codes. However, when you write a CL program that must test the RTNTYPE return variable, you should avoid writing code that appears something like IF (&rtntype = '02') DO ... ENDDO ELSE IF (&rtntype = '15') DO ... ENDDO
Instead, to make your CL program easier to read and maintain, you should include a standard list of variables, such as the CL code listed in Figure 31.5, in the program. Then, you can change the code above to appear as IF (&rtntype = &@diag) DO ... ENDDO ELSE IF (&rtntype = &@escape) DO ... ENDDO Monitoring for a MessageThe MONMSG command is available only in a CL program. It provides a technique to trap error/exception conditions by monitoring for escape, notify, and status messages. It also provides a technique to direct the execution of the program based on the particular error conditions detected.
Figure 31.6 lists the MONMSG command parameters. You can use the MSGID parameter to name from one to 50 specific or generic message identifiers for which the command will monitor. A specific message identifier is a message ID that represents only one message, such as CPF9802, which is the message ID for the message "Not authorized to object &2." A generic message is a message ID that represents a group of messages, such as CPF9800, which includes all messages in the CPF9801 through CPF9899 range. Thus, the command
MONMSG CPF9802 +
EXEC(GOTO ERROR)
monitors for the specific message CPF9802, whereas the command
MONMSG CPF9800 +
EXEC(GOTO ERROR)
monitors for all escape, notify, and status messages in the CPF9801 through CPF9899 range.
The second parameter on the MONMSG command is the CMPDTA parameter. You have the option of using this parameter to specify comparison data that will be used to check against the message data of the message trapped by the MONMSG command. If the message data matches the comparison data (actually only the first 28 positions are compared), the MONMSG command is successful and the action specified by the EXEC parameter is taken. For example, the command
MONMSG CPF9802 CMPDTA('MAINMENU') EXEC(DO)
monitors for the CPF9802 message identifier, but only executes the command found in the EXEC parameter if the CMPDTA value 'MAIN MENU' matches the first eight positions of the actual message data of the trapped CPF9802 message. The EXEC parameter lets you specify a CL command that is processed when the MONMSG traps a valid message. If no EXEC value is found, the program simply continues with the next statement found after the MONMSG command.
You can use the MONMSG command to monitor for messages that might occur during the execution of a single command. This form of MONMSG use is called a command-level message monitor. It is placed immediately after the CL command that might generate the message and might appear as CHKOBJ &OBJLIB/&OBJ &OBJTYPE MONMSG CPF9801 EXEC(GOTO NOTFOUND) MONMSG CPF9802 EXEC(GOTO NOTAUTH)
The MONMSG commands here monitor only for messages that might occur during the execution of the CHKOBJ command. You should use this implementation to anticipate error conditions in your programs. When a command-level MONMSG traps a message, you can then take the appropriate action in the program to continue or end processing. For example, you might code the following: DLTF QTEMP/WORKF MONMSG CPF2105 to monitor for the CPF2105 "File not found" message. In this example, if the CPF2105 error is found, the program simply continues processing as if no error occurred. That may be appropriate for some programs.
Now, examine the following code: CHKOBJ QTEMP/WORK *FILE MONMSG CPF9801 EXEC(DO) CRTPF FILE(QTEMP/WORK) RCDLEN(80) ENDDO CLRPFM QTEMP/WORK This code uses the MONMSG command to determine whether or not a particular file exists. If the file does not exist, the program uses the CRTPF (Create Physical File) command to create the file. The program then uses the CLRPFM (Clear Physical File Member) command to clear the existing file (if the program just created the new file, the member will already be empty).
In addition to using the command-level message monitor to plan for errors from specific commands, you can use another form of MONMSG to catch other errors that might occur. This form of MONMSG use is called a program-level message monitor, and you must position it immediately after the last declare statement and before any other CL commands. Figure 31.7 illustrates the placement of a program-level message monitor. When you implement a program-level message monitor, I recommend that you use the message identifier CPF9999 instead of the widely used CPF0000. Using CPF9999 provides two important functions over CPF0000. First, CPF9999 catches some messages that CPF0000 will not catch because CPF9999 is the "Function Check" error, which occurs only after some other program error, including errors triggered by CPFxxxx escape messages, MCHxxxx escape messages (machine errors), and escape messages from other message identifier groups. CPF0000 only monitors for actual CPFxxxx messages. Second, the CPF9999 "Function Check" message provides the actual failing statement number, which is not available from the CPFxxxx error message. Specifying the CPF9999 message ID as the program-level message monitor makes this additional information available. Working with ExamplesFigure 31.8 is a portion of a CL program that provides several examples of program message processing to help you tie together the information I've presented here and in Chapter 30. The program contains a standard list of DCL statements for defining variables used in normal message processing. You may want to place these variables in a source member that you can copy into CL programs as needed. (Remember, you have to use your editor to do the copying because CL has no /COPY equivalent.) The program-level message monitor (A in Figure 31.8) is coded to handle any unexpected errors. If an unexpected error occurs during program execution, this MONMSG causes execution to continue at the label GLOBAL_ERR. At GLOBAL_ERR, the program first prevents an infinite loop by testing to determine whether the program has already initiated the error-handling process. An infinite loop might occur when an unexpected error occurs during the error-handling process that the program-level MONMSG controls. The &msg_flag controls the overall message process. The program sets the value of &msg_flag to &@true and continues at label CLEAN_UP. Your programs should have a mechanism for cleaning up any temporary objects, whether the program ends normally or abnormally with an error. After processing statements at CLEAN_UP, the program continues at label RSND_BGN. If the &msg_flag variable is &@true, the program has found an error condition. The program then sends each message on the current program message queue to the calling program (which in turn might continue to send the messages back in the program stack to the command processor or some other program that either ends abnormally or displays the messages to the user who requested the function). Notice that RCVMSG is used to receive each message from the program queue (D). The MONMSG CPF0000 is used here to catch any error that might occur during the RCVMSG command process and immediately go to the end of the program without attempting to receive any other messages. As the program receives each message, the return variable &RTNTYPE is tested and only the messages that have a &RTNTYPE of &@diag or &@escape are processed. The SNDPGMMSG command sends each processed message to the calling program message queue as a *DIAG message. Finally, at the RSND_END label, the program sends one generic escape message "Operation ended in error ...." to the calling program. That escape message terminates the current program and returns control to the calling program. The sample code in Figure 31.8 contains several examples of command-level message monitors. The first example is the MONMSG CPF9801 that follows the CHKOBJ command (B). If the CPF9801 "Object not found" message is trapped, the program first removes this message by using RCVMSG with RMV(*YES), and then sends a more meaningful message to the program queue using the SNDPGMMSG command. Notice that the value for the TOPGMQ parameter on the SNDPGMMSG command is *SAME to direct the message to the current program queue. The &msg_flag is set to &@true and GOTO CLEAN_UP sends control of the program to the CLEAN_UP label, where cleanup and then error message processing occurs. Another example of the command-level message monitor is the MONMSG CPF0864 that appears immediately after the RCVF statement (C). If the MONMSG traps the CPF0864 "End of file" message, the program removes this message from the current program message queue using RCVMSG with RMV(*YES). Because the "End of file" message is expected and not an error, it is appropriate to remove that message from the program queue to prevent confusion in debugging any errors. Next the program uses the GOTO RCD_END statement to pass control of the program to the RCD_END label where the program sends a normal completion message to the calling program message queue. What Else Can You Do with Messages?Now that you understand the mechanics, you may want to know what else you can do with messages. Listed below are three possible solutions using messages:
These are only examples of how you might use messages to perform tasks on the system. With the mechanics under your belt, it's time for you to explore how you can use messages to enhance your own applications. Starter Kit for the AS/400, 2nd Edition Copyright 1994 by Duke Press DUKE COMMUNICATIONS INTERNATIONAL Loveland, Colorado All rights reserved. No part of this book may be reproduced in any form by any electronic or mechanical means (including photocopying, recording, or information storage and retrieval) without permission in writing from the publisher. It is the reader's responsibility to ensure procedures and techniques used from this book are accurate and appropriate for the user's installation. No warranty is implied or expressed. This book was printed and bound in the United States of America. Second Edition: April 1994 ISBN 10882419-09-X |
| Sponsored Links | Featured Links | |
Penton Technology Media Connected Home | SQL Server Magazine | Windows IT Pro Report Bugs | Contact Us | Comments/Suggestions | Terms & Conditions | Privacy Policy | Trademarks See Membership Levels | Subscribe | Free E-mail Newsletters | Free RSS Feeds | My Profile | Upgrade Now | Renew Now Copyright © 2009 - Penton Technology Media System i is a trademark of International Business Machines Corporation and is used by Penton Media, Inc., under license. SystemiNetwork.com is published independently of International Business Machines Corporation, which is not responsible in any way for the content. Penton Media, Inc., is solely responsible for the editorial content and control of the System iNetwork. |