- Introduction to Messaging
- Components of the JMS Architecture
- The Two JMS Message Models
- The JMS Interfaces
- The Details of a JMS Message
- Message Selection and Filtering
- Using the JMS Point-to-Point Model
- Using the JMS Publish/Subscribe Model
- Synchronous Versus Asynchronous Messaging
- Message Persistence
- Using Transactions with JMS
- Using JMS with Enterprise JavaBeans
The Details of a JMS Message
A JMS message can be broken down into three parts:
- Message header
- Message properties
- Message body
The following sections describe the three parts in detail.
The Message Header
Every JMS message includes message header fields that are always passed from producer to consumer. The purpose of the header fields is to convey extra information to the consumer outside the normal content of the message body. The JMS provider sets some of these fields automatically after a message is sent to the consumer, but the MessageProducer has the opportunity to set some fields programmatically.
The JMSCorrelationID header field provides a way to correlate related messages. This is normally used for a request/response scenario. This can either be a vendor-specific ID, an application-specific string, or a provider-native byte value.
If a JMSCorrelationID is generated by the application, it must NOT start with an ID: prefix. This is reserved for vendor-generated message IDs.
The JMSDestination header field specifies the destination for the message. This is either a queue or topic. The JMS provider sets this field automatically when the send method is called.
The JMSExpiration header field specifies the expiration or time-to-live value for a message. If the value is set to 0, the message will never expire. When a message does expire, the JMS provider typically will discard the message. Also, any persistent messages will be deleted based on expiration values.
The JMSDeliveryMode header field specifies persistent or nonpersistent messaging. When using persistent messages, the message is stored in a persistent store. It's up to the vendor and application to determine what type of persistent store is used. This may be a RDBMS or just a file. When using persistent messaging, the send method is not considered to be successful until a message is stored.
Messages that are set as NON_PERSISTENT are not stored and can be lost if there is a system failure. A persistent message will be delivered as long as the send method is successful. If you need to ensure that messages are delivered, you should consider using persistent messages.
Setting up persistent messages is not that hard to do, but it does generally require more up-front work due to the storage configuration settings.
The JMSMessageID header field contains a value that uniquely identifies each message sent by a provider. This value is set by the provider automatically and returned to the message producer when the send method completes. All JMSMessageID values must start with an ID: prefix.
JMSMessageIDs might not be consistent across JMS providers. There might be duplicates if you are using two different JMS vendors across systems. You should not trust that these values would always be unique.
JMS defines 10 priority levels, 0 through 9. 0 is the lowest priority and level 9 is the highest. Levels 04 indicate a range of normal priorities, and levels 59 indicate a range of expedited priority. Priority level 4 is typically the default for a message producer.
A message priority might not be honored at all times by a JMS provider. The JMS specification does not force a provider to adhere to the priority under all circumstances. Be careful when using this value for event-important messages.
The JSMRedelivered header field indicates that the message probably was delivered to the consumer previously, but the client did not acknowledge the message. If your application is very sensitive to duplicate messages, you probably want to inspect this field and decide whether you want to pay attention to the message or ignore it.
The JMSReplyTo header field indicates where a response should be sent. If this field is null, then no response is expected. A message with a null value in the JMSReplyTo header field is sometimes referred to as a JMS datagram.
If the JMSReplyTo field is not null, it should contain either a queue or a topic. Just because there is a value for the JMSReplyTo field does not mean a client must send a response. It's up to the client application to determine whether a response is necessary. If a reply is sent, the client can set the JMSCorrelationID and the reply can be matched up with the request.
The provider sets the JMSTimestamp header field in a JMS message at the time the message is sent. A producer may inform the JMS provider that it does not need the JMSTimestamp field set. This might help increase performance by reducing the overhead in getting and setting the current timestamp value for every message sent. A client can tell the provider not to worry about the JMSTimestamp field by setting the disableMessageTimestamp property on the MessageProducer interface. The following method signature can be used:
setDisableMessageTimestamp(boolean value) throws JMSException;
You would need to set the value to true for the JMSTimestamp value not to be set by the provider. The JMSTimestamp values are stored in milliseconds.
The JMSType message header field can be used to indicate the type or nature of the message. The JMS specification allows plenty of flexibility with this field and does not specify any naming syntax or possible list of values. It's entirely up to the JMS vendor or application. However, it's recommended that you set this field to something rather than leaving it null.
The message property fields are similar to header fields described previously in the "The Message Header" section, except these fields are set exclusively by the sending application. When a client receives a message, the properties are in read-only mode. If a client tries to modify any of the properties, a MessageNotWriteableException will be thrown.
The properties are standard Java name/value pairs. The property names must conform to the message selector syntax specifications defined in the Message interface. The following are valid property types:
Property fields are most often used for message selection and filtering. By using a property field, a message consumer can interrogate the property field and perform message filtering and selection. To find out more about message filtering, see the "Message Selection and Filtering" section later in this chapter.
It's okay for a property value to be duplicated in the message body. Although the JMS specification does not define a policy for what should or should not be made a property, application developers should note that JMS providers likely will handle data in a message's body more efficiently than data in a message's properties. For best performance, applications should use message properties only when they need to customize a message's header. The primary reason for doing this is to support customized message selection and filtering.
Here's an example of how to set a property value. Say for example that you need to set an auction type value so that a consumer would consume the message only if the type was a reverse auction. You could set the string value using the following fragment:
message.setStringProperty( "AuctionType", "Reverse" );
The message consumer can inspect this field to see whether the type is Reverse. The message interface has a getStringProperty method that takes a name argument and retrieves the string value. The consumer might also want to set up a message selector to filter out certain messages. You'll see more about this in the later section, "Message Selection and Filtering."
The Message Body
The message body contains the main information that is being delivered from the MessageProducer to the MessageConsumer. All JMS messages extend the javax.jms.Message interface and provide implementations for the interface's methods. Table 10.3, earlier, listed the Java interfaces that extend the Message interface to provide behavior for different types of messages.
You create a new message by using one of the create methods defined in the Session interface. For example, to create an ObjectMessage, you would do the following:
Message newMessage = session.createObjectMessage();
Most of the Message types have a second create method that takes an argument of the thing that it will be wrapping. To create an ObjectMessage for an object that you already have instantiated, you could do the following:
Message newMessage = session.createObjectMessage( myObject );
The argument used in this second constructor must be serializable.