Sun Java System Message Queue 3.7 UR1 Developer's Guide for Java Clients

Working With Messages

This section describes how to use the Message Queue Java API to compose, send, receive, and process messages.

Message Structure

A message consists of the following parts:

The following sections discuss each of these in greater detail.

Message Header

Every message must have a header containing identifying and routing information. The header consists of a set of standard fields, which are defined in the Java Message Service Specification and summarized in Table 2–4. Some of these are set automatically by Message Queue in the course of producing and delivering a message, some depend on settings specified when a message producer sends a message, and others are set by the client on a message-by-message basis.

Table 2–4 Message Header Fields

Name 

Description 

JMSMessageID

Message identifier 

JMSDestination

Destination to which message is sent 

JMSReplyTo

Destination to which to reply 

JMSCorrelationID

Link to related message 

JMSDeliveryMode

Delivery mode (persistent or nonpersistent) 

JMSPriority

Priority level 

JMSTimestamp

Time of transmission 

JMSExpiration

Expiration time 

JMSType

Message type 

JMSRedelivered

Has message been delivered before? 

The JMS Message interface defines methods for setting the value of each header field: for instance,

outMsg.setJMSReplyTo(replyDest);

Table 2–5 lists all of the available header specification methods.

Table 2–5 Message Header Specification Methods

Name 

Description 

setJMSMessageID

Set message identifier 

setJMSDestination

Set destination 

setJMSReplyTo

Set reply destination 

setJMSCorrelationID

Set correlation identifier from string 

setJMSCorrelationIDAsBytes

Set correlation identifier from byte array 

setJMSDeliveryMode

Set delivery mode 

setJMSPriority

Set priority level 

setJMSTimestamp

Set time stamp 

setJMSExpiration

Set expiration time 

setJMSType

Set message type 

setJMSRedelivered

Set redelivered flag 

The message identifier (JMSMessageID) is a string value uniquely identifying the message, assigned and set by the message broker when the message is sent. Because generating an identifier for each message adds to both the size of the message and the overhead involved in sending it, and because some client applications may not use them, the JMS interface provides a way to suppress the generation of message identifiers, using the message producer method setDisableMessageID (see Sending Messages).

The JMSDestination header field holds a Destination object representing the destination to which the message is directed, set by the message broker when the message is sent. There is also a JMSReplyTo field that you can set to specify a destination to which reply messages should be directed. Clients sending such a reply message can set its JMSCorrelationID header field to refer to the message to which they are replying. Typically this field is set to the message identifier string of the message being replied to, but client applications are free to substitute their own correlation conventions instead, using either the setJMSCorrelationID method (if the field value is a string) or the more general setJMSCorrelationIDAsBytes (if it is not).

The delivery mode (JMSDeliveryMode) specifies whether the message broker should log the message to stable storage. There are two possible values, PERSISTENT and NON_PERSISTENT, both defined as static constants of the JMS interface DeliveryMode: for example,

outMsg.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);

The default delivery mode is PERSISTENT, represented by the static constant Message.DEFAULT_DELIVERY_MODE.

The choice of delivery mode represents a tradeoff between performance and reliability:

The message’s priority level (JMSPriority) is expressed as an integer from 0 (lowest) to 9 (highest). Priorities from 0 to 4 are considered gradations of normal priority, those from 5 to 9 of expedited priority. The default priority level is 4, represented by the static constant Message.DEFAULT_PRIORITY.

The Message Queue client runtime sets the JMSTimestamp header field to the time it delivers the message to the broker, expressed as a long integer in standard Java format (milliseconds since midnight, January 1, 1970 UTC). The message’s lifetime, specified when the message is sent, is added to this value and the result is stored in the JMSExpiration header field. (The default lifetime value of 0, represented by the static constant Message.DEFAULT_TIME_TO_LIVE, denotes an unlimited lifetime. In this case, the expiration time is also set to 0 to indicate that the message never expires.) As with the message identifier, client applications that do not use a message’s time stamp can improve performance by suppressing its generation with the message producer method setDisableMessageTimestamp (see Sending Messages).

The header field JMSType can contain an optional message type identifier string supplied by the client when the message is sent. This field is intended for use with other JMS providers; Message Queue clients can simply ignore it.

When a message already delivered must be delivered again because of a failure, the broker indicates this by setting the JMSRedelivered flag in the message header to true. This can happen, for instance, when a session is recovered or a transaction is rolled back. The receiving client can check this flag to avoid duplicate processing of the same message (such as when the message has already been successfully received but the client’s acknowledgment was missed by the broker).

See the Java Message Service Specification for a more detailed discussion of all message header fields.

Message Properties

