It’s a fairly obvious statement that a message contains a header and a payload, the header being used to direct a message to its destination(s) with the payload being the data to be consumed.

Some message formats (for instance Solace Message Format, SMF) allow user properties to be added to the message, along with the header, or for structured data types to be added to the message. When should these be used, and how?  How should the payload be encoded? As a developer, how do I access message properties?

Message Format Support in Solace

  • REST: HTTP style messaging with a twist: persistence!
  • MQTT: Lightweight interoperable protocol originating in IoT applications;
  • AMQP: Interoperable open standard enterprise messaging standard;
  • JMS: enterprise, open Java API for messaging;
  • OpenMAMA: an open API for capital market data systems;
  • SMF: Solace Message Format, Solace’s proprietary format that is the canonical format on the Solace platform;
  • Web Messaging: A Solace proprietary format for web browser messaging

This series of blog posts will examine some of these details, looking at message structure, user-defined header properties, options for payload encoding with things like JSON, Google Protocol Buffers and Solace options like structured data types.  I’ll use Java in the majority of code snippets but in general these discussions apply equally to all Solace messaging APIs.

Contents

This first post will deal with message structure and the basics of acquiring and populating a message.  I’ll talk mostly about Solace Message Format (SMF), as JMS message structure is explained in detail in the JMS Specification, and MQTT structure is also explained the MQTT spec.  Later on I’ll discuss how headers are mapped between these different specifications.

Here’s what we’ll look at:

  1. Message structure & basics (this post)
  2. Structured Data Types and User Properties
  3. Structuring message payload: Solace structured messages, data formats like JSON and 3rd party serialization solutions like Google Protocol Buffers.

Message Structure

inside-solace-message-intro_image-1SMF messages have 3 main components: the headers; application properties; and the payload.

The header contains the message metadata, including obvious fields such as destination.  Application and User properties are optionally added to a message to aid application development.  Typically, information that can’t be contained in the message destination, but that the application needs to know about before processing the message, should be added here.

Lastly, there’s the payload.

That’s all I’ll say about overall message structure. The core concept article gives some good details and references into customer documentation for further details.

Acquiring a message

OK, so you need to send a message – how?

The Java API has two ownership models for sending messages: Session-independent and Session-dependent (see the MessageProducer documentation for more details). I’ll only talk about Session-independent messages as that is our recommended pattern. For more information, see the Get Started Guide. Here I’ll summarize for illustrative purposes.

To create a message, use the Java API Factory, JCSMPFactory:

TextMessage msg = JCSMPFactory.onlyInstance().createMessage(TextMessage.class);

Note that in JCSMP we acquire a message of a particular type – in this case a TextMessage.  I’ll use the name “Message” to refer generically to the various message interface types.  The C and .Net APIs have a single message type but support structured payloads which allow applications to fully interoperate with the JCSMP and JMS based applications using typed messages.

The message can be re-used between multiple send() calls – just call the functions you need with the new values:

msg.setSequenceNumber(0);
producer.send(msg,  topic)
msg.setSequenceNumber(1);
producer.send(msg.topic);

On the receiving side, the message is received in the OnReceive callback:

public void onReceive(BytesXMLMessage msg) {…

If you wish to re-use a message but set all the contents back to defaults, Message.reset(). When using session-independent messages, this allows a thread to acquire a message, populate it, send it and repopulate it before sending rather than disposing and re-acquiring on every send.

Accessing Message Contents

Message properties are accessed through getters and setters.  A simple example is:

void Message.setApplicationMessageId(String)and:

String Message.getApplicationMessageId().

If the header property being set is Boolean, an is…() method is often used to retrieve the value:

boolean Message.isAckImmediately()

Seeing Message Headers

The simplest way to see message headers in action is to turn on the –md option to sdkperf.  This will display any received message and will include message headers for you, any User Properties, and the user payload:

^^^^^^^^^^^^^^^^^^ Start Message ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Destination:                            Topic 'a/sample/topic'
Class Of Service:                       USER_COS_1
DeliveryMode:                           DIRECT
Message Id:                             6
Binary Attachment:                      len=8
3a 30 30 30 30 30 30 30                               :0000000
^^^^^^^^^^^^^^^^^^ End Message ^^^^^^^^^^^^^^^^^^^^^^^^^^^

This display output is provided for convenience in the Solace Java API: Message.dump()

sdkperf is a useful test harness for messaging: if you are unsure of exactly what you are publishing, it’s worth having an sdkperf subscriber running printing the message contents like this.

The Destination

Mapping header properties between messaging protocols

A feature of the Solace platform is that messages are transparently converted between protocols.  An incoming, say, REST message can be consumed by MQTT, SMF or JMS clients without any translation or bridging.  How are the differing header requirements dealt with?

Simply put: we provide one-to-one mappings between headers where we can; if a header is missing from a protocol we define a Solace specific property.  You can see this in action in the way we deal with http headers with REST – see the REST Messaging Protocol Guide for more details and also see the next post.  The JMS property mappings are described here.

In any pub/sub messaging system, it’s clear that the Topic, Subject, Destination, however you describe it, is one of the most important parts of the header.

In the Solace Java API, called JCSMP, all message routing is performed using destinations.  Destinations can be a topics or queues.

JCSMPFactory.createTopic(…)

JCSMPFactory.createQueue(…)

Topic space design is out of scope here – see this post for more details

MessageProducer.send(…) accepts the destination, setting the destination field.

Delivery Mode

Another fundamental message header property is the delivery mode: direct or guaranteed.  On the producer side, we simply:
Message.setDeliveryMode(…)

On the consumer side, of course, you need to set up the correct consumer flow and deal with acknowledgements.  This is out of scope for this post, so take a look at the tutorials for publish/subscribe and persistence.

Application properties

The JSCMP API provides many methods for getting and setting useful message properties. This provides an easy way of dealing with simple, common data.  It also provides the mechanism for exchanging header information between different message protocols: for instance, HTTP header information such as HTTP content-type for REST messages is put in an SMF Application property – see the side panel above.

You can also define your own User Defined Properties.  I’ll talk about this, and when you should use a property or encode data in the payload, in the next post, Inside a Solace Message, Part 2: Using Header Properties.

Payload

In the Java API the type of the payload is determined by the type of the message that you’re going to send – a TextMessage will send Text (use SetText(…)), while a BytesMessage will send uninterpreted bytes (use setData(…)). Solace APIs also provide Structured Data Types for sending in a message, which are guaranteed to safely traverse different machine architectures (32 vs 64 bit, little- vs big-endian). More coming in the payload post, Inside a Solace Message, Part 3: Payload Data.

In summary, messages have headers with header properties. Many of these properties are used for messaging routing, however many are useful for application developers and are passed through the messaging layer untouched. They are accessed with API getter/setter functions.

Messages can also have a payload. We’ll look at how to structure payload data in a later post.

It’s possible to create User Defined Properties if you need some application feature not included with the standard header properties. This, along with using application properties, is the subject of Inside a Solace Message, Part 2: Using Header Properties.

Tom Fairbairn

Tom works in Solace's systems architecture team helping customers define their architectures to address challenges such as the increasing digitisation of business across a wide range of industries including retail, financial services and smart city initiatives. He joined Solace in 2013 as part of the Singapore Technical Support team, then moved to London in 2014.

If you own a smart phone or tablet, the chances are you already use Tom’s work, as prior to Solace Tom was an IC designer responsible for implementing the low-power hardware in the processors present in over 90% of these devices.