Message Body

JMS messages are categorized depending on the type of content that the messages can have in their body. Accordingly the JMS specification defines five different types of messages that cover most of the enterprise messaging requirements. The JMS specification defines interfaces to represent all these message types and these interfaces extend the interface javax.jms.Message. This interface defines the common methods required for all the messages. These methods can be categorized into two broad categories:

  • Methods for handling the different message header fields

  • Methods for handling the message properties

In addition to the aforementioned methods the interface defines two extra methods. The first one is for explicitly acknowledging the message if the session is created in CLIENT ACKNOWLEDGE mode:

     public void acknowledge() throws JMSException, IllegalStateException 

Acknowledging a message will acknowledge all the previously unacknowledged messages, including the redelivered ones.

The second method is used for clearing the body contents of a message. This will clear only the message body and doesn't alter the message header and message properties:

     public void clearBody() throws JMSException 

The interface javax.jms.Session defines factory methods for creating different types of JMS messages. These methods were discussed in detail in the previous chapter. The class diagram shown below depicts the class hierarchy for different types of JMS messages:

click to expand

Bytes Messages

Bytes messages are used for exchanging messages carrying a stream of uninterpreted bytes in the message body. Bytes messages are extremely useful in integrating JMS messages with the provider's native messages. It is the responsibility of the message recipient to interpret the stream of bytes into a decipherable message.

Bytes messages can be used in conjunction with the JMSType header to define a repository of custom messages. The message sender will populate the byte stream and the JMSType header. The message recipient can interrogate the JMSType header and use the decoding algorithm specific to the message type to interpret the byte stream. Decoding the uninterpreted stream of bytes in a bytes message is explained in the example towards the end of the section.

The interface javax.jms.Session defines a factory method for creating bytes messages:

     public BytesMessage createBytesMessage() throws JMSException 

JMS defines bytes messages using the interface javax.jms.BytesMessage. This interface defines basic methods for sequentially writing and reading Java primitives, strings, and Java primitive wrapper objects. It also provides methods for sequentially writing and reading unsigned bytes in and out of the stream.

readXXX() Methods

Note that all the methods listed below are public. Unless otherwise stated the read methods only read a value from the byte stream if enough bits are left in the stream from the current pointer that can be read and interpreted as the appropriate Java primitive:

Method

Description

boolean readBoolean() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a boolean value from the byte stream.

byte readByte()

Reads a signed byte value from the byte stream.

int readBytes (byte [] bytes) throws JMSException, MessageNotReadableException, MessageEOFException

Reads an array of bytes into the byte array passed as the argument. The number of bytes read is either the length of the array passed or the number of bytes left in the stream whichever is less. The method returns the number of bytes read.

int readBytes (byte [] bytes, int length) throws JMSException, MessageNotReadableException, MessageEOFException

Reads either the number of bytes specified by the length argument or the number of bytes remaining in the stream whichever is less. This method also returns the number of bytes read.

char readChar() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a unicode character value from the byte stream.

double readDouble() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a 64-bit signed double value from the byte stream.

float readFloat() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a 32-bit signed float value from the byte stream.

int readInt() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a 32-bit signed integer value from the byte stream.

long readLong() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a 64-bit signed long value from the byte stream.

short readShort() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a 16-bit signed short value from the byte stream.

String readUTF() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a UTF-encoded string from the byte stream.

int readUnsignedByte() throws JMSException, MessageNotReadableException, MessageEOFException

Reads the next available 8 bits from the byte stream as an unsigned integer, if enough bits are left in the stream from the current stream pointer.

int readUnsignedShort() throws JMSException, MessageNotReadableException, MessageEOFException

Reads the next available 16 bits from the byte stream as an unsigned integer, if enough bits are left in the stream from the current stream pointer.

The readXXX() methods throw a MessageNotReadableException if the message is in write-only mode. A message is in write-only mode the first time it is created or immediately after the method clearBody() is invoked on the message. The readXXX() methods also throw MessageEOFException if not enough bytes are available in the stream to be read.

writeXXX() Methods

For each readXXX() method in the table above, there is a corresponding writeXXX() method. Below is a selection, and those that are missing can be extrapolated from the methods already described:

Method

Description

void writeBoolean (boolean value) throws MessageNotWritableException, JMSException

Writes the specified boolean value into the stream.

void writeByte (byte value) throws MessageNotWritableException, JMSException

Writes the specified byte value into the stream.

Method

Description

void writeBytes (byte [] bytes) throws MessageNotWritableException, JMSException

Writes the contents of the byte array into the stream.