A message property consists of a name string and an associated value, which must be either a string or one of the standard Java primitive data types (int, byte, short, long, float, double, or boolean). The Message interface provides methods for setting properties of each type (see Table 2–6). There is also a setObjectProperty method that accepts a primitive value in objectified form, as a Java object of class Integer, Byte, Short, Long, Float , Double, Boolean, or String . The clearProperties method deletes all properties associated with a message; the message header and body are not affected.

Table 2–6 Message Property Specification Methods

Name 

Description 

setIntProperty

Set integer property 

setByteProperty

Set byte property 

setShortProperty

Set short integer property 

setLongProperty

Set long integer property 

setFloatProperty

Set floating-point property 

setDoubleProperty

Set double-precision property 

setBooleanProperty

Set boolean property 

setStringProperty

Set string property 

setObjectProperty

Set property from object 

clearProperties

Clear properties 

The JMS specification defines certain standard properties, listed in Table 2–7 . By convention, the names of all such standard properties begin with the letters JMSX; names of this form are reserved and must not be used by a client application for its own custom message properties. Similarly, property names beginning with JMS_SUN are reserved for provider-specific properties defined by Message Queue itself; these are discussed in Chapter 3, Message Queue Clients: Design and Features

Table 2–7 Standard JMS Message Properties

Name 

Description 

JMSXUserID

Identity of user sending message 

JMSXAppID

Identity of application sending message 

JMSXDeliveryCount

Number of delivery attempts 

JMSXGroupID

Identity of message group to which this message belongs 

JMSXGroupSeq

Sequence number within message group 

JMSXProducerTXID

Identifier of transaction within which message was produced 

JMSXConsumerTXID

Identifier of transaction within which message was consumed 

JMSXRcvTimestamp

Time message delivered to consumer 

JMSXState

Message state (waiting, ready, expired, or retained) 

Message Body

The actual content of a message is contained in the message body. JMS defines six classes (or types) of message, each with a different body format:

Each of these is a subinterface of the generic Message interface, extended with additional methods specific to the particular message type.

Composing Messages

The JMS Session interface provides methods for creating each type of message, as shown in Table 2–8. For instance, you can create a text message with a statement such as

TextMessage  outMsg = mySession.createTextMessage();

In general, these methods create a message with an empty body; the interfaces for specific message types then provide additional methods for filling the body with content, as described in the sections that follow.

Table 2–8 Session Methods for Message Creation

Name 

Description 

createMessage

Create null message 

createTextMessage

Create text message 

createStreamMessage

Create stream message 

createMapMessage

Create map message 

createObjectMessage

Create object message 

createBytesMessage

Create bytes message 


Note –

Some of the message-creation methods have an overloaded form that allows you to initialize the message body directly at creation: for example,

TextMessage
   outMsg = mySession.createTextMessage("Hello, World!");

These exceptions are pointed out in the relevant sections below.


Once a message has been delivered to a message consumer, its body is considered read-only; any attempt by the consumer to modify the message body will cause an exception (MessageNotWriteableException) to be thrown. The consumer can, however, empty the message body and place it in a writeable state by calling the message method clearBody:

outMsg.clearBody();

This places the message in the same state as if it had been newly created, ready to fill its body with new content.

Composing Text Messages

You create a text message with the session method createTextMessage. You can either initialize the message body directly at creation time

TextMessage  outMsg = mySession.createTextMessage("Hello, World!");

or simply create an empty message and then use its setText method (see Table 2–9 ) to set its content:

TextMessage  outMsg = mySession.createTextMessage();
outMsg.setText("Hello, World!");
Table 2–9 Text Message Composition Method

Name 

Description 

setText

Set content string 

Composing Stream Messages

The session method createStreamMessage returns a new, empty stream message. You can then use the methods shown in Table 2–10 to write primitive data values into the message body, similarly to writing to a data stream: for example,

StreamMessage  outMsg = mySession.createStreamMessage();
outMsg.writeString("The Meaning of Life");
outMsg.writeInt(42);
Table 2–10 Stream Message Composition Methods

Name 

Description 

writeInt

Write integer to message stream 

writeByte

Write byte value to message stream 

writeBytes

Write byte array to message stream 

writeShort

Write short integer to message stream 

writeLong

Write long integer to message stream 

writeFloat

Write floating-point value to message stream 

writeDouble

Write double-precision value to message stream 

writeBoolean

Write boolean value to message stream 

writeChar

Write character to message stream 

writeString

Write string to message stream 

writeObject

Write value of object to message stream 

reset

Reset message stream 

As a convenience for handling values whose types are not known until execution time, the writeObject method accepts a string or an objectified primitive value of class Integer, Byte, Short, Long, Float, Double , Boolean, or Character and writes the corresponding string or primitive value to the message stream: for example, the statements

Integer  meaningOfLife = new Integer(42);
outMsg.writeObject(meaningOfLife);

are equivalent to

outMsg.writeInt(42);

This method will throw an exception (MessageFormatException) if the argument given to it is not of class String or one of the objectified primitive classes.

Once you’ve written the entire message contents to the stream, the reset method

outMsg.reset();

puts the message body in read-only mode and repositions the stream to the beginning, ready to read (see Processing Messages). When the message is in this state, any attempt to write to the message stream will throw the exception MessageNotWriteableException. A call to the clearBody method (inherited from the superinterface Message) deletes the entire message body and makes it writeable again.

Composing Map Messages

Table 2–11 shows the methods available in the MapMessage interface for adding content to the body of a map message. Each of these methods takes two arguments, a name string and a primitive or string value of the appropriate type, and adds the corresponding name-value pair to the message body: for example,

StreamMessage  outMsg = mySession.createMapMessage();
outMsg.setInt("The Meaning of Life", 42);
Table 2–11 Map Message Composition Methods

Name 

Description 

setInt

Store integer in message map by name 

setByte

Store byte value in message map by name 

setBytes

Store byte array in message map by name 

setShort

Store short integer in message map by name 

setLong

Store long integer in message map by name 

setFloat

Store floating-point value in message map by name 

setDouble

Store double-precision value in message map by name 

setBoolean

Store boolean value in message map by name 

setChar

Store character in message map by name 

setString

Store string in message map by name 

setObject

Store object in message map by name 

Like stream messages, map messages provide a convenience method (setObject) for dealing with values whose type is determined dynamically at execution time: for example, the statements

Integer  meaningOfLife = new Integer(42);
outMsg.setObject("The Meaning of Life", meaningOfLife);

are equivalent to

outMsg.setInt("The Meaning of Life", 42);

The object supplied must be either a string object (class String) or an objectified primitive value of class Integer, Byte , Short, Long, Float, Double, Boolean, or Character; otherwise an exception (MessageFormatException) will be thrown.

Composing Object Messages

The ObjectMessage interface provides just one method, setObject (Table 2–12 ), for setting the body of an object message:

ObjectMessage  outMsg = mySession.createObjectMessage();
outMsg.setObject(bodyObject);

The argument to this method can be any serializable object (that is, an instance of any class that implements the standard Java interface Serializable). If the object is not serializable, the exception MessageFormatException will be thrown.

Table 2–12 Object Message Composition Method

Name 

Description 

setObject

Serialize object to message body 

As an alternative, you can initialize the message body directly when you create the message, by passing an object to the session method createObjectMessage:

ObjectMessage  outMsg = mySession.createObjectMessage(bodyObject);

Again, an exception will be thrown if the object is not serializable.

Composing Bytes Messages

The body of a bytes message simply consists of a stream of uninterpreted bytes; its interpretation is entirely a matter of agreement between sender and receiver. This type of message is intended primarily for encoding message formats required by other existing message systems; Message Queue clients should generally use one of the other, more specific message types instead.

Composing a bytes message is similar to composing a stream message (see Composing Stream Messages). You create the message with the session method createBytesMessage, then use the methods shown in Table 2–13 to encode primitive values into the message’s byte stream: for example,

BytesMessage  outMsg = mySession.createBytesMessage();
outMsg.writeUTF("The Meaning of Life");
outMsg.writeInt(42);
Table 2–13 Bytes Message Composition Methods

Name 

Description 

writeInt

Write integer to message stream 

writeByte

Write byte value to message stream 

writeBytes

Write byte array to message stream 

writeShort

Write short integer to message stream 

writeLong

Write long integer to message stream 

writeFloat

Write floating-point value to message stream 

writeDouble

Write double-precision value to message stream 

writeBoolean

Write boolean value to message stream 

writeChar

Write character to message stream 

writeUTF

Write UTF-8 string to message stream 

writeObject

Write value of object to message stream 

reset

Reset message stream 

As with stream and map messages, you can use the generic object-based method writeObject to handle values whose type is unknown at compilation time: for example, the statements

Integer  meaningOfLife = new Integer(42);
outMsg.writeObject(meaningOfLife);

are equivalent to

outMsg.writeInt(42);

The message’s reset method

outMsg.reset();

puts the message body in read-only mode and repositions the byte stream to the beginning, ready to read (see Processing Messages). Attempting to write further content to a message in this state will cause an exception (MessageNotWriteableException). The inherited Message method clearBody can be used to delete the entire message body and make it writeable again.

Sending Messages

