BigAdmin System Administration Portal
Community-Submitted Article
Print-friendly VersionPrint-friendly Version
This content is submitted by a BigAdmin user. It has not been reviewed for technical accuracy by Sun Microsystems, though it may have been lightly edited to improve readability. If you find an error or would like to comment on the article, please contact the submitter or use the comment field at the bottom of the article. Community submissions may not follow Sun trademark guidelines. For information on Sun trademarks, please see http://www.sun.com/suntrademarks/.
 
 

Java Message Service: Understanding the Basics

Nishant M.C., October 2007

Introduction

Java Message Service (JMS) is a Java technology that helps applications communicate using messages. Applications exchange messages with the help of messaging systems or Message Oriented Middleware (MOM). MOM products usually provide load balancing, scalability, fault tolerance, and transactional support, which make a messaging system an enterprise-quality service.

First let's look at the details of enterprise messaging systems. Then let's consider what JMS is, why it is used, and where it is used. Then we'll look at some code that demonstrates how applications can communicate with the help of JMS.

Note: I assume that you are familiar with common Java Platform, Enterprise Edition technologies, such as JavaServer Pages (JSP) and Servlets. For the demonstration, I use Sun Java System Application Server 9.0, Java SDK 1.5, and an IDE.

Contents

This article covers the following topics:

Enterprise Messaging Systems

Enterprise messaging systems allow applications to convey messages to one or more other applications. By message I mean the data packet that contains the business and network routing headers, which indicate where the message should go.

Enterprise messaging systems are not a new concept. They have been in existence for many years. Enterprise messaging systems act as a common endpoint where sender applications can post messages and receiver applications can receive messages, ensuring that messages are properly distributed among applications. These systems can also be described as a server system that facilitates the transfer of messages among applications.

Common enterprise messaging system products available in the marketplace are IBM MQ series, Microsoft MSMQ, Sonic MQ, and Softwired iBus. With these products, applications are provided with an API for creating a message that contains a payload (application data) and the network routing headers. The same API is used to receive a message from other applications. Each vendor implements its own network protocol, routing facilities, and administration facilities, but the basic semantics of the API provided by each vendor are the same.

In enterprise messaging systems, messages are delivered asynchronously from one system to another system. This means the sender application doesn't have to wait until the receiver application receives the message. The sender application is free to send a message and continue doing other work.

Messages are treated as autonomous units that contain all the information needed for processing the data in the message. Once the sender application sends a message to the network, the sender application continues doing other work. The message is taken care of by the MOM (server system). The receiver application looks for messages in the server system and gets the message. This process is similar to a mail system. Figure 1 depicts a messaging system.

Figure 1

Figure 1: Messaging System
(Click to Enlarge)

The server system or MOM can be implemented efficiently by vendors in variety of ways. For example, the server system can use any protocol for communicating with the applications. Another difference among vendors can be the architecture.

There are mainly two kinds of architectures:

  • Centralized
  • Decentralized

What I described previously was a centralized architecture, that is, the applications depend on the server system for the exchange of messages. The server system can also be called a message router or a message broker, and it is responsible for delivering a message from one client to another client. So the sending client and the receiving client are decoupled. Clients see only the message server, not other clients. This architecture allows clients to be added and removed without impacting the system.

Figure2

Figure 2: Centralized Architecture
(Click to Enlarge)

In reality, the message server is a cluster of distributed servers operating as a single unit.

In a decentralized architecture, there is no message server. Tasks such as persistence, transaction, and security are taken care of by the client systems. Thus, the client systems are capable of performing the enterprise services on the messages. Message routing, that is, the sending of messages to their proper destinations, is delegated to the network router.

Figure2

Figure 3: Decentralized Architecture
(Click to Enlarge)

There is another architecture, which is a hybrid architecture. As you probably guessed, this architecture is a combination of both the centralized and the decentralized architectures. Centralized server systems may communicate with decentralized message systems in this architecture.

Thus when we say "message server" in a centralized architecture, the server system may be a single message server or a clustered message server system; in the decentralized model, the "message server" points to the server functionality in the client systems.

