Example 2: Managed MQ subscriber
The managed MQ subscriber is the preferred pattern for most subscriber applications. A managed subscription is one where IBM® MQ handles the subscription and does the registering and de-registering for you. The example requires no administrative definition of queues, topics or subscriptions.
This simplest kind of managed subscriber typically uses a non-durable subscription. The
example focuses on a non-durable subscription. The subscription lasts only as long as the lifetime
of the subscription handle from MQSUB. Any publications that match the topic string
during the lifetime of the subscription are sent to the subscription queue (and possibly a retained
publication if the flag MQSO_NEW_PUBLICATIONS_ONLY is not set or defaulted, an
earlier publication matching the topic string was retained, and the publication was persistent or
the queue manager has not terminated, since the publication was created).
You can also use a durable subscription with this pattern. Typically if a managed durable subscription is used it is done for reliability reasons, rather than to establish a subscription that, without any errors occurring, would outlive the subscriber. For more information about different life cycles associated with managed, unmanaged, durable and non-durable subscriptions see the related topics section.
Durable subscriptions are often associated with persistent publications, and non-durable subscriptions with non-persistent publications, but there is no necessary relationship between subscription durability and publication persistence. All four combinations of persistence and durability are possible.
For the managed non-durable case considered, the queue manager creates a subscription queue that is purged and deleted when the queue is closed. The publications are removed from the queue when the non-durable subscription is closed.
- On demand subscription: the subscription topic string is dynamic. It is provided by the application when it runs.
- Self managing queue: the subscription queue is self defining and managing.
- Self managing subscription lifecycle: non-durable subscriptions only exist for the
duration of the subscriber application.
- If you define a durable managed subscription, then it results in a permanent subscription
queue and publications continue to be stored on it with no subscriber programs being active. The
queue manager deletes the queue (and clears any unretrieved publications from it) only after the
application or administrator has chosen to delete the subscription. The subscription can be deleted
using an administrative command, or by closing the subscription with the
MQCO_REMOVE_SUBoption. - Consider setting
SubExpiryfor durable subscriptions so that publications cease to be sent to the queue and the subscriber can consume any remaining publications before removing the subscription and causing the queue manager to delete the queue and any remaining publications on it.
- If you define a durable managed subscription, then it results in a permanent subscription
queue and publications continue to be stored on it with no subscriber programs being active. The
queue manager deletes the queue (and clears any unretrieved publications from it) only after the
application or administrator has chosen to delete the subscription. The subscription can be deleted
using an administrative command, or by closing the subscription with the
- Flexible topic string deployment: Subscription topic management is simplified by defining the root part of the subscription using an administratively defined topic. The root part of the topic tree is then hidden from the application. By hiding the root part an application can be deployed without the application inadvertently creating a topic tree that overlaps with another topic tree created by another instance, or another application.
- Administered topics: by using a topic string in which the first part matches an administratively
defined topic object, publications are managed according to the attributes of the topic object.
- For example, if the first part of the topic string matches the topic string associated with a clustered topic object, then the subscription can receive publications from other members of the cluster
- The selective matching of administratively defined topic objects and programmatically defined subscriptions enables you to combine the benefits of both. The administrator provides attributes for topics, and the programmer dynamically defines subtopics without being concerned about the management of topics.
- It is the resultant topic string which is used to match the topic object that provides the
attributes associated with the topic, and not necessarily the topic object named in
sd.Objectname, although they typically turn out to be one and the same. See Example 2: Publisher to a variable topic.
By making the subscription durable in the example, publications continue to be sent to the
subscription queue after the subscriber has closed the subscription with the
MQCO_KEEP_SUB option . The queue continues to receive publications when the
subscriber is not active. You can override this behavior by creating the subscription with the
MQSO_PUBLICATIONS_ON_REQUEST option and using MQSUBRQ to request
the retained publication.
The subscription can be resumed later by opening the subscription with the MQCO_RESUME option.
You can use the queue handle, Hobj, returned by MQSUB in a
number of ways. The queue handle is used in the example to inquire on the name of the subscription
queue. Managed queues are opened using the default model queues
SYSTEM.NDURABLE.MODEL.QUEUE or SYSTEM.DURABLE.MODEL.QUEUE. You can
override the defaults by providing your own durable and non-durable model queues on a topic by topic
basis as properties of the topic object associated with the subscription.
Regardless of the attributes inherited from the model queues, you cannot reuse a managed queue handle to create an additional subscription. Nor can you obtain another handle for the managed queue by opening the managed queue a second time using the returned queue name. The queue behaves as if it has been opened for exclusive input .
Unmanaged queues are more flexible than managed queues. You can, for example share unmanaged queues, or define multiple subscriptions on the one queue. The next example, , demonstrates how to combine subscriptions with an unmanaged subscription queue.
The results are shown in Figure 3.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
void inquireQname(MQHCONN HConn, MQHOBJ Hobj, MQCHAR48 qName);
int main(int argc, char **argv)
{
MQCHAR48 topicNameDefault = "STOCKS";
char topicStringDefault[] = "IBM/PRICE";
MQCHAR48 qmName = ""; /* Use default queue manager */
MQCHAR48 qName = ""; /* Allocate to query queue name */
char publicationBuffer[101]; /* Allocate to receive messages */
char resTopicStrBuffer[151]; /* Allocate to resolve topic string */
MQHCONN Hconn = MQHC_UNUSABLE_HCONN; /* connection handle */
MQHOBJ Hobj = MQHO_NONE; /* publication queue handle */
MQHOBJ Hsub = MQSO_NONE; /* subscription handle */
MQLONG CompCode = MQCC_OK; /* completion code */
MQLONG Reason = MQRC_NONE; /* reason code */
MQLONG messlen = 0;
MQSD sd = {MQSD_DEFAULT}; /* Subscription Descriptor */
MQMD md = {MQMD_DEFAULT}; /* Message Descriptor */
MQGMO gmo = {MQGMO_DEFAULT}; /* get message options */
char * topicName = topicNameDefault;
char * topicString = topicStringDefault;
char * publication = publicationBuffer;
char * resTopicStr = resTopicStrBuffer;
memset(resTopicStr, 0, sizeof(resTopicStrBuffer));
switch(argc){ /* Replace defaults with args if provided */
default:
topicString = argv[2];
case(2):
if (strcmp(argv[1],"/")) /* "/" invalid = No topic object */
topicName = argv[1];
else
*topicName = '\0';
case(1):
printf("Optional parameters: topicName, topicString\nValues \"%s\" \"%s\"\n",
topicName, topicString);
}
MQHOBJ Hobj = MQHO_NONE;- You cannot explicitly open a non-durable managed subscription queue to receive publications, but
you do need to allocate storage for the object handle the queue manager returns when it opens the
queue for you. It is important to initialize the handle to
MQHO_OBJECT. This indicates to the queue manager that it needs to return a queue handle to the subscription queue. MQSD sd = {MQSD_DEFAULT};- The new subscription descriptor, used in
MQSUB. MQCHAR48 qName;- Although the example doesn't require knowledge of the subscription queue, the example does
inquire the name of the subscription queue - the
MQINQbinding is a little awkward in the C language, so you might find this part of the example useful to study.
do {
MQCONN(qmName, &Hconn, &CompCode, &Reason);
if (CompCode != MQCC_OK) break;
strncpy(sd.ObjectName, topicName, MQ_TOPIC_NAME_LENGTH);
sd.ObjectString.VSPtr = topicString;
sd.ObjectString.VSLength = MQVS_NULL_TERMINATED;
sd.Options = MQSO_CREATE | MQSO_MANAGED | MQSO_NON_DURABLE | MQSO_FAIL_IF_QUIESCING ;
sd.ResObjectString.VSPtr = resTopicStr;
sd.ResObjectString.VSBufSize = sizeof(resTopicStrBuffer)-1;
MQSUB(Hconn, &sd, &Hobj, &Hsub, &CompCode, &Reason);
if (CompCode != MQCC_OK) break;
gmo.Options = MQGMO_WAIT | MQGMO_NO_SYNCPOINT | MQGMO_CONVERT;
gmo.WaitInterval = 10000;
inquireQname(Hconn, Hobj, qName);
printf("Waiting %d seconds for publications matching \"%s\" from \"%-0.48s\"\n",
gmo.WaitInterval/1000, resTopicStr, qName);
do {
memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));
md.Encoding = MQENC_NATIVE;
md.CodedCharSetId = MQCCSI_Q_MGR;
memset(publicationBuffer, 0, sizeof(publicationBuffer));
MQGET(Hconn, Hobj, &md, &gmo, sizeof(publicationBuffer-1),
publication, &messlen, &CompCode, &Reason);
if (Reason == MQRC_NONE)
printf("Received publication \"%s\"\n", publication);
}
while (CompCode == MQCC_OK);
if (CompCode != MQCC_OK && Reason != MQRC_NO_MSG_AVAILABLE) break;
MQCLOSE(Hconn, &Hsub, MQCO_REMOVE_SUB, &CompCode, &Reason);
if (CompCode != MQCC_OK) break;
MQDISC(&Hconn, &CompCode, &Reason);
} while (0);
printf("Completion code %d and Return code %d\n", CompCode, Reason);
return;
}
void inquireQname(MQHCONN Hconn, MQHOBJ Hobj, MQCHAR48 qName) {
#define _selectors 1
#define _intAttrs 1
MQLONG select[_selectors] = {MQCA_Q_NAME}; /* Array of attribute selectors */
MQLONG intAttrs[_intAttrs]; /* Array of integer attributes */
MQLONG CompCode, Reason;
MQINQ(Hconn, Hobj, _selectors, select, _intAttrs, intAttrs, MQ_Q_NAME_LENGTH, qName,
&CompCode, &Reason);
if (CompCode != MQCC_OK) {
printf("MQINQ failed with Condition code %d and Reason %d\n", CompCode, Reason);
strcpy(qName, "unknown queue");
}
return;
}
W:\Subscribe2\Debug>solution2
Optional parameters: topicName, topicString
Values "STOCKS" "IBM/PRICE"
Waiting 10 seconds for publications matching "NYSE/IBM/PRICE" from "SYSTEM.MANAGED.NDURABLE.48A0AC7403300020 "
Received publication "150"
Completion code 0 and Return code 0
W:\Subscribe2\Debug>solution2 / NYSE/IBM/PRICE
Optional parameters: topicName, topicString
Values "" "NYSE/IBM/PRICE"
Waiting 10 seconds for publications matching "NYSE/IBM/PRICE" from "SYSTEM.MANAGED.NDURABLE.48A0AC7403310020 "
Received publication "150"
Completion code 0 and Return code 0
strncpy(sd.ObjectName, topicName, MQ_Q_NAME_LENGTH);- If topicName is null or blank (default value), the topic name is not used to compute the resolved topic string.
sd.ObjectString.VSPtr = topicString;- Rather than solely use a predefined topic object, in this example the programmer provides a topic object and a topic string, that are combined by
MQSUB. Notice the topic string is aMQCHARVstructure. sd.ObjectString.VSLength = MQVS_NULL_TERMINATED;- An alternative to setting the length of a
MQCHARVfield. sd.Options = MQSO_CREATE | MQSO_MANAGED | MQSO_NON_DURABLE | MQSO_FAIL_IF_QUIESCING;- After defining the topic string, the
sd.Optionsflags need the most careful attention. There are many options, the example specifies only the most commonly used ones. The other options use the default values.- As the subscription is non-durable, that is, it has a lifetime of the open subscription
in the application, set the
MQSO_CREATEflag . You can also set the (default)MQSO_NON_DURABLEflag for readability. - Complementing
MQSO_CREATEisMQSO_RESUME. Both flags can be set together; the queue manager either creates a new subscription or resumes an existing subscription, whichever is appropriate. However, if you do specifyMQSO_RESUMEyou must also initialize theMQCHARVstructure forsd.SubName, even if there is no subscription to resume. Failure to initializeSubNameresults in a return code of2440: MQRC_SUB_NAME_ERRORfromMQSUB.Note:MQSO_RESUMEis always ignored for a non-durable managed subscription: but specifying it without initializing theMQCHARVstructure forsd.SubNamedoes cause the error. - In addition there is a third flag affecting how the subscription is opened,
MQSO_ALTER. Given the correct permissions, the properties of a resumed subscription are changed to match other attributes specified inMQSUB.Note: At least one of theMQSO_CREATE,MQSO_RESUMEandMQSO_ALTERflags must be specified. See Options (MQLONG). There are examples of using all three flags in Example 3: Unmanaged MQ subscriber. - Set
MQSO_MANAGEDfor the queue manager to manage the subscription for you automatically.
- As the subscription is non-durable, that is, it has a lifetime of the open subscription
in the application, set the
sd.ObjectString.VSLength = MQVS_NULL_TERMINATED;- Optionally, omit setting the length of
MQCHARVfor null terminated strings and use the null terminator flag instead. sd.ResObjectString.VSPtr = resTopicStr;- The resulting topic string is echoed in first
printfin the program. Set upMQCHARV ResObjectStringfor IBM MQ to return the resolved string back to the program.Note:resTopicStringBufferis initialized to nulls inmemset(resTopicStr, 0, sizeof(resTopicStrBuffer)). Returned topic strings do not end with a trailing null. sd.ResObjectString.VSBufSize = sizeof(resTopicStrBuffer)-1;- Set the buffer size of the
sd.ResObjectStringto one less than its actual size. This prevents overwriting the null terminator that is provided, in case the resolved topic string fills the entire buffer.Note: No error is returned if the topic string is longer thansizeof(resTopicStrBuffer)-1. Even ifVSLength > VSBufSizthe length returned insd.ResObjectString.VSLengthis the length of the complete string and not necessarily the length of the returned string. Testsd.ResObjectString.VSLength < sd.ResObjectString.VSBufSizto confirm the topic string is complete. MQSUB(Hconn, &sd, &Hobj, &Hsub, &CompCode, &Reason);- The
MQSUBfunction creates a subscription. If it is non-durable you are probably not interested in its name, though you can inspect its status in IBM MQ Explorer. You can provide thesd.SubNameparameter as input, so you know what name to look for; you obviously have to avoid name clashes with other subscriptions. MQCLOSE(Hconn, &Hsub, MQCO_REMOVE_SUB, &CompCode, &Reason);- Closing both the subscription and the subscription queue is optional. In the example the subscription is closed, but not the queue. The
MQCLOSEMQCO_REMOVE_SUBoption is the default in this case anyway as the subscription is non-durable. UsingMQCO_KEEP_SUBis an error.Note: the subscription queue is not closed byMQSUB, and its handle,Hobj, remains valid until the queue is closed byMQCLOSEorMQDISC. If the application terminates prematurely, the queue and subscription are cleaned up by the queue manager sometime after application termination.