In order to send messages to a message broker, you must create a message producer using the session method createProducer:

MessageProducer  myProducer = mySession.createProducer(myDest);

The scope of the message producer is limited to the session that created it and the connection to which that session belongs. Table 2–14 shows the methods defined in the MessageProducer interface.

Table 2–14 Message Producer Methods

Name 

Description 

getDestination

Get default destination 

setDeliveryMode

Set default delivery mode 

getDeliveryMode

Get default delivery mode 

setPriority

Set default priority level 

getPriority

Get default priority level 

setTimeToLive

Set default message lifetime 

getTimeToLive

Get default message lifetime 

setDisableMessageID

Set message identifier disable flag 

getDisableMessageID

Get message identifier disable flag 

setDisableMessageTimestamp

Set time stamp disable flag 

getDisableMessageTimestamp

Get time stamp disable flag 

send

Send message 

close

Close message producer 

The createProducer method takes a destination as an argument, which may be either a (point-to-point) queue or a (publish/subscribe) topic. The producer will then send all of its messages to the specified destination. If the destination is a queue, the producer is called a sender for that queue; if it is a topic, the producer is a publisher to that topic. The message producer’s getDestination method returns this destination.

You also have the option of leaving the destination unspecified when you create a producer

MessageProducer  myProducer = mySession.createProducer(null);

in which case you must specify an explicit destination for each message. This option is typically used for producers that must send messages to a variety of destinations, such as those designated in the JMSReplyTo header fields of incoming messages (see Message Header).


Note –

The generic MessageProducer interface also has specialized subinterfaces, QueueSender and TopicPublisher, for sending messages specifically to a point-to-point queue or a publish/subscribe topic. These types of producer are created by the createSender and createPublisher methods of the specialized session subinterfaces QueueSession and TopicSession, respectively. However, it is generally more convenient (and recommended) to use the generic form of message producer described here, which can handle both types of destination indiscriminately.


A producer has a default delivery mode (persistent or nonpersistent), priority level, and message lifetime, which it will apply to all messages it sends unless explicitly overridden for an individual message. You can set these properties with the message producer methods setDeliveryMode, setPriority, and setTimeToLive, and retrieve them with getDeliveryMode, getPriority, and getTimeToLive. If you don’t set them explicitly, they default to persistent delivery, priority level 4, and a lifetime value of 0, denoting an unlimited message lifetime.

The heart of the message producer interface is the send method, which is available in a variety of overloaded forms. The simplest of these just takes a message as its only argument:

myProducer.send(outMsg);

This sends the specified message to the producer’s default destination, using the producer’s default delivery mode, priority, and message lifetime. Alternatively, you can explicitly specify the destination

myProducer.send(myDest, outMsg);

or the delivery mode, priority, and lifetime in milliseconds

myProducer.send(outMsg, DeliveryMode.NON_PERSISTENT, 9, 1000);

or all of these at once:

myProducer.send(myDest, outMsg, DeliveryMode.NON_PERSISTENT, 9, 1000);

Recall that if you did not specify a destination when creating the message producer, you must provide an explicit destination for each message you send.

As discussed earlier under Message Header, client applications that have no need for the message identifier and time stamp fields in the message header can gain some performance improvement by suppressing the generation of these fields, using the message producer’s setDisableMessageID and setdisableMessageTimestamp methods. Note that a true value for either of these flags disables the generation of the corresponding header field, while a false value enables it. Both flags are set to false by default, meaning that the broker will generate the values of these header fields unless explicitly instructed otherwise.

When you are finished using a message producer, you should call its close method

myProducer.close();

allowing the broker and client runtime to release any resources they may have allocated on the producer’s behalf.

Receiving Messages

Messages are received by a message consumer, within the context of a connection and a session. Once you have created a consumer, you can use it to receive messages in either of two ways:

These two forms of message consumption are described in the sections Receiving Messages Synchronously and Receiving Messages Asynchronously.

Creating Message Consumers

The session method createConsumer creates a generic consumer that can be used to receive messages from either a (point-to-point) queue or a (publish/subscribe) topic:

MessageConsumer  myConsumer = mySession.createConsumer(myDest);

If the destination is a queue, the consumer is called a receiver for that queue; if it is a topic, the consumer is a subscriber to that topic.


Note –

The generic MessageConsumer interface also has specialized subinterfaces, QueueReceiver and TopicSubscriber, for receiving messages specifically from a point-to-point queue or a publish/subscribe topic. These types of consumer are created by the createReceiver and createSubscriber methods of the specialized session subinterfaces QueueSession and TopicSession, respectively. However, it is generally more convenient (and recommended) to use the generic form of message consumer described here, which can handle both types of destination indiscriminately.


