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();
}
}