Chapter 3. Anatomy of a JMS Message

This chapter focuses on the anatomy of a message: the individual parts that make up a message (headers, properties, and the different kinds of message payloads). Appendix B, Appendix C, and Appendix D cover additional information that will prove invaluable as a reference when developing JMS applications. Appendix B, provides in-depth information on the purpose and application of JMS headers; Appendix C, covers the rules governing the use of JMS properties; and Appendix D, covers the syntax of message selectors. Although you do not need to read these appendixes to understand subsequent chapters in this book, you will need them as a reference when implementing real JMS applications. After you finish reading this chapter, take a look at Appendixes Appendix B, Appendix C, and Appendix D so you're familiar with their content.

The Message is the most important part of the entire JMS specification. All data and events in a JMS application are communicated with messages, while the rest of JMS exists to facilitate the transfer of messages. They are the lifeblood of the system.

A JMS message both carries application data and provides event notification. Its role is unique to distributed computing. In RPC-based systems (CORBA, Java RMI, DCOM), a message is a command to execute a method or procedure, which blocks the sender until a reply has been received. A JMS message is not a command; it transfers data and tells the receiver that something has happened. A message doesn't dictate what the recipient should do and the sender doesn't wait for a response. This decouples the sender from the receiver, making messaging systems and their messages far more dynamic and flexible than request-reply paradigms.

A Message object has two parts: the message data itself, called the payload or message body, and the message headers and properties (see Figure 3.1).

Anatomy of a message

Figure 3.1. Anatomy of a message

Messages come in various types that are defined by the payload they carry. The payload itself might be very structured, as with StreamMessage and MapMessage objects, or fairly unstructured, as with TextMessage, ObjectMessage, and BytesMessage types. Messages can carry important data or simply serve as notifications of events in the system. In most cases, messages are both notifications and vehicles for carrying data.

The message headers provide metadata about the message describing who or what created the message, when it was created, how long the data is valid, etc. The headers also contain routing information that describes the destination of the message (topic or queue), how a message should be acknowledged, and a lot more. In addition to headers, messages can carry properties that can be defined and set by the JMS client. JMS consumers can choose to receive messages based on the values of certain headers and properties, using a special filtering mechanism called message selectors .

Headers

Every JMS message has a set of standard headers. Each header is identified by a set of accessor and mutator methods that follow the idiom setJMS<HEADER>( ) , getJMS<HEADER>( ) . Here is a partial definition of the Message interface that shows all the JMS header methods:

public interface Message {
    
    public Destination getJMSDestination(  ) throws JMSException; 
    public void setJMSDestination(Destination destination) 
    throws JMSException; 

    public int getJMSDeliveryMode(  ) throws JMSException; 
    public void setJMSDeliveryMode(int deliveryMode) 
    throws JMSException; 

    public String getJMSMessageID(  ) throws JMSException; 
    public void setJMSMessageID(String id) throws JMSException; 

    public long getJMSTimestamp(  ) throws JMSException; 
    public void setJMSTimestamp(long timestamp) throws JMSException; 

    public long getJMSExpiration(  ) throws JMSException; 
    public void setJMSExpiration(long expiration) throws JMSException; 

    public boolean getJMSRedelivered(  ) throws JMSException; 
    public void setJMSRedelivered(boolean redelivered) 
    throws JMSException; 

    public int getJMSPriority(  ) throws JMSException; 
    public void setJMSPriority(int priority) throws JMSException; 

    public Destination getJMSReplyTo(  ) throws JMSException; 
    public void setJMSReplyTo(Destination replyTo) throws JMSException; 

    public String getJMSCorrelationID(  ) throws JMSException; 
    public void setJMSCorrelationID(String correlationID) 
    throws JMSException; 
    public byte[] getJMSCorrelationIDAsBytes(  ) throws JMSException; 
    public void setJMSCorrelationIDAsBytes(byte[] correlationID) 
    throws JMSException; 

    public String getJMSType(  ) throws JMSException; 
    public void setJMSType(String type) throws JMSException; 

}

JMS headers are divided into two large groups: automatically assigned headers and developer-assigned headers. The next two sections discuss these two types.

Automatically Assigned Headers

Most JMS headers are automatically assigned; their value is set by the JMS provider when the message is delivered, so that values assigned by the developer using the setJMS<HEADER>( ) methods are ignored. In other words, for headers that are automatically assigned, using the mutator methods explicitly is fruitless.[1] This doesn't mean, however, that the developer has no control over the value of these headers. Some automatically assigned headers depend on declarations made by the developer when creating the Session and MessageProducer (i.e., TopicPublisher). These cases are clearly illustrated in the header definitions that follow.

JMSDestination

The JMSDestination header identifies the destination with either a Topic or Queue object, both of which are Destination types. Identifying the message's destination is valuable to JMS clients that consume messages from more than one topic or queue:

Topic destination = (Topic) message.getJMSDestination(  );

JMSDeliveryMode

There are two types of delivery modes in JMS: persistent and nonpersistent. A persistent message should be delivered once-and-only-once , which means that if the JMS provider fails, the message is not lost; it will be delivered after the server recovers. A nonpersistent message is delivered at-most-once , which means that it can be lost permanently if the JMS provider fails. In both persistent and nonpersistent delivery modes the message server should not send a message to the same consumer more then once, but it is possible (see the section on JMSRedelivered for more details):

int deliverymode = message.getJMSDeliveryMode(  );
if (deliverymode = javax.jms.DeliveryMode.PERSISTENT) {
   ...
} else { // equals DeliveryMode.NON_PERSISTENT
   ...
}

The delivery mode can be set using the setJMSDeliveryMode( ) method on the producer (i.e., TopicPublisher). Once the delivery mode is set on the MessageProducer, it is applied to all messages delivered using that producer. The default setting is PERSISTENT :

// Set the JMS delivery mode on the message producer
TopicPublisher topicPublisher = topicSession.createPublisher(topic);
topicPublisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

JMSMessageID

The JMSMessageID is a String value that uniquely identifies a message. How unique the identifier is depends on the vendor. The JMSMessageID can be useful for historical repositories in JMS consumer applications where messages need to be uniquely indexed. Used in conjunction with the JMSCorrelationID, the JMSMessageID is also useful for correlating messages:

String messageid = message.getJMSMessageID(  );

JMSTimestamp

The JMSTimestamp is set automatically by the message producer when the send( ) operation is invoked. The timestamp is a long value that measures time in milliseconds:

long timestamp = message.getJMSTimestamp(  );

JMSExpiration

A Message object's expiration date prevents the message from being delivered to consumers after it has expired. This is useful for messages whose data is only valid for a period of time:

long timeToLive = message.getJMSExpiration(  );

The expiration time for messages is set in milliseconds on the producer (i.e., TopicPublisher) using the setTimeToLive( ) method:

TopicPublisher topicPublisher = topicSession.createPublisher(topic);
// Set time to live as 1 hour (1000 millis x 60 sec x 60 min)
topicPublisher.setTimeToLive(3600000);

By default the timeToLive is zero (0), which indicates that the message doesn't expire. Calling setTimeToLive( ) with a zero argument ensures that a message is created without an expiration date.

JMSRedelivered

The JMSRedelivered header indicates that the message was redelivered to the consumer. The JMSRedelivered header is true if the message is redelivered, and false if it's not. A message may be marked redelivered if a consumer failed to acknowledge previous delivery of the message, or when the JMS provider is not certain whether the consumer has already received the message:

boolean isRedelivered = message.getJMSRedelivered(  )

Message redelivery is covered in more detail in Chapter 6.

JMSPriority

The message producer may assign a priority to a message when it is delivered. There are two categories of message priorities: levels 0-4 are gradations of normal priority; levels 5-9 are gradations of expedited priority. The message servers may use a message's priority to prioritize delivery of messages to consumers—messages with an expedited priority are delivered ahead of normal priority messages:

int priority = message.getJMSPriority(  );

The priority of messages can be declared by the JMS client using the setPriority( ) method on the producer:

TopicPublisher topicPublisher = TopicSession.createPublisher(someTopic);
topicPublisher.setPriority(9);

Developer-Assigned Headers

While many of the JMS headers are set automatically when the message is delivered, several others must be set explicitly on the Message object before it is delivered by the producer.

JMSReplyTo

In some cases, a JMS message producer may want the consumers to reply to a message. The JMSReplyTo header, which contains a javax.jms.Destination, indicates which address a JMS consumer should reply to. A JMS consumer is not required to send a reply:

message.setJMSReplyTo(topic);
...
Topic topic = (Topic) message.getJMSReplyTo(  );

JMSCorrelationID

The JMSCorrelationID provides a header for associating the current message with some previous message or application-specific ID. In most cases the JMSCorrelationID will be used to tag a message as a reply to a previous message identified by a JMSMessageID , but the JMSCorrelationID can be any value, not just a JMSMessageID :

message.setJMSCorrelationID(identifier);
...
String correlationid = message.getJMSCorrelationID(  );

JMSType

JMSType is an optional header that is set by the JMS client. Its main purpose is to identify the message structure and type of payload; it is currently supported by only a few vendors. Some MOM systems (IBM's MQSeries, for example) treat the message body as uninterpreted bytes. These systems often provide a message type as a simple way for applications to label the message body. So a message type can be useful when exchanging messages with non-JMS clients that require this type of information to process the payload.

Other MOM systems (e.g., Sun's JMQ) and EAI systems (e.g., SagaVista and MQIntegrator) directly tie each message to some form of external message schema, with the message type as the link. These MOM systems require a message type because they provide metadata services bound to it.



[1] According to the specification authors, the setJMS<HEADER>( ) methods were left in the Message interface for "general orthogonality," or to keep it semantically symmetrical to balance the getJMS<HEADER>( ) methods—a fairly strange but established justification.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset