Configuring MQ on iSeries
MQ is reliable, flexible, and fairly simple to configure and program. It's an excellent choice for addressing connectivity and system integration issues.
By Michael Ryan10/01/2002
Computing professionals know about hardware and software. Another "ware" term, "middleware," is important when discussing systems integration. I define middleware as the tools needed to connect disparate systems.
Middleware is software that manages several parts of a connection between systems-the physical and logical communications, data translation, error recovery, message sequencing and so on. A programmer can put all of these pieces together, perhaps with socket programming or some client/server technique. I've done this many times and written several articles on the topic, but a product that can handle some of the details is often helpful.
This is where IBM's WebSphere* MQ (formerly MQSeries*) comes into play. (I'll be using the term MQ to refer to these software products from IBM.) More MQ information is available online.) MQ is used for applications between systems where the connectivity and assured delivery of messages is critical (e.g., moving information between banking and financial institutions).
MQ is used for time-independent, message-driven applications. Unlike socket programming, where a server must be running and listening in order for a client to connect, MQ can send a message to another system even if the other system isn't ready to receive it. For instance, the systems application or entire system or network could be down.
This article provides an overview of MQ configuration on iSeries, describes its components and shows RPG code samples for an iSeries implementation of MQ. While this article doesn't show all of MQs flexibility and power, it presents an example of how MQ can be used.
Overview of MQ Components
MQ uses four main components-messages, queues, queue managers and channels. (More information regarding MQ components can be found in the article"Family Portrait.") It's important to note that this isn't a complete list of MQ components; others are available for different MQ configurations.
Messages-A message is the data sent between applications. This is a series of bytes that has meaning to the application but no particular meaning to MQ. An MQ message has two parts-the application data and a message descriptor. The application data is what the applications are sending and receiving with each other. The message descriptor is unseen by the application programmer. It contains information such as the "from" and "to" queue names, the priority, MQ levels, security information, etc.
Queues-A queue is a repository for messages that may be sent to or received from a queue. MQ queues are similar to OS/400* data queues (object type *DTAQ). APIs access MQ data queues just like OS/400 data queues (QSNDDTAQ and QRCVDTAQ). Another similarity is that both MQ queues and OS/400 data queues are application-independent. While the application must be aware of the data queue or MQ queue and understand its characteristics and usage, the queue may be interacting with many different applications. Queues also may be used exclusively by a certain application, or applications may be using the queues for different reasons.
OS/400 data queues are homogenous (they work with a single system) and iSeries specific. MQ queues are heterogeneous (they work with multiple systems) and aren't platform-specific. MQ queues may be put-enabled (messages can be placed on the queue) and/or get-enabled (messages may be retrieved from the queue). Other characteristics include the number of messages that may be placed on a queue (queue depth) and the size of the messages placed on the queue (queue size).
Queue managers-Queue managers are responsible for queues. Every queue, whether local or remote, belongs to a queue manager. A local queue is owned or managed by a queue manager to which the application is connected. A remote queue is owned or managed by a queue manager to which the partner application is connected. These queue managers could be on the same system or, more likely, different systems. Remote and local queues are queue manager and application concepts-they aren't location specific. This is the mechanism for MQ communications-a local application puts messages on a remote queue, which is a local queue to the remote system, and the remote application puts messages on a local queue, which is remote to the local application. Figure 1 details this interaction.
Channels-Channels are the tie between the actual communications and the MQ queue managers. This is where the type of communications (TCP/IP or SNA) that'll be used for MQ communications is identified. Two types of channels exist-message and Message Queuing Interface (MQI) channels. A message channel is used for server-to-server communication. Message channels are unidirectional, so you need two channels (sending and receiving) for communication between two servers. An MQI channel is bi-directional and connects a client to a server. This brings up an interesting point-when clients connect to a server, the only queues used exist on the server-there are no client queues. With server-to-server communications, each server has its own set of remote and local queues to communicate with the other system.
Another part of MQ configuration and implementation is the listener process, which listens for an incoming message from a system. Once received, the message is directed to the appropriate queue. Triggering causes the messages in a queue to be sent based on size or depth (as explained previously) or by an external event. Initiator and transmission queues are special queues that cause message queues to become active and process the queue entries.
When I first started working with MQ on iSeries I noticed that the MQ commands, displays and interaction with objects differed from other OS/400 products. As you work with MQ, you'll see that some of the screen refreshes and the methods of interacting with MQ (such as function keys) take time to get used to. They're not difficult to use-just different.
MQ is installed via a standard OS/400 installation using the Restore Licensed Program (RSTLICPGM) command. Use this command:
RSTLICPGM LICPGM(5733A38) DEV(OPT01) OPTION(*BASE)
to install the base MQ code. This command:
RSTLICPGM LICPGM(5733A38) DEV(OPT01) OPTION(1)
restores the sample programs, which I suggest installing-they're helpful when you're developing your first MQ program. As mentioned in the sidebar,"More Than Software," the QMQM library is loaded in the QSYS file system, and directories and objects are created in the IFS. A number of OS/400 commands should be examined to ensure the default authority is correct. Some commands must have *PUBLIC authority granted or revoked, and others must have users QMQM and/or QMQMADM granted authority. These two user profiles are automatically added by the MQ installation. Consult the IBM documentation for a complete list of the commands that must be examined or changed. Because the MQ jobs run in the QMQM subsystem, start the subsystem before doing any configuration. To start the subsystem, use the Start Subsystem command, specifying subsystem description QMQM:
Jobs don't actually appear in the subsystem until you start a queue manager-I'll outline the queue manager configuration and startup later in the article.
When you have MQ installed and the QMQM subsystem active, use the Work with MQSeries Managers (WRKMQM) command to create a queue manager. Figure 2 shows an example of the WRKMQM command display. This screen enables you to perform MQ functions (e.g., Change, Delete, Display Start and End managers, Work with Queues and Work with Channels; other options are available for other MQ functions).
Create a queue manager by pressing F6 from the WRKMQM display or using the Create MQ Manager (CRTMQM) command. Figure 3 shows the display. While MQ provides many options, the only parameters I need are the Message Queue Manager Name (TEST.QMANAGER) and the Description. Another parameter that may be used is the Default Queue Manager parameter, which allows you to establish this queue manager as the default and enables you to specify this default when creating other objects. I usually don't make a queue manager as a default. Because a system may have multiple queue managers, I'd rather specify the queue manager I'm working with to avoid using the wrong one. Press "Enter" to create the queue manager. You'll then be returned to the Work with Queue Manager display. Option 14 starts this queue manager. (Note: The queue manager must be started to configure queues or channels.)
After creating the queue manager, I usually create the channel. In this example, the channel definition is straightforward. Select the channels display by entering option 20 next to the newly created queue manager name. When the display is shown, press F6 to create a new channel. (Note: There will be many channels already created. These are system channels used by certain MQ configurations.) Figure 4 shows the display that will be shown.
I've entered the channel name (TEST.CHNL), channel type (*SVRCN-a server connection channel) and description. (Reminder: The queue manager name is already completed because I selected the channels option next to the queue manager name on the WRKMQM screen.) An important parameter is the message channel agent user ID. I've specified this to be user profile QMQM, which means that the authority for puts and gets to the channel uses this user profiles authority. As with the queue manager definition, a number of parameters are available, though I only need the ones specified for this example. Enter the appropriate parameters and press "Enter" to create the channel definition. (Note: The Create MQM Channel (CRTMQMCHL) command also could be used to create the channel definition.)
The next step is to create the queues. At the WRKMQM screen, enter option 18 next to the queue manager name. Several already-created queues will be displayed. As with channels, these are system queues used by certain MQ configurations. Press F6 to create a new queue definition (see Figure 5).
I've specified the queue name (TEST.TO.NT), type (*LCL-a local queue) and description. Again, the queue manager name is filled in because I created this queue from the WRKMQM display. This queue would be used to send messages (replies) from the iSeries system to the Windows* system. I also would create another queue (TEST.FROM.NT) for messages (requests) sent from the Windows system to the iSeries system. With the exception of the name and description, the parameters for the queue definition would be identical to those on the TEST.TO.NT queue.
As seen on other MQ displays, there are many parameters to choose from when creating MQ objects. I've selected the parameters on these different commands to create a queue manager, channel and queue definition for a client (Windows 2000 in this case) to communicate with an iSeries server. A server-to-server connection requires different values for the commands, especially in the queue definition. You must specify both local and remote queues and have information (or specify the values) for the remote system.
Programming with MQ
The programming model for MQ applications is similar to any communications program-one system (usually the client) sends a message (a request for services) to the other system (usually the server). The server then replies with the information desired. This process continues until the client or server ends the communication. MQ is no exception. However, when contrasting MQ with sockets programming, the amount of code needed for an MQ program is less than a socket program because MQ does more for the application programmer. For example, when I write an MQ program, I'm not concerned with procedures such as bind(), socket(), listen(), accept() or gethostbyname(). Even though MQ usually uses TCP/IP, as a programmer I don't have to code the low-level communications and handshaking. My program also doesn't need to ensure that the message was delivered-MQ handles that. I still need error detection and recovery in my MQ programs-I just don't need these functions to the extent that I do with sockets programming. MQ uses APIs to make connections with queue managers and queues to send and receive data. The MQI defines the APIs used in communication. The MQ API set is fairly small:
MQCONN-Connect to a queue manager
MQDISC-Disconnect from a queue manager
MQOPEN-Open an MQ object (queue)
MQCLOSE-Close an MQ object (queue)
MQPUT-Put a message to an MQ queue
MQGET-Get a message from an MQ queue
These APIs are all that are needed to provide communications when using MQ. Other APIs that are available for more advanced applications include:
MQINQ-Inquire about an MQ object (queue, queue manager, channel...)
MQSET-Set some of the attributes of a queue
MQBEGIN-Begin a unit of work in a commitment control environment
MQCMIT-Commit a unit of work in a commitment control environment
MQBACK-Rollback a unit of work in a commitment control environment
Most MQ applications follow this general flow of events:
Connect to a queue manager with MQCONN
Open the queues needed for communication with MQOPEN
Use MQPUT and MQGET to send and receive messages
Close the opened queues with MQCLOSE
Disconnect from the queue manager with MQDISC
Using MQ in an RPG application is simple. The procedures called by the APIs are in service program QMQM/LIBMQM, so ensure this service program is bound by reference with your RPG program. The data structures and prototype definitions can be found in library QMQM. Code Sample 1 shows an example of /INCLUDEs. These includes bring in the named constants, object and message descriptor information, and put and get option information. Review these include files to identify the field names and prototyped names to use in your program.
Code Sample 2 shows an example of connecting to a queue manager. "QMName" contains the queue managers name. "HConn" is returned for the API call and contains the handle thatll be used in later calls. The handle identifies the specific queue manager and is used internally by MQ. "OCode" and "Reason" are returned by the API call and identify errors. (Note: Check OCode to see if its equal to the named constant CCFail-if so, an error occurred. Put appropriate error-recovery code here.) This call completes the first step in the MQ application flow-connecting to the queue manager.
Code Sample 3 shows how to open an MQ queue. I set variable Opts to special-named constant values "OOInpq" and "OOFIQ." This tells MQ to open the queue as input and to fail if quiescing, which means that this call should fail if MQ is rendered inactive (i.e., because the system is coming down for maintenance or backups, etc.). I specify variable ODon to have the value of ReplyQue, a variable that contains a queue name. (Note: There already must be an association between the queue manager and queue-this was established when MQ was configured.) "HConn" is the queue manager handle, which was obtained in the MQConn call. The MQOD variable specified in the MQOpen call is a data structure. "ODon" is one of the many subfields in the data structure. "HinObj" is returned from the API call and contains the handle for the opened queue. Use this handle for "MQPuts" and "MQGets." As before, "OCode" and "Reason" are returned from the API call and indicate an error. If "Reason" doesn't contain the value in named constant RCNone, an error has occurred.
An MQPut follows the same pattern (see Code Sample 4). MQ* variables are subfields in data structure MQMD and MQPMO. Set these variables to specific values determined by application needs. "BufLen" contains the length of the buffer of information being sent. "BufPtr" is a variable of type pointer that contains the buffers address. And as before, "Reason" determines if an error has occurred.
The MQGet call is almost identical (see Code Sample 5). Again, note the MQ* variables, which contain specific values used by the MQGet call to control its operation and are subfields in the MQMD and MQGMO data structures. "BufLen" is the length of the receive buffer, "BufPtr" is a pointer to the buffer and "MsgLen" is the number of bytes returned from the MQGet call. In this case, I'm looking for a specific Reason code and not signaling an error if its encountered. A 2033 Reason code (in named constant RC2033) indicates that an MQGet was performed and no data was available to be read. This is certainly possible-the other system may have no data to send at that time.
Code Sample 6 shows an example of MQClose, which closes an open queue. Specify the queue manager handle HConn, the queue handle HinObj and an options field. Again, check the returned "Reason" for errors.
The MQDisc call, used to disconnect from the queue manager, is shown in Code Sample 7. This is a simple call that specifies the handle from the MQConn call and checks the Reason code after the call completes.
The C client code that runs on the Windows 2000 system doesnt differ much-one of MQs advantages. Code Sample 8 shows an example of the MQOpen call in C. It looks similar to the RPG, doesn't it? MQ uses the same APIs regardless of the platform on which it runs. Sure, the syntax may differ and the method of including the structures and prototypes is language-dependent, but the API calls are the same with the same name, prototype definitions and field names.
The Middleware Solution
MQ is a strong and robust solution to middleware problems. Whenever a need for connectivity arises, especially between different platforms, I always see if MQ can solve it. The reliability, flexibility, ease of configuration and programming make MQ an excellent choice for connectivity and system integration issues.
This article provided an overview of MQ and its components, and gave a glimpse at the type of programming needed for an MQ solution. Examine your requirements-do you have a need for connectivity that requires guaranteed delivery, store and forward processing, once-only delivery and is easy to implement? If so, take a look at MQ-you'll be glad you did.
Michael Ryan, i5 Business Systems edition technical editor, has more than 25 years of experience with the System i platform.