ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


A Java Message Service Primer

by TA Flores
05/03/2001

Developers have long faced the daunting task of moving data between heterogeneous systems. One impediment to exchanging information has been the difficulty of getting humans to agree on precisly how to exchange and format that data. Java Message Service solves part of this problem by providing a means of interacting with existing J2EE applications or legacy systems in a neutral manner.

JMS makes available common sets of interfaces for sending and receiving messages reliably and asynchronously. Asynchronous messaging is an obvious choice for use with disconnected clients such as cell phones and PDAs. In addition JMS is a means of integrating enterprise systems in a loosely coupled (if not completely de-coupled) manner with the primary objective of creating applications that are seemingly portable across messaging vendors, while freeing development staff from the inherent complexities integrating these enterprise systems.

Java Message Service supports two messaging models: Point-to-Point messaging (P2P) and Publish Subscribe messaging (Pub/Sub). Although the JMS specification doesn't require a vendor to support both messaging models, there are several that do. A developer should be familiar with the advantages and disadvantages of both messaging models in order to make informed design decisions.

P2P messaging is designed for use in a one-to-one delivery of messages. An application developer should use P2P messaging when every message must be successfully processed. Unlike the Pub/Sub messaging model, P2P messages are always delivered.

Some of the characteristics of the P2P messaging model are as follows.

The Pub/Sub model is designed for one-to-many broadcasts of messages. An application developer may wish to use Pub/Sub messaging when it is acceptable for some level of unreliability to exist. In other words, it is feasible that all consumers will receive not all messages or no consumer will receive any message.

Some of the characteristics of the Pub/Sub messaging model are as follows.

JMS eases the timing dependency of the pub/sub model by permitting the creation of durable subscriptions, which may receive messages while subscribers are not active. In addition, the use of durable subscriptions provides the flexibility and reliability achieved through the use of queues, while still permitting messages to be sent to many recipients.

Topic Subscriber topic Subscriber = 
  topicSession.createDurableSubscriber(topic, subscriptionName);

A Connection object represents a connection to the messaging system for either messaging model. The server and client-side objects required to manage the state of the JMS connection are created by the connection. Connections are created by a Connection Factory and located via a JNDI lookup.

//Getting a QueueConnectionFactory for P2P 
QueueConnectionFactory = queueConnectionFactory( );             
        Context messaging = new InitialContext( );
        QueueConnectionFactory = (QueueConnectionFactory)
Messaging.lookup(“QueueConnectionFactory”);

//Getting a TopicConnectionFactory for pub/sub
TopicConnectonFactory topicConnectionFactory;

Context messaging = new InitialContext(); 
topicConnectionFactory = (TopicConnectionFactory) 
        messaging.lookup(“TopicConnectionFactory”);

Note: The code for the Point-to-Point and the PublishSubscribe is very similar.

Message acknowledgment is handled automatically by commit and recovery if the session is marked as transactional. If a session is not transactional, you have three options for message acknowledgment.

queueSession = //P2P
queueConnection.createQueueSession(false, session.AUTO_ACKNOWLEDGE);

topicSession = //Pub-Sub
topicConnection.createTopicSession(false, session.AUTO_ACKNOWLEDGE);

NOTE: In this example a session object is created from the connection; false indicates the session is non-transactional, and the session will automatically acknowledge receipt of a message.

JMS currently has two modes of delivery for messages. A message marked as NON_PERSISTENT is delivered at most once, while a message marked as PERSISTENT will use a store and forward mechanism and should be delivered once and only once. If a JMS service is offline for whatever reason, the persisted message is not lost but, rather, will be delivered when the service is brought back online. The default message delivery mode is nonpersistent. Although using nonpersistent messages may reduce overhead and storage needs, this delivery mode should only be used if you can afford not to receive all messages.

Although the JMS specification doesn't require JMS vendors to implement priority routing of messages, it does, however, require a best effort to deliver expedited messages ahead of normal messages. JMS defines priority routing levels from 0 to 9; 0 is the lowest priority and 9 the most expedited. More specifically, 0 to 4 are varying degrees of normal priority and 5 to 9 are varying degrees of expedited priority. For example,

topicPublisher.publish //Pub-Sub
        (message, DeliveryMode.PERSISTENT, 8, 10000);

Or

queueSender.send//P2P
        (message, DeliveryMode.PERSISTENT, 8, 10000);

This code snippet, for both messaging models, reflects the delivery mode as being persistent, the priority routing as expedited, and time to live is 10000 (measured in milliseconds). If the time to live were set to zero this would indicate the message would never expire. Time to live proves useful when messages need to be expired or otherwise invalidated.

JMS defines five different message body formats, also called message types, which allow you to send and receive data in several different forms, providing some level of compatibility with existing messaging formats.

The JMS API provides methods for creating messages of each type and for setting the payload. For example, to create and send a TextMessage to a queue, you might use the following statement:

TextMessage message = queueSession.createTextMessage(); message.setText(textMsg); // textMsg is a String queueSender.send(message);

