JMS and the Importance of Messaging


In this section we will describe messaging architecture, illustrate its logical similarity with P2P architecture, and describe the Java Messaging Service (JMS). We will also discuss JMS providers (which are part of the J2EE platform), and put them to service.

What Is Messaging?

Messaging is a mechanism through which two or more entities (applications or software modules) can communicate with each other. Communicating entities operate independently of each other; that is, they are not bound to each other in any way. For example, if one entity sends a message to another entity, it will not wait for the response from the other side. This type of messaging is called asynchronous communication.

Messaging Versus Request-Response

Consider what happens when you use an Internet browser program such as Internet Explorer. The browser sends an HTTP request and receives an HTTP response from a Web server. The browser remains busy after issuing the HTTP request until it receives the response (normally an HTML file), although the browser does not have much to do other than waiting for the response. In this way the browser is bound to the Web server, at least for a while.

This is called synchronous communication, and it's the main difference between request-response and messaging models. With messaging, we have asynchronous communication that does not require waiting for response. Therefore, messaging entities remain independent of each other.

Telephonic conversation is a real-life example of synchronous communication between humans. On the other hand, the exchange of letters or emails is asynchronous in nature. The only difference between email and messaging is that messaging always takes place between software components and software applications. Unlike email, messaging never involves human interaction.

Messaging Clients and Messaging Providers

In the request-response model, we normally have clients and servers. Clients request and servers respond (serve). Servers are normally designed to serve a number of clients simultaneously, and therefore have more powerful computing capabilities compared to clients.

On the other hand, the messaging model has only clients and no servers. This gives rise to the equality of clients. Each client is supposed to a peer of every other client.

However, this does not mean that there is no requirement to handle administration issues, such as the following:

  • Different messaging clients will be interested in messages related to different topics. For example, consider a messaging-based workflow solution for a company. The purchase manager (a messaging client) is interested in receiving messages related to new purchase requisition requests. The after-sales support manager (another messaging client) is interested in messages related to faulty equipment from customers. The messaging administration module will keep a record of these different topics and map the list of clients interested in different topics.

  • If a message needs to be sent to a client who is currently offline, the messaging administration module will keep the message stored until the client is online again. The JMS API can ensure that a message is delivered once and only once. Lower levels of reliability are available for applications that can afford to miss messages, or to receive duplicate messages.

Management tasks such as these call for a management entity, called a messaging provider.

Messaging Clients as Peers in P2P

Messaging clients are logically similar to the concept of peers in P2P. A client acting as a peer can send messages for several purposes for example, the discovery of other peers, searching for information, and so on.

Imagine a messaging application in which clients follow the XML formats of JXTA. This messaging application will become very similar to a JXTA-based P2P application. This similarity emphasizes the role of messaging in P2P applications.

Role of Messaging Providers in P2P

JXTA calls for an architecture in which messages sent by peers can be forwarded to other peers and so on, until they reach the required destination peer.

An example of this type of messaging is the JXTA search specification. If a JXTA peer wants to search for a specific bit of information, it will send an XML message to the relevant peers. The peers receiving this message might in turn forward this search request to other relevant peers. This way, the search message has the chance of eventually reaching a peer that has the requested information.

This type of architecture can be realized through messaging. You will need to build a messaging provider application that can also act as messaging client to other messaging providers. In this way, it can forward specific messages from its clients to other providers, who will in turn forward the message to their clients and other providers.

Message-Oriented Middleware (MOM)

Based on the concepts of messaging discussed previously, proprietary implementations are available from different vendors. Some examples of proprietary messaging frameworks are the following:

  • MQSeries from IBM

  • SonicMQ from Progress

  • FiranoMQ from Firano

These messaging frameworks are called message-oriented middleware (MOM).

Sun released the specification of JMS, which eventually became a part of the J2EE specification. The reference implementation of JMS is now included in J2EE SDK v1.3.

Java Messaging Service

JMS aims to provide access to vendor-specific MOM implementations through a standard Java API. JMS allows you to have proprietary implementations of JMS administrative objects. This means that vendors are free to implement administrative objects in any manner they want. (JMS manages these objects administratively rather than programmatically. Therefore, they are called administered objects. Administered objects are meant to administrate topics and connections as described previously in the section "Messaging Clients and Messaging Providers.") The only restriction is at the API level. JMS providers talk to JMS clients according to the API; JMS clients will access JMS providers to get messaging services.

The interaction between JMS clients, the JMS provider, and administered objects is shown in Figure 12.1.

Figure 12.1. The JMS architecture.

graphics/12fig01.gif

Figure 12.1 shows four components: JMS clients, the JMS provider, administered objects, and the JNDI (Java Naming and Directory Interface) namespace. These components work as follows:

  • JMS clients Clients can be of two types: message producers and message consumers. Producers have the role of producing messages that consumers will receive. Any messaging peer (client) can assume either or both roles in the messaging architecture.

  • JMS provider This is actually the JMS implementation that provides a communication framework to all messaging clients. The provider plays the role of a broker that keeps information about what topics are available for messaging and which clients are interested in a particular topic. Because JMS is part of the J2EE v1.3 specification, all J2EE v1.3 platform implementations have a built-in JMS provider.

  • The JMS provider creates administered objects and places (stores) them in a JNDI namespace. JNDI provides a standard interface for distributed Java applications to store files and look them up later. JMS clients will use JNDI methods to look up and check what topics are available.

JMS allows two messaging domains: publishing/subscribing and point-to-point. The publishing/subscribing domain is a topic-based messaging mechanism in which message producers publish their messages on a topic. Message consumers subscribe to the topics in order to consume (receive) messages. Every message will be delivered to all subscribers of the topic.

The point-to-point domain is based on queues. Message senders will send their messages to message queues. Each message queue is destined for one consumer. Consumers will receive messages from their queues and acknowledge receipt to senders. In this way, each message will be received by only one message consumer.

Both queues and topics are referred to as destinations. A JMS-administered object is supposed to administrate destinations and connections to destinations. To provide connections to destinations, JMS provides two interfaces: QueueConnectionFactory and TopicConnectionFactory. A JSM client will use either of the two connection factories to connect to a JMS provider.

Putting JMS to Work

We will now discuss the architecture of a complete messaging system based on JMS. While designing a JMS messaging system, you will need to address the following issues:

  • Configure a JMS provider. You will normally not implement a JMS provider yourself. Many implementations are already available as a part of J2EE application servers. You will only need to configure it according to your application.

  • Use JMS-client API in your messaging logic.

Let's see how you will accomplish these tasks by taking a simple messaging application as a design example.

A JMS-Based Messaging Application

We will now demonstrate the design and implementation of a simple messaging application that is very similar to the concept of P2P. The architecture of our application is shown in Figure 12.2.

Figure 12.2. Four instances of a JMS-based messaging application.

graphics/12fig02.gif

We have shown four instances of our application running simultaneously. Each instance will act as a peer. Theoretically, there can be any number of simultaneous instances of this application, but for the sake of simplicity and clarity we'll discuss only four instances. Think of each instance as a peer in our messaging system.

Another simplification that we have assumed is that the messaging peers have already discovered each other that is, all four peers know the addresses of the other peers. Therefore, they can send and receive messages to each other.

Each messaging peer has three modules:

  • The messaging client is capable of sending and receiving messages to and from other peers (application instances).

  • The messaging provider listens to the messages sent from other peers and forwards them to peers that are interested in receiving messages related to particular topics.

  • The GUI, seen in Figure 12.3, contains the following components:

    • A drop-down list to select which peer to contact

    • A set of radio buttons to specify whether you want to send a message or start listening to incoming messages

    • A drop-down list to select a topic of interest

    • A button to send messages

    • A button to start listening for incoming messages

    • A text area to display messages

    Figure 12.3. The GUI for our messaging application.

    graphics/12fig03.jpg

Using the GUI, each peer can send a message to any other peer. Each peer can also register itself with any other peer to receive messages related to any particular topic.

Let's see how our messaging application works. We'll need to perform the following steps detailed in the following sections.

Configuring the Provider

In this application we have used Sun's J2EE SDK v1.3 that includes a JMS provider implementation, so you will need a running J2EE server. Once the server is running, use the following command to create a topic connection factory (an administrator object):

 j2eeadmin -addJmsFactory MyTopicConnectionFactory topic  

Even if you don't do this, the application will run using the default topic connection factory.

Then use the following commands to create topics (also administered objects):

 j2eeadmin -addJmsDestination Topic1 topic  j2eeadmin -addJmsDestination Topic2 topic 

This will create two topics on the JMS provider, which is now set. You will need to configure the provider for every instance of this application.

Logic for the Messaging Client

We have written two classes for our messaging client: MessageConsumer (Listing 12.1) and MessageProducer (Listing 12.2). MessageConsumer.java implements a JMS client that consumes JMS messages, and MessageProducer.java implements a JMS client that produces JMS messages.

Listing 12.1 MessageConsumer.java
 import javax.swing.*; import javax.jms.*; import javax.naming.*; import java.io.*; import java.util.*; public class MessageConsumer {        // We would like to keep a reference of TopicConnection        // and TopicSession so that we can destroy it at the end.        private TopicConnection               topicConnection = null;        private TopicSession                      topicSession = null;        // This Subscriber will listen for incoming messages.        private TopicSubscriber               topicSubscriber = null;        // This listener will receive the incoming messages.        private TextListener                      topicListener = null;        // The TextArea to disply the messages.        private JTextArea                             textArea = null;        // Names of machines over the network.        // These machines will have JMS Providers running.        // In real applications, names of machines should come        // from the GUI.        private final String COMPUTER1 = "computer6";        private final String COMPUTER2 = "computer13";        private final String COMPUTER3 = "computer14";        private final String COMPUTER4 = "computer15";        // Connect to the JMS Provider and start listening.        public void connectToProvider (String topicName, String jmsProvider) {               // We will get a reference of System properties.               // We will add the Properties related to JMS functionality.               Properties env = System.getProperties();               env.put("com.sun.jms.internal.java.naming.factory.initial", "com.sun. graphics/ccc.gifenterprise.naming.SerialInitContextFactory");               // Port number where the Naming service of               // remote JMS Provider is listening.               env.put("rg.omg.CORBA.ORBInitialPort","1050");               try {                      // Set the network address of target machine                      // where JMS Provider is running.                      if (jmsProvider.equals("Server1"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER1);                      else if (jmsProvider.equals("Server2"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER2);                      else if (jmsProvider.equals("Server3"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER3);                      else if (jmsProvider.equals("Server4"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER4);                      // Get an object of JNDI InitialContext                      // from given JMS Provider.                      Context jndiContext = new InitialContext(env);                      // Get JMS Administrator Objects                      TopicConnectionFactory topicConnectionFactory = ( graphics/ccc.gifTopicConnectionFactory) jndiContext.lookup("TopicConnectionFactory");                      Topic topic = (Topic) jndiContext.lookup(topicName);                      // Establish the connection.                      topicConnection = topicConnectionFactory.createTopicConnection();                      // Create the session.                      topicSession = topicConnection.createTopicSession (false, Session. graphics/ccc.gifAUTO_ACKNOWLEDGE);                      // Create the subscriber for the topic of interest.                      // Specify a TextListener object (inner class)                      // who will receive messages.                      topicSubscriber = topicSession.createSubscriber(topic);                      topicListener = new TextListener();                      topicSubscriber.setMessageListener(topicListener);                      textArea.setText("starting the listener for messages");                      // We are all set, Start listening.                      topicConnection.start();         } catch (NamingException e) {                textArea.append ( "JNDI lookup failed: "+e.toString());         } catch (JMSException e) {                textArea.append("JMS Exception occurred: " + e.toString());         }     } // connectToProvider()        // Stop Listening for messages and close the Connection.        public void stopListening() {            if (topicConnection != null) {                try {                             topicSubscriber.close();                             topicSession.close();                             topicConnection.close();                             textArea.setText("\nStoped Listining" );                } catch (JMSException e) {                             textArea.setText("\nException in listing" );                      }            }        } // stopListening()        public void setDisplayArea (JTextArea textArea) {               this.textArea = textArea;        }// setDisplayArea        // Inner class to receive messages.        private class TextListener implements MessageListener {            public void onMessage(Message message) {                TextMessage msg = null;                try {                       // Our application can only handle Text Messages.                       if (message instanceof TextMessage) {                              msg = (TextMessage) message;                              textArea.append( "\n\n The Message is Received: \n\t"  + msg. graphics/ccc.gifgetText());                       } else {                                    textArea.append( "\n Message of wrong type: " +  graphics/ccc.gifmessage.getClass().getName());                       }//else                   } catch (JMSException e) {                             textArea.append("JMSException in onMessage(): " + e.toString( graphics/ccc.gif));                   } catch (Throwable te) {                       textArea.append("Exception in onMessage():" + te.getMessage());                   }//catch            } // onMessage()        } // TextListener class }// Message Consumer class 
Listing 12.2 MessageProducer.java
 import javax.jms.*; import javax.naming.*; import java.util.*; public class MessageProducer {        // We would like to keep a reference of TopicConnection, Session        // and TopicPublisher so that we can destroy it at the end.        private TopicConnection        topicConnection = null;        private TopicPublisher topicPublisher = null;        private TopicSession topicSession = null;        // Names of machines over the network.        // These machines will have JMS Providers running.        // In real applications, names of machines should come        // from the GUI.        private final String COMPUTER1 = "computer6";        private final String COMPUTER2 = "computer13";        private final String COMPUTER3 = "computer14";        private final String COMPUTER4 = "computer15";        // Connect to the JMS Provider and send the message.        public String connectToProvider (String topicName, String jmsProvider ,String msg  graphics/ccc.gif) {            // We will get a reference of System properties.            // We will add the Properties related to JMS functionality.               Properties env = System.getProperties();               env.put("com.sun.jms.internal.java.naming.factory.initial", "com.sun. graphics/ccc.gifenterprise.naming.SerialInitContextFactory");               // Port number where the Naming service of               // remote JMS Provider is listening.               env.put("rg.omg.CORBA.ORBInitialPort","1050");               try {                      // Set the network address of target machine                      // where JMS Provider is running.                      if (jmsProvider.equals("Server1"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER1);                      else if (jmsProvider.equals("Server2"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER2);                      else if (jmsProvider.equals("Server3"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER3);                      else if (jmsProvider.equals("Server4"))                             env.put("org.omg.CORBA.ORBInitialHost",COMPUTER4);                      // Get an object of JNDI InitialContext                      //from given JMS Provider                      Context jndiContext = new InitialContext(env);                      // Get JMS Administrator Objects                      TopicConnectionFactory topicConnectionFactory = ( graphics/ccc.gifTopicConnectionFactory) jndiContext.lookup("TopicConnectionFactory");                   Topic topic = (Topic) jndiContext.lookup(topicName);                   // Establish TopicConnection and TopicSession.                   topicConnection = topicConnectionFactory.createTopicConnection();                   topicSession = topicConnection.createTopicSession(false, Session. graphics/ccc.gifAUTO_ACKNOWLEDGE);                   // Create the publisher for the topic of interest.                   topicPublisher = topicSession.createPublisher(topic);                   // Create Message Object and set the text.                   TextMessage message = topicSession.createTextMessage();                   message.setText(msg);                   // We are all set, Send message.                   topicPublisher.publish(message);            } catch (JMSException e) {                   String err = new String("Exception occurred: " + e.toString());                   return err;            } catch (NamingException e) {                   String err = new String ("Error in JNDI context= " + e.toString());                   return err;             }               finally {                   if (topicConnection != null) {                       try {                                    topicPublisher.close();                                    topicSession.close();                                    topicConnection.close();                       } catch (JMSException e) {                                    return "Connection Closing Exception: "+ e.toString();                   }                 }//if         } // finally               return "Message Sent Successfully";     } //connectToProvider() } // end MessageProducer Class 
The MessageConsumer Class

As its name implies, the MessageConsumer class is responsible for tasks related to message consuming. This includes

  • Creating a connection with the topic of interest registered with a JMS provider (topics are also referred to as destinations).

  • Creating a session and registering interest in receiving messages related to a particular topic.

The MessageConsumer class has a public method connectToProvider that takes two strings as parameters. The first parameter is the name of the topic for which the MessageConsumer wants to register itself to receive messages. The second parameter is the provider address (the address of a JMS provider inside the other peer). We'll call it a remote provider.

The ConnectToServer method implements the following sequence:

  1. Sets a system properties object to include parameters and creates the JNDI context.

  2. Looks up the topic ConnectionFactory object in the JNDI context. (Recall that we created the topic connection factory while configuring the JML provider.)

  3. Looks up the topic of interest in the topic ConnectionFactory. (Recall that we created topics while configuring the JML provider.)

  4. Creates a topic connection and a session.

  5. Creates a subscriber for the topic and sets a message listener. The message listener will be activated after receiving of a message.

Our MessageConsumer has an inner class called TextListener, which is an asynchronous event handler. The TextListener class implements the MessageListener interface, which requires implementing just one method named onMessage. onMessage receives control whenever a new message is detected. In the onMessage method, you define the actions to be taken when a message arrives. Our implementation of the onMessage method simply displays the incoming message in a text area.

The MessageProducer Class

Whereas the MessageConsumer class has the responsibility of receiving (or consuming) messages related to particular topics, the MessageProducer class is used to send messages to their corresponding topics (destinations), so that the messages can be consumed by interested MessageConsumer objects.

The MessageProducer class has a method named connectToProvider that works very much like the connectToServer method of the MessageConsumer class. The only difference occurs after the connection and session are both established. This time a publisher is created instead of a subscriber, and a message is published with the publisher. The message will reach the remote provider, which will route it to all interested peers.

GUI for the Messaging Application

We have implemented a simple JFC-based GUI for this messaging application. It puts the MessageProducer and MessageConsumer classes to work. The GUI is one of the downloads from this chapter at this book's Web site.

Limitations of our Messaging System

This messaging application presents a skeleton design and implementation of a messaging system. The purpose is to demonstrate JMS. You can build your own messaging logic on top of a similar architecture.



JavaT P2P Unleashed
JavaT P2P Unleashed
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 209

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