13.7 Chat Program

 < Day Day Up > 



13.7 Chat Program

We now use RMI and the ability to send objects or object references to write a simple chat program. This chat program allows multiple users to connect to a chat server; when any user types text in the text box and hits the "Send" button, the text will be sent to the server, and the server will then send the message out to all the users currently registered with the chat server. The design of this server requires that some objects must be migrating and some non-migrating. For example, the text string that the user types in must be able to transfer to the server and then to each of the clients; hence, it must be a migrating object. The chat client, however, will register with the chat server but will run on the client computer; hence, it will be a non-migrating object. It will also require the use of the Java Event Model from Chapter 7 to implement the server, with a slight twist to take care of a possible deadlock situation.

13.7.1 The Chat Server

The chat server will be presented first, as it is the heart of the system. Also, because it uses interfaces to interact with the chat clients, it is really a separate entity, and the design of the interfaces it uses will drive the design of the chat client. Because the chat server is simply an implementation of the Java Event Model, it draws upon just three methods to deal with requests from clients: add listeners, remove listeners, and process the events. One main method is used to create the object and register it with rmiregistry. However, to be a true event program, it also must define the listener and the event. Also, to implement RMI, the remote interface must be defined. The listener, a ChatListener, is shown in Exhibit 15 (Program13.4a), the ChatEvent is shown in Exhibit 16 (Program13.4b), and the ChatServer interface is shown in Exhibit 17 (Program13.4c). Implementation of the ChatServer is shown in Exhibit 18 (Program13.4d).

Exhibit 15: Program13.4a: The ChatListener

start example

 interface ChatListener extends java.rmi.Remote, java.util.EventListener {   public void chatEventSent(ChatEvent cme)     throws java.rmi.RemoteException; } 

end example

Exhibit 16: Program13.4b: The ChatServer Interface

start example

 public interface ChatServer extends java.rmi.Remote {   public void addChatListener(ChatListener A)     throws java.rmi.RemoteException;   public void removeChatListener(ChatListener cl)     throws java.rmi.RemoteException;   public void sendChatMessage(String message)     throws java.rmi.RemoteException; } 

end example

Exhibit 17: Program13.4c: The ChatEvent

start example

 public class ChatEvent extends java.util.EventObject {   String message;   public ChatEvent(Object source, String message) {     super(source);   this.message = new String(message);   }   public String getMessage() {     return message;   } } 

end example

Exhibit 18: Program13.4d: The ChatServer

start example

 import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.util.*; public class ChatServerImp extends UnicastRemoteObject     implements ChatServer {   Vector chatListeners = new Vector();   public ChatServerImp() throws RemoteException {   }   public void addChatListener(ChatListener cl)       throws java.rmi.RemoteException {     System.out.println("Adding Listener");     chatListeners.add(cl);   }   public void removeChatListener(ChatListener cl)       throws java.rmi.RemoteException {     System.out.println("Removing Listener");     chatListeners.remove(cl);   }   public void sendChatMessage(String message)       throws java.rmi.RemoteException {     Vector v;     synchronized(this) {v = (Vector)chatListeners.clone();}   try {     for (Enumeration e = v.elements(); e.hasMoreElements();) {       ChatListener cl = (ChatListener)e.nextElement();       cl.chatEventSent(new ChatEvent(this, message));     }   } catch(Exception e) {     e.printStackTrace();   }   }   public static void main(String argv[]) {     try {       ChatServerImp thisExample = new ChatServerImp();       Naming.rebind("// localhost/ChatServer," thisExample);     } catch (Exception e) {        System.out.println("ChatServer err: "+ e.getMes- sage());        e.printStackTrace();     }   } } 

end example

This implementation is short and should be immediately recognizable to anyone who understood the Java Event Model that was implemented in Chapter 7. Listeners are added to the server using the addChatListener and removeChatListener methods. The method that processes the event in the server is sendChatMessage, which receives messages from the clients and sends the text out to all of the listeners using the chatEventSent method putting the text of the message in the event object that is sent.

It is interesting to note that the client program does not ever get a copy of the server; instead, all of the methods to be called are placed in an interface. This is different from how the Java Event Model is implemented in the Java AWT, where, for instance, a program would have direct access to the JButton and not an interface representing a JButton. However, this does not change how the event source is implemented and in fact helps to isolate the actual source from the client program.

The server should help to show that the models of components that have been developed so far in the text are applicable when new concurrency mechanisms, such as distributed programming, are used. The actual implementation details of the asynchronous activities (in this case, processes on a remote computer) and the details of how the objects are accessed (here, via skeleton and stub files) do not change the details of how the components work.