JMS Overview

In the previous section, I didn't mention JMS, because I was discussing the scenarios that existed before JMS came into existence. Now that you understand enterprise messaging systems and MOM, we can look at what JMS provides.

JMS is an API for enterprise messaging that was developed by Sun Microsystems. It is just an abstraction of the APIs provided by the MOM systems for clients to communicate with the server. This scenario is similar to the scenario used in the Java DataBase Connectivity (JDBC) software for communicating with different databases or in the Java Naming and Directory Interface (JNDI) API for accessing different naming and directory services.

Sun took the lead for developing the JMS API. Other vendors were also involved in the project, so the API was designed to support almost all MOM systems. In addition, the API has some features that could support future MOM systems.

The JMS API was created to support the messaging system and not the Remote Procedure Call (RPC) based systems, such as those using CORBA and Enterprise JavaBeans (EJB) architecture. It was intended to make this messaging model a first-class distributed computing paradigm for Java technology, equal with the remote method call (RPC) based systems like CORBA and EJB.

JMS Messaging Models

JMS provides two models (types) of messaging:

  • Publish-and-subscribe (pub/sub)
  • Point-to-point queuing (p2p)

The publish-and-subscribe or pub/sub model is intended for broadcasting one-to-many messages, and the point-to-point queuing or p2p method is intended for broadcasting one-to-one messages.

Applications that play the role of sender or receiver are called messaging clients, and the MOM system or server is called the message provider. The message provider can be a single system or a cluster of systems. A JMS application is a business system composed of messaging clients and a message provider. The sender part of the application is generally called a producer, and the receiver part of the application is generally called a consumer.

Publish-and-Subscribe (Pub/Sub) Messaging Model

In the pub/sub model, the producer can send a message to many consumers. This "one-to-many" scenario is similar to broadcasting information to many users. The channel, pipe, or line through which the message is passed to different consumers is called a topic.

Similar to how users subscribe to news media, consumers can subscribe to a particular topic or channel. Every consumer who subscribes to a topic will get the messages that are sent to this channel topic by the producer. The pub/sub based model is a "push" model, so consumers do not have to request or poll for messages.

In this model, the producer sending a message is not dependent on the consumer receiving the message. That is, the producer just sends the message to the server and then it does other tasks. The producer doesn't need to wait until the consumer receives the message. Its job is over when it sends the message to the server.

Likewise, the consumer receives the message for a particular topic when any message is posted on that topic. Once the consumer subscribes to a particular topic, its job is over. The instructions to be executed when a message arrives for a topic are called automatically.The consumer doesn't need to worry about that.

The pub/sub model is asynchronous, whereas many web applications are synchronous, meaning the client usually waits for a response, for example, a page of text when a user sends a request by clicking a button in a registration form or when an event occurs. Asynchronous systems are very different from synchronous systems, and they are being used more frequently in many enterprise systems as well as in web applications, such as Asynchronous JavaScript and XML (AJAX) and Web services (fire and forget operations).

Point-to-Point Queuing (P2P) Messaging Model

The P2P model allows JMS clients to send and receive messages both synchronously and asynchronously using another channel called queues. Normally, in the P2P model, a consumer has to request a message that a producer sends to the channel queue. In other words, this is a "pull" or poll-based model rather than a push-based model such as pub/sub.

However, in JMS, an option exists that allows P2P clients to use a push model that is similar to the pub/sub model. A queue may have multiple receivers, but only one receiver may consume each message at a time. P2P also allows clients to see the content of the queue before consuming its messages.

Examples of JMS Usage in Enterprises

Now that you have an idea of what JMS is, the next questions are why is JMS used and where is it used? The following information is a short introduction to its use in the enterprise world.

Most enterprises have legacy applications and new applications that cannot interoperate. Organizations have a strong desire to integrate these applications so the applications can cooperate and share information. The integration of such applications is generally called Enterprise Application Integration (EAI). A variety of home-grown solutions are used for EAI, but enterprise messaging systems are central to most of them. EAI messaging systems allow applications to communicate data and events with each other while remaining physically independent.

