Example 1: Publisher to a fixed topic

An IBM MQ program to illustrate publishing to an administratively defined topic.

Note: The compact coding style is intended for readability not production use.
Figure 1. Simple IBM MQ publisher to a fixed topic.

See the output in Figure 2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
int main(int argc, char **argv)
{
    char     topicNameDefault[]   = "IBMSTOCKPRICE";
    char     publicationDefault[] = "129";
    MQCHAR48 qmName = "";

    MQHCONN  Hconn = MQHC_UNUSABLE_HCONN; /* connection handle                      */
    MQHOBJ   Hobj = MQHO_NONE;            /* object handle sub queue                */
    MQLONG   CompCode = MQCC_OK;          /* completion code                        */
    MQLONG   Reason = MQRC_NONE;          /* reason code                            */
    MQOD     td = {MQOD_DEFAULT};         /* Object descriptor                      */
    MQMD     md = {MQMD_DEFAULT};         /* Message Descriptor                     */
    MQPMO   pmo = {MQPMO_DEFAULT};        /* put message options                    */
    MQCHAR   resTopicStr[151];            /* Returned vale of topic string          */
    char *   topicName   = topicNameDefault;   
    char *   publication = publicationDefault;
    memset   (resTopicStr, 0, sizeof(resTopicStr));

    switch(argc){                         /* replace defaults with args if provided */
        default:
            publication = argv[2];
        case(2): 
            topicName = argv[1];
        case(1):
            printf("Optional parameters:  TopicObject Publication\n");
    }
    do {
        MQCONN(qmName, &Hconn, &CompCode, &Reason);
        if (CompCode != MQCC_OK) break;
        td.ObjectType = MQOT_TOPIC;        /* Object is a topic                     */
        td.Version = MQOD_VERSION_4;       /* Descriptor needs to be V4             */
        strncpy(td.ObjectName, topicName, MQ_TOPIC_NAME_LENGTH);
        td.ResObjectString.VSPtr = resTopicStr;
        td.ResObjectString.VSBufSize = sizeof(resTopicStr)-1;
        MQOPEN(Hconn, &td, MQOO_OUTPUT | MQOO_FAIL_IF_QUIESCING, &Hobj, &CompCode, &Reason);
        if (CompCode != MQCC_OK) break;
        pmo.Options = MQPMO_FAIL_IF_QUIESCING | MQPMO_RETAIN;
        MQPUT(Hconn, Hobj, &md, &pmo, (MQLONG)strlen(publication)+1, publication, &CompCode, &Reason); 
        if (CompCode != MQCC_OK) break;
        MQCLOSE(Hconn, &Hobj, MQCO_NONE, &CompCode, &Reason);
        if (CompCode != MQCC_OK) break;
        MQDISC(&Hconn, &CompCode, &Reason);
    } while (0);
    if (CompCode == MQCC_OK) 
        printf("Published \"%s\" using topic \"%s\" to topic string \"%s\"\n",  
               publication, td.ObjectName, resTopicStr);
    printf("Completion code %d and Return code %d\n", CompCode, Reason);
}
Figure 2. Sample output from first publisher example
X:\Publish1\Debug>PublishStock
Optional parameters:  TopicObject Publication
Published "129" using topic "IBMSTOCKPRICE" to topic string "NYSE/IBM/PRICE"
Completion code 0 and Return code 0

X:\Publish1\Debug>PublishStock IBMSTOCKPRICE 155
Optional parameters:  TopicObject Publication
Published "155" using topic "IBMSTOCKPRICE" to topic string "NYSE/IBM/PRICE"
Completion code 0 and Return code 0

