This chapter describes how to develop message queuing applications that use MessageQ MQSeries Connection to exchange messages. It covers the following topics:
You should be familiar with the MessageQ and MQSeries application development environments and follow the coding standards required by each. If you are writing the MessageQ half of an application, you must code the MessageQ application programming interfaces (API) for attaching to the bus, locating target queues, sending and receiving messages, and controlling the application message flow.
Conversely, if you are writing the MQSeries half of the application, you must code to the MQSeries message queuing interface (MQI) for connecting to a Message Queue Manager (MQM), opening and closing queues, sending and receiving messages, and controlling the message flow.
Recall that your application interacts with the local messaging system (the one that your application is attached or connected to). Forwarding messages to another messaging system is the responsibility of the Queue Message Bridge (QMB) and is not directly under your application's control.
Exchanging messages between MessageQ and MQSeries applications is complex because it requires knowledge of how messages are sent and received by each system. The QMB performs the transformation of the message header data into the format required by the message queuing system running on the receiver platform, but the application developer must understand how the QMB performs the transformation to ensure the correct result. You can use the MessageQ MQSeries Connection to exchange information between both new and legacy applications. Legacy applications may require some alteration unless they use only a simple datagram or request/reply paradigm.
When designing message-driven applications, you must decide how the messages are to be sent and received. You may want to design your application so that it sends a request and receives a reply. Or, you may want it to send a datagram message only. To have your application behave in the way you want it to, you must design the following into your application:
Using Application Programming Interfaces
Designing Applications to Use MessageQ MQSeries Connection
This topic describes the key design considerations for integrating applications that exchange messages between MessageQ and MQSeries applications.
When designing message-based applications, you must determine what services are needed by your application and how to access these services. In message-based applications, your application accesses target applications by sending a message to a queue that is controlled by the target. This queue must be known and accessible to the application sending the message.
Because the QMB provides communication services to both MessageQ and MQSeries applications, it must present queues to each messaging system in a way that the messaging system recognizes. MessageQ MQSeries Connection does this through the QMB Local Service Queue (LSQ). The receiving application is located on the target messaging system and is listening on a queue known as the Remote Service Queue (RSQ). The QMB manages the LSQ and maintains the LSQ to RSQ relationship.
Before a configured MQSeries or MessageQ queue can be used as a QMB LSQ or RSQ, you must configure it appropriately in the QMB queue configuration file. The entries in this file determine the LSQ to RSQ relationship.
For more information on the QMB configuration file, see the Configuring the Queue Message Bridge topic.
To define queues for applications that send messages from MessageQ clients to MQSeries servers, follow these steps:
Determining Queues that Your Application Needs
Defining Queues for MessageQ Clients to MQSeries Servers
For information on defining MessageQ queues, see the MessageQ Installation and Configuration Guide for UNIX.
To define queues for applications that send messages from MQSeries clients to MessageQ servers, follow these steps:
For information on defining MQSeries queues, see the MQSeries Command Reference Manual.
When designing message queuing applications, you will have to decide the characteristics of the message that your application will send. The characteristics of the message can determine the behavior of the receiving application, so it is important that you select the correct characteristics. The following topics describe some of the message characteristics from which you can choose:
You can use the QMB to exchange the following types of messages:
Selecting the Type for Message Exchange
The QMB determines the type of message using the MessageQ message type and class fields or by using the MQSeries MsgType header.
An application program must give special consideration to a request type message exchange. Certain rules must be followed to allow the successful delivery of the reply to the originating application's reply queue. Applications that intend to send reply messages must adhere to the following rules:
Processing Reply Messages
pams_get
source address if the receiving application is written using MessageQ.
The QMB uses an internally created entity, known as a Connection Index (CI), to link a reply message to a reply target. This CI is carried in a message header field of the request and returned in the same field of the reply.
The QMB passes the CI in the appropriate message header fields. The actual data structure used in the message header fields varies depending on the direction of the request message as follows:
For the ApplIdentityData field to pass the CI, call MQOPEN to open the MQSeries queue with the MQOO_SET_ALL_CONTEXT option. Then, construct all put messages with the MQPMO_SET_ALL_CONTEXT option.
MessageQ MQSeries Connection supports multiple replies from a single request. Because of the difference in the way reply messages are processed by MessageQ or MQSeries, the rules for sending multiple replies are different. These differences are explained in the following topics:
Processing Multiple Replies
The MQSeries application sending the replies formats the data of each reply, includes the ApplIdentityData, and sends the message to the ReplyToQ and ReplyToQMgr received in the request message descriptor. The MQSeries application repeats this for each reply until complete.
Listing 2-1 shows an MQSeries server code fragment, which gets messages, processes them, and sends a reply.
How MQSeries Applications Process Multiple Replies
Listing 2-1
MQSeries Server Code Fragment to Send a Reply
/*****************************************************************/
/* MQSeries Server Code Fragment to Get messages from the */
/* message queue, process the message, and send a reply */
/* request. */
/*****************************************************************/
/* Open the message queue for shared input. */
/*****************************************************************/
memcpy(odG.ObjectName, /* Name of input queue */
QName,
MQ_Q_NAME_LENGTH);
O_options = MQOO_INPUT_SHARED /* Open queue for shared input */
+ MQOO_FAIL_IF_QUIESCING,
+ MQOO_SAVE_ALL_CONTEXT; /* Allow context passing */
MQOPEN(Hcon, /* Connection handle */
&odG, /* Object descriptor for queue */
O_options, /* Open options */
&Hobj, /* Object handle */
&CompCode, /* MQOPEN completion code */
&Reason); /* Reason code */
if(Reason != MQRC_NONE)
{
}
if(CompCode == MQCC_FAILED)
exit(Reason);
.
.
.
buflen = sizeof(buffer) - 1;
while (CompCode == MQCC_OK)
{
gmo.Options = MQGMO_ACCEPT_TRUNCATED_MSG
+ MQGMO_WAIT; /* Wait for new messages */
gmo.WaitInterval = MQWI_UNLIMITED; /* Waiting forever */
md.Encoding = MQENC_NATIVE;
md.CodedCharSetId = MQCCSI_Q_MGR;
MQGET(Hcon, /* Connection handle */
Hobj, /* Object handle */
&md, /* Message descriptor */
&gmo, /* GET options */
buflen, /* Buffer length */
buffer, /* Message buffer */
&messlen, /* Message length */
&CompCode, /* Completion code */
&Reason); /* Reason code */
if (Reason != MQRC_NONE)
printf("MQGET: ended with reason code %ld\n", Reason);
if ((CompCode == MQCC_OK) || (CompCode == MQCC_WARNING))
{ /* Check byte order ENDIAN */
if((strncmp(md.ApplOriginData,"BEND",4)) == 0)
{
}
else if((strncmp(md.ApplOriginData,"LEND",4)) == 0) {
}
/*******************************************************/
/* Only process REQUEST messages */
/*******************************************************/
if (md.MsgType == MQMT_REQUEST)
{
/*****************************************************************/
/* Send reply using MQPUT1 */
/*****************************************************************/
md.MsgType = MQMT_REPLY;
/*****************************************************************/
/* Copy the ReplyToQ name to the object descriptor. */
/* Copy the ReplyToQMgr name to the object descriptor. */
/*****************************************************************\
strncpy(odR.ObjectName, md.ReplyToQ, MQ_Q_NAME_LENGTH);
strncpy(odR.ObjectQMgrName, md.ReplyToQMgr,
MQ_Q_MGR_NAME_LENGTH);
md.Report = MQRO_NONE; /* No report data. */
pmo.Options = MQPMO_SET_ALL_CONTEXT + /* Allow DMQ address*/
MQPMO_FAIL_IF_QUIESCING;/* to be passed in */
pmo.Context = Hobj; /* md.ApplIdentityData*/
/*****************************************************************/
/* Because this code fragment uses the same message descriptor */
/* (md.) for both receiving and sending messages, we do not have*/
/* to save the ApplIdentityData field and copy to the output MD */
/* before we send the reply. If separate input and output */
/* message descriptors were used, a copy would be required. */
/* The ApplIdentityData field carries the DMQ client address, */
/* which is the end target for the reply data. */
/*****************************************************************/
send_reply = process_message(buffer);
while(send_reply > 0)
{
-=send_reply;
MQPUT1(Hcon, /* Connection handle */
&odR, /* Object descriptor */
&md, /* Message descriptor */
&pmo, /* Default options */
messlen, /* Message length */
buffer, /* Message buffer */
&CompCode, /* Completion code */
&Reason); /* Reason code */
if(Reason != MQRC_NONE)
printf("MQPUT1: ended with reason code %ld\n", Reason);
} /* End send_reply. */
} /* End request. */
} /* End message for reply. */
} /* End Get message loop. */
Typically, when a message queuing application responds to a request, the reply may be one in a series of messages, the last in a series, or the only message. The way that a MessageQ application indicates this is to use the message header type field in the reply. If the message type field is a positive integer, the reply is the only or last message. If the message type field is negative, the reply is one in a series of messages. All MessageQ replies are mapped to the MQSeries MQMT_REPLY. It then becomes the responsibility of the MQSeries application to handle the first, last, and only replies appropriately.
This scheme of handling first, last, and only replies allows the QMB server to keep the connection active until all replies for a request have been processed. When the last or only message is processed, the CI is removed from the CI table. To make the message type field negative, the sending process subtracts the message type field from zero (0 minus message type field) before inserting it into the message type field for the reply message.
Listing 2-2 shows a code fragment for a MessageQ server to send a reply.
Listing 2-2 MessageQ Server Code Fragment to Send a Reply
/*****************************************************************/
/* This is a code fragment of a MessageQ server that listens */
/* on a queue for messages. This queue is a QMB Remote Service */
/* Queue (RSQ) that is defined in a QMB configuration file. */
/* Messages arriving on the MQSeries LSQ are forwarded by the */
/* QMB to this MessageQ RSQ for processing. */
/*****************************************************************/
while (TRUE)
{
/* Listen, process, and reply if required. */
memset(msg_area, 0, sizeof(msg_area));
status = pams_get_msgw(msg_area,
&prio,
&source,
&class,
&type,
&max_len,
&length,
&timeout,
(int32 *) sel_addr,
&lpsb,
&show_buf,
&show_buf_len,
(char *)0,
(char *) 0, /* Reserved*/
(char *) 0 ); /* Reserved*/
if(status != PAMS_SUCCESS)
{
}
else
{ /* Check the received message class and act accordingly.*/
if(class < 0) /* Neg class is a QMB range=implied request */
{
send_reply = process_request(msg_area);
/* To send a reply, you need: receive source address */
/* receive class */
/* receive type */
while(send_reply >= 0)
{
-=send_reply; /* Decrement reply count */
prio = 0; /* Regular priority */
delivery = PDEL_MODE_NN_MEM; /* No Notification */
timeout = 300; /* Wait 30 seconds */
send_uma = PDEL_UMA_DISC; /* If can't deliver */
/* it, discard. */
put_msg_size = length;
if(send_reply == 0)
s_type = type; /* Set type pos - last or only*/
else
s_type = (0 - type);/* Set type neg-reply element */
status = pams_put_msg(msg_area,
&prio,
&source, /* Reply add passed */
&class, /* Forward class back*/
&s_type, /* Forward type back */
&delivery,
&put_msg_size,
&timeout,
(struct psb *) &lpsb,
&send_uma,
(q_address *) 0,
(char *) 0,
(char *) 0,
(char *) 0 );
if(! (status & 1) )
{
}
} /* end send_reply */
}
else if(class == MSG_CLAS_QMB)
{
if(type == MSG_TYPE_DATAGRAM)
{
}
else if(type == MSG_TYPE_RTS_ERROR)
{
}
else
{
}
}
else if(class == MSG_CLAS_PAMS)
{
if(type == MSG_TYPE_AVAIL)
{
}
else if(type == MSG_TYPE_UNAVAIL)
{
}
else
{
}
}
If your MessageQ server application is required to process multiple reply messages, make sure that the CI purge interval (by using the -i
parameter on the QMB startup command line) is long enough to allow all the replies to occur. (See the Starting the Queue Message Bridge topic for more information on the qmbsrv
command line.)
Messaging systems use message types to control the logic of a program in a message-driven environment. A message type is a specific identifier associated with a message. These types may be assigned by the messaging systems or user application and are contained in the message header portion of the message. Because it's part of the message, message types are passed in each message sent and received.
In addition to message types, MessageQ provides a message control field called a message class. In MessageQ message-driven applications, the message class and message type are usually tightly coupled with the program logic. Therefore, when designing and implementing a message-driven interface that has defined message classes and types, all participating programs must understand and follow the rules governing message classes and types.
The specific MessageQ message classes and types defined for use with the QMB allow for maximum flexibility when porting existing MessageQ applications. These applications may contain their own message class and type definitions, which must now coexist with the additional QMB definitions.
Whether porting existing applications or writing new ones, you must design the programs to obey the rules defined by the message class and message type values of the QMB, regardless of whether the application is a sending or receiving messages.
The QMB uses both specific values and ranges of values (both positive and negative) for message class and message type identifiers.
The message flow dialog between a QMB process and a MessageQ Client or Server is driven by a set of predefined MessageQ class and type identifiers. The actual values in these message identifiers are determined by the following factors:
The class and type fields may be specific values or ranges of values depending on the combination of the previous factors. Generally, sending applications are required to supply specific class and type values to facilitate the correct message disposition. Likewise, receiving applications (if sending back a reply) are required to return the received class and type fields with the appropriate reply processing indicator (multi or single element).
See the Table 2-1 describes the message classes that are available to a MessageQ client.qmbuser.h
include file for the actual class and type values. The qmbuser.h
include file is found in /usr/kits/DMC32B/include
(or in /usr/kits/DMC32C/include
).
Table 2-2 describes the message classes that are available to a MessageQ server.
Table 2-3 describes the message types that are available to a MessageQ client.
Table 2-4 describes the message types that are available to a MessageQ server.
The MessageQ MQSeries Connection supports the MQSeries message types described in Table 2-5. These message types are inserted or received in the MQSeries Message Descriptor (MQMD) by the MQSeries applications. The message types vary depending on the source and state of the dialog.
Table 2-5 describes the message types that are available to an MQSeries client.
In Table 2-5, MQMT_RTS is a user-defined MQSeries message type. It is defined as follows:
# define MQMT_RTS MQMT_APPL_FIRST + 1
MQSeries client applications must be prepared to receive this message type as a valid response to a MQMT_REQUEST.
Table 2-6 describes the message types that are available to an MQSeries server.
To recover messages after a system mishap, your application must have message persistence defined. Message persistence describes a message that is written to nonvolatile storage. This means that persistent messages can survive a system restart.
Messages received with MQSeries Persistence or MessageQ Message Recovery Services are forwarded by the QMB with the corresponding persistence or MRS mode.
MessageQ MQSeries Connection locksteps the MQSeries persistence mode with the MessageQ Message Recovery Services and delays the confirmation of a message until the message has been forwarded and safely stored. Persistence mode processing may affect the performance of your application. You may want to have separate persistence and nonpersistence services.
Note: MQSeries dynamic temporary queues do not support message persistence. MessageQ temporary queues do not support Message Recovery Services.
Message priority is the priority the message assumes for delivery.
Message priority is maintained between the MessageQ and MQSeries messaging systems as follows:
priority 0.
MQSeries queues defined with a Message Delivery Sequence as first-in/first-out (FIFO) order ignore priority.
Message header data is defined to be the MQSeries message descriptor (MQMD) and the MessageQ message attributes. The content of many fields is the same in meaning but quite different in format. Because of these differences in the message header formats between the two messaging systems, a limited amount of mapping of fields is performed by the QMB applications. Include a user application level header as part of the message body to allow application-specific information exchange (if required by the application design).
There is a loose coupling between a subset of MQSeries and MessageQ message header fields, which are maintained by the QMB server in a limited manner.
Table 2-7 describes coupling of MQSeries and MessageQ message header fields. How Message Header Data is Mapped
The content of the message buffer is forwarded as received. It is the responsibility of the receiving application to interpret the byte order and normalize it, if required. The QMB forwards byte order information as follows:
We recommend that you use string data (if possible) or user-defined fields in the message body to carry application-specific information about the message.
Character code conversion between the QMB and MVS based MQSeries applications is supported as follows:
Character Code Conversion
When designing message queuing applications, you choose the characteristics of the message. These characteristics indicate to the QMB how the message is to be processed and include the following:
Guidelines for Choosing Message Characteristics
The guidelines to choose message characteristics are as follows:
Suppose you want your MessageQ client to send a request to an MQSeries server and receive a reply. In order for the message to be sent, the proper MessageQ, MQSeries, and QMB configurations must be correct and all messaging systems and application programs must be executing.
The following example provides details about the MessageQ client and MQSeries server applications:
Sending a Request to an MQSeries Server
Name = DMQ_CLIENT
rspq = DMQCAdd
Message = Request Data
Class = MSG_CLAS_QMB
Type = MSG_TYPE_REQUEST
Target queue = MQS_ECHO
Table 2-8 describes the required queue definitions to send a message to an MQSeries server for the previous example.
The MessageQ LSQ named MQS_ECHO is associated to the MQSeries RSQ named MQS_ECHO_SERVER in a QMB configuration file. This file stores the LSQ-to-RSQ relationship that the QMB uses when forwarding messages.
Listing 2-3 shows the QMB configuration file for the MQS Series server.
Listing 2-3 MQSeries Server Queue Message Bridge Configuration File
!LSQ Name LSQ RSQ RSQ
!Name Owner Name Association
!
MQS_ECHO D MQS_ECHO_SERVER S
Table 2-9 shows the contents of the QMB configuration file.
Figure 2-1 shows the sending of a request to an MQSeries server.
Figure 2-1 Sending a Request to an MQSeries Server
Note the following about Figure 2-1:
ReplyToQ = MQS_REPLYQ
The QMB maps and forwards the message to the MQSeries RSQ named MQS_ECHO_SERVER.
Consider the following application that sends a reply from an MQSeries server to a MessageQ client:
Name = MQS_ECHO_SERVER
Message = Reply Data
ReplyToQ = MQS_REPLYQ
Type = MQMT_REPLY
ApplIdentityData = CI
Target = ReplyToQ and ReplyToQMgr
Name = DMQ_CLIENT
Message = Reply Data
Class = MSG_CLAS_QMB
Type = MSG_TYPE_REPLY
Figure 2-2 shows the sending of a reply to a MessageQ client.
Note the following about Figure 2-2:
Figure 2-2 Sending a Reply to a MessageQ Client
When writing message-based applications, use MessageQ and MQSeries system functions and services, where applicable. These functions can help determine the status and state of the bridge, associated application programs, network services, and messaging systems.
Suppose that you want to send a request to an MessageQ server. In order for this to work, you must have MessageQ, MQSeries, and QMB properly configured and running.
Consider the following application that sends an MQSeries message to a MessageQ server:
Name = MQS_CLIENT
Message = Request Data
ReplyToQ = CLI_REPLYQ
Type = MQMT_REQUEST
Target queue = DMQ_ECHO
Name = DMQ_ECHO_SERVER
Src = DMQ_REPLYQ
Message = Request Data
Class = QMB range
Type = CI
Table 2-10 provides an example of the required queue definitions.
The MQSeries LSQ (named DMQ_ECHO) is associated to the MessageQ RSQ (DMQ_ECHO_SERVER) in a QMB configuration file entry, as shown in Listing 2-4.
Listing 2-4 Example -4: DMQ_ECHO Queue Message Bridge Configuration File
!LSQ Name LSQ RSQ RSQ
!Name Owner Name Association
!
DMQ_ECHO M DMQ_ECHO_SERVER S
Table 2-11 shows the key to the QMB configuration file for the DMQ_ECHO_SERVER application.
Figure 2-3 shows an MQSeries client sending a request to the MessageQ server.
Figure 2-3 Sending a Request to a MessageQ Server
In Figure 2-3, note that an MQSeries client (named MQS_CLIENT) sends a request message to the MQSeries LSQ named DMQ_ECHO. The QMB performs the following functions:
Consider the following application that sends a reply from a MessageQ server to the MQSeries client:
Name = DMQ_ECHO_SERVER
Src = DMQ_REPLYQ
Message = Reply Data
Class = QMB range
Type = CI
Target queue = DMQ_REPLYQ (same as SRC)
Name = MQS_CLIENT
Message = Reply Data
ReplyToQ = CLI_REPLYQ
Type = MQMT_REPLY
In Figure 2-4, the MessageQ server (DMQ_ECHO_SERVER) reads the message, processes it, and sends back a reply to the queue (DMQ_REPLYQ) designated in the respq field. The reply must include the request class and type.
When the reply message arrives in the MessageQ to MQSeries (QMBDM) DMQ_REPLYQ, the QMBDM program performs the following functions:
Figure 2-4 Sending a Reply to an MQSeries Client
MessageQ MQSeries Connection has the following restrictions and limitations: