One outstanding feature of the popular Apache ActiveMQ message broker is its rich message conversion capability, which allows cross-platform/cross-language exchange by serialization of JMS object and map messages to XML or JSON. Non-Java clients do not have to support the native ActiveMQ wire protocol. Instead, they can use a simple protocol such as STOMP.
Java (producer side)
Here is a small example for a Java client application which sends a MapMessage to the ActiveMQ message broker. The code sets four key-value pairs of a MapMessage instance, and sends it to the destination queue:
ConnectionFactory f = new ActiveMQConnectionFactory(); Connection conn = f.createConnection("user", "password"); Session session = conn.createSession(false, AUTO_ACKNOWLEDGE); Destination dest = session.createQueue("Habari.MapMessage"); conn.start(); MessageProducer producer = session.createProducer(dest); MapMessage msg = session.createMapMessage(); msg.setString("hello", "world"); msg.setInt("int", 42); msg.setBoolean("false", false); msg.setBoolean("true", true); producer.send(msg); conn.close();
When this code completes, the JMS MapMessage instance will wait in the message broker queue for a consumer. At this point, it is neither XML nor JSON encoded. It is stored in the internal broker native format of ActiveMQ.
Delphi / Free Pascal (consumer side)
As explained above, the MapMessage waits in the message broker, but is not serialized to XML or JSON. If we need a serialized message, our client must tell this the ActiveMQ broker when it creates a consumer for the queue. It does so by including a special header in the STOMP subscription frame. We want to receive the MapMessage as XML, so the header is:
transformation=jms-map-xml
Now the broker knows which serialization our client understands. ActiveMQ sends the MapMessage to our STOMP client as XML.
<map> <entry> <string>false</string> <boolean>false</boolean> </entry> <entry> <string>true</string> <boolean>true</boolean> </entry> <entry> <string>hello</string> <string>world</string> </entry> <entry> <string>int</string> <int>42</int> </entry> </map>
Our Delphi / Free Pascal example client receives the message and display its key/value pairs:
procedure ReceiveAsXML(Transformer: IMessageTransformer); const DEST_NAME = 'Habari.MapMessage'; DEST_PARAMS = '?transformation=jms-map-xml'; var Connection: IConnection; Session: ISession; Consumer: IMessageConsumer; Destination: IQueue; MapMessage: IMapMessage; MapNames: PMStrings; I: Integer; Key: string; begin Connection := TBTConnection.MakeConnection; try try SetTransformer(Connection, Transformer); Connection.Start; Session := Connection.CreateSession(amAutoAcknowledge); Destination := Session.CreateQueue(DEST_NAME+DEST_PARAMS); Consumer := Session.CreateConsumer(Destination); WriteLn('Waiting for messages ...'); MapMessage := Consumer.Receive as IMapMessage; if Assigned(MapMessage) then begin MapNames := MapMessage.GetMapNames; for I := 0 to Length(MapNames) - 1 do begin Key := MapNames[I]; WriteLn(Key + '=' + MapMessage.GetString(Key)); end; end; Connection.Stop; except on E: Exception do WriteLn(E.Message); end; finally Connection.Close; end; WriteLn('Press any key'); ReadLn; end;
Conclusion
This proof-of-concept example code shows how ActiveMQ message broker integration with non-Java clients may be implemented with no changes to existing code. ActiveMQ is able to deliver a JMS MapMessage to a STOMP client in a cross-platform format, so that Delphi and Free Pascal clients may consume them.
About Habari Client for ActiveMQ
The Habari Client library includes example implementations of XML message transformers based on various XML and JSON parsers, including the default IXMLDocument based parser. Note that the example implementations in the shipping versions only support properties of string type for sending and receiving. Support for receiving boolean and integer values is planned for the next release.
Habari Client library for ActiveMQ is available from Habarisoft.