Businesses can exchange data using Electronic Data Interchange (EDI), but the cost of entering data is high and data is entered using batch processes, not as real-time business events.

The Internet, XML, and modern messaging systems have radically changed how businesses exchange data. Modern messaging systems are central to most businesses because they enable organizations to exchange data without requiring that organizations tightly integrate their business systems. For example, a manufacturer can set up a topic for bids on raw materials. Suppliers can subscribe to a topic by responding back to the manufacturer. Suppliers can be added or removed at will.

These days, many systems are geographically dispersed, and businesses face problems related to the geographic dispersion of enterprise systems. Many systems have to communicate with the centralized systems at corporate headquarters. Sensitive data that is administered locally has to be synchronized with the main office. JMS allows a safe and secure exchange of data across geographically distributed business.

Benefits of JMS

The fundamental concept of MOM is that communication between applications is intended to be asynchronous. This means there will always be one-way communication between systems. Once a message is sent, the producer can do other jobs; the producer doesn't have to wait for a response. In this type of messaging system, each system is decoupled from other systems. So the failure of one system does not affect other systems.

Network failures can occur, and JMS guarantees delivery, that is, JMS guarantees a message will be received by the consumer when the consumer recovers to normal state.

JMS also has a store-and-forward mechanism, which means that the underlying messaging server writes the message to a persistent system if the intended consumers are currently unavailable. The store-and-forward mechanism delivers all the messages that the consumer missed while it was unavailable.

Practice Using JMS

So far, I have discussed what JMS is, why it is used, and where it is used. Now let's see how to use JMS.

Prerequisites

The first problem in implementing a sample message system is deciding the kind of message server to use and its availability. In this example, we are going to use Sun Java System Application Server 9.0, which is easy to download. This example requires that Java SDK 1.5 be installed as well, or you can download a complete package from download a complete package.

Then, for creating the producer and the consumer, the required Java code file must be compiled. So it is best to download an IDE. I use Eclipse 3.2, which can be downloaded from http://www.eclipse.org/downloads/.

From the installation of Sun Java System Application Server, you can obtain the JAR files that are required for the Java program to run. In your classpath, include the JAR files appserv-rt.jar, appserv-admin.jar, and javaee.jar from the lib folder, and include imqjmsra.jar from the lib\install\applications\jmsra folder of the Sun Java System Application Server installation. This is all you need.

Configuring the Message Server

Let's create a simple chat program because messages are closely related to the word "chat," and the chat program is about sending and receiving messages. As you might guess, the ingredients for a JMS application are a message producer application, a message server, and a consumer application. These applications are tested using the Sun Java System Message Queue server with Microsoft Windows XP as the operating system. Let's start by first configuring the message server.

Note: I tested the sample program using Microsoft Windows XP. However, because the client program and the server run on the Java platform, the sample program should run on all platforms supported by Java technology. If you run the program on the Solaris Operating System, you can start the Sun Java System Application Server and the admin console by using http://localhost:4848/ as the URL in your in the browser. (The default port number for the admin console is 4848. If you changed the port number during the installation, substitute the appropriate port number.)

If you are using Windows XP, start the Sun Java System Application Server by clicking Start->Programs->Sun Microsystems->Application Server PE 9->Start Default server. After the server starts, start the admin console by clicking Start->Programs->Sun Microsystems->Application Server PE 9->Admin console. Provide the admin user name and password. You should get a screen similar to Figure 4.

Figure 4

Figure 4: Sun Java System Application Server Admin Console
(Click to Enlarge)

Then expand the resource node by clicking the arrow to the left of Resources in the tree. Then expand the JMS Resources node, and then click Connection Factories.

Create a new connection factory by clicking the New button on the right side of the screen. You should receive a screen similar to the one shown in Figure 5.

Figure 4

Figure 5: New JMS Connection Factory
(Click to Enlarge)

Type TopicConnectionFactory into the JNDI Name and Description fields, select javax.jms.TopicConnectionFactory for the Type, and then click OK. You should see your TopicConnectionFactory in the list of connection factories.

Then click Destination Resources in the tree (below Connection Factories). Create a JMS destination resource by clicking the New button on the right side of the screen.