void writeBytes (byte [] bytes, int offset, int length) throws MessageNotWritableException, JMSException

Writes the number of bytes specified by the length argument starting from the offset specified by the offset argument from the byte array into the stream.

void reset()

Puts the message in read-only mode and resets the stream pointer to the beginning of the stream.

The writeXXX() methods throw a MessageNotWritableException if the message is in read-only mode. A message is in read-only mode immediately after the reset() method is invoked on the message. The reset() method can be either invoked manually or invoked by the send() method.

Both the readXXX() and writeXXX() methods throws JMSException if the method call fails due to some internal error.

Text Messages

Text messages contain plain text as the content of their message body. Text messages are very useful for exchanging XML documents between applications. JMS defines text messages using the interface javax.jms.TextMessage.

The interface javax.jms.Session defines two methods for creating text messages. The first one creates a text message with an empty message body and the second one creates a text message prepopulated with the text passed as an argument to the method:

     public TextMessage createTextMessage() throws JMSException     public TextMessage createTextMessage (String text) throws JMSException 

The methods defined in the interface javax.jms.TextMessage are explained below:

Method

Description

String getText() throws JMSException

Returns the text stored in the message body

void setText (String text) throws JMSException, MessageNotWritableException

Sets the body content of the message to the specified text

The first method throws a JMSException if it fails to return the message text due to some internal error. The second method throws a MessageNotWritableException if the message is in read-only mode and JMSException if it fails to set the message text due to some internal error. A message is read-only after it is delivered to a client.

Object Messages

Object messages are used for exchanging serialized Java objects using JMS messages. JMS defines object messages using the interface javax.jms.ObjectMessage.

The interface javax.jms.Session defines two methods for creating object messages. The first one creates an object message with an empty message body and the second one creates an object message pre-populated with the object passed as an argument to the method:

     public ObjectMessage createObjectMessage() throws JMSException     public ObjectMessage createObjectMessage (Serializable object)                                                throws JMSException 

The methods defined in the interface javax.jms.ObjectMessage are explained below:

Method

Description

Object getObject() throws JMSException, MessageFormatException

Returns the object stored in the body of the message

void setObject (Serializable object) throws JMSException, MessageFormatException, MessageNotWritableException

Sets the body content of the message to the passed object

The first method throws a JMSException if it fails to return the message object due to some internal error. The second method throws a MessageNotWritableException if the message is read-only and JMSException if it fails to set the message object due to some internal error. A message is read-only after it is delivered to a client. Both methods throw a MessageFormatException if object deserialization fails.

Stream Messages

Stream messages are used for exchanging a sequential stream of Java primitives, objects, and strings using JMS messages. The only objects supported are types of Java primitive wrapper classes like java. lang. Integer, java. lang. Long, etc. JMS defines stream messages using the interface javax.jms.StreamMessage.

The interface javax.jms.Session defines a method for creating stream messages:

     public StreamMessage createStreamMessage() throws JMSException 

The interface javax.jms.StreamMessage defines all the methods defined in the interface javax.jms.BytesMessage except for those for reading unsigned bytes and shorts and those for reading and writing UTF-encoded strings. However, this interface defines extra methods for reading and writing Java objects and unicode strings:

Method

Description

Object readObject() throws JMSException, MessageNotReadableException, MessageEOFException

Reads a serialized object from the stream

void writeObject (Object obj) throws JMSException, MessageNotWritableException

Writes the specified serializable object to the stream

String readString() throws JMSException, MessageNotReadableException, MessageEOFException, MessageFormatException

Reads a Java unicode string object from the stream

void writeString (String value) throws JMSException, MessageNotWritableException

Writes the specified string in unicode format into the stream

The writeXXX() methods throw a MessageNotWritableException if the message is in read-only mode. A message is in read-only mode after the reset() method is in invoked on the message. The reset() method can be either invoked manually or is invoked by the send() method. Both the readXXX() and writeXXX() methods throw a JMSException if the method call fails due to some internal error.

Map Messages

Map messages are used for exchanging a map of Java primitives, strings, and objects of type Java primitive wrapper classes as key/value pairs. Keys are always defined as strings whereas values can be any of the aforementioned types. JMS defines map messages using the interface javax.jms.MapMessage.

The interface javax.jms.Session defines a method for creating map messages. The signature of the method is shown below:

     public MapMessage createMapMessage() throws JMSException 

The various methods defined in the interface javax.jms.MapMessage are explained below.

The getXXX() Methods

All the getXXX() methods defined by the javax.jms.MapMessage return a value that is stored in the map against the key name specified by the argument name:

Method

Description