A subscriber created for a topic destination with the createConsumer method is always nondurable, meaning that it will receive only messages that are sent (published)to the topic while the subscriber is active. If you want the broker to retain messages published to a topic while no subscriber is active and deliver them when one becomes active again, you must instead create a durable subscriber, as described in Durable Subscribers.

Table 2–15 shows the methods defined in the MessageConsumer interface, which are discussed in detail in the relevant sections below.

Table 2–15 Message Consumer Methods

Name 

Description 

getMessageSelector

Get message selector 

receive

Receive message synchronously 

receiveNoWait

Receive message synchronously without blocking 

setMessageListener

Set message listener for asynchronous reception 

getMessageListener

Get message listener for asynchronous reception 

close

Close message consumer 

Message Selectors

If appropriate, you can restrict the messages a consumer will receive from its destination by supplying a message selector as an argument when you create the consumer:

String  mySelector = "/* Text of selector here */";
MessageConsumer  myConsumer = mySession.createConsumer(myDest, mySelector);

The selector is a string whose syntax is based on a subset of the SQL92 conditional expression syntax, which allows you to filter the messages you receive based on the values of their properties (see Message Properties). See the Java Message Service Specification for a complete description of this syntax. The message consumer’s getMessageSelector method returns the consumer’s selector string (or null if no selector was specified when the consumer was created):

String  mySelector = myConsumer.getMessageSelector();

Note –

Messages whose properties do not satisfy the consumer’s selector will be retained undelivered by the destination until they are retrieved by another message consumer. The use of message selectors can thus cause messages to be delivered out of sequence from the order in which they were originally produced. Only a message consumer without a selector is guaranteed to receive messages in their original order.


In some cases, the same connection may both publish and subscribe to the same topic destination. The createConsumer method accepts an optional boolean argument that suppresses the delivery of messages published by the consumer’s own connection:

String  mySelector = "/* Text of selector here */";
MessageConsumer
        myConsumer = mySession.createConsumer(myDest, mySelector, true);

The resulting consumer will receive only messages published by a different connection.

Durable Subscribers

To receive messages delivered to a publish/subscribe topic while no message consumer is active, you must ask the message broker to create a durable subscriber for that topic. All sessions that create such subscribers for a given topic must have the same client identifier (see Using Connections ). When you create a durable subscriber, you supply a subscriber name that must be unique for that client identifier:

MessageConsumer
        myConsumer = mySession.createDurableSubscriber(myDest, "mySub");

(The object returned by the createDurableSubscriber method is actually typed as TopicSubscriber, but since that is a subinterface of MessageConsumer, you can safely assign it to a MessageConsumer variable. Note, however, that the destination myDest must be a publish/subscribe topic and not a point-to-point queue.)

You can think of a durable subscriber as a “virtual message consumer” for the specified topic, identified by the unique combination of a client identifier and subscriber name. When a message arrives for the topic and no message consumer is currently active for it, the message will be retained for later delivery. Whenever you create a consumer with the given client identifier and subscriber name, it will be considered to represent this same durable subscriber and will receive all of the accumulated messages that have arrived for the topic in the subscriber’s absence. Each message is retained until it is delivered to (and acknowledged by) such a consumer or until it expires.


Note –

Only one session at a time can have an active consumer for a given durable subscription. If another such consumer already exists, the createDurableSubscriber method will throw an exception.


Like the createConsumer method described in the preceding section (which creates nondurable subscribers), createDurableSubscriber can accept an optional message selector string and a boolean argument telling whether to suppress the delivery of messages published by the subscriber’s own connection:

String  mySelector = "/* Text of selector here */";
MessageConsumer  myConsumer
                    = mySession.createDurableSubscriber(myDest, "mySub",
                                                        mySelector, true);

You can change the terms of a durable subscription by creating a new subscriber with the same client identifier and subscription name but with a different topic, selector, or both. The effect is as if the old subscription were destroyed and a new one created with the same name. When you no longer need a durable subscription, you can destroy it with the session method unsubscribe:

mySession.unsubscribe("mySub");

Receiving Messages Synchronously

Once you have created a message consumer for a session, using either the createConsumer orcreateDurableSubscriber method, you must start the session’s connection to begin the flow of incoming messages:

myConnection.start();

(Note that it is not necessary to start a connection in order to produce messages, only to consume them.) You can then use the consumer’s receive method to receive messages synchronously from the message broker:

Message  inMsg = myConsumer.receive();

This returns the next available message for this consumer. If no message is immediately available, the receive method blocks until one arrives. You can also provide a timeout interval in milliseconds:

Message  inMsg = myConsumer.receive(1000);

