More About Messages
So far, you've seen a lot about queues and topics, but not much about messages. Messages are the most important part of JMS, and there are many different types of messages and many interesting features common to all messages.
A JMS message has three parts, as shown in Figure 19.7.
Figure 19.7. A JMS message has a header, properties, and a body.
The message header is a set of values that is present on every message. The properties are similar to header values in that they are separate from the actual message content and can appear on any type of message. You can think of the properties as header values that you define yourself. The message body can be any number of different types. JMS defines five different types of message.
The message headers are a set of common values assigned to every message. They include the message priority, message type, and several other items. You can't add your own header types, but the properties section of the message functions like custom headers. Table 19.1 lists the standard JMS message header values.
|JMSCorrelationID||A string or byte-array value relating one message to another.|
|JMSDeliveryMode||The message delivery mode (persistent or non-persistent).|
|JMSDestination||Where the message was sent (this will be a topic or a queue).|
|JMSExpiration||The time when this message expires.|
|JMSMessageID||A unique message ID assigned by the message server.|
|JMSPriority||The message priority (0-9) in which 9 is the highest (most important). 0-4 is normal, and 5-9 is high priority.|
|JMSRedelivered||If true, this message might have been delivered before but the receiver didn't acknowledge it.|
|JMSReplyTo||A destination where a reply can be sent.|
|JMSTimestamp||The time the message was first sent to the message server.|
|JMSType||A provider-specific message type.|
The Message class has get/set methods for examining and modifying the header attributes. The message server sets the value of some of these attributes when you send the message. For example, it stores the destination in the message automatically. You don't need to set the value yourself.
Message properties are essentially custom header fields. You store properties in a message with an associated name and retrieve them using the name. A property can be one of the following types: boolean, byte, double, float, int, long, Object, short, or String. The MessageClass has get/set methods for setting properties of each type. For example, to set a long property, call setLongProperty:
To retrieve the value, call getLongProperty:
long timeNoSee = myMessage.getLongProperty("TimeNoSee");
Don't use any property names starting with JMS. That prefix is reserved for the JMS API.
There are five different types of JMS messages:
The BytesMessage and StreamMessage message types work much like Java IO streams in that you can read and write different data values. For instance, both messages have writeInt, writeDouble, readBytes, and readUTF just like the DataInputStream and DataOutputStream classes. The difference between BytesMessage and StreamMessage is that StreamMessage also writes out data types, so it can determine whether you try to read the wrong kind of value. It can also perform any necessary type conversions. The BytesMessage class just reads and writes raw bytes. It is most useful for matching existing message formats.
The ObjectMessage class lets you store a single serializable Java object in a message using setObject. You can retrieve the object with getObject.
The MapMessage class lets you create a group of name-value pairs for a message. You can store the same data types as you can in the message properties, plus you can store an array of bytes. To store a long value, call setLong:
To retrieve a long value, call getLong:
long timeNoSee = myMsg.getLong("TimeNoSee");
The other data types follow a similar pattern (getShort, setShort, getString, setString, and so on).
Finally, the TextMessage class lets you store a single string in a message using setText and retrieve the string with getText.
Many message servers let you filter messages at the server, saving the receivers and subscribers a lot of trouble. If you publish a data item that only 1 in 100 subscribers is inte rested in, should all 100 have to see the message? If you use message selectors, they don't have to. A message selector lets you define a string similar to a database query that filters a message based on header and property values.
The format of the query is basically the same as an SQL WHERE clause. For example, to filter all messages where the StockSymbol property equals SUNW, use the filter StockSymbol='SUNW'.
The filters come into play when you first create your QueueReceiver or TopicSubscriber. You can supply an additional parameter to the createReceiver and createSubscriber methods specifying a message selector (filter).
For example, commercial aircraft use a system called ACARS (Aircraft Communication and Reporting System) to transmit position data and other information to sites on the ground. A typical ACARS feed sends data for all aircraft, not just for a specific airline. You might set up a program to listen for ACARS messages and then publish them, setting various message properties to help identify the message. For example, you would likely include the airline and the flight number in the header file.
A program monitoring all United flights might set up a subscriber like this:
TopicSubscriber sub = topicSession.createSubscriber( "ACARS", "Airline='UA'");
A program tracking a specific Delta flight might create a subscriber this way:
TopicSubscriber sub = topicSession.createSubscriber( "ACARS", "Airline='DL'AND FlightNumber=1579");