boolean getBoolean (String name) throws JMSException, MessageFormatException

Gets a boolean value from the map

byte getByte (String name) throws JMSException, MessageFormatException

Gets a byte value from the map

byte [] getBytes (String name) throws JMSException, MessageFormatException

Gets a byte array from the map

char getChar (String name) throws JMSException, MessageFormatException

Gets a char value from the map

double getDouble (String name) throws JMSException, MessageFormatException

Gets a double value from the map

float getFloat (String name) throws JMSException, MessageFormatException

Gets a float value from the map

int getInt (String name) throws JMSException, MessageFormatException

Gets an int value from the map

long getLong (String name) throws JMSException, MessageFormatException

Gets a long value from the map

short getShort (String name) throws JMSException, MessageFormatException

Gets a short value from the map

String getString (String name) throws JMSException, MessageFormatException

Gets a unicode string value from the map

The readXXX() methods throw a MessageFormatException if the value stored in the map is not convertible to the specified type. For example calling a getInt() for a value stored as a long will throw this exception.

The setXXX() Methods

All the setXXX() methods defined by javax.jms.MapMessage store the input arguments as a name/value pair in the message map. For each getXXX() method in the table above, there is a corresponding setXXX() method. Below is a selection, and those that are missing can be extrapolated from those already described:

Method

Description

void setBoolean (String name, boolean value) throws JMSException, MessageNotWritableException

Stores a boolean specified by the argument value against a key name specified by the argument name.

void setByte (String name, byte value) throws JMSException, MessageNotWritableException

Stores a byte specified by the argument value against a key name specified by the argument name.

void setBytes (String name, byte[] value) throws JMSException, MessageNotWritableException

Stores a byte array specified by the argument value against a key name specified by the argument name.

The writeXXX() methods throw a MessageNotWritableException if the message is in read-only mode. A message is in read-only mode immediately after it is delivered to a client.

Miscellaneous Methods

Method

Description

void setBytes (String name, byte[] value, int offset, int length) throws JMSException, MessageNotWritableException

Stores a portion of the byte array specified by the argument value starting at the index specified by the argument offset and of length as specified by the argument length against a key name specified by the argument name.

boolean itemExists (String name) throws JMSException

Checks whether the specified key name exists in the map.

Enumeration getMapNames() throws JMSException

Returns an enumeration of all the key names stored in the map.

Message Encoding/Decoding Application

In this section we will be writing two JMS clients. The first client will randomly generate messages of different formats that carry the same information, and send them to a message queue. The second client will read the message from the queue and retrieve the information from the message by interrogating the message type.

The MessageFactory Class

This class acts as a factory for creating different types of JMS messages:

     import javax.jms.Message;     import javax.jms.TextMessage;     import javax.jms.BytesMessage;     import javax.jms.StreamMessage;     import javax.jms.ObjectMessage;     import javax.jms.MapMessage;     import javax.jms.Session;     import javax.jms.JMSException;     public class MessageFactory { 

The class first declares enumerated constants for defining the different message types:

     public static final int TEXT_MESSAGE = 0;     public static final int BYTES_MESSAGE = 1;     public static final int STREAM_MESSAGE = 2;     public static final int OBJECT_MESSAGE = 3;     public static final int MAP_MESSAGE = 4; 

A factory method is provided for creating different types of JMS messages. Depending on the value passed for the argument messageType this method calls specific private methods to create the appropriate type of JMS message using the Session object passed to the method, encapsulating the information defined by the argument message. This method also prints the type of the message that is created:

        public Message createMessage (int messageType, Session session,               String message) throws JMSException {           Message msg;           switch (messageType) {              case TEXT_MESSAGE:                 System.out.println ("Sending text message.");                 msg = createTextMessage (session, message);                 break;              case BYTES_MESSAGE:                 System.out.println ("Sending text message.");                 msg = createBytesMessage (session, message);                 break;              case STREAM_MESSAGE:                 System.out.println ("Sending stream message.");                 msg = createStreamMessage (session, message);                 break;              case OBJECT_MESSAGE:                 System.out.println ("Sending object message.");                 msg = createObjectMessage (session, message);                 break;              case MAP_MESSAGE:                 System.out.println ("Sending map message.");                 msg = createMapMessage (session, message);                 break;              default:                 throw new IllegalArgumentException ("Invalid message.");           }           return msg;        } 

This method returns an object of type TextMessage encapsulating the information specified by the argument message:

        private TextMessage createTextMessage (Session session, String message)              throws JMSException {           return session.createTextMessage (message);        } 

This method returns an object of type BytesMessage encapsulating the information specified by the argument message as a platform-dependent string:

        private BytesMessage createBytesMessage (Session session, String message)              throws JMSException {           BytesMessage msg = session.createBytesMessage();           msg.writeUTF (message);           return msg;        } 

This method returns an object of type StreamMessage encapsulating the information specified by the argument message as a stream of bytes:

        private StreamMessage createStreamMessage (Session session,             String message) throws JMSException {           StreamMessage msg = session.createStreamMessage();           msg.writeString (message);           return msg;        } 

This method returns an object of type ObjectMessage encapsulating the information specified by the argument message as an object of type java.lang.String:

        private ObjectMessage createObjectMessage (Session session,             String message) throws JMSException {           return session.createObjectMessage (message);        } 

This method returns an object of type MapMessage encapsulating the information specified by the argument message:

        private MapMessage createMapMessage (Session session, String message)              throws JMSException {           MapMessage msg = session.createMapMessage();           msg.setString ("message",message);           return msg;        }     } 