13.7.2 The Chat Client

Using the definition of the chat server from Section 13.7.1, the client is designed to meet the requirements of the event model it uses. First, a GUI is designed that allows the user to type messages that are sent to the server and to see the messages that are typed by the other users. Developing this GUI is relatively straightforward and is provided in Exhibit 19 (Program13.4e). Once the GUI is designed, messages must be sent to the server so that they can be relayed to all the ChatListeners registered with the server. To do this, the client program must retrieve the server from the rmiregistry and then register itself with the server. Care must be taken to ensure that all the objects are run on the correct computers, so the concept of migrating and nonmigrating object is very important. It is important that the programmer keep in mind each object, how it is used, and where it will be run.

Exhibit 19: Program13.4e: The ChatClient Program

start example

 import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.util.*; import java.io.*; import java.awt.event.*; import java.awt.*; import javax.swing.*; public class ChatClient extends UnicastRemoteObject      implements ChatListener, Serializable {   private ChatServer chatServer;   private TextArea messageBox;   public ChatClient() throws java.rmi.RemoteException {     createFrame();     try {       chatServer = (ChatServer)Naming.lookup(         "// localhost/ChatServer");       chatServer.addChatListener(this);     } catch(Exception e) {       e.printStackTrace();     }   }   public void createFrame() {     JFrame chatFrame = new JFrame("Chat Program");     Container chatContainer = chatFrame.getContentPane();     JScrollPane sp = new JScrollPane(messageBox = new TextArea         (40, 10));     messageBox.setEditable(false);     chatContainer.add(messageBox, BorderLayout.CENTER);     JPanel controlPanel = new JPanel();     final TextField inputText = new TextField(40);     controlPanel.add(inputText);     JButton sendButton = new JButton("Send");     sendButton.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         try {           chatServer.sendChatMessage(inputText.getText() +             "\n");         } catch (Exception ex) {           ex.printStackTrace();         }         inputText.setText("");       }     });     controlPanel.add(sendButton);     JButton exitButton = new JButton("Exit");     exitButton.addActionListener(new ActionListener() {       public void actionPerformed(ActionEvent e) {         try {           chatServer.removeChatListener(ChatClient.this);           System.exit(0);         } catch (Exception ex) {           ex.printStackTrace();         }       }     });     controlPanel.add(exitButton);     chatContainer.add(controlPanel, BorderLayout.SOUTH);     chatFrame.setSize(500,400);     chatFrame.setVisible(true);   }   public void chatEventSent(ChatEvent ce)       throws java.rmi.RemoteException {     messageBox.append(ce.getMessage());   }   public static void main(String argv[]) {     try {       ChatClient chatClient = new ChatClient();     } catch (Exception e) {       e.printStackTrace();     }   } } 

end example

Because the client is the interface for the user, it must stay on the user's computer; thus, the chat client must be a non-migrating object that registers with the server. The actual message that is sent, however, is just a text string and thus is a migrating object that is sent to the server. The event that is sent to each listener should once again be a migrating object that is unwrapped on the client computer.

This all needs to be accounted for in the design. Note that some of this was part of the server's event model design. For example, the ChatListener interface extends java.rmi.Remote. The following table outlines what must be done for each class in this system to make it work:

What Must Be Extended or Implemented

Affected Classes or Interfaces

java.rmi.Remote

ChatServer, ChatListener

java.rmi.UnicastRemoteObject

ChatServerImp, ChatClient

java.io.serializable

ChatEvent

To make this program work, the following files must be on the client:

  • ChatListener.class

  • ChatServer.class

  • ChatEvent.class

  • ChatServerImp_Stub.class

  • ChatClient_Stub.class

  • ChatClient_Skel.class

and the following files must be on the server:

  • ChatServer.class

  • ChatListener.class

  • ChatServerImp.class

  • ChatServerImp_Stub.class

  • ChatServerImp_Skel.class

  • ChatClient_Stub.class

When these files are in place, rmiregistry is run from the ChatServerImp directory, and the ChatServerImp is run, followed by the Chat Client. This will bring up the chat program correctly. Note that multiple clients can be run with a single server.

This should make it plain that care must be taken in designing distributed components. If a programmer is simply hacking to implement the component and not using simple and tested methods, it is likely that other details will be missed, such as what parts of the model run on what computers and what race conditions or deadlock conditions are possible. That this can happen is illustrated by the fact that this simple chat program has a fairly obvious deadlock condition in it. If the actionListener for the "Send" button and the chatEventSent method both have a synchronized lock on the same object, the program will deadlock. Resolving this deadlock is left for an exercise at the end of the chapter.



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

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