What is CMS?
The CMS API is a C++ corollary to the JMS API in Java which is used to send and receive messages from clients spread out across a network or located on the same machine. In CMS we've made every attempt to maintain as much parity with the JMS api as possible, diverging only when a JMS feature depended strongly on features in the Java programming language itself. Even though there are some differences most are quite minor and for the most part CMS adheres to the JMS spec, so having a firm grasp on how JMS works should make using CMS that much easier.
One of the first places to start if you are already familiar with JMS is to take a look at the CMS API documentation
Getting Started with CMS
This section covers the basics of using the CMS API. In order to aid in the discussion we assume here that you are using ActiveMQ-CPP to connect to an ActiveMQ Broker, of course with CMS you could also link to another implementation of the CMS API in your C++ application and connect to some other Message service.
The CMS ConnectionFactory
The first interface you will generally use in the CMS API is the ConnectionFactory. A ConnectionFactory allows you to create CMS Connection objects which maintain a connection the some Messaging service, e.g. an ActiveMQ broker.
The simplest way to obtain an instance of a CMS ConnectionFactory is to use the static method createCMSConnectionFactory that all CMS Provider libraries are required to implement. The code snippet below demonstrates how to obtain a new ConnectionFactory:
As you can see the createCMSConnectionFactory takes a single string parameter which is in the form of a URI that defines where the Connection that is created is to connect to as well as the protocol that should be used, TCP/IP in the case of the above example. Additionally configuration information can be encoded in the URI. Refer to the Configuration page for more information on the configuration parameters that can be passed to ActiveMQ-CPP via the URI.
Once you've created a ConnectionFactory the next thing to do is to create a CMS Connection using the ConnectionFactory. A Connection is the Object that manages the client's connection to the Provider. The next section covers the use of a CMS Connection, the code to create a Connection is shown below:
Upon creation the Connection object attempts to connect to the CMS Provider, if the connection fails then an CMSException is thrown with a description of the error that occurred stored in its Message property.
Connections and Authentication
There are a couple versions of the createConnection method that allow you to specify login parameters for the newly created Connection. The one you would use most often takes a user-name and password pair that is transmitted to the Broker for authentication. Should the credentials be invalid a CMSException would be thrown. The sample code below shows how to pass a user-name and password in when creating your Connection object.
If you don't want to hard code values into your source code or write code to read the login data from somewhere else there is another option for passing in the user-name and password, the URI that you pass to createConnectionFactory can be encoded so that the connectionFactory will read the values from the system environment when it parses the URI. The example below shows how to create a connection factory with the login data set in the URI.
As you can see it is pretty simple to have values on the URI come from the system environment. This method will work for any parameter that you can specify in the URI.
The CMS Connection interface defines an object that is the client's active connection to the CMS provider. In most cases the client will only create one connection object since it is considered a heavyweight object.
A connection serves several purposes:
A CMS Connection is created from a CMS ConnectionFactory as we saw previously. If the ConnectionFactory create call is successful then the Connection object returned is connected to the CMS Provider. The Connection object is created in a stopped state, no messages will be delivered to Message consumers that a client creates until the Connection is started. Its normal to leave a Connection in the stopped state until the client has created the initial set of Sessions, Message Producers, and Message Consumers that it intends to use. Once the setup phase of the client completes it should call the Connection's start method to begin receiving messages from the Provider. Failing to call the start method is a very common error among new users of CMS and JMS clients, if you find you aren't receiving any messages the first thing to check is that you called start.
After creating the Connection the client must create a CMS Session in order to create message producers and consumers. The code snippet below puts together what we've seen so far and then shows how to create a CMS Session object from our Connection instance, the section that follows will discuss the CMS Session in more detail.
Once you've successfully created a CMS Connection the next thing you would normall do is create one or more CMS Session objects using your new Connectio instance. A Session is defined as a Single Threaded context for producing and consuming messages.
A session serves several purposes:
A session can create and service multiple message producers and consumers.
When a client creates a CMS session it must specify the mode in which the Session will acknowledge the messages that it receives and dispatches. The modes supported are summarized in the table below.
In the previous section we showed you how to create a session, lets take a look at that example again here:
In this code snippet the Session is created using the createSession with no arguments, this creates a Session that is in the AUTO_ACKNOWLEDGE mode. To create a Session with one of the modes listed above there is a second create method in the CMS Session interface that takes a single argument specifying the mode, lets take a look at an example:
As you can see there's not much difference here, just specify the mode you want for acknowledgement and you are ready to create your Session resources, in the next few section we will walk through the types of objects that you can create from a Session and show you the basics of using them.
Objects created from CMS Session
In this section we cover the types of objects that are created from an instance of a CMS Session object.
As its name implies CMS exists to send and receive messages, so it makes sense to start by covering the message that you can send and receive using CMS. There are four main Message types supported by CMS at the the time of this writing, others may follow but we will stick to the ones that are fully supported right now. The table below shows the message type along with a brief description of the Message's use, for complete details on the interface and usage of a particular message type, see the CMS API Documentation.
Now that we've seen the types of Message's we can create lets see how to actually create them and explore some of the operations that are available in the Message classes.
Creating a Message
As you may have already guessed, Messages are created using the CMS Session instance we created previously. The Session supplies methods for creating each of the four Message types we covered above. The Session is the Factory that create the providers implementation of the Message interfaces defined in CMS, it knows how to configure the internal data structures and prevents the client from being tied to the provider implementation directly, this is why we have to use the Session to create a Message object instead of creating them directly. Lets take a look at a code snippet that create a TextMessage instance and sets some properties on that Message.
As you can see from the code above creating a TextMessage is much like creating a Session or Connection instance, you just call the createTextMessage in your instance of a CMS Session and you get back a new TextMessage pointer that you can then populate with text and properties.
As its name implies the CMS Destination interface defines an object that represents an endpoint that the Messages are routed to by the Messaging broker. Clients create Destinations and send messages to them or wait for messages to be received on a destination they have subscribed to. There are two basic types of Destinations in CMS, the Topic and the Queue, there are two subtypes of these the Temporary Topic and Temporary Queue. The table below summarizes the four different Destination types.
Now that we've seen what the destination types are, lets look at a code snippet showing how to create a Destination object. The example below shows how to create a Topic instance using what should now be a pretty familiar pattern.
Creating a Topic or Queue requires passing a name for the Destination, the name is similar to an address, messages sent the the "EXAMPLE-TOPIC" destination are received by clients that have subscribed to that same Destination.
Now that we've covered how to create messages and destinations we will look at creating a CMS MessageConsumer. The MessageConsumer allows the client application to receive messages sent by other clients to topics or queues. Before we talk about receiving Messages with the consumer lets first look at how one is created.
As you can see the MessageConsumer is created by calling createConsumer from an instance of a CMS Session object. The MessageConsumer is given a Destination to listen for Messages on at the time you create it. Once you create a MessageConsumer its time to start using it to receive messages, the MessageConsumer two methods for receiving messages, one is synchronous and the other is asynchronous.
The synchronous method of receiving Messages involves a call the to the consumers receive method. A call to receive will block until a Message is received on the Destination in question if no time-out is given, or will return NULL if the time-out given elapses and no new Messages arrive. Lets take a look at a simple example of a synchronous message polling loop using CMS.
As you can see in the code above we called the MessageConsumer's receive method expecting that it will return a new Message object at some point. The code here will block until a new message is received. This sort of approach assumes that your program has nothing else that it needs to do until a message arrives, however there is an alternative should your application need to perform other processing. The code sample below shows a polling loop that uses the MessageConsumer's receiveNoWait method to poll and return immediately to allow for other processing to occur.
The asynchronous method involves implementing the CMS MessageListener interface and passing an instance of your implementation to the MessageConsumer's setMessageListener method. When a new message arrives your listener's onMessage method will be called by the consumer in the context of another thread to allow you to process the Message received. Below is a code snippet that demonstrates implementing the MessageListener interface.
In the sample above we create a new class called SimpleListener that prints the contents of a TextMessage when received, or print an message indicating that it did not receive a TextMessage as it had expected to. Notice that the onMessage method receives a pointer to the base Message interface and we then attempt to dynamic cast to the type we think we should have received. This allows your code to process multiple message types in one method. The pointer passed is owned by the caller or onMessage so you shouldn't store it or delete it, if you need to keep a copy of the Message around you must create a copy by calling Message's clone method.
Now that we have a MessageListener implementation to work with its time to see how to setup asynchronous consumption using the MessageConsumer we created previously.
That's it, we will now receive the messages sent to the Destination we created in the onMessage method of our SimpleListener instance.
We've seen how to consume Messages now how do we produce them in the first place? The answer is the CMS MessageProducer which is used to send messages to the broker for distribution to the various clients listening for Messages on the topic or queue. Creating a MessageProducer is much the same as creating a MessageConsumer, you first create your Connection, Session, and Destination objects then use the Session to create a MessageProducer. The code snippet below demonstrates how to create a MessageProducer.
Now that we've covered most of the basics of the CMS API its time to take a look at some complete examples that demonstrate how you might use the CMS API in your own application. The first example will show how to create a simple Asynchronous consumer that can receive TextMessage objects from an ActiveMQ broker, then in the second example we will look at a simple producer that publishes TextMessage objects to the destination that our consumer is listening on.
Simple Asynchronous Consumer
In the Simple Asnychronous Consumer example we wrap the CMS API usage in a class called SimpleAsyncConsumer. This class exposes a single constructor that allows the user to create an instance of the class that connects to a particular broker and destination and whether the destination is a Queue or Topic. The user can also specify the Acknowledgement Mode shoukd be CLIENT_ACKNOWLEDGE instaned of the default AUTO_ACKNOWLEDGE mode.
Once an instance of this class is created the user calls the runConsumer method to start listening on the specified destination. The runConsumer method creates a Connection to the broker and start a new Session configured with the configured Acknowledgement mode. Once a Session is created a new Consumer can then be created and attached to the configued Destination. Since we want to listen asynchronously for new messages the SimpleAsyncConsumer inherits from cms::MessageListener so that it can register itself as a Message Listener with the MessageConsumer created in runConsumer.
After the runConsumer method returns the main method waits for user input to exit, all messages received while the application is running will be dispatched to the onMessage method of SimpleAsyncConsumer and if the message is a TextMessage its contents will be printed onscreen.
Much like the Simple Asynchronous Consumer example the Simple Producer example wraps up the CMS API detials needed to create a producer into a class called SimpleProducer. This class exposes a simillar interface to the consumer example, there is one constructor that allows an instance to be created with configuration options for the broker and destination as well as the number of messages to send to the configured destination. Once created the client code simply calls the SimpleProducer's run method to publish the specified number of messages. Once the run method completes the client is free to close the SimpleProducer which cleans up the allocated CMS resources, once closed the application exits.