Figure 4

Figure 6: New JMS Destination Resource
(Click to Enlarge)

Type Topic into the JNDI Name and Description fields, and select javax.jms.Topic for the Type. Add a property name by typing Topic into the Name field, and then click OK. You should see your Topic in the list of destination resources.

Now you have successfully configured the message server by creating the connection factory and the channel (topic).

Creating the Application

Let's move on to the application. We are going to create an application (producer functionality), which sends messages to the topic we just created. This application will also listen to the topic for messages (consumer functionality). In other words, the application will act as both the producer and the consumer. You will understand more by looking at the following code for the application:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.StringTokenizer;

import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;

public class Chat implements MessageListener{

    private TopicSession pubSession;
    private TopicSession subSession;
    private TopicPublisher topicPublisher;
    private TopicConnection topicConnection;
    private String connectionID;
    private String userId;

    public void set(TopicSession pubSession, TopicSession subSession,
      TopicPublisher topicPublisher, TopicConnection topicConnection,
      String connectionID,String userId) {

		this.pubSession = pubSession;
		this.subSession = subSession;
		this.topicPublisher = topicPublisher;
		this.topicConnection = topicConnection;
		this.connectionID = connectionID;
		this .userId=userId;
	}


    public Chat(String topicName,String connectionID,String
      connectionPassword,String userId)throws Exception{

    if(null==userId)
      throw new Exception();
      InitialContext initialContext=(InitialContext)getIntialContext();
      TopicConnectionFactory topicConnectionFactory=
        (TopicConnectionFactory)initialContext.lookup
        ("TopicConnectionFactory");
      TopicConnection topicConnection=
        topicConnectionFactory.createTopicConnection
        (connectionID,connectionPassword);
      TopicSession pubSession=
        topicConnection.createTopicSession
       (false,Session.AUTO_ACKNOWLEDGE);
      TopicSession subSession=
        topicConnection.createTopicSession
       (false,Session.AUTO_ACKNOWLEDGE);
      Topic chatTopic=(Topic)initialContext.lookup(topicName);
      TopicPublisher topicPublisher=
        pubSession.createPublisher(chatTopic);
      TopicSubscriber topicSubscriber=
        subSession.createSubscriber(chatTopic);
        topicSubscriber.setMessageListener(this);
        set(pubSession, subSession, topicPublisher, topicConnection,
        connectionID,userId);
        topicConnection.start();
	}


    public static Context getIntialContext()throws javax.naming.
     NamingException{
        Hashtable env=new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
          "com.sun.enterprise.naming.SerialInitContextFactory");
        env.put("java.naming.provider.url", "iiop://localhost:3700");
        return new javax.naming.InitialContext(env);
	}

    public void onMessage(javax.jms.Message message) {

		try{
    TextMessage textMessage=(TextMessage)message;
    String text=textMessage.getText();
    StringTokenizer stringTokenizer=new StringTokenizer(text,":");

    if(!userId.equalsIgnoreCase(stringTokenizer.nextToken().trim()))
          System.out.println(text);
       }catch(JMSException exception){
            exception.printStackTrace();
		}
	}


    protected void writeMessage(String text)throws JMSException{
      TextMessage textMessage=pubSession.createTextMessage();
      textMessage.setText(userId+" : "+text);
      topicPublisher.publish(textMessage);
	}

    public void close()throws JMSException{
      topicConnection.close();
	}


 public static void main(String args[])throws Exception{
   try{

   Chat chat=new Chat("Topic","guest","guest","User3");
   System.out.println("Welcome "+chat.userId);
   BufferedReader bufferedReader=
     new BufferedReader(new InputStreamReader(System.in));
   while(true){
     String message=bufferedReader.readLine();
     if(message.equalsIgnoreCase("exit")){
        chat.close();
        System.exit(0);
     }else
        chat.writeMessage(message);

    }
  }catch(Exception e){
     e.printStackTrace();
    }

}


}

In the main method, we create a Chat object to demonstrate the messaging. The Chat class has a constructor that takes the TopicName, ConnectionId, and the connection password and user name as parameters.

The resources, such as ConnectionFactory and Topic that we configured in the message server, can be accessed by the clients with the help of the JNDI API.

For those who are not familiar with the JNDI API, it provides an abstraction to several naming and directory services. Using a naming service, the objects, files, and devices can be bound to a name. So instead of using a cryptic network address, we can refer to an object using a name. The client can use the JNDI API to obtain references to the JMS objects that are in the message server.

The following piece of code is used to create a context for the JNDI resources. A context is like the root of a file system, and once we get the root, we can travel to any directory and get the appropriate file. For getting files, we need the connection factory name, the protocol, the IP address, and the port, which we use to connect to the naming service in the server.

InitialContext initialContext=
 (InitialContext)getIntialContext();

public static Context getIntialContext()throws javax.naming.
  NamingException{
   Hashtable env=new Hashtable();
   env.put(Context.INITIAL_CONTEXT_FACTORY,
     "com.sun.enterprise.naming.SerialInitContextFactory");
   env.put("java.naming.provider.url", "iiop://localhost:3700");
     //localhost:<RMIport>
   return new javax.naming.InitialContext(env);
	}

This method creates a HashMap and then fills it with necessary information. Using that information, the IntialContext is created, as follows.

initialContext=(InitialContext)getIntialContext();
  TopicConnectionFactory topicConnectionFactory=
    (TopicConnectionFactory)initialContext.lookup
    ("TopicConnectionFactory");
  TopicConnection topicConnection=
    topicConnectionFactory.createTopicConnection
    (connectionID,connectionPassword);
  TopicSession pubSession=topicConnection.
    createTopicSession (false,Session.AUTO_ACKNOWLEDGE);
  TopicSession subSession=topicConnection.createTopicSession
    (false,Session.AUTO_ACKNOWLEDGE);
  Topic chatTopic=(Topic)initialContext.lookup(topicName);
  TopicPublisher topicPublisher=pubSession.createPublisher
    (chatTopic);
  TopicSubscriber topicSubscriber=subSession.createSubscriber
    (chatTopic);

We use this context to get references to the JMS objects from the server. By using the JNDI Name TopicConnectionFactory, which we provided in the message server, we get the reference to the TopicConnectionFactory.

Using this topicConnectionFactory, the topic connection object is referenced by providing the connection ID and the connection password. The default connection ID and password is guest. (We can change these in the message server by changing the property user name and the password in the TopicConnectionFactory settings shown below the field in which we provided the description.)

Then two sessions are created from the connection, one for publishing the messages to the topic and one for receiving messages from the topic. The reason for implementing separate sessions is that threading restrictions are imposed by JMS. The Boolean parameter in createTopicSession indicates whether a message should be transacted. A transacted message automatically manages the incoming and outgoing messages within in a transaction. Transactions are important in enterprise applications, but they are not needed in our application, so we leave the value as false.

The next parameter is the String value, which indicates the session type. Here we are using AUTO_ACKNOWLEDGE, which means the message is automatically acknowledged after it is received by the client. The reference to the topic is also obtained using the context, and then using topic, we create the topic publisher and the topic subscriber (consumer). TopicPublisher publishes messages to the topic, and TopicSubscriber listens for messages in the topic.

topicSubscriber.setMessageListener(this);
set(pubSession, subSession, topicPublisher, topicConnection,
  connectionID, userId);
topicConnection.start();

TopicSubscriber sets the messagelistener as the application. Then we increase the scope of the local variables to class variables so that these objects can also be used by other methods.

Focusing our attention back on the main method, the buffered reader is created for system input. If the user types exit, the program exits. Otherwise, the message is published to the topic using the writeMessage method.

protected void writeMessage(String text)throws JMSException{
  TextMessage textMessage= pubSession.createTextMessage();
  textMessage.setText(userId+" : "+text);
  topicPublisher.publish(textMessage);
  }

In the writeMessage method, a text message is created with the help of pubSession, and using topicPublisher, the message is published.

When the message is published to the topic, the server pushes the message to the listeners. Since our application is also a listener, the listener method onMessage() is invoked.

public void onMessage(javax.jms.Message message) {

  try{
     TextMessage textMessage=(TextMessage)message;
     String text=textMessage.getText();
     StringTokenizer stringTokenizer=new StringTokenizer(text,":");

     if(!userId.equalsIgnoreCase(stringTokenizer.nextToken().trim()))
        System.out.println(text);
        }catch(JMSException exception){
          exception.printStackTrace();
        }
  }

We know that the message we want to publish is a text message, so we are explicitly type casting the message to textMessage, and then if the text is sent by the same application it is ignored. This application is a publisher and a subscriber, so it receives the same message that is published. We need to see only the messages published by other applications to the topic, that is, messages from the other chat clients.

Running the Application

To run our application, it will be good to supply parameters to the Chat constructor through the command line parameters.

As discussed earlier, configure the message server (Sun Java System Application Server), and keep it in server started mode. If you are running the client application with the command line parameters, start two application instances using different user names.

If you are running the application in an editor, you can run one application instance with a hard-coded user name and run another instance by changing the value of the user name.

Try to post a message from one application instance and see that the message is pushed to the other application instance. If that happens, you have successfully created an enterprise messaging system!!

You can change the implementation of the application from topic to queue to achieve P2P communication. The following code is for creating a connection, a session, and a receiver (instead of a subscriber in the pub/sub model) for the P2P model.

QueueConnectionFactory
queueConnectionFactory=(QueueConnectionFactory)initialContext.
  lookup("QueueConnectionFactory");

  QueueConnection queueConnection = queueConnectionFactory.
    createQueueConnection(connectionID,connectionPassword);
  QueueSession queSession= queueConnection. createQueueSession
    (false,javax.jms.Session.AUTO_ACKNOWLEDGE);
  Queue chatQueue=( Queue)initialContext.lookup(queueName);
  QueueReceiver queueReceiver= queSession.createReceiver(chatQueue);

queueReceiver.setMessageListener(this);

queueConnection.start();

There is no difference in the code except for the Receiver object instead of the Subscriber object. To ensure you understand how to create a queue in the server, here is the code for sending information to the queue:

QueueSender queueSender =queSession.createSender(chatQueue);

queueSender.send(message,javax.jms.DeliveryMode.PERSISTENT,
  javax.jms.Message.DEFAULT_PRIORITY,1800000);

This is an overloaded method from QueueSender for sending the message in the queue, and the parameters specify the message, delivery mode, priority, and message expiration. TopicPublisher also has an overloaded publish method.

Conclusion

In this article, I discussed what JMS is, why it is used, where it is used, and how it is used. My aim was to enable you to see that JMS is an important technology in the enterprise world by providing information about enterprise problems that require loosely coupled, asynchronous, enterprise-quality systems.

There might be other ways to solve enterprise problems, but I hope this information helps you to make good decisions about using this technology. I assumed this information might be useful for developers who are new to this paradigm. Often, when developers look for enterprise-quality technology, they don't find low-level information that is appropriate for those who are new to the field. If you have any comments about this article, please communicate them to me.

About the Author

Nishant M.C. is a software engineer currently working on the Java 2 Platform, Enterprise Edition (J2EE) platform. He has a great passion for Java technology, and analyzes various Java enterprise technologies. He holds a Bachelor of Engineering degree from Anna University, India. You can contact him at nishantmc [at] gmail.com.

The information and links on this page have been provided by a BigAdmin user. The submitter is solely responsible for such information and links. Sun is not responsible for the availability of external sites or resources, and does not endorse and is not responsible or liable for any content, advertising, products, or other materials on or available from such sites or resources. Sun will not be responsible or liable, directly or indirectly, for any actual or alleged damage or loss caused by or in connection with use of or reliance on the information posted here, or goods or services available on or through any external site or resource.
 
 

Comments (latest comments first)

Discuss and comment on this resource in the BigAdmin Wiki

Unless otherwise licensed, code in all technical manuals herein (including articles, FAQs, samples) is provided under this License.


BigAdmin
  
 
BigAdmin Upgrade Hub