Section 11.3. The Anatomy of Messages


11.3. The Anatomy of Messages

Creating 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 Properties

Table 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.

Table 11-1. Standard JMS message headers

Field name

Data type

When set

Description

JMSCorrelationID

String

Before send

Correlates multiple messages. This field can be used in addition to the JMSMessageID header as an application-defined message identifier (JMSMessageIDs are assigned by the provider).

JMSDeliveryMode

Destination

During send

Indicates to the message receiver which Destination the Message was sent to.

JMSDestination

int

During send

Indicates which delivery mode to use to deliver this message, DeliveryMode.PERSISTENT or DeliveryMode.NON_PERSISTENT. PERSISTENT delivery indicates that the messaging provider should take measures to ensure that the message is delivered despite failures on the JMS server. NON_PERSISTENT delivery doesn't require the provider to deliver the message if a failure occurs on the JMS server.

JMSExpiration

long

During send

The time the message will expire on the provider. If no client receives the message by this time, the provider drops the message. It is calculated as the sum of the current time plus the time-to-live of the MessageProducer that sent the message. The value is given in milliseconds since the epoch (January 1, 1970, 00:00:00 GMT). A value of 0 indicates no expiration time.

JMSMessageID

String

During send

A unique message ID assigned by the provider. Message IDs always start with the prefix "ID:". These IDs are unique for a given JMS provider. Applications can set their own message identifier using the JMSCorrelationID header.

JMSPriority

int

During send

A provider-assigned value indicating the priority with which the message will be delivered. JMS providers aren't required to implement strict priority ordering of messages. This field is simply a "hint" from the server about how the message will be handled. Message priorities and how they are assigned are determined by the configuration of the JMS provider.

JMSRedelivered

boolean

Before delivery

A provider-provided value that indicates to the receiver that the message may have been delivered in the past with no acknowledgment from the client. On the sender, this header value is always unassigned.

JMSReplyTo

Destination

Before send

A Destination, set by the sending client, indicating where a reply message should be sent.

JMSTimestamp

long

During send

The time at which the message was handed off to the JMS provider to be sent. This value is given in milliseconds since the epoch (January 1, 1970, 00:00:00 GMT).

JMSType

String

Before send

A message type, set by the sending client. Some JMS providers require that this header be set, so it's a good idea to set it even if your application isn't using it. Some JMS providers also allow an administrator to configure a set of message types that will be matched against this header and used to selectively set handling of the message based on its type.


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 Types

To support various application scenarios, JMS provides the following subclasses of Message, each providing a different type of message body:


TextMessage

Arguably the most popular type of Message, this has a body that is a simple String. The format of the String contents is left to the application to interpret. The String may contain a simple informational phrase, it may contain conversational text input by a user in a collaboration application, or it may contain formatted text such as XML.


BytesMessage

This type of message contains an array of bytes as its body. BytesMessages can be used to send binary data in a message, and it can be used to wrap a native message format with a JMS message.


ObjectMessage

The body of this message is a serialized Java object.


MapMessage

The body of this message is a set of name/value pairs. The names are Strings, and the values are Java primitive types, like double, int, and String. The values of the entries are accessed using get/setXXX( ) methods on MapMessage. Note that these entries are stored in the body of the message: they aren't message header properties and can't be used for message selection (see "Filtering Messages" later in this chapter).


StreamMessage

A StreamMessage contains a stream of Java primitive data types (double, int, String, etc.). Data elements are written sequentially to the body of the message using various writeXXX( ) methods, and they are read sequentially on the receiving end using corresponding readXXX( ) methods. If the receiver doesn't know the types of data in the message, they can be introspected using the readObject( ) method:

     StreamMessage sMsg = ...;     Object item = sMsg.readObject( );     if (item instanceof Float) {       float fData = ((Float)item).floatValue( )l       ...     } 

11.3.3. Accessing Message Content

When 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 Messages

JMS 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.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net