import java.io.*; import java.net.*; import java.rmi.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import com.oreilly.servlet.RemoteDaemonHttpServlet; public class ChatServlet extends RemoteDaemonHttpServlet implements ChatServer { // source acts as the distributor of new messages MessageSource source = new MessageSource(); // socketClients holds references to all the socket-connected clients Vector socketClients = new Vector(); // rmiClients holds references to all the RMI clients Vector rmiClients = new Vector(); // doGet() returns the next message. It blocks until there is one. public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); PrintWriter out = res.getWriter(); // Return the next message (blocking) out.println(getNextMessage()); } // doPost() accepts a new message and broadcasts it to all // the currently listening HTTP and socket clients. public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // Accept the new message as the "message" parameter String message = req.getParameter("message"); // Broadcast it to all listening clients if (message != null) broadcastMessage(message); // Set the status code to indicate there will be no response res.setStatus(res.SC_NO_CONTENT); } // getNextMessage() returns the next new message. // It blocks until there is one. public String getNextMessage() { // Create a message sink to wait for a new message from the // message source. return new MessageSink().getNextMessage(source); } // broadcastMessage() informs all currently listening clients that there // is a new message. Causes all calls to getNextMessage() to unblock. public void broadcastMessage(String message) { // Send the message to all the HTTP-connected clients by giving the // message to the message source source.sendMessage(message); // Directly send the message to all the socket-connected clients Enumeration enum = socketClients.elements(); while (enum.hasMoreElements()) { Socket client = null; try { client = (Socket)enum.nextElement(); PrintStream out = new PrintStream(client.getOutputStream()); out.println(message); } catch (IOException e) { // Problem with a client, close and remote it try { if (client != null) client.close(); } catch (IOException ignored) { } socketClients.removeElement(client); } } // Directly send the message to all RMI clients enum = rmiClients.elements(); while (enum.hasMoreElements()) { ChatClient chatClient = null; try { chatClient = (ChatClient)enum.nextElement(); chatClient.setNextMessage(message); } catch (RemoteException e) { // Problem communicating with a client, remove it deleteClient(chatClient); } } } protected int getSocketPort() { // We listen on port 2428 (look at a phone to see why) return 2428; } public void handleClient(Socket client) { // We have a new socket client. Add it to our list. socketClients.addElement(client); } public void addClient(ChatClient client) { // We have a new RMI client. Add it to our list. rmiClients.addElement(client); } public void deleteClient(ChatClient client) { // Remote the specified client from our list. rmiClients.removeElement(client); } } // MessageSource acts as the source for new messages. // Clients interested in receiving new messages can // observe this object. class MessageSource extends Observable { public void sendMessage(String message) { setChanged(); notifyObservers(message); } } // MessageSink acts as the receiver of new messages. // It listens to the source. class MessageSink implements Observer { String message = null; // set by update() and read by getNextMessage() // Called by the message source when it gets a new message synchronized public void update(Observable o, Object arg) { // Get the new message message = (String)arg; // Wake up our waiting thread notify(); } // Gets the next message sent out from the message source synchronized public String getNextMessage(MessageSource source) { // Tell source we want to be told about new messages source.addObserver(this); // Wait until our update() method receives a message while (message == null) { try { wait(); } catch (Exception ignored) { } } // Tell source to stop telling us about new messages source.deleteObserver(this); // Now return the message we received // But first set the message instance variable to null // so update() and getNextMessage() can be called again. String messageCopy = message; message = null; return messageCopy; } }