In this case, if no message arrives before the specified timeout interval (1 second in the example) expires, the method will return with a null result. An alternative method, receiveNoWait, returns a null result immediately if no message is currently available:

Message  inMsg = myConsumer.receiveNoWait();

Receiving Messages Asynchronously

If you want your message consumer to receive incoming messages asynchronously, you must create a message listener to process the messages. This is a Java object that implements the JMS MessageListener interface. The procedure is as follows:

ProcedureTo Set Up a Message Queue Java Client to Receive Messages Asynchronously

  1. Define a message listener class implementing the MessageListener interface.

    The interface consists of the single method onMessage, which accepts a message as a parameter and processes it in whatever way is appropriate for your application:


    public class MyMessageListener implements MessageListener
      {
            public void onMessage (Message  inMsg)
          {
            /* Code here to process message */
          }
       }
  2. Create a message consumer.

    You can use either the createConsumer or createDurableSubscriber method of the session in which the consumer will operate: for instance,


    MessageConsumer  myConsumer = mySession.createConsumer(myDest);
  3. Create an instance of your message listener class.


    MyMessageListener  myListener = new MyMessageListener();
  4. Associate the message listener with your message consumer.

    The message consumer method setMessageListener accepts a message listener object and associates it with the given consumer:


    myConsumer.setMessageListener(myListener);
  5. Start the connection to which this consumer’s session belongs.

    The connection’s start method begins the flow of messages from the message broker to your message consumer:


    myConnection.start();

    Once the connection is started, the Message Queue client runtime will call your message listener’s onMessage method each time it has a message to deliver to this consumer.

    To ensure that no messages are lost before your consumer is ready to receive them, it is important not to start the connection until after you have created the message listener and associated it with the consumer. If the connection is already started, you should stop it before creating an asynchronous consumer, then start it again when the consumer is ready to begin processing.

    Setting a consumer’s message listener to null removes any message listener previously associated with it:


    myConsumer.setMessageListener(null);

    The consumer’s getMessageListener method returns its current message listener (or null if there is none):


    MyMessageListener  myListener = myConsumer.getMessageListener();

Acknowledging Messages

If you have specified client-acknowledge as your session’s acknowledgment mode (see Acknowledgment Modes), it is your client application’s responsibility to explicitly acknowledge each message it receives. If you have received the message synchronously, using a message consumer’s receive (or receiveNoWait) method, you should process the message first and then acknowledge it; if you have received it asynchronously, your message listener’s onMessage method should acknowledge the message after processing it. This ensures that the message broker will not delete the message from persistent storage until processing is complete.


Note –

In a transacted session (see Transacted Sessions), there is no need to acknowledge a message explicitly: the session’s acknowledgment mode is ignored and all acknowledgment processing is handled for you automatically by the Message Queue client runtime. In this case, the session’s getAcknowledgeMode method will return the special constant Session.SESSION_TRANSACTED.


Table 2–16 shows the methods available for acknowledging a message. The most general is acknowledge, defined in the standard JMS interface javax.jms.Message:

inMsg.acknowledge();

This acknowledges all unacknowledged messages consumed by the session up to the time of call. You can use this method to acknowledge each message individually as you receive it, or you can group several messages together and acknowledge them all at once by calling acknowledge on the last one in the group.

Table 2–16 Message Acknowledgment Methods

Function 

Description 

acknowledge

Acknowledge all unacknowledged messages for session 

acknowledgeThisMessage

Acknowledge this message only 

acknowledgeUpThroughThisMessage

Acknowledge all unacknowledged messages through this one 

The Message Queue version of the Message interface, defined in the package com.sun.messaging.jms, adds two more methods that provide more flexible control over which messages you acknowledge. The acknowledgeThisMessage method just acknowledges the single message for which it is called, rather than all messages consumed by the session; acknowledgeUpThroughThisMessage acknowledges the message for which it is called and all previous messages; messages received after that message remain unacknowledged.

Browsing Messages

If the destination from which you are consuming messages is a point-to-point queue, you can use a queue browser to examine the messages in the queue without consuming them. The session method createBrowser creates a browser for a specified queue:

QueueBrowser  myBrowser = mySession.createBrowser(myDest);

The method will throw an exception (InvalidDestinationException) if you try to pass it a topic destination instead of a queue. You can also supply a selector string as an optional second argument:

String        mySelector = "/* Text of selector here */";
QueueBrowser  myBrowser  = mySession.createBrowser(myDest, mySelector);

Table 2–17 shows the methods defined in the QueueBrowser interface. The getQueue and getMessageSelector methods return the browser’s queue and selector string, respectively.

Table 2–17 Queue Browser Methods

Name 

Description 

