- Message Definition
- JMS Message Structure
- Using the JMS Message Interface
- Summary
Using the JMS Message Interface
A message is created using the Session interface (see Chapter 2), and the Session interface defines create methods for all the message types, including the root Message interface (Table 3-1).
Table 3-1. Accessor Methods for Session Interface
createBytesMessage() createMapMessage() createMessage() createObjectMessage() createObjectMessage(java.io.seriablizable object) //create with content createStreamMessage() createTextMessage() createTextMessage(String value) // create with content |
As shown, createObjectMessage and createTextMessage are overloaded so that they can be instantiated with data at the time of creation. Creation of a message takes the form
//create message BytesMessage bMsg = session.createBytesMessage();
If a message is being received, then the MessageConsumer creates the message and returns it to an object variable:
//receive message TextMessage replyMsg = (TextMessage)receiver.receive();
This assumes that the type of message being received is known. In our example, if the message retrieved was actually a BytesMessage, then an exception would be thrown. Another approach is to use the root interface to receive the object variable, and then test the object and cast to the allowed type:
//receive message Message msg = receiver.receive(); if(msg instanceof TextMessage){ TextMessage tMsg = (TextMessage)msg; ...... }
The root Message interface defines the accessor methods for the header fields and properties; the typed Message interfaces extend this to include accessor methods for manipulating the data they contain in their message body. We examine the methods and sample usage for each of the typed interfaces in turn.
BytesMessage
Table 3-2 details the accessor methods associated with BytesMessage.
Table 3-2. Accessor Methods for BytesMessage Interface
readBoolean() readByte() readBytes(byte[] value) readBytes(byte[] value, int length) readChar() readDouble() readFloat() readInt() readLong() readUnsignedByte() readUnsignedShort() readUTF() |
writeBoolean(boolean value) writeByte(byte value) writeBytes(byte[] value) writeBytes(byte[] value, int length) writeChar(char value) writeDouble(double value) writeFloat(float value) writeInt(int value) writeLong(long value) writeObject(Object value) //works only for object primitive types writeShort(short value) writeUTF(String value) |
Use of these methods follow a simple rule: If you build the message body by using write methods in a certain order, then you unpack the message body by using the corresponding read methods in the same order. Let's use our familiar John Doe example laid out in a record-oriented format to illustrate:
/*Sender*/ //create and send BytesMessage String firstName = "JOHN"; String lastName = "DOE"; int age = 33; BytesMessage bOutMsg = session.createBytesMessage(); bOutMsg.writeBytes(firstName.getBytes()); bOutMsg.writeBytes(lastName.getBytes()); bOutMsg.writeInt(age); sender.send(bOutMsg); /*Receiver*/ //receive and unpack BytesMessage BytesMessage bInMsg = (BytesMessage)receiver.receive(); byte[] data = new byte[4]; bInMsg.readBytes(data, 4); String firstName = new String(data); bInMsg.readBytes(data, 3); String lastName = new String(data, 0, 3); int age = bInMsg.readInt();
If we want to retrieve the entire contents of BytesMessage using readBytes(byte [] value), we must determine the length of the message body so that we can appropriately initialize the byte array that will contain the read data. Unfortunately, JMS 1.0.2b does not provide any means for determining the length of the message body, and if the length of the incoming message is not known, we may be forced to adopt processing of the form
//receive and unpack BytesMessage BytesMessage bInMsg = (BytesMessage)receiver.receive(); int msgLength = 0; int BUF_SIZE = 50000; //some efficient maximum byte[] data = new byte[BUF_SIZE]; while(true){ int len = bInMsg.readBytes(data); if(len > 0){ msgLength += len; }else{ break; } } // now we know the message length // so reset and read in one go. byte[] message = new byte[msgLength]; //if msgLength <= BUF_SIZE, then we already //have the contents if (msgLength <= BUF_SIZE) { System.arraycopy(data, 0, message, 0, msgLength); } else { bInMsg.reset();//reset cursor to beginning bInMsg.readBytes(message); }
Notice that we introduce the utility method reset(), which repositions the stream of bytes to the beginning and allows us to read in the entire byte array. JMS 1.1 addresses this issue by specifying a getBodyLength() method in the BytesMessage interface, which allows us to greatly simplify the process of dynamically sizing our byte array as follows:
//receive and unpack BytesMessage BytesMessage bInMsg = (BytesMessage)receiver.receive(); int msgLength = bInMsg.getBodyLength(); byte[] message = new byte[msgLength]; bInMsg.readBytes(message);
TextMessage
As shown in Table 3-3, TextMessage offers an interface that is quite simple to use.
Table 3-3. Accessor Methods for TextMessage Interface
getText() |
setText(String value) |
It provides a setText and a getText method, which enable the message body to be populated or content extracted:
/*Sender*/ //create and send TextMessage String firstName = "JOHN"; String lastName = "DOE"; String age = "33"; TextMessage tOutMsg = session.createTextMessage(); String message = firstName + ";" + lastName + ";" + age; tOutMsg.setText(message); sender.send(tOutMsg); /*Receiver*/ //receive and unpack TextMessage TextMessage tInMsg = (TextMessage)receiver.receive(); String message = tInMsg.getText();
StreamMessage
StreamMessage represents a sequence of primitive types, as shown in Table 3-4.
Table 3-4. Accessor Methods for StreamMessage Interface
readBoolean() readByte() readBytes(byte[] value) readChar() readDouble() readFloat() readInt() readLong() readObject() readShort() readString() |
writeBoolean(boolean value) writeByte(byte value) writeBytes(byte[] value) writeBytes(byte[] value, int offset, int length) writeChar(char value) writeDouble(double value) writeFloat(float value) writeInt(int value) writeLong(long value) writeObject(Object value) //works only for object primitive types writeShort(short value) writeString(String value) |
It provides methods that allow the various primitives to be written or read:
/*Sender*/ //create and send StreamMessage String firstName = "JOHN"; String lastName = "DOE"; int age = 33; StreamMessage sOutMsg = session.createStreamMessage(); sOutMsg.writeString(firstName); sOutMsg.writeString(lastName); sOutMsg.writeInt(age); sender.send(sOutMsg); /*Receiver*/ //receive and unpack StreamMessage StreamMessage sInMsg = (StreamMessage)receiver.receive(); String firstName = sInMsg.readString(); String lastName = sInMsg.readString(); int age = sInMsg.readInt();
As with BytesMessage, StreamMessage also provides a reset method with which the stream can be repositioned to its beginning.
MapMessage
The MapMessage interface (Table 3-5) introduces a name variable that represents the key associated with the typed field. Because data is accessed based on the name (key), it does allow random access to data fields.
Table 3-5. Accessor Methods for MapMessage Interface
getBoolean(String name) getByte(String name) getBytes(String name) getChar(String name) getDouble(String name) getFloat(String name) getInt(String name) getLong(String name) getObject(String name) getShort(String name) getString(String name) |
setBoolean(String name, boolean value) setByte(String name, byte value) setBytes(String name, byte[] value) setBytes(String name, byte[] value, int offset, int length) setChar(String name, char value) setDouble(String name, double value) setFloat(String name, float value) setInt(String name, int value) setLong(String name, long value) setObject(String name, Object value) setShort(String name, short value) setString(String name, String value) |
To facilitate key management, the MapMessage interface offers two utility methods: itemExists and getMapNames. itemExists(String Name) takes as argument a key name and checks for its existence, returning a Boolean. getMapNames() returns an Enumeration object containing all the key names defined in the message. The use of MapMessage follows the now familiar pattern:
/*Sender*/ //create and send MapMessage String firstName = "JOHN"; String lastName = "DOE"; int age = 33; MapMessage mOutMsg = session.createMapMessage(); mOutMsg.setString("first", firstName); mOutMsg.setString("last", lastName); mOutMsg.setInt("age", age); sender.send(mOutMsg); /*Receiver*/ //receive and unpack MapMessage MapMessage mInMsg = (MapMessage)receiver.receive(); String firstName = mInMsg.getString("first"); String lastName = mInMsg.getString("last"); int age = mInMsg.getInt("age");
ObjectMessage
As with TextMessage, ObjectMessage (Table 3-6) offers a simple interface that supports the insertion or retrieval of objects from the message.
Table 3-6. Accessor Methods for ObjectMessage Interface
getObject() |
setObject(Object value) |
Objects passed must implement the java.io.Serializable interface:
/*Sender*/ //create and send ObjectMessage String firstName = "JOHN"; String lastName = "DOE"; int age = 33; Person pObj = new Person(firstName, lastName, age); ObjectMessage objOutMsg = session.createObjectMessage(); objOutMsg.setObject(pObj); sender.send(objOutMsg); /*Receiver*/ //receive and unpack ObjectMessage ObjectMessage objInMsg = (ObjectMessage)receiver.receive(); Person pObj = (Person)objInMsg.getObject();