11.3. The Anatomy of MessagesCreating a messaging-based application involves more than establishing communication channels between participants. Players in a message-driven system need to understand the content of the messages and know what to do with them. Native messaging systems, such as WebSphere MQ or Microsoft MQ (MSMQ), define their own proprietary formats for messages. JMS attempts to bridge these native messaging systems by defining its own standard message format. All JMS clients can interact with any messaging system that supports JMS. "Supports" in this case can mean one of two things. The messaging system can be implemented in a native, proprietary architecture, with a JMS bridge that maps the JMS message formats (and other aspects of the JMS specification) to the native scheme and back again. Or the messaging system can be written to use the JMS message format as its native format. JMS messages are made up of a set of standard header fields, optional client-defined properties, and a body. JMS also provides a set of subclasses of Message that support various types of message bodies. 11.3.1. Message Header Fields and PropertiesTable 11-1 lists the standard header fields that any JMS message can have. The table indicates the name and type of the field, when the field is set in the message delivery process, and a short description of the semantics of the field.
These standard message headers are read and written using corresponding accessors on the Message interface. The JMSTimestamp field, for example, is set using setJMSTimestamp( ) and read using getJMSTimestamp( ). A client can also create its own custom properties on a Message, using a set of generic property accessors on the Message interface. Custom message properties can be boolean, byte, short, int, long, float, double, or String values, and they are accessed using corresponding get/setXXXProperty( ) methods on Message. A boolean header can be set using the setBooleanProperty( ) method, for example. Each custom property has to be given a unique name, specified when the value is set. For example, we could set a custom boolean property with the name "reviewed" on a message, like so: TextMessage tMsg = ...; tMsg.setBooleanProperty("reviewed", false); Custom property names have certain restrictions on them. They have to be valid Java identifiers, they can't begin with "JMSX" or "JMS_" (these are reserved for JMS-defined and vendor-defined properties, respectively), and they can't be one of the following reserved words: NULL, trUE, FALSE, NOT, AND, OR, BETWEEN, LIKE, IN, IS, or ESCAPE. Custom property names are also case-sensitive, so "reviewed" isn't the same property as "Reviewed". 11.3.2. JMS Message TypesTo support various application scenarios, JMS provides the following subclasses of Message, each providing a different type of message body:
11.3.3. Accessing Message ContentWhen a client receives a Message, its body is read-only. Attempting to change the body of a received Message will cause a MessageNotWriteableException to be thrown. When a message sender first creates a Message object, the body of the Message is unset. For TextMessages and ObjectMessages, this means that their body starts with a null value, and for MapMessages, this means there are no entries in the message body. For BytesMessages and StreamMessages, their bodies start in write-only mode, and they can't be read by the sending client until it calls their reset( ) method. If you create a BytesMessage or StreamMessage and attempt to read its body content before calling reset( ) on it, a MessageNotReadableException will be thrown. A Message's body can be emptied by the sender at any point by calling its clearBody( ) method. This reverts its body back to its initial state, but doesn't affect any of the header or property values on the Message. Calling clearBody( ) on a BytesMessage or StreamMessage puts its body back into write-only mode, until a subsequent call to BytesMessage.reset( ) or StreamMessage.reset( ) is made. The sender can clear any custom properties on a Message by calling its clearProperties( ) method. The standard JMS header fields have to be updated using their specific accessors on the Message interface. Receivers of messages can't call clearBody( ), clearProperties( ), or reset( ) on a received Message since the Message is read-only at this point. 11.3.4. Filtering MessagesJMS allows messaging participants to selectively filter what Messages it receives from a JMS provider. This is done using message selectors, which are expressions that filter messages based on the values found in their headers and custom properties. The syntax of message selectors is based on SQL92 conditional expressions. A complete description of the JMS message selector syntax is provided in Appendix E, but let's take a quick look at the concept of message filters. A message selector is associated with a MessageConsumer when it is created from a Session. Each type of Session (QueueSession and TopicSession) has overloaded versions of their consumer create methods that take a message selector as a String argument. For example, the following creates a QueueReceiver that receives only messages that have a custom property named transaction-type and whose JMSType header field is acknowledge: String selector = "JMSType = 'acknowledge' AND transaction-type IS NOT NULL"; QueueReceiver receiver = qSession.createReceiver(queue, selector); Message filtering is performed by the JMS provider. When the provider determines that a message should be delivered to a particular MessageConsumer, based on the rules of the particular message context (point-to-point or publish-subscribe), it first checks that consumer's message selector, if one exists. If the selector evaluates to true when the message's headers and properties are applied to it, then the message is delivered; otherwise, it isn't. Undelivered messages are handled differently, depending on the message context, as described in the following sections. |