The MessageClient Class

This class randomly produces messages of different types carrying the same information and sends them to a message queue:

     import javax.jms.Queue;     import javax.jms.QueueConnection;     import javax.jms.QueueSession;     import javax.jms.QueueSender;     import javax.jms.QueueConnectionFactory;     import javax.jms.JMSException;     import java.util.Random; 

The class declares static constants for JNDI initial context factory, provider URL, and JNDI names for the queue connection factory and queue. It also declares a string message that will be sent using different message types and a queue connection:

     public class MessageClient {        public static final String CTX_FACT =           "com.sun.jndi.fscontext.RefFSContextFactory";        public static final String PROV_URL = "file:C:\\temp";        public static final String QCF_NAME = "QCFactory";        public static final String QUEUE_NAME = "myQueue";        public static final String message =           "Peace sells, but who is buying?";        private QueueConnection qCon; 

In the main() method an instance of the class MessageClient is instantiated:

        public static void main (String args []) throws Exception {           final MessageClient client = new MessageClient(); 

A shutdown hook is added to call the finalize() method. Shutdown hooks are a feature in JDK 1.3 for adding any code that needs to be executed on program termination, regardless of how the program is terminated:

           Runtime.getRuntime().addShutdownHook (new Thread() {              public void run() {                 client.finalize();              }           }); 

The method sendMessage() is called on the instance of the class:

           client.sendMessage();        } 

The queue connection is closed in the finalize() method which is called from the shutdown hook:

     public void finalize() {           try {              System.out.println ("\nClosing the connection.\n");              qCon.close();           } catch (JMSException ignore) {              ignore.printStackTrace();           }        } 

In the sendMessage() method the JNDIService class is initialized and the queue and queue connection factory are looked up. A queue connection is created and then a queue session is created. The queue session is then used to create a queue sender:

        public void sendMessage() throws Exception {           JNDIService.init (CTX_FACT, PROV_URL);           Queue queue = (Queue)JNDIService.lookup (QUEUE_NAME);           QueueConnectionFactory qcf =              (QueueConnectionFactory)JNDIService.lookup (QCF_NAME);           qCon = qcf.createQueueConnection();           QueueSession qSes = qCon.createQueueSession (false,              QueueSession.AUTO_ACKNOWLEDGE);           QueueSender sender = qSes.createSender (queue); 

An instance of the class MessageFactory is created. A random number generator is used to generate numbers from 0 to 4. The random number is then passed to the MessageFactory instance in an infinite loop to create a JMS message that is sent to the message queue:

           Random rand = new Random();           MessageFactory factory = new MessageFactory();           System.out.println();           while(true) {              //Create a random number from 0-4              int type = rand.nextInt (5);              sender.send(factory.createMessage (type, qSes, message));              Thread.sleep(1000);           }        }     } 

The MessageProcessor Class

This class is used to decode JMS messages by interrogating the type of the message and printing the information stored in the message:

     import javax.jms.Message;     import javax.jms.TextMessage;     import javax.jms.BytesMessage;     import javax.jms.StreamMessage;     import javax.jms.ObjectMessage;     import javax.jms.MapMessage;     import javax.jms.Session;     import javax.jms.JMSException; 

This class defines a single method for decoding JMS messages:

     public class MessageProcessor {        public void processMessage (Message message) throws JMSException {           Object msg = null; 

If the message is of type TextMessage the message type is printed and the message is cast to a text message. The message body is then retrieved using the getText() method:

           if (message instanceof TextMessage) {               System.out.print("Text Message: ");               TextMessage textMessage = (TextMessage)message;               msg = textMessage.getText(); 

If the message is of type BytesMessage the message type is printed and the message is cast to a bytes message. The message body is then retrieved using the readUTF() method:

           }else if (message instanceof BytesMessage) {              System.out.print ("Bytes Message: ");              BytesMessage bytesMessage = (BytesMessage)message;              msg = bytesMessage.readUTF(); 

If the message is of type StreamMessage the message type is printed and the message is cast to a stream message. The message body is then retrieved using the readString() method:

           } else if (message instanceof StreamMessage) {              System.out.print ("Stream Message: ");              StreamMessage streamMessage = (StreamMessage)message;              msg = streamMessage.readString(); 

If the message is of type ObjectMessage the message type is printed and the message is cast to an object message. The message body is then retrieved using the readObject() method:

           } else if (message instanceof ObjectMessage) {              System.out.print ("Object Message: ");              ObjectMessage objectMessage = (ObjectMessage)message;              msg = objectMessage.getObject(); 

If the message is of type MapMessage the message type is printed and the message is cast to a map message. The message body is then retrieved using the getString() method:

           } else if(message instanceof MapMessage) {              System.out.print ("Map Message: ");              MapMessage mapMessage = (MapMessage)message;              msg = mapMessage.getString("message");           } 

Finally the information stored in the message body is printed:

           System.out.println(msg);        }     } 

The MessageServer Class

This is a JMS client that receives a message from a message queue and uses the MessageProcessor class to decode the message:

    import javax.jms.Queue;    import javax.jms.QueueConnection;    import javax.jms.QueueSession;    import javax.jms.QueueReceiver;    import javax.jms.QueueConnectionFactory;    import javax.jms.MessageListener;    import javax.jms.Message;    import javax.jms.JMSException;    import java.util.Random; 

The class declares static constants for JNDI initial context factory, provider URL, and JNDI names for the queue connection factory and queue:

     public class MessageServer {        public static final String CTX_FACT =           "com.sun.jndi.fscontext.RefFSContextFactory";        public static final String PROV_URL = "file:C:\\temp";        public static final String QCF_NAME = "QCFactory";        public static final String QUEUE_NAME = "myQueue";        private QueueConnection qCon; 

In the main() method an instance of the class MessageServer is instantiated:

        public static void main(String args[]) throws Exception {           final MessageServer server = new MessageServer(); 

A shutdown hook is added to call the finalize() method:

           Runtime.getRuntime().addShutdownHook (new Thread() {              public void run() {                 server.finalize();              }           }); 

The method receiveMessage() is called on the instance of the class:

           server.receiveMessage();        } 

In the finalize() method, which is called from the shutdown hook, the queue connection is closed:

        public void finalize() {           try {              System.out.println("\nClosing the connection.\n");              qCon.close();           } catch(JMSException ignore) {              ignore.printStackTrace();           }        } 

In the receiveMessage() method the JNDIService class is initialized and the queue and queue connection factory are looked up. A queue connection is created and then a queue session is created. The queue session is then used to create a queue receiver:

        public void receiveMessage() throws Exception {           JNDIService.init (CTX_FACT, PROV_URL);           Queue queue = (Queue)JNDIService.lookup (QUF_NAME);           QueueConnectionFactory qcf =              (QueueConnectionFactory)JNDIService.lookup (QCF_NAME);           qCon = qcf.createQueueConnection();           QueueSession qSes = qCon.createQueueSession (false,                           QueueSession.AUTO_ACKNOWLEDGE);           QueueReceiver receiver = qSes.createReceiver (queue); 

An instance of the class MessageProcessor is created:

           final MessageProcessor processor = new MessageProcessor();           System.out.println(); 

A message listener is registered with the queue receiver to handle asynchronous message delivery. In the onMessage() callback method the MessageProcessor instance is used to decode and print the message:

           receiver.setMessageListener (new MessageListener()              public void onMessage (Message message) {                 try {                    processor.processMessage (message);                 }catch (JMSException ignore) {                    ignore.printStackTrace();                 }              }           });           qCon.start();        }     } 

Running the Application

Start JMQ and run the configuration script to store the required administered objects. Compile all the classes and run the MessageClient and MessageServer classes in different command windows with the required JAR files in the classpath. The JAR files required are jmq. jar, providerutil. jar, and fscontext. jar that can be found in the lib directory under JMQ installation, and j2ee.jar that can be found in the lib directory under J2EE installation. The figures below show the screenshots of the client and the server running:

click to expand

click to expand



Professional JMS
Professional JMS
ISBN: 1861004931
EAN: 2147483647
Year: 2000
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net