Guide to Queuing

Developing the Client Application

A queuing client application can have a combination of sources and receivers.

To write queuing client applications, use standard Ultra Messaging programming practices. A UMQ queuing context must be able to establish broker connections. If the client application contains non-brokered sources or receivers, these sources or receivers must use a separate, non-brokered context.

Writing UMQ source and receiver applications includes creating brokered contexts, creating sources, creating receivers, and setting attributes by configuration files or other methods.

Coding examples are in C language.

# Before You Start <-

Before starting to develop client applications, decide on a language, verify that the Ultra Messaging libraries work with your development environment, and determine a general approach for setting configuration options.

Decide on the language to write sending and receiving client applications.
  You can use C, Java, or for the .NET framework, C#. To use Java or .NET, you must install the optional Java or .NET Ultra Messaging APIs.

Decide how to set configuration options.
  For options that are unique to the application and which are not expected to change, you might want to use attribute-setting functions or methods. To be able to select and set option values without recompiling, use configuration files.

Verify that the correct libraries are installed and accessible by the client applications.
  You must install the following libraries:

  - Ultra Messaging C API and included third-party APIs
  - For Java or .NET, the appropriate Ultra Messaging Java or .NET API

Decide on locations for Ultra Messaging and broker configuration files
  Environment variables or configuration file calls point to these locations.

# Create the Context <-

A client application that sends or receives messages must have one or more context objects.

When you create a brokered context, set the IP address and port of the broker, and configure the attributes in a context attribute object. After you create the context, you can delete the context attribute object. The following code fragments show context coding concepts.

Create a context attribute object with the current default configuration.

if (lbm_context_attr_create_from_xml(&cattr, "MyCtx") == LBM_FAILURE) {
fprintf(stderr, "lbm_context_attr_create_from_xml: %s\n", lbm_errmsg());
exit(1);
}

Set the configuration options that must be unique or permanent for the context. The following example sets configuration option broker to 10.29.3.190:5672.

if (lbm_context_attr_str_setopt(cattr, "broker", "10.29.3.190:5672") == LBM_FAILURE) {
fprintf(stderr, "lbm_context_attr_str_setopt:broker: %s\n", lbm_errmsg());
exit(1);
}

Create the context object using the context attribute object.

if (lbm_context_create(&ctx, cattr, NULL, NULL) == LBM_FAILURE) {
fprintf(stderr, "lbm_context_create: %s\n", lbm_errmsg());
exit(1);
}

Delete the context attribute object, which is no longer needed.

lbm_context_attr_delete(cattr);

# Create the Sources <-

A client application that sends messages must have at least one source object.

When you create a source, configure the attributes in a topic attribute object. After you create the source, you can delete the topic attribute object. The following code fragments show source coding concepts.

Create a topic attribute object with the current default configuration. The topic name supplied should be the same as is passed to lbm_src_topic_alloc() below.

if (lbm_src_topic_attr_create_from_xml(&tattr, "MyCtx", "QueueName") == LBM_FAILURE) {
fprintf(stderr, "lbm_src_topic_attr_create_from_xml: %s\n", lbm_errmsg());
exit(1);
}

Set configuration options that must be unique or permanent for the source. The following example sets configuration option transport to BROKER.

if (lbm_src_topic_attr_str_setopt(tattr, "transport", "BROKER") == LBM_FAILURE) {
fprintf(stderr, "lbm_src_topic_attr_str_setopt:transport: %s\n", lbm_errmsg());
exit(1);
}

Create a source topic object using the context and topic attribute object. The topic string becomes the queue name.

if (lbm_src_topic_alloc(&topic, ctx, "QueueName", tattr) == LBM_FAILURE) {
fprintf(stderr, "lbm_src_topic_alloc: %s\n", lbm_errmsg());
exit(1);
}

Delete the topic attribute object, or retain it for subsequent source topic allocation.

lbm_src_topic_attr_delete(tattr);

Create the source object using context and source topic object. An example of the function "app_src_callback" is shown below.

if (lbm_src_create(&src, ctx, topic, app_src_callback, NULL, NULL) == LBM_FAILURE) {
fprintf(stderr, "lbm_src_create: %s\n", lbm_errmsg());
exit(1);
}

If needed, set the message properties for the message. In this example we set message property Price to a value of 170 and message property Quan to 49.

lbm_src_send_ex_info_t info; int32_t value;
int ret;
...
/* setup */
memset(&info, 0, sizeof(lbm_src_send_ex_info_t));
info.flags = LBM_SRC_SEND_EX_FLAG_PROPERTIES;
lbm_msg_properties_create(&info.properties);
/* set user defined message properties */
value = 170;
ret = lbm_msg_properties_set(info.properties, "Price",
(void *) &value,
LBM_MSG_PROPERTY_INT, sizeof(int32_t));
assert(ret==LBM_OK);
value = 49;
ret = lbm_msg_properties_set(info.properties, "Quan",
(void *) &value, LBM_MSG_PROPERTY_INT,
sizeof(int32_t));
assert(ret==LBM_OK);

Send the message and delete the message properties object.

lbm_src_send_ex(src, "HelloWorld", 10, 0, &info);
...
lbm_msg_properties_delete(info.properties);

Verify that the source has registered with the broker. Verify that the sent message is stable at the broker.

int app_src_callback(lbm_src_t *src, int event, void *ed, void *cd) {
...
switch (event) {
...
case LBM_SRC_EVENT_UMQ_REGISTRATION_COMPLETE_EX:
... /* handle the registration complete */
break;
case LBM_SRC_EVENT_UMQ_MESSAGE_STABLE_EX:
.... /* handle message stability */
break;
}
...
}
...
}

# Create the Receivers <-

A client application that receives messages must have at least one receiver object.