The following selected lines of code illustrate aspects of writing a publisher application for IBM MQ.

    char topicNameDefault[] = "IBMSTOCKPRICE";
    A default topic name is defined in the program. We can override it by providing the name of a different topic object as the first argument to the program.

    MQCHAR resTopicStr[151];
    resTopicStr is pointed at by td.ResObjectString.VSPtr and is used by MQOPEN to return the resolved topic string. Make the length of resTopicStr one larger than the length passed in td.ResObjectString.VSBufSize to give space for null termination.

    memset (resTopicStr, 0, sizeof(resTopicStr));
    Initialize resTopicStr to nulls to ensure the resolved topic string returned in an MQCHARV is null terminated.

    td.ObjectType = MQOT_TOPIC
    There is a new type of object for publish/subscribe: the topic object.

    td.Version = MQOD_VERSION_4;
    To use the new type of object, we must use at least version 4 of the object descriptor.

    strncpy(td.ObjectName, topicName, MQ_OBJECT_NAME_LENGTH);
    The topicName is the name of a topic object, sometimes called an administrative topic object. In the example the topic object needs to be created beforehand, using IBM MQ Explorer or this MQSC command,
    DEFINE TOPIC(IBMSTOCKPRICE) TOPICSTR(NYSE/IBM/PRICE) REPLACE;
    

    td.ResObjectString.VSPtr = resTopicStr;
    The resolved topic string is echoed in the final printf in the program. Set up the MQCHARV ResObjectString structure for IBM MQ to return the resolved string back to the program.

    MQOPEN(Hconn, &td, MQOO_OUTPUT | MQOO_FAIL_IF_QUIESCING, &Hobj, &CompCode, &Reason);
    Open the topic for output; just like opening a queue for output.

    pmo.Options = MQPMO_FAIL_IF_QUIESCING | MQPMO_RETAIN;
    You want new subscribers to be able receive the publication, and by specifying MQPMO_RETAIN in the publisher, when you start a subscriber it receives the latest publication, published before the subscriber started, as its first matching publication. The alternative is to provide subscribers with publications published only after the subscriber started. Additionally a subscriber has the option to decline to receive a retained publication by specifying MQSO_NEW_PUBLICATIONS_ONLY in its subscription.

    MQPUT(Hconn, Hobj, &md, &pmo, (MQLONG)strlen(publication)+1, publication, &CompCode, &Reason);
    Add 1 to the length of the string passed to MQPUT to pass the null termination character to IBM MQ as part of the message buffer.

What does the first example demonstrate? The example imitates as closely as possible the tried and tested traditional pattern for writing point to point IBM MQ programs. An important feature of the IBM MQ programming pattern is that the programmer is not concerned where messages are sent. The task of the programmer is to connect to a queue manager, and pass it the messages that are to be distributed to recipients. In the point-to-point paradigm, the programmer opens a queue (probably an alias queue) that the administrator has configured. The alias queue routes messages to a target queue, either on the local queue manager, or to a remote queue manager. While the messages are waiting to be delivered, they are stored on queues somewhere between the source and the destination.

In the publish/subscribe pattern, instead of opening a queue, the programmer opens a topic. In our example, the topic is associated with a topic string by an administrator. The queue manager forwards the publication, using queues, to local or remote subscribers that have subscriptions that match the topic string of the publication. If publications are retained the queue manager keeps the latest copy of the publication, even if it has no subscribers now. The retained publication is available to forward to future subscribers. The publisher application plays no part in selecting or routing the publication to a destination; its task is to create and put publications to the topics defined by the administrator.

This fixed topic example is atypical of many publish/subscribe applications: it is static. It requires an administrator to define the topic strings and change the topics that are published on. Commonly publish/subscribe applications need to know some or all the topic tree. Perhaps topics change frequently, or perhaps although the topics do not change much, the number of topic combinations is large and it is too onerous for an administrator to define a topic node for every topic string that might need to be published on. Perhaps topic strings are not known in advance of publication; a publisher application might use information from the publication content to specify a topic string, or it might have information about topic strings to publish on from another source, such as human input from a browser. To cater for more dynamic styles of publishing, the next example shows how to create topics dynamically, as part of the publisher application.

Topics couple publishers and subscribers together. Designing the rules, or architecture, for naming topics, and organizing them in topic trees is an important step in developing a publish/subscribe solution. Look carefully at the extent to which organization of the topic tree binds of publisher and subscriber programs together, and binds them to the content of the topic tree. Ask yourself the question whether changes in the topic tree affect publisher and subscriber applications, and how we can minimize the effect. Built into the architecture of the IBM MQ publish/subscribe model is the notion of an administrative topic object that provides the root part, or root subtree, of a topic. The topic object gives you the option of defining the root part of the topic tree administratively that simplifies application programming and operations, and consequently improves maintainability. For example, if we are deploying multiple publish/subscribe applications that have isolated topic trees, then by administratively defining the root part of the topic tree, we can guarantee the isolation of topic trees, even if there is no consistency in the topic naming conventions adopted by the different applications.

In practice, publisher applications cover a spectrum from solely using fixed topics, as in this example, and variable topics, as in the next. Example 2: Publisher to a variable topic also demonstrates combining the use of topics and topic strings.

Parent topic: Writing publisher applications


Related concepts