Modern technologies such as laptop computers, cell phones, and wireless handheld devices have created an "always online and connected" culture in the IT community. Some companies tend to abuse their position of power, pressing their IT staff to be available 24x7x365, anytime something goes wrong. Whether this trend is a modern variety of worker exploitation is a matter of opinion. My belief is that IT staff should oppose this trend and defend their right to lead a balanced life. Despite my personal beliefs, the same thing that happened to me could happen to you: You might be asked to resend unanswered inquiry messages from the QSYSOPR message queue to the mobile devices of your personnel. I had no choice; I had to do it. The first thing I did to solve my business problem was look through the powerful APIs IBM provides.
As you might expect, I found the appropriate tool in the enormous mass of information provided by the Information Center: the Message Handling APIs, and more specifically, the List Nonprogram Messages (QMHLSTM) API. But before going further on this subject, let's recap some concepts that I discussed in some of my previous articles.
APIs can be classified according to their operating environment:
APIs that return information in a user space are called List APIs, and those that return information in a receiver variable are called Retrieve APIs. Receiver variables are data structures defined in the WORKING-STORAGE SECTION of a Cobol program, whereas user spaces are permanent objects that exist in the system space. Hence, user spaces can be shared by different Cobol run units. User spaces can be processed in two ways: using the Retrieve User Space (QUSRTVUS) API, or by means of pointers. As long as ILE Cobol supports pointers, we will always use the second way.
The first two categories of APIs have a common error-handling interface known as an error-code parameter, a variable-length data structure available in the QSYSINC include library.
The information returned by the List APIs consists of two parts: a header (or fixed portion) and a list portion. In the fixed portion, there are three important special fields: the offset to the list portion, the number of entries in the list portion, and the size of each entry. In turn, the list portion is made up of a series of entries, where each entry is a data structure that represents a particular instance of the returned information.
As a general rule, List APIs and Retrieve APIs require a parameter called "format name." This parameter determines the format of the information returned from the API to the calling program.
In general, the first parameter for List APIs is a qualified user space name, and this user space must exist before the API is invoked.
Some APIs, including QMHLSTM, have a keyed interface. By means of key values, you can provide directives to the API about what information you are interested in. An example of an API with keyed interface is the Save Object List (QSRSAVO) that I presented in my article "Planning for a Smooth Deployment in Cobol" (January, 15, 2009, article ID 57613). For the QMHLSTM API, you can use key values to tell the API that it must return the inquire messages from QSYSOPR with (1) the replacement data, (2) the message help, and (3) the default reply. This is a great tool, but it makes the interface much more complex! Key values are passed in a varying-length data structure. Therefore, before calling the API, you must provide the number of keys, the offset to the array that contains the key values inside the data structure, and the length of the data structure. The API documentation terms this structure as "message selection information." As long as there are several formats for the message selection information, the API requires a second format name, called "format of message selection information."
The API documentation tells us that QMHLSTM requires six parameters:
All these parameters must be passed "by reference," because the QMHLSTM API belongs to the first category (i.e., OPM APIs). For each parameter, a pointer with the address of the information being passed is put into the parameter list. You don't have to worry about this issue, because the Cobol compiler generates implicit pointers for each parameter and does the work for you. As long as pass-by-reference is the default for the Cobol language, in my sample program (downloadable at the end of this article), I call the API in this way:
CALL "QMHLSTM"
USING user-space-name
returned-information-fmt
message-selection-info
size-message-selection-info
message-selection-info-fmt
qus-ec
END-CALL
Now I explain in more detail each of these parameters:
For us Cobol programmers, the real difficulty is to manipulate the varying-length data structures. Therefore, let's examine more closely how the message selection information parameter is defined in our sample program. First, let's look at how the people of IBM have defined it in the QSYSINC/QCBLLESRC(QMHLSTM) include source member:
*****************************************************************
*Type Definition for the input parameter section of the user-
*space.
**** ***
*NOTE: The following type definition only defines the fixed
* portion of the format. The array of identifiers of
* fields to return specified, and call message queue
* specified are each varying length and immediately follow
* what is defined here.
*****************************************************************
01 QMH-LSTM-INPUT.
05 USERSPACE-NAME PIC X(00010).
05 USERSPACE-LIBRARY PIC X(00010).
05 FORMAT-NAME PIC X(00008).
05 MESSAGE-SELECTION PIC X(00008).
05 SIZE-MESSAGE-SELECTION PIC S9(00009) BINARY.
05 MAX-MESSAGES-REQUESTED PIC S9(00009) BINARY.
05 LIST-DIRECTION PIC X(00010).
05 SELECTION-CRITERIA PIC X(00010).
05 SEVERITY-CRITERIA PIC S9(00009) BINARY.
05 MAX-MESSAGE-LENGTH PIC S9(00009) BINARY.
05 MAX-MESSAGE-HELP-LENGTH PIC S9(00009) BINARY.
05 OFFSET-MESSAGE-NAME PIC S9(00009) BINARY.
05 OFFSET-MESSAGE-KEY PIC S9(00009) BINARY.
05 NUMBER-MESSAGE-QUEUE PIC S9(00009) BINARY.
05 OFFSET-FIELD-RETURN PIC S9(00009) BINARY.
05 NUMBER-FIELD-RETURN PIC S9(00009) BINARY.
05 CODED-CHARACTER-SET-ID PIC S9(00009) BINARY.
05 DATE-TIME-CRITERIA PIC X(00013).
* @A1A
*05 RESERVED PIC X(00001).
*
* Varying length
*05 MESSAGE-QUEUE-NAME PIC X(00001).
*
* Varying length
*05 START-MESSAGE-KEY PIC X(00004).
*
* Varying length
*05 ID-FIELD-RETURN PIC S9(00009) BINARY
* OCCURS 00001 TIMES.
*
* Varying length
*****************************************************************
Pay special attention to this sentence at the commentary note: "The following type definition only defines the fixed portion of the format." This means that it's our responsibility to define the varying portion of the format. We can't include this piece of code using the COPY directive in our program without some changes. In my sample program, I've adapted the above code in this way:
01 parameter-fields.
03 user-space-name.
05 userspace-name PIC x(10)VALUE "TMPSPACE".
05 userspace-library PIC x(10)VALUE "QTEMP".
03 returned-information-fmt PIC x(8)VALUE "LSTM0100".
03 message-selection-info.
05 fixed-portion.
07 max-messages-requested PIC s9(9) BINARY VALUE -1.
07 list-direction PIC x(10) VALUE "*PRV".
07 selection-criteria PIC x(10) VALUE "*MNR".
07 severity-criteria PIC s9(9) BINARY VALUE 0.
07 max-message-length PIC s9(9) BINARY VALUE -1.
07 max-message-help-length PIC s9(9) BINARY VALUE -1.
07 offset-message-name PIC s9(9) BINARY.
07 offset-message-key PIC s9(9) BINARY.
07 number-message-queue PIC s9(9) BINARY.
07 offset-field-return PIC s9(9) BINARY.
07 number-field-return PIC s9(9) BINARY.
05 varying-portion.
07 qualified-message-queue-name.
10 message-queue-name PIC x(10) VALUE "QSYSOPR".
10 message-queue-library PIC x(10) VALUE "*LIBL".
07 starting-message-key PIC x(4) VALUE x"FFFFFFFF".
07 identifier-field-return-table.
10 id-field-return-201 PIC s9(9) BINARY VALUE 201.
10 id-field-return-301 PIC s9(9) BINARY VALUE 301.
10 id-field-return-401 PIC s9(9) BINARY VALUE 401.
10 id-field-return-601 PIC s9(9) BINARY VALUE 601.
10 id-field-return-1001 PIC s9(9) BINARY VALUE 1001.
07 id-field-return-table REDEFINES
identifier-field-return-table.
10 id-field-return
OCCURS 5 TIMES PIC s9(9) BINARY.
03 size-message-selection-info PIC s9(9) BINARY.
03 message-selection-info-fmt PIC x(8) VALUE "MSLT0100".
In the preceding code, I've used the VALUE clause to specify the initial content for several fields into the message selection information parameter. These values are taken from the API documentation. For example, the value -1 in the MAX-MESSAGES-REQUESTED field indicates a "*NOMAX" condition. That is, the API must return all the messages that fit with the conditions specified in the other fields. Note that I haven't used the VALUE clause to initialize the offset fields. Offset fields must contain the offset values to several varying-length data structures into the varying portion of the format. To avoid "magic numbers" for the offset fields, I've initialized it in the PROCEDURE DIVISION by using the LENGTH OF intrinsic function.
MOVE LENGTH OF fixed-portion TO offset-message-name
COMPUTE offset-message-key =
LENGTH OF fixed-portion +
LENGTH OF qualified-message-queue-name
END-COMPUTE
COMPUTE offset-field-return =
LENGTH OF fixed-portion +
LENGTH OF qualified-message-queue-name +
LENGTH OF starting-message-key
END-COMPUTE
MOVE LENGTH OF message-selection-info TO
size-message-selection-info
The concept of offsets can be a source of trouble for Cobol programmers because Cobol is a "base 1" language, whereas System APIs receive and send information using a base of 0. Therefore, for List APIs, an offset value is the relative position from the first byte of the user space, which has an offset value of 0.
Notice that in the varying portion, I've defined a five-element array, which contains the list of identifiers of fields to return. Each of these identifiers represents some type of message information that the API must return in the user space. For example, identifier 201 represents "replacement data," 401 represents "message help," and so forth. I could have defined a two-element, a three-element, or even a 31-element array.
The next challenge is to manipulate the returned information in the user space, through the use of pointers. This is achieved by
Finally we have access to all the unanswered inquiry messages in the QSYSOPR message queue! The last challenge is how to send out these critical message alerts to the mobile devices of the support staff. This requires more than knowledge of Cobol; this requires a team of experts in network communications, network security, and so forth. But within that team, you as a Cobol programmer will play an important role!
The IBM i Information Center provides documentation about QMHLSTM API.
You can download the sample program that illustrates the concepts presented here.