import java.rmi.*; import java.rmi.server.*; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; /** * Java Chat - A simple chatroom application - Server Class. * * This class is the chat server and is responsible for * receiving new connections from users, registering the * new user in the collection hashtable and forwarding * all received messages to all current users, including * the sender of the message. * * @author Sean Handley * @version November, 2006 */ public class ChatServer extends UnicastRemoteObject implements IChatServer { private static final long serialVersionUID = -1979903245133596876L; private Hashtable<Integer,String> messages; private Integer key; private ArrayList<ICallback> clients; /** * Chat server constructor. * * @throws java.rmi.RemoteException */ public ChatServer() throws RemoteException { key = 0; messages = new Hashtable<Integer, String>(); clients = new ArrayList<ICallback>(); Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { exitProcedure(); } } ); } /* * Close the server gracefully. */ private void exitProcedure() { logoutAllClients(); } /* * Log out all clients currently connected. */ private synchronized void logoutAllClients() { Iterator it = clients.iterator(); while(it.hasNext()) { ICallback n = (ICallback)it.next(); try { n.forceLogout(); clients.remove(n); } catch(NullPointerException e) { //n might not exist any more... //if it doesn't, ignore - it should be removed by disconnect } catch(Exception e) { //at this point, just die... } } notify(); } /** * Main entry point for the chat server. Sets up the registry and binds * the server name. * * @param args */ public static void main (String args[]) { try { //Create a new chat server ChatServer cs = new ChatServer(); //Launch the registry - saves invoking it manually java.rmi.registry.LocateRegistry.createRegistry(1099); //bind the name into the registry and we're ready to go Naming.rebind("CHAT-SERVER", cs); } catch (java.net.MalformedURLException e) { System.err.println("Malformed URL for Chat Server name " + e.toString()); } catch (RemoteException e) { System.err.println("General Communication Error: " + e.toString()); } } /** * Get the latest message. * * @throws java.rmi.RemoteException * @return message */ public String getMessage() throws RemoteException { return messages.get(key -1); } /** * Check to see if a user exists in the clients collection. * * @param username * @return true if username is found in the clients list, false otherwise * @throws java.rmi.RemoteException */ public synchronized boolean checkUser(String username) throws RemoteException { Iterator it = clients.iterator(); Boolean found = false; while(it.hasNext()) { if(((ICallback)it.next()).getUsername().equalsIgnoreCase(username)) { found = true; break; } } notify(); return found; } /** * Check a username to see if it's in the clients collection. If not, * add it to the collection and proceed with communication, returning a zero * to indicate the user was not found in the collection. Otherwise, returns * a one to indicate the opposite. * * @throws java.rmi.RemoteException * @param cb the callback interface representing the client * @return true if connection was successful, false otherwise */ public synchronized boolean addClient(ICallback cb) throws RemoteException { Boolean found = checkUser(cb.getUsername()); notify(); if(!found) { clients.add(cb); notify(); return true; } else { notify(); return false; } } /** * Removes the supplied callback interface object from the clients list, * disconnecting it from future communications. * * @param cb the callback interface representing the client * @throws java.rmi.RemoteException */ public synchronized void removeClient(ICallback cb) throws RemoteException { Iterator it = clients.iterator(); while(it.hasNext()) { if(((ICallback)it.next()).getUsername().equalsIgnoreCase(cb.getUsername())) { clients.remove(cb); break; } } notify(); } /** * Announce the presence of a new user by sending a special message. * * @throws java.rmi.RemoteException * @param username */ public synchronized void announce(String username) throws RemoteException { sendMessage("*** " + username + " has just entered the room."); notify(); } /** * Announce the name change of a user by sending a special message. * * @param oldName * @param newName * @throws java.rmi.RemoteException */ public synchronized void announceNameChange(String oldName, String newName) throws RemoteException { sendMessage("*** " + oldName + " is now known as " + newName + "."); notify(); } /** * Indicates that a user has left by sending a special message. * * @param username * @throws java.rmi.RemoteException */ public synchronized void leave(String username) throws RemoteException { sendMessage("*** " + username + " has just left the room."); notify(); } /** * Retrieve a list of usernames from the clients collection. * * @return a list of current clients * @throws java.rmi.RemoteException */ public ArrayList<String> getUsers() throws RemoteException { Iterator it = clients.iterator(); ArrayList<String> clientsList = new ArrayList<String>(); while(it.hasNext()) { clientsList.add(((ICallback)it.next()).getUsername()); } return clientsList; } /** * Sends the message by adding it to the collection and then * invoking the callback method of each client currently * in the clients list. * * @param msg * @throws java.rmi.RemoteException */ public void sendMessage(String msg) throws RemoteException { //add the message to the collection addMessage(msg); distributeMessage(); } /* * Add the message to the messages collection. */ private synchronized void addMessage(String msg) { messages.put(key++, msg); notify(); } /* * Distribute the message to each client in the clients collection. */ private synchronized void distributeMessage() { Iterator it = clients.iterator(); while(it.hasNext()) { ICallback n = (ICallback)it.next(); try { n.doCallback(); } catch(NullPointerException e) { //n might not exist any more... //if it doesn't, ignore - it should be removed by disconnect } catch(Exception e) { System.err.println(e.toString()); } } notify(); } }