When you create a receiver, configure the attributes in a topic attribute object. The following code fragments show receiver coding concepts.

Create a receiver topic object using the context object with the current default configuration. The topic string is the queue name.

if (lbm_rcv_topic_lookup(&topic, ctx, "QueueName", NULL) == LBM_FAILURE) {
fprintf(stderr, "lbm_rcv_topic_lookup: %s\n", lbm_errmsg());
exit(1);
}

Create the receiver object using the context and topic object.

if (lbm_rcv_create(&rcv, ctx, topic, app_rcv_callback, NULL, NULL) == LBM_FAILURE) {
fprintf(stderr, "lbm_rcv_create: %s\n", lbm_errmsg());
exit(1);
}

Ultra Messaging receives a message and then delivers the message to the application. The message might have message properties. After the callback delivers the message and returns, Ultra Messaging automatically tells the broker that the message is consumed.

/* Message callback function provided at Receiver Create */
int app_rcv_callback(lbm_rcv_t *rcv, lbm_msg_t *msg, void *clientd) {
...
switch (msg->type) {
case LBM_MSG_DATA:
if (msg->properties != NULL) {
int ret;
lbm_msg_properties_iter_t *prop_iter=NULL;
ret = lbm_msg_properties_iter_create(&prop_iter);
assert(ret==LBM_OK);
ret = lbm_msg_properties_iter_first(prop_iter, msg->properties);
while (ret == LBM_OK) {
if (strcmp("Price", prop_iter->name) == 0) {
assert(prop_iter->type==LBM_MSG_PROPERTY_INT);
printf("Price is %d\n", (int32_t) *((int32_t *) prop_iter->data));
}
else if (strcmp("Quan", prop_iter->name) == 0) {
assert(prop_iter->type==LBM_MSG_PROPERTY_INT);
printf("Quantity is %d\n", (int32_t) *((int32_t *) prop_iter->data));
}
else {
printf("Error: Unknown property name: %s, type: %d",
prop_iter->name, prop_iter->type);
}
ret = lbm_msg_properties_iter_next(prop_iter);
}
lbm_msg_properties_iter_delete(prop_iter);
}
... /* implement processing the payload in msg->data */ }
break;
... /* implement processing for other message types */
default:
... /* implement default handler */
break;
}
}

# Client Configuration <-

Ultra Messaging has multiple ways to set configuration options. Determine which methods are most appropriate for your application.

You can set configuration options programmatically or with configuration files. You can set configuration options in the following ways:

- Use a plain text configuration file. You can also assign the file pathname to an environment variable. You can read this file with a function or method.
- Use an XML configuration file for a more flexible approach to setting configuration options. You can point to this file in the same way you use a plain text configuration file. A single XML file can contain options for multiple applications. Moreover, for a single application, you can configure multiple objects such as named contexts and event queues, with different values for the same options. Also, Ultra Messaging Manager (UMM) can manage XML configuration files.
- Set configuration options individually using function or method calls. You can use this technique to override options set from configuration files.

For detailed information about setting configuration options, see Configuration Overview.

# Sample Applications <-

The UMQ package includes sample applications for testing and educational purposes.

- umqrcv (see Example umqrcv.c, Example umqrcv.java, Example umqrcv.cs).

- umqsrc (See Example umqsrc.c, Example umqsrc.java, Example umqsrc.cs).

For descriptions and argument information on example applications, as well as source code for all languages, see the C Examples source code, Java Examples source code, or .NET Examples source code documentation.

# Developing Client Applications for JMS <-

You can write messaging applications with the Java Message Service (JMS) API and programming model.

Use the Oracle JMS API to develop or port Java messaging applications written according to the JMS specification. The Oracle JMS Specification 1.1 contains requirements and guidelines for developing JMS- based Java messaging applications.

You can write Ultra Messaging and JMS applications that interoperate through the broker by setting and getting message properties.

## Message Types <-

The JMS message consists of a header and body. You can set a JMS body type in the application and optionally identify the body type in a header field.

The following table shows JMS message types and their Ultra Messaging message property JMS_UM_MessageType numeric values:

JMS Message Type | `JMS_UM_MessageType` Value
--- | ---
TextMessage | 0
BytesMessage | 1
MapMessage | 2*
Message | 3
ObjectMessage | 4*
StreamMessage | 5*

- Not supported in Ultra Messaging queuing. Ultra Messaging handles this JMS message type as a BytesMessage.

# Message Components <-

When a broker delivers a message to an Ultra Messaging receiver, Ultra Messaging maps JMS message properties and header fields to Ultra Messaging message properties. When an Ultra Messaging source application sends a message to a broker, Ultra Messaging maps message properties to JMS message properties and header fields.

JMS header fields, JMS-defined properties, provider-specific properties, and user properties map to message properties in an Ultra Messaging message. JMS message data in the message body maps to data in an Ultra Messaging message.

The following figure shows how a JMS message maps to an Ultra Messaging message:

A JMS message header consists of the following header fields:

- JMSDestination
- JMSDeliveryMode
- JMSMessageID
- JMSTimestamp
- JMSCorrelationID
- JMSReplyTo
- JMSRedelivered
- JMSType
- JMSExpiration
- JMSPriority

# JMS Message Properties <-

Ultra Messaging transports JMS message properties of the following categories:

## JMS-defined Properties <-

The JMS Specification defines these properties and reserves the use of their names. JMS-defined properties use a "JMSX" prefix.

## Provider-specific Properties <-

Ultra messaging defines and reserves the following provider-specific properties:

- JMS_UM_MessageType
- JMS_UM_Regid
- JMS_UM_Stamp
- JMS_UM_SQN

## User Properties <-

Properties that you define for applications. You can use User Properties as Message Selectors.