Chapter 10. Building Collaborative ApplicationsContents:In the previous chapter we built up a set of base classes for building collaborative applications, in both RMI and message-passing forms. In this chapter, we'll put those classes to work, using them to build some collaborative applications: a simple chat system and a shared whiteboard. We'll only be building applications based on the RMI version of our collaborative framework, but the mapping to message-passing versions is straightforward. 10.1. A Simple Chat SystemIt's a pretty simple matter to build a basic chat system on top of our base classes. All we need to do is write a subclass of our collaborator that acts as a chat client, receiving messages from remote chat clients and displaying them in a text window next to their name. We can pull each client's name from its Identity. Example 10-1 shows an RMIChatClient based on our RMI collaborative system. The RMIChatClient extends the RMICollaboratorImpl class, and also implements the java.awt.event.ActionListener interface, so that it can act as a listener for its own AWT elements. This AWT interface includes a TextArea for showing the chat session, a TextField for the user to type in chat messages, and a Button to submit the messages to the chat server, which in our case is simply one of our RMIMediatorImpl objects routing messages to other RMIChatClients. The constructor for the RMIChatClient simply connects to the specified mediator, then initializes its graphical elements by calling its initGraphics() method. The initGraphics() method creates a Frame, inserts the TextArea, TextField, and Button in the correct locations, then registers itself as an ActionListener for the button. The RMIChatClient's actionPerformed() method, which is called whenever the "Send" button is pressed, simply gets the text in the TextField when the button is pressed, and broadcasts it to all the other chat clients by calling its broadcast() method with a message tag of "chat." It then clears the TextField to let the user type in the next message. The RMIChatClient also has a notify() implementation that accepts chat messages from the mediator, and writes them to the TextArea along with the name of the sender (from its Identity). Figure 10-1 shows the chat screen that a user would see. Figure 10-1. A chat client implemented using the RMICollaboratorExample 10-1. An RMI-Based Chat Clientpackage dcj.util.Collaborative; import java.awt.Frame; import java.awt.TextArea; import java.awt.TextField; import java.awt.Button; import java.awt.Label; import java.awt.event.*; import java.rmi.RemoteException; import java.util.Properties; import java.io.IOException; public class RMIChatClient extends RMICollaboratorImpl implements java.awt.event.ActionListener { TextArea chatArea; TextField chatInput; public RMIChatClient(String name, String host, String mname) throws RemoteException { super(name); Properties p = new Properties(); p.put("host", host); p.put("mediatorName", mname); connect(p); initGraphics(); } public boolean notify(String tag, String msg, Identity src) throws IOException, RemoteException { // Print the message in the chat area. chatArea.append("\n" + src.getName() + ": " + msg); return true; } protected void initGraphics() throws RemoteException { Frame f = new Frame(); f.setLayout(null); f.addNotify(); f.setSize(f.getInsets().left + 405, f.getInsets().top + 324); chatArea = new java.awt.TextArea(); chatArea.setBounds(f.getInsets().left, f.getInsets().top,405, 300); f.add(chatArea); chatInput = new java.awt.TextField(); chatInput.setBounds(f.getInsets().left + 84, f.getInsets().top + 300,264,24); f.add(chatInput); Button button = new java.awt.Button("Send"); button.setBounds(f.getInsets().left + 348, f.getInsets().top + 300,60,24); f.add(button); button.addActionListener(this); Label label = new java.awt.Label("Chat here:"); label.setBounds(f.getInsets().left,f.getInsets().top + 300,84,24); label.setAlignment(label.RIGHT); f.add(label); f.setTitle("RMI Chat Client"); f.show(); } public void actionPerformed(ActionEvent e) { // See if there's something to say... String msg = chatInput.getText(); if (msg.length() > 0) { try { // Broadcast our message to the rest of the chat clients boolean success = broadcast("chat", msg); if (success) { System.out.println("Sent message OK."); } else { System.out.println("Failed to send message."); } // Clear the chat input field chatInput.setText(""); } catch (Exception exc) { } } } } Our RMIChatClient's notify() method doesn't include any synchronized code blocks. Since each chat client connects to a single mediator, and since the mediator synchronizes all of its message-routing functions at any given time, there's only one thread that could be making remote calls to the chat client's notify() method. This chat system was simple to implement because many of the really hard issues with collaborative systems are not a problem here. The data passed between the agents is small and simple ( just text strings), so communication performance shouldn't be a problem. Every message is broadcast to every other chat client, so flexible communications aren't an issue either. And while there is a kind of shared "state" between the chatting agents (the list of chat messages that each displays), it isn't a problem keeping this shared state consistent. Each chat client sends its chat messages using the broadcast() method, but doesn't display the message locally until it's received back from the chat mediator. This way all messages displayed in each client's chat window are synchronized, because they all pass through the mediator and are sent in the same order to each chat client. Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|