Thursday, September 12, 2013

How to use ActiveMQ with a language other than Java

http://www.openlogic.com/wazi/bid/313375/how-to-use-activemq-with-a-language-other-than-java

Apache ActiveMQ is an open source message server that you can use to send text or binary messages to remote systems with guaranteed delivery, even when there have been network disruptions. I recently wrote about how to develop a simple remote logging application in Java for ActiveMQ, but ActiveMQ also supports languages other than Java via two externally accessible network protocols – OpenWire and STOMP – so you can access and use ActiveMQ from client programs written in such languages as C/C++, .Net, PHP, and Ruby.

OpenWire, which is the protocol used inside ActiveMQ, is designed for maximum performance and features, while the Simple Text Oriented Messaging Protocol (STOMP) is designed for ease of implementation and interoperability. OpenWire is native to ActiveMQ, while STOMP support is built into other message servers, such as RabbitMQ. OpenWire is the best protocol when working exclusively with ActiveMQ, as it offers maximum performance, while STOMP is better for projects where compatibility with other message servers is important.

You don't need to learn the bits and bytes of the actual protocol to use either OpenWire or STOMP, as developers have created a variety of client libraries for different programming languages.

The ActiveMQ team maintains two client libraries for C-like programming languages. One is the Apache NMS project, which provides a .Net Messaging API that enables programs written in C# and Visual Basic to interact with the ActiveMQ server. The NMS API has multiple providers that implement the API using different protocols, including OpenWire and STOMP.

The other client library, ActiveMQ-CPP, provides a C++ Messaging Service API (CMS). Like the NMS API, the CMS library architecture allows for pluggable transports and protocols, including OpenWire and STOMP.

To see how to use ActiveMQ with a language other than Java, let's implement the same remote system logger from my previous article, but this time in C++, using ActiveMQ-CPP. In the system we wrote last time, a program logs events, warnings, and errors that occur during a program's execution and sends these log messages to a specified client via the ActiveMQ server. If you are unfamiliar with the remote logger idea or with concepts like topics, queues, or durability, please read the previous article.

Building ActiveMQ-CPP

Before you build the CMS library on CentOS you need to install a few prerequisites, including the CppUnit unit test suite and the right compilers. You need a C and C++ compiler, make, automake, patch, openssl, and the Apache Portable Runtime (APR) library. You can install them all via yum as root:
# yum -y install gcc gcc-c++ automake cppunit cppunit-devel apr apr-devel apr-util-devel patch openssl-devel
Download the latest CMS library and unpack the archive file into a directory of your choice. Change directory to that directory and run configure:
# ./configure --prefix=/usr
Building the CMS library using gcc 4.4.7 as used by CentOS 6 fails unless you apply a small patch to the library first. Download the patch into the directory that contains the CMS source code and apply the patch from the .diff file:
# patch src/main/activemq/state/ConnectionStateTracker.cpp < amq-cpp-4.7.0-build.diff
Now you can build and install the library:
 # make ; make install 
To check that the library has been built and installed correctly, run the unit tests with the following commands:
# make check
# src/test/activemq-test
The first command above builds the unit tests and the second command runs them. Once you see that everything is functioning as expected, you can stop the tests, since the suite runs some 2,000 tests and all we really want to do is make sure the library works properly.

Start your engines

In our example programs the logger is a message producer – that is, software that needs to send messages to a remote system. The log reader is a message consumer. The producer submits each message to the ActiveMQ message server, which processes it and sends it to the message consumer. This means that the logger doesn't communicate directly with the log reader, but uses the message server as an intermediary.
In our example C++ logger program we instantiate a class called Logger, then call the init() method to establish a link with the message server. The program sends log messages to the log reader via the public method logIt().
The log reader is simpler and cruder. We instantiate the class LogReader and call a main method. After initializing the link with the ActiveMQ server, the program sits in a loop receiving messages.
To be able to submit messages to the server, the logger needs to perform several initialization steps:
  • Create a connection
  • Create a session for that connection
  • Create a destination topic for that session
  • Create a producer from the session to the topic
These are the same steps that the Java version of the logger requires. They are common to all clients connecting to a Java Messaging Service regardless of language.
Here is the code in C++:
// Create a ConnectionFactory
ConnectionFactory *connectionFactory(ConnectionFactory::createCMSConnectionFactory(brokerURI));

// Create a Connection
connection = connectionFactory->createConnection();
connection->start();

// Create a Session
session = connection->createSession(Session::AUTO_ACKNOWLEDGE);

// Create the destination topic
destination = session->createTopic("TEST.FOO");

// Create a MessageProducer from the Session to the topic
producer = session->createProducer(destination);
producer->setDeliveryMode(DeliveryMode::PERSISTENT);
The steps (and subsequent code) for the log reader are similar but not identical:
  • Create a connection
  • Set the client ID
  • Create a session for that connection
  • Create a destination topic for that session
  • Create a consumer for the session and destination
In C++ it looks like this:
// Create a ConnectionFactory
ConnectionFactory *connectionFactory(ConnectionFactory::createCMSConnectionFactory(brokerURI));

// Create a Connection
connection = connectionFactory->createConnection();
connection->setClientID("clientID1");
connection->start();

// Create a Session
session = connection->createSession(Session::AUTO_ACKNOWLEDGE);

// Create the destination topic
destination = session->createTopic("TEST.FOO");

// Create a MessageConsumer from the Session to the topic
consumer = session->createDurableConsumer(destination, "Log reader","", false);

Sending and receiving

Once you've created the producer and consumer objects, sending and receiving message is as simple as calling producer->send() and consumer->receive(). I've coded the former call into the method below. The Logger class contains a logIt() method that prepends the date to the log message and wraps it up in a TextMessage object and then sends it to the log reader via ActiveMQ:
void logIt(string m)
{
        time_t now = time(0);
        strftime(datestr,80,"%c", localtime(&now));
        string text = (string) datestr + ": " + m;
        std::auto_ptr  message(session->createTextMessage(text));
        printf("Sending: %s\n", text.c_str());
        producer->send(message.get());
}
The downloadable zip archive of the project contains the two source files: logger.cpp and logreader.cpp. To see how they work, unzip the archive into a directory of your choosing. To build the logger use the command:
g++ -I/usr/include/activemq-cpp-3.7.0 -I/usr/include/apr-1 -lactivemq-cpp -lssl -o logger logger.cpp
The command to build the log reader is similar:
g++ -I/usr/include/activemq-cpp-3.7.0 -I/usr/include/apr-1 -lactivemq-cpp -lssl -o logreader logreader.cpp
You can run the logger with the command ./logger from within the source directory, and the log reader with ./logreader.
Since the ActiveMQ message server delivers their messages, these C++ logging programs can interoperate with their counterparts from the previous article. The Java logger could send messages to be received by the C++ logreader, or the C++ logger could generate messages to be received by the Java logreader.

Conclusion


As you can see, the C++ versions of the these programs are similar to the Java versions. Don't let lack of familiarity with Java stop you from using ActiveMQ; any programmer with some C++ experience should be able to write programs that interact with ActiveMQ.

No comments:

Post a Comment