getQueue

Get queue from which this browser reads 

getMessageSelector

Get message selector 

getEnumeration

Get enumeration of all messages in the queue 

close

Close browser 

The most important queue browser method is getEnumeration, which returns a Java enumeration object that you can use to iterate through the messages in the queue, as shown in Example 2–4.


Example 2–4 Browsing a Queue


Enumeration  queueMessages = myBrowser.getEnumeration();
Message      eachMessage;

while ( queueMessages.hasMoreElements() )
  { eachMessage = queueMessages.nextElement();
    /* Do something with the message */
  }
               

The browser’s close method closes it when you’re through with it:

myBrowser.close();

Closing a Consumer

As a matter of good programming practice, you should close a message consumer when you have no further need for it. Closing a session or connection automatically closes all consumers associated with it; to close a consumer without closing the session or connection to which it belongs, you can use its close method:

myConsumer.close();

For a consumer that is a nondurable topic subscriber, this terminates the flow of messages to the consumer. However, if the consumer is a queue receiver or a durable topic subscriber, messages will continue to be accumulated for the destination and will be delivered the next time a consumer for that destination becomes active. To terminate a durable subscription permanently, call its session’s unsubscribe method with the subscriber name as an argument:

mySession.unsubscribe("mySub");

Processing Messages

Processing a message after you have received it may entail examining its header fields, properties, and body. The following sections describe how this is done.

Retrieving Message Header Fields

The standard JMS message header fields are described in Table 2–4. Table 2–18 shows the methods provided by the JMS Message interface for retrieving the values of these fields: for instance, you can obtain a message’s reply destination with the statement

Destination  replyDest = inMsg.getJMSReplyTo();
Table 2–18 Message Header Retrieval Methods

Name 

Description 

getJMSMessageID

Get message identifier 

getJMSDestination

Get destination 

getJMSReplyTo

Get reply destination 

getJMSCorrelationID

Get correlation identifier as string 

getJMSCorrelationIDAsBytes

Get correlation identifier as byte array 

getJMSDeliveryMode

Get delivery mode 

getJMSPriority

Get priority level 

getJMSTimestamp

Get time stamp 

getJMSExpiration

Get expiration time 

getJMSType

Get message type 

getJMSRedelivered

Get redelivered flag 

Retrieving Message Properties

Table 2–19 lists the methods defined in the JMS Message interface for retrieving the values of a message’s properties (see Message Properties). There is a retrieval method for each of the possible primitive types that a property value can assume: for instance, you can obtain a message’s time stamp with the statement

long  timeStamp = inMsg.getLongProperty("JMSXRcvTimestamp");
Table 2–19 Message Property Retrieval Methods

Name 

Description 

getIntProperty

Get integer property 

getByteProperty

Get byte property 

getShortProperty

Get short integer property 

getLongProperty

Get long integer property 

getFloatProperty

Get floating-point property 

getDoubleProperty

Get double-precision property 

getBooleanProperty

Get boolean property 

getStringProperty

Get string property 

getObjectProperty

Get property as object 

getPropertyNames

Get property names 

propertyExists

Does property exist? 

There is also a generic getObjectProperty method that returns a property value in objectified form, as a Java object of class Integer, Byte, Short, Long, Float , Double, Boolean, or String . For example, another way to obtain a message’s time stamp, equivalent to that shown above, would be

Long  timeStampObject = (Long)inMsg.getObjectProperty("JMSXRcvTimestamp");
long  timeStamp = timeStampObject.longValue();

If the message has no property with the requested name, getObjectProperty will return null; the message method propertyExists tests whether this is the case.

The getPropertyNames method returns a Java enumeration object for iterating through all of the property names associated with a given message; you can then use the retrieval methods shown in the table to retrieve each of the properties by name, as shown in Example 2–5.


Example 2–5 Enumerating Message Properties


Enumeration  propNames = inMsg.getPropertyNames();
String       eachName;
Object       eachValue;

while ( propNames.hasMoreElements() )
  { eachName  = propNames.nextElement();
    eachValue = inMsg.getObjectProperty(eachName);
    /* Do something with the value */
  }
               

Processing the Message Body

The methods for retrieving the contents of a message’s body essentially parallel those for composing the body, as described earlier under Composing Messages. The following sections describe these methods for each of the possible message types (text, stream, map, object, and bytes).

Processing Text Messages

The text message method getText (Table 2–20 ) retrieves the contents of a text message’s body in the form of a string:

String  textBody = inMsg.getText();
Table 2–20 Text Message Access Method

Name 

Description 

getText

Get content string 

Processing Stream Messages

Reading the contents of a stream message is similar to reading from a data stream, using the access methods shown in Table 2–21: for example, the statement

