Previous Section
 < Day Day Up > 
Next Section


Java Messaging Service

The next topic related to distributed computing is the Java Messaging Service (JMS), designed by the Java architects to perform message-oriented distributed applications. The message-oriented applications are asynchronous in nature, as they do not wait for (or do not expect) an acknowledgement from the message recipient. Then where does the message go and how is it handled in between? There is a piece of software known as middleware, which resides between the client and the server applications. The client and server never talk directly; rather, the entire communication is handled by the middleware, and this middleware is capable of handling messages directed to different destinations. This is done by the middleware typically by creating and maintaining one or more queues, known as message queues. A queue is a data structure that handles messages in FIFO (first in, first out) order, which means the message received by the queue first in the sequence will be delivered first to the recipient. Examples of such middleware are many, such as IBM MQ Series (very familiar to the UNIX and IBM mainframe developer community), Microsoft Message Queuing service on the Windows platform, Sonic MQ messaging server, and so on. Because these middleware systems provide asynchronous message distribution, they are known as message-oriented middleware (MOM) in the industry. The distinguishing feature of the message-oriented distributed applications is that because the communication between the client and server is facilitated by the middleware, the client and server may be running in different environments. For example, a C++ client on a Windows operating system would be able to work with a Java-based (or specifically J2EE-based) server running on a Linux-based system. What is necessary is that the middleware should be able to support both the platforms and provide an API for that platform. The client and server will then use the appropriate API to send messages in the format acceptable by the middleware. The message-oriented distributed application concepts have been used in the industry for a long time; in fact, it is one of the ancient (still powerful) mechanisms of asynchronous message distribution. The advent of modern distributed application concepts such as Microsoft DCOM, CORBA, and the latest J2EE and Microsoft .NET framework has been successful in bringing newer concepts into existence—such as online (or synchronous) communication between clients and servers—and have created the impression in the minds of many professionals that the asynchronous architectures such as MOM do not have a place in modern application development. This is a total misconception. The existence of asynchronous architectures is not determined or governed by the advent of synchronous mode architectures, however excellent they are. The existence is governed by the business need. There is always a business need for asynchronous mode communication. In fact, a successful and high-performance enterprise application uses fewer resources during peak business hours and should be able to perform asynchronous business operations during off-peak hours (or sometimes even during peak hours). In addition, most businesses do need support to conduct business during off-hours. For example, when we deposit a check in our bank account or withdraw cash from the ATM(particularly during off-hours), we are essentially triggering a number of asynchronous transactions. Consider what happens when we withdraw money from a cash station in a shopping mall. How is it being debited from our account? The bank that maintains the ATM in the shopping mall and the bank where we maintain our account do not necessarily have to be the same; in fact, they are often different banks. However, a number of asynchronous processes between the two banks (or from any bank to any other bank) work behind the scenes without our knowledge to set up necessary transactions that ensure accuracy and timeliness as needed by the business process. The banking example is cited here because most banking systems use many asynchronous processes. These systems wake up when their customers start sleeping. Therefore, the advent of modern technologies did not reduce the importance of asynchronous mode software such as MOM; rather, it enhanced the usability of asynchronous mode communication channels, using the new tools and techniques of the modern era.

There are two types of message-oriented applications. In the first type, the message sender publishes the message for which interested recipients would subscribe. In other words, whoever subscribes to the service will receive the message; hence this type of applications are known as publish-subscribe type. In the second type of applications, the sender specifies one destination as the recipient, and hence this is known as point-to-point type. Whichever type is used, the MOM ensures delivery of the messages to the recipient, as the messages are stored to persistent disk storage. In the case of the point-to-point type message communication, this persistent storage is a queue maintained and managed by the MOM, and in the case of the publisher-subscriber type message delivery system, it is known as a topic, to which the subscribers subscribe or register. If the recipient is not ready to receive the messages, the messages will remain in the queue until the recipient is ready. Once the recipient is ready and starts receiving the messages, it acknowledges the sender for each of the received messages. The second message is sent by the queue administrator (which is MOM) only after successful acknowledgement of the first message.

Having said that much about the need of asynchronous mode systems, and having provided a brief overview of what a MOM can do for distributed computing, it is now time to focus our attention toward understanding the concepts behind this architecture and build an example to see how it works (or to get a feeling that it works). Because we are currently focusing on Java-based messaging services, also known as JMS, we will look at a JMS-based example. JMS is one of the components distributed as part of the J2EE (Java 2 Enterprise Edition) architecture and is an integral part of J2EE API from the 1.3 Release onward. JMS was available as a separate API for releases prior to J2EE 1.3. The example application in this section will be built using the point-to-point mode communication and is developed to run on J2EE Releases 1.3.1 and later.

The steps involved in building the example are listed here.

Listings 7.21 and 7.22 are available on the accompanying CD-ROM.

Listing 7.21: JmsMsgSender.java
Start example
import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;

// This is an example JMS message sender program that sends simple
// message objects using the Point to Point mode communication.
// The program first sends a name and then a calendar object with
// current time. The recipient should receive them in the same order.

public class JmsMsgSender {
    QueueConnection   queueConn;
    QueueSession             queueSession;
    QueueSender              queueSender;
    QueueConnectionFactory   queueConnFactory;
    Queue                           queue;
    Context                  jndiContext;
    TextMessage              txtMessage;
    ObjectMessage            objMessage; 
    String                          msgQueueName;
    String                          myName;

    JmsMsgSender(String queueName, String helloName) {
        try {
          // set the message queue name obtained from command prompt 
              arguments
           msgQueueName = new String(queueName);
          myName = new String(helloName);
          // create initial JNDI context 
            jndiContext = new InitialContext();

          // obtain the queue connection factory for the queueu from 
                  the initial JNDI context
            queueConnFactory = (QueueConnectionFactory)
                jndiContext.lookup("QueueConnectionFactory");

          // obtain the queue from the initial JNDI context
            queue = (Queue) jndiContext.lookup(msgQueueName);

          // from the queue connection factory create a queue 
                connection object and then
          // create a queue session object.
            queueConn = queueConnFactory.createQueueConnection();
            queueSession = queueConn.createQueueSession(false, 
                    Session.AUTO_ACKNOWLEDGE);

          // the queue sender object is used to send messages to the 
              queue
            queueSender = queueSession.createSender(queue);

          // create a text message and object message for the session
            txtMessage = queueSession.createTextMessage();
          objMessage = queueSession.createObjectMessage();

          // send myName as the first message
          txtMessage.setText(myName);
          queueSender.send(txtMessage);
          System.out.println("Sent '" + myName + "' as first message");

          // send a calendar object with current timestamp as the 
              second message
          Calendar currTime = Calendar.getInstance();
          objMessage.setObject(currTime);
          queueSender.send(objMessage);
          System.out.println("Sent current time '"+ currTime.getTime().toString()+ "' as second message");

        } 
      catch (NamingException ne) {
            System.out.println("Error while creating/accessing JNDI 
                Initial Context " +
                "Context : " + ne.toString());
            System.exit(-1);
        }
        catch (JMSException je) {
            System.out.println("JMSException occurred while sending a 
                message " + 
                je.toString());
      }
      catch (Exception ex) {
            System.out.println("Error occured while running the message 
                sender program");
          ex.printStackTrace();
            System.exit(-1);
        }
    }

    public static void main(String[] args) {
        try {
          // make sure that the queue name is provided in the command 
              line argument
            if (args.length != 2) {
            throw new Exception("Queue name (one word) and your name 
                (one word) should be specified at command prompt");
            }
      }
      catch (Exception ex) {
            System.out.println("Error occured while running the message 
                sender program");
          ex.printStackTrace();
            System.exit(-1);
        }
      JmsMsgSender msgSender = new JmsMsgSender(args[0], args[1]);
    }
}
End example

Listing 7.22: JavaMsgRecipient.java
Start example
import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;

// This is an example JMS message recipient program that receives simple
// message objects from the queue using the Point to Point mode 
    communication.
// The messages received by the recipient are sent by the JmsMsgSender.
    java program. 

public class JmsMsgRecipient {
    QueueConnection       queueConn;
    QueueSession                 queueSession;
    QueueReceiver                queueRecipient;
    QueueConnectionFactory       queueConnFactory;
    Queue                               queue;
    Context                      jndiContext;
    TextMessage                  txtMessage;
    ObjectMessage                objMessage; 
    String                              msgQueueName;

    JmsMsgRecipient(String queueName) {
        try {
          // set the message queue name obtained from command prompt 
            arguments
            msgQueueName = new String(queueName);
          // create initial JNDI context 
            jndiContext = new InitialContext();

          // obtain the queue connection factory for the queueu from 
               the initial JNDI context
            queueConnFactory = (QueueConnectionFactory)
                jndiContext.lookup("QueueConnectionFactory");

          // obtain the queue from the initial JNDI context
            queue = (Queue) jndiContext.lookup(msgQueueName);

          // from the queue connection factory create a queue 
              connection object and then
          // create a queue session object.
            queueConn = queueConnFactory.createQueueConnection();
            queueSession = queueConn.createQueueSession(false, 
                    Session.AUTO_ACKNOWLEDGE);

          // the queue sender object is used to send messages to the 
                  queue
            queueRecipient = queueSession.createReceiver(queue);
          queueConn.start();
      
          // create a text message and object message for the session
            txtMessage = queueSession.createTextMessage();
          objMessage = queueSession.createObjectMessage();
          Calendar currTime = null;

            while (true) {
                Message msg = queueRecipient.receiveNoWait();
                if (msg != null) {
                    if (msg instanceof TextMessage) {
                  // retrieve the text message and display
                        txtMessage = (TextMessage) msg;
                        System.out.println("First message received : " 
                            + txtMessage.getText());
                    } 
                else if (msg instanceof ObjectMessage) {
                  objMessage = (ObjectMessage) msg;
                        currTime = (Calendar)objMessage.getObject();
                        System.out.println("Second message received : " 
                            + currTime.getTime().toString());
                    }
                else {
                  break;
                }
                }
            }

        } 
      catch (NamingException ne) {
            System.out.println("Error while creating/accessing JNDI 
                Initial Context " +
                "Context : " + ne.toString());
            System.exit(-1);
        }
        catch (JMSException je) {
            System.out.println("JMSException occurred while receiving a 
                message " + 
                je.toString());
      }
      catch (Exception ex) {
            System.out.println("Error occured while running the message 
                recipient program");
          ex.printStackTrace();
            System.exit(-1);
        }
    }

    public static void main(String[] args) {
        try {
          // make sure that the queue name is provided in the command 
              line argument
            if (args.length != 1) {
            throw new Exception("Queue name (one word) should be 
                specified at command prompt");
            }
      }
      catch (Exception ex) {
            System.out.println("Error occured while running the message
                     
                recipient program");
          ex.printStackTrace();
            System.exit(-1);
        }
      JmsMsgRecipient msgRecipient = new JmsMsgRecipient(args[0]);
    }
}
End example

Figure 7.19 and Figure 7.20 display the console output of the sender and recipient programs, respectively. Although the examples are simple, they are very clear and provide an easy way of understanding the JMS principles. While the example discussed the point-to-point mode, the publisher-subscriber mode is left to the readers, as it is beyond the scope of the discussion. The JMS concepts discussed in this chapter are useful in understanding some of the advanced topics discussed in Chapter 8.

Click To expand
Figure 7.19: Console output of the JMS Sender program.
Click To expand
Figure 7.20: Console output of the JMS Recipient program.


Previous Section
 < Day Day Up > 
Next Section