In order to receive messages in an asynchronous manner a message listener needs to be created and register one or more implementations of the JMS MessageListener interface with the MessageConsumer. The Session (Topic or Queue) is responsible for making certain messages are passed to the listener by calling the onMessage method.

import javax.jms.*;

public class ExampleListener implements MessageListener {

    //Cast the message to a TextMessage 

    public void onMessage(Message message) {
        TextMessage textMsg = null;
           
                // unpack and handle the message
    }
}

When we create our QueueReceiver and TopicSubscriber, we pass in the message selector String:

//P2P QueueReceiver 
QueueReceiver receiver;
receiver = session.createReceiver(queue, selector);

//Pub-Sub TopicSubscriber 
TopicSubscriber subscriber;
subscriber = session.createSubscriber(topic, selector);

To start the delivery of messages, whether Pub/Sub or P2P, the required start method is called.

TopicConnection.start( ); //pub-sub

QueueConnection.start( ); //P2P

When a message is consumed, the message arrives as a generic Message object that must be cast to the appropriate message type. This is a getter method that is used to extract or otherwise unpack the message contents. The following code fragment uses the StreamMessage type.

private void unPackMessage (Message message) {
        
   String eName;
   String position;
   double rate;
   StreamMessage message;

   Message = session.createStreamMessage( );
        
   //note the following must be written in the order 
//in which they were read

   message.writeString(eName);
   message.writeString(position);
   message.writeDouble(rate);

   //implement the logic necessary to deal with the message
   }

To stop the delivery of messages, whether Pub/Sub or P2P, the stop method is called.

TopicConnection.start( ); //pub-sub

QueueConnection.start( ); //P2P

Other J2EE components -- servlets or EJBs -- may act as message producers; however, they may only do so synchronously due to their request-response nature. No discussion of JMS would be complete without at least brief mention being made of the EJB 2.0 proposed final draft, which has introduced a new EJB, the Message Drive Bean -- the way to send XML-based messages. Although XML is not currently a supported message type, sending an XML document is as simple as creating a text message type and adding the XML document to the payload of the message, thereby sending data in a non-proprietary manner. It is worth noting, however, that some JMS vendors have an XML message type available. But using non-standard message types may risk portability.

String reportData; //the contents of reportData = XML document
TextMessage message;

message = session.createTextMessage();
message.setText (reportData);

A message driven bean (MDB) is an asynchronous message consumer invoked by the container when messages arrive. Unlike entity or session EJB, MDBs have no home or remote interfaces and are anonymous; they're aren't visible to clients. MDBs participate in a JMS-based system as a consumer, implementing business logic on the server. A client may locate a JMS destination associated with an MDB by using JNDI. For example,

Context initialContext = new InitialContext(); 
Queue reportInfoQueue = (javax.jms.Queue)initialContext.lookup 
                        (“java:comp/env/jms/reportInfoQueue”);

The MDB is composed of the Bean class and corresponding XML deployment descriptor. The Bean class implements the MessageDriveBean interface:

import javax.ejb.*; 
import jms.Message.*;


public interface MessageDriveBean {


  public void ejbCreate(); 
  public void ejbRemove(); 
  public void setMessageDrivenContext(MessageDrivenContext ctx); 
 }

And the MessageListener interface:

import javax.jms.*; 
         
public interface MessageListener { 
   
   public void onMessage( ); 
  } 

The Deployment Descriptor

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>MDB</ejb-name>
<ejb-class>MDB</ejb-class>
<transaction-type>Container</transaction-type>
<message-driven-destination>
<jms-destination-type>javax.jms.Queue</jms-destination-type>
</message-driven-destination>
<security-identity>
<run-as-specified-identity>
<role-name>everyone</role-name>
</run-as-specified-identity>
</security-identity>
</message-driven>
</enterprise-beans>
</ejb-jar>

Now that we have covered the basics of JMS that you will need to get started. What can you build with JMS? Just about anything.

For example, systems exist for sales, inventory, customer service, and accounting departments, respectively. These departmental systems have most likely been around for some time and the processing required to move transactions through to update all of these systems is no small task. This is where a message service would be appropriate.

A salesperson works to complete a sale and during the transaction inventory levels will be verified. When the salesperson completes the sales transaction, a message is be sent to the inventory system for fulfillment; once the order is filled a message is then sent to the shipping and receiving folks indicating the order is ready for shipment. When the order is successfully shipped that system would then notify the customer service and accounting systems that the order shipping successfully. All corresponding updates to each system take place automatically based on messages sent to each respective system.

JMS is not used to integrate just one organization, but, rather, JMS can integrate many organizations that may participate in message driven environments (think: supply chain partnerships). JMS is a significant tool in the development and integration of enterprise applications. Since many companies have a combination of legacy systems and newly developed systems, the use of messaging is a significant step toward integrating the entire enterprise, whether the systems are all contained within the same organization or multiple organizations.

TA Flores is an independent enteprise Java developer.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.