Sunday, July 15, 2007

Exploring ActiveMQ via Stomp

Building on my previous post where I explored ways to get a legacy Progress application system to talk to a modern .NET based back-end, the next possible solution will involve communicating to a JMS system called AtiveMQ.

ActiveMQ
I actually came across the Apache Foundation's ActiveMQ project while searching for a message queuing back-end for a Ruby project. I was in search of a scalable message queue that was platform agnostic (I had considered SysV message queues but they are not easily portable from one OS to another). The ActiveMQ site says it all: "Apache ActiveMQ is the most popular and powerful open source Message Broker." Wow! So how can I leverage ActiveMQ to help one system talk to another when they cannot do so natively? This is the next problem I set out to solve...

The general idea is to have a legacy Progress system send and receive messages off a message queue and at the same time have a modern .NET system send and receive messages off of a message queue. The two systems do not need to talk to each other directly, they only need to talk to the messaging system.

The first step was to determine how could a legacy Progress system communicate with the ActiveMQ broker services? And this is where ActiveMQ really shines, as the web-site states: "Supports a variety of Cross Language Clients and Protocols from Java, C, C++, C#, Ruby, Perl, Python, PHP" and then there is this quote under cross languae clients: "Stomp support so that clients can be written easily in C, Ruby, Perl, Python, PHP to talk to ActiveMQ as well as any other popular Message Broker". Stomp!? What is Stomp? Check out the Stomp web-site and you will see that Stomp stands for: "Streaming Text Orientated Messaging Protocol". Take a look at the protocol definition and you will find that it is so simple that you could literally communicate to a Stomp enabled system via telnet. The folks at Stomp have produced a connector that is being used in the ActiveMQ message broker to allow a 'simple', yet fairly powerful, connection to be established to a really great JMS system without too much hassle.

So now what do we do? We have discovered a really cool JMS system that offers a simple Stomp connector and yet we are stuck with an old Progress system which has no network socket capabilities, ARGH! Well, you browse over to the Stomp web-site and check out their C client (since we already know how to extend Progress with C using the HLC mechanism this should be part of the solution). The libstomp component is only available via SVN and they only have directions on compiling on OS X with the X Code2 IDE but the code is available and should save me a couple of days worth of work. libstomp requires the APR (Apache Runtime Portable Library) so step one is to download and install this, if your development platform does not already have it (my platform, HPUX, did not but it compiled and installed w/o any problems).
[The APR library is really cool and I plan to post about it soon, this library is new to me so I was like a kid in a candy store, hyper.]
Then I compiled the stomp 'library' (only a single source and header file) which is more like just another object to be linked in later. To ease the Progress application development efforts the Stomp API that I built via the HLC was fairly basic: a function for each of the major Stomp message types that automatically built the Stomp frames and transmitted them on the wire. For simplicity and appeal I chose to keep the socket and frame level details out of Progress.
After a couple of days of massaging Progress and C code around I was able to subscribe to a JMS style queue being hosted by ActiveMQ and send and receive messages. In order to test a client other that Progress a small Ruby client was constructed to help simulate the final environment (two distinct applications sharing data via a JMS queue). Here is a small client that will send a sample message to a queue name that is given as an argument

require 'stomp'

conn = Stomp::Connection.open "", "", "servera", 61613, true

puts "Connection Open: #{conn.open?}"

destination = $*[0] if $*[0] != nil

for i in 1..10 do
conn.send destination, "Some text to send to the consumers out there. #{i}", { "receipt" => 'something-for-me' }
end

conn.disconnect
I had no problems pushing 5000 messages onto the queue in a very short amount of time with this client and the ActiveMQ broker process is running on a 2-way HP9000 L-class system. I was then able to receive the messages back off of the queue almost as quickly. Here is a sample of the Progress code that is using my libstomp based API:

/* Subscribe with automatic acks. */
CALL STOMP_SUBSCRIBE "/queue/testq" "auto".

ASSIGN stomp_get_frame_tout = FALSE.

DO WHILE TRUE:
CALL STOMP_GET_FRAME 5. /* Try to read a frame, wait up to 5 seconds */

IF stomp_get_frame_tout = TRUE THEN
DO: /* unsubscribe and quit */
CALL STOMP_UNSUBSCRIBE "/queue/testq".
/* Polietly let the broker know we are leaving */
CALL STOMP_DISCONNECT.
QUIT.
END.

/* Read in the frame body */
DO TRANSACTION:
create tt.

CALL STOMP_GET_BODY.

MESSAGE tt.data VIEW-AS ALERT-BOX.
END.
END.

So we now have an API from Progress which can interact with a Stomp connected message broker that is JMS compliant. The next portion was to get a .NET application to speak to the same ActiveMQ server and interact with the queue. A co-worker of mine worked this piece out using the Spring.NET project (hopefully he will post some details on the client, keep watching: The Space of .Nethingness!).

Next time we will explore possible solution #3.. A fully featured ESB from Bostech Corporation. Their ChainBuilder ESB product is opensource, JBI compliant, and uses Eclipse as a GUI front-end.