int  intVal = inMsg.readInt();

retrieves an integer value from the message stream.

Table 2–21 Stream Message Access Methods

Name 

Description 

readInt

Read integer from message stream 

readByte

Read byte value from message stream 

readBytes

Read byte array from message stream 

readShort

Read short integer from message stream 

readLong

Read long integer from message stream 

readFloat

Read floating-point value from message stream 

readDouble

Read double-precision value from message stream 

readBoolean

Read boolean value from message stream 

readChar

Read character from message stream 

readString

Read string from message stream 

readObject

Read value from message stream as object 

The readObject method returns the next value from the message stream in objectified form, as a Java object of the class corresponding to the value’s primitive data type: for instance, if the value is of type int, readObject returns an object of class Integer. The following statements are equivalent to the one shown above:

Integer  intObject = (Integer) inMsg.readObject();
int      intVal    = intObject.intValue();

Processing Map Messages

The MapMessage interface provides the methods shown in Table 2–22 for reading the body of a map message. Each access method takes a name string as an argument and returns the value to which that name is mapped: for instance, under the example shown in Composing Map Messages, the statement

int  meaningOfLife = inMsg.getInt("The Meaning of Life");

would set the variable meaningOfLife to the value 42.

Table 2–22 Map Message Access Methods

Name 

Description 

getInt

Get integer from message map by name 

getByte

Get byte value from message map by name 

getBytes

Get byte array from message map by name 

getShort

Get short integer from message map by name 

getLong

Get long integer from message map by name 

getFloat

Get floating-point value from message map by name 

getDouble

Get double-precision value from message map by name 

getBoolean

Get boolean value from message map by name 

getChar

Get character from message map by name 

getString

Get string from message map by name 

getObject

Get object from message map by name 

itemExists

Does map contain an item with specified name? 

getMapNames

Get enumeration of all names in map 

Like stream messages, map messages provide an access method, getObject, that returns a value from the map in objectified form, as a Java object of the class corresponding to the value’s primitive data type: for instance, if the value is of type int, getObject returns an object of class Integer. The following statements are equivalent to the one shown above:

Integer  meaningObject = (Integer) inMsg.getObject("The Meaning of Life");
int      meaningOfLife = meaningObject.intValue();

The itemExists method returns a boolean value indicating whether the message map contains an association for a given name string:

if ( inMsg.itemExists("The Meaning of Life") )
  { /* Life is meaningful */
  }
else
  { /* Life is meaningless */
  }

The getMapNames method returns a Java enumeration object for iterating through all of the names defined in the map; you can then use getObject to retrieve the corresponding values, as shown in Example 2–6.


Example 2–6 Enumerating Map Message Values


Enumeration  mapNames = inMsg.getMapNames();
String       eachName;
Object       eachValue;

while ( mapNames.hasMoreElements() )
  { eachName  = mapNames.nextElement();
    eachValue = inMsg.getObject(eachName);
    /* Do something with the value */
  }
                  

Processing Object Messages

The ObjectMessage interface provides just one method, getObject (Table 2–23 ), for retrieving the serialized object that is the body of an object message:

Object  messageBody = inMsg.getObject();

You can then typecast the result to a more specific class and process it in whatever way is appropriate.

Table 2–23 Object Message Access Method

Name 

Description 

getObject

Get serialized object from message body 

Processing Bytes Messages

The body of a bytes message simply consists of a stream of uninterpreted bytes; its interpretation is entirely a matter of agreement between sender and receiver. This type of message is intended primarily for decoding message formats used by other existing message systems; Message Queue clients should generally use one of the other, more specific message types instead.

Reading the body of a bytes message is similar to reading a stream message (see Processing Stream Messages): you use the methods shown in Table 2–24 to decode primitive values from the message’s byte stream. For example, the statement

int  intVal = inMsg.readInt();

retrieves an integer value from the byte stream. The getBodyLength method returns the length of the entire message body in bytes:

int  bodyLength = inMsg.getBodyLength();
Table 2–24 Bytes Message Access Methods

Name 

Description 

getBodyLength

Get length of message body in bytes 

readInt

Read integer from message stream 

readByte

Read signed byte value from message stream 

readUnsignedByte

Read unsigned byte value from message stream 

readBytes

Read byte array from message stream 

readShort

Read signed short integer from message stream 

readUnsignedShort

Read unsigned short integer from message stream 

readLong

Read long integer from message stream 

readFloat

Read floating-point value from message stream 

readDouble

Read double-precision value from message stream 

readBoolean

Read boolean value from message stream 

readChar

Read character from message stream 

readUTF

Read UTF-8 string from message stream