A Chat Application Using JMS

The sample chat application explained in this section uses most of the concepts explained in this chapter. This application was tested against JMQ from Sun Microsystems, but it should run against any other JMS-compliant provider.

Architecture

The chat application uses the JMS Pub/Sub messaging model. The chat clients are Swing-based applications that take the user name as a command-line argument. All the chat clients subscribe to a common topic to which they publish messages. The Administered objects are stored in the standard WinNT file system namespace and are looked up using the standard JNDI file system context SPI (Service Provider Interface) provided by Sun Microsystems. The Administered objects can be stored in any other standard naming and directory service like LDAP and looked up using JNDI, provided there is an SPI available for the service.

The object model for this application is depicted in the diagram shown below:

click to expand

Configuring Administered Objects

The config.bat script described earlier in the chapter can be used for storing a topic named myTopic and a topic connection factory called TCFactory. These are required to run the sample application.

The JNDIService Class

This class is a generic utility class for looking up objects stored in a JNDI namespace:

     import javax.naming.NamingException;     import javax.naming.Context;     import javax.naming.InitialContext;     import java.util.Properties;     public class JNDIService {     private static Properties prop; 

The class has a static initialization method for setting the provider URL and initial context factory:

     public static void init(String contextFactory, String providerURL) {        prop = new Properties() ;        if (contextFactory != null) {           prop.put (Context. INITIAL_CONTEXT_FACTORY, contextFactory) ;        }        if (providerURL != null) {           prop.put (Context.PROVIDER_URL, providerURL);        }     } 

In addition it defines a method for returning the home interface:

         public static Object lookup (String jndiName) throws NamingException {            Context ctx = new InitialContext (prop);            Object obj = ctx.lookup(jndiName);            ctx.close();            return obj;         }     } 

The MessageSender Class

This class is used for sending messages to a given topic using a given topic session:

     import javax.jms.Topic;     import javax.jms.TopicSession;     import javax.jms.JMSException;     import javax.jms.TextMessage;     import javax.jms.TopicPublisher;     public class MessageSender {        private Topic topic;        private TopicSession session;        private TopicPublisher publisher; 

Both the topic and the topic session are passed to the class in the constructor:

     public MessageSender (Topic topic, TopicSession session)              throws JMSException {        this.topic = topic;        this.session = session;        publisher = session.createPublisher(topic);     } 

This class also provides a method to send messages to the topic passed in the constructor

         public void sendMessage (String message) throws JMSException {            TextMessage outMsg = session.createTextMessage();            outMsg.setText(message);            publisher.publish(outMsg);         }     } 

The MessageReceiver Class

This class is used for handling asynchronous message delivery:

     import javax.jms.Topic;     import javax.jms.TopicSession;     import javax.jms.JMSException;     import javax.jms.TextMessage;     import javax.jms.TopicSubscriber;     import javax.jms.MessageListener;     public class MessageReceiver implements MessageListener {        private Topic topic;        private TopicSession session;        private TopicSubscriber subscriber;        private MessageHandler messageHandler; 

Similar to MessageSender this class is also initialized with a topic and a topic session. A subscriber is created for the passed session and a message listener for asynchronous message delivery is implemented:

        //Initialize topic and session        public MessageReceiver(Topic topic, TopicSession session)                 throws JMSException {           this.topic = topic;           this.session = session;           subscriber = session.createSubscriber(topic);           subscriber.setMessageListener(this);        }        //Register the message handler        public void setMessageHandler(MessageHandler messageHandler) {           this.messageHandler = messageHandler;        } 

The callback method of the message listener interface will notify the MessageHandler interface that is registered to the MessageReceiver. The MessageHandler is explained in the next section:

        //Callback method for message listener        public synchronized void onMessage(javax.jms.Message message) {           try {              TextMessage inMsg = (TextMessage)message;              if(messageHandler != null) {                 messageHandler .handleMessage (inMsg.getText());              }           } catch(JMSException ignore) {              ignore.printStackTrace();           }        }     } 

The MessageHandler Class

This interface defines a single callback method called handleMessage() that takes an argument of type String. Classes that want to be notified by the MessageReceiver on an asynchronous message delivery create an implementation of this interface and register it with the MessageReceiver using the setMessageHandler() method of MessageReceiver:

     public interface MessageHandler {        //Use my asynchronous message handler for notification        public void handleMessage(String message);     } 

The MessagePanel Class

This class extends javax.swing.JPanel and provides the users with facilities for sending and viewing messages:

     import javax.swing.JTextArea;     import javax.swing.JTextField;     import javax.swing.JButton;     import javax.swing.JPanel;     import javax.swing.JScrollPane;     import java.awt.FlowLayout;     import java.awt.BorderLayout;     import java.awt.event.ActionEvent;     import java.awt.event.ActionListener;     import javax.jms.JMSException; 

This class implements the MessageHandler interface and is initialized with instances of a MessageSender, a MessageReceiver, and the name of the user who is currently using the application:

     public class MessagePanel extends JPanel implements MessageHandler {        private JTextArea messageArea;        private JTextField messageField;        private JButton sendButton;        private MessageSender sender;        private String user;        public MessagePanel (MessageSender sender,           MessageReceiver receiver, String user) { 

The class registers itself as the MessageHandler for the MessageReceiver and the MessageReceiver will call this class's handleMessage() callback method whenever it receives a message asynchronously. The callback method will update the display with the newly received message:

           receiver.setMessageHandler(this);           this.sender = sender;           this.user = user;           buildUI();        }        //Callback method for asynchronous message delivery        public void handleMessage(String message) {           messageArea.append ("\r\n" + message);        } 

This panel has a non-editable text area for displaying the messages and a text field for typing the messages to be sent. The panel also provides a send button for sending the messages:

     private void buildUI() { 

Create a text area to display the incoming messages and set it to non-editable:

        messageArea = new JTextArea(25,25);        messageArea.setEditable(false); 

Create a text field to enter outgoing messages:

        messageField = new JTextField(50); 

Create a button to send the message:

        sendButton = new JButton("send"); 

Create an instance of javax.swing.JPanel and set the layout to FlowLayout where the components are laid on the panel from right to left. Add the text field and button to the panel:

            JPanel bottomPanel = new JPanel();            bottomPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));            bottomPanel.add(messageField);            bottomPanel.add(sendButton); 

Set the layout of the root panel to BorderLayout and add the panel created in the last step to the bottom of the root panel. Add the text area to a scroll-pane so that it can be scrolled and add the scroll-pane to the center of the root panel:

            setLayout(new BorderLayout());            add(bottomPanel, BorderLayout.SOUTH);            add(new JScrollPane(messageArea), BorderLayout.CENTER); 

The send button has an anonymous inner class as its action listener and whenever the button is clicked it will append the message in the text area to the name of the user and send the message using the MessageSender:

            sendButton.addActionListener(               new ActionListener() {                  public void actionPerformed(ActionEvent e) {                     if(!messageField.getText().equals("")) {                        try {                           sender.sendMessage(user + ": " +                              messageField.getText());                        } catch(JMSException ex) {                           ex.printStackTrace();                           throw new RuntimeException(ex.getMessage());                        }                     }                  }               }            ) ;        }     } 

The ChatApp class

This is the main class for the application:

     import javax.swing.JFrame;     import javax.jms.Topic;     import javax.jms.TopicConnectionFactory;     import javax.jms.TopicConnection;     import javax.jms.TopicSession;     import javax.jms.JMSException;     import javax.naming.NamingException;     import java.awt.event.WindowAdapter;     import java.awt.event.WindowEvent;     public class ChatApp {        public static final String CTX_FACT =           "com.sun.jndi.fscontext.RefFSContextFactory";        public static final String PROV_URL = "file:C:\\temp";        public static final String TCF_NAME = "TCFactory";        public static final String TOPIC_NAME = "myTopic";        private TopicConnection conn; 

If no user name is given at the command line, "Anonymous" is used instead:

        public static void main(String args[]) {           try {              String user = "Anonymous";              if(args.length > 0) {                 user = args[0];              }              new ChatApp().init(user);           } catch(NamingException e) {              e.printStackTrace();              System.exit(0);           } catch(JMSException e) {              e.printStackTrace();              System.exit(0);           }        } 

This class looks up the topic connection factory and topic using JNDIService and creates a topic connection and a topic session:

        private void init(String user) throws NamingException, JMSException {           JNDIService.init(CTX_FACT, PROV_URL);           TopicConnectionFactory tcf =              (TopicConnectionFactory)JNDIService.lookup(TCF_NAME);           Topic topic = (Topic)JNDIService.lookup(TOPIC_NAME);           conn = tcf.createTopicConnection();           TopicSession sess = conn.createTopicSession(false,                 TopicSession.AUTO_ACKNOWLEDGE); 

The topic and topic session are used to initialize instances of MessageSender and MessageReceiver:

           MessageSender sender = new MessageSender(topic, sess);           MessageReceiver receiver = new MessageReceiver(topic, sess); 

A new instance of a javax.swing.JFrame is created:

           JFrame frame = new JFrame("Chat: " + user);           frame.addWindowListener(              new WindowAdapter() {                 public void windowClosing(WindowEvent e) {                    try {                       conn.close();                    } catch(JMSException ex) {                       ex.printStackTrace();                    } finally {                       System.exit(0) ;                    }                 }              }           ) ; 

The instances of MessageSender and MessageReceiver along with the user name passed in as a command-line argument are used to initialize an instance of MessagePanel. The instance of MessagePanel is added to the JFrame and the frame is made visible:

           MessagePanel panel = new MessagePanel(sender, receiver, user);           frame.getContentPane().add(panel);           frame.setSize(300,300);           frame.pack();           frame.show();           conn.start();        }     } 

Running the Application

To run the application, compile all the classes with the required JAR files in the classpath. All the classes can be compiled without any errors using the j2ee.jar file in the classpath. Run the configuration script to store the required administered objects and start the JMQ router. Invoke the JVM on ChatApp. class, passing a user name as a command-line argument with the following files in the classpath:

  • jmq.jar - this can be found in the lib directory of the JMQ installation.

  • providerutil. jar and fscontext. jar - these files can also be found in the lib directory of the JMQ installation. These files contain the JNDI SPI classes.

  • j2ee.jar

The screenshots below shows Fred and Barney chatting using our chat application:

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