//     $Id: ServerRMI.java,v 3.8 1998/12/14 04:06:25 mk2z Exp $    
 ////////////////////////////////////////////////////////////////////////////
 //
 // File: ServerRMI.java
 //
 // Purpose: This is the main RMI module on the server side. On startup,
 //        it starts its own registry and binds itself into the registry.
 //        It also implements methods from the ServerRMI***Iface.
 //
 // Authors:
 //  Orim   Miro Kresonja  mk2z@virginia.edu
 //  txe    Travis Emmitt
 //  smg    Jimbo      smg9c@virginia.edu
 //
 // Modifications:
 //   02-NOV-1998  Orim  Initial creation
 //   09-NOV-1998  smg   Added automatic version header
 //   15-NOV-1998  Orim  Initial modifications for version 3.0
 //   17-NOV-1998  Orim  Final exception throwing fixes
 //   04-DEC-1998  Orim  New features for v4.0
 //   08-DEC-1998  Orim  Added state knowledge to ServerRMI
 //
 ////////////////////////////////////////////////////////////////////////////
 import java.rmi.*;
 import java.rmi.server.*;
 import java.rmi.registry.LocateRegistry;
 import java.util.*;
 public class ServerRMI
     extends UnicastRemoteObject
     implements ServerRMItoClientIface, ServerRMItoDemonIface
 {
   // Note: on change of the next two variables, see ClientRMI.java
   //       and DemonRMI.java
   private static final int    SERVER_PORT = 1202;
   private static final String SERVER_RMI_NAME = "ServerRMI";
   private static final int    FAILED = 0; // failed (password)
   // ServerRMI keeps the state of the simulation
   private static final int SIM_CHOOSEMODE = 0;
   private static final int SIM_WAITSUBMIT = 1;
   private static final int SIM_RUNNING    = 2;
   private static final int SIM_SHUTDOWN   = 3;
   private int sim_state = SIM_CHOOSEMODE;
   private static final int DEMON_ID   = 0;
   private static final int EXP_DEMONS = 1;
   private Connections connect_obj = null;
   private ServerEngine my_engine;
   ///////////////////////////////////////////////////////////////////
   //
   // ServerRMI [constructor] - Creates its own registry, and binds 
   // itself in there.
   //   
   ///////////////////////////////////////////////////////////////////
   public ServerRMI (ServerEngine engine, int port, int exp_clients)
   throws RemoteException {
     // Initialize variables
     my_engine = engine;
     try {
       connect_obj = new Connections(exp_clients, EXP_DEMONS);
       // Create and install the security manager
       System.setSecurityManager(new RMISecurityManager());
       Print("Creating registry");
       if (port == 0) {
     port = SERVER_PORT;
       }
       LocateRegistry.createRegistry(port);
       String location = "//:" + port + "/" + SERVER_RMI_NAME;
       Print ("Binding server to '" + location + "'");
       Naming.rebind (location, this);
       Print ("Binding done");
       // state = start of the entire simulation
       sim_state = SIM_CHOOSEMODE;
     }
     catch (Exception e) {
       Print ("Exception - " + e.getMessage());
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   //  ConnectClient - Implementation of a ServerRMItoClientIface method. 
   //  Client asks for the initial map, and is given one. 
   //   
   ///////////////////////////////////////////////////////////////////
   public Map ConnectClient ()
   throws RemoteException, Exception {
     try {
     return(my_engine.GetMap());
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   //  ConnectDemon - Implementation of a ServerRMItoDemonIface method. 
   //  Demon asks for the initial map, and is given one. 
   //   
   ///////////////////////////////////////////////////////////////////
   public Map ConnectDemon ()
   throws RemoteException, Exception {
     try {
     return(my_engine.GetMap());
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   //  RegisterClient - Implementation of a ServerRMItoClientIface method. 
   //  If that module is free, the server accepts the remote pointer 
   //  to a client, adds it to its list, and if that is the last module,
   //  notifies the ServerEngine that all the clients have reported in.
   //  Note: The clients are returned numbers starting with 0, increasing
   //  by 1 with each registration.
   //   
   ///////////////////////////////////////////////////////////////////
   public int RegisterClient (int module, ClientRMIIface obj)
   throws RemoteException, Exception {
     int password = FAILED;
     try {
     password=connect_obj.AddClient(module, obj);
     if (password != FAILED) {
        Print("Client registered for module "+(module));
        SendConnectionsToDemon(connect_obj);
     }
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(password);
   }
   ///////////////////////////////////////////////////////////////////
   //
   //  RegisterDemon - Implementation of a ServerRMItoDemonIface method. 
   //  A Demon is unconditionally registered (once). If there are other
   //  demons running, the newest one will not register.
   //   
   ///////////////////////////////////////////////////////////////////
   public int RegisterDemon (DemonRMIIface obj)
   throws RemoteException, Exception {
     int password = FAILED;
     try {
     password = connect_obj.AddDemon(DEMON_ID, obj);
     if (password != FAILED) {
            Print("Demon registered");
        // send connections, state_of_sim to demon
        SendConnectionsToDemon(connect_obj);
        SendStateToDemon(sim_state);
     }
     else {
        obj.ReceiveState(SIM_SHUTDOWN);
     }
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(password);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // ReceiveModule - Implementation of a ServerRMIIface method. 
   // Once the clients complete changing their own modules, they
   // pass them back to server in a following way: ClientEngine ->
   // ClientRMI -> ServerRMI -> ServerEngine. This Method acts as a 
   // bridge between the last two stations, ServerRMI (this class)
   // and ServerEngine (its UpdateMap method is called).
   // 
   ///////////////////////////////////////////////////////////////////
   public void ReceiveModule(Module m, int client_id)
   throws RemoteException, Exception {
     try {
     Print("Received Module from Client "+client_id);
     connect_obj.SetClientSubStatus(client_id, true);
     my_engine.UpdateMap(m, client_id);
     // Send status to demon
         SendConnectionsToDemon(connect_obj);
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // SubmitMode - accepts exec. mode from DemonRMI, passes them 
   // onto ServerEngine.
   // 
   ///////////////////////////////////////////////////////////////////
   public boolean SubmitMode(int mode, int runs)
   throws RemoteException, Exception {
     try {
     // change the simulation mode
     my_engine.SubmitMode(mode, runs);
     sim_state = SIM_WAITSUBMIT;
     // just in case, set everybody's submission to false
     connect_obj.SetAllSubmitFalse();
     // then we re-send the maps out
     //? Am I supposed to selectively do this (on Auto, do not resend?)
     Map m = my_engine.GetMap();
     SendMapAll(m);
     // send state to demon
     SendStateToDemon(sim_state);
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(true);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // StartRun - accepts parameters from DemonRMI, then starts the run
   // of the simulation. 
   // 
   ///////////////////////////////////////////////////////////////////
   public boolean StartRun (int propagation, long delay)
   throws RemoteException, Exception {
     try {
     // change the simulation status, run it
     sim_state = SIM_RUNNING;
     my_engine.BeginSimulation(propagation, delay);
     // send state to demon
     SendStateToDemon(sim_state);
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(true);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Abort - tells the server to stop the current run.
   // 
   ///////////////////////////////////////////////////////////////////
   public boolean Abort()
   throws RemoteException, Exception {
     try {
     sim_state = SIM_CHOOSEMODE;
     my_engine.Abort();
     // then we re-send the maps out
     Map m = my_engine.GetMap();
     SendMapAll(m);
     // send state to demon
     SendStateToDemon(sim_state);
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(true);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // Shutdown - tells all clients to shutdown.
   // 
   ///////////////////////////////////////////////////////////////////
   public boolean Shutdown()
   throws RemoteException, Exception {
     ClientRMIIface client_iface = null;
     try {
     int num_exp = connect_obj.GetNumClients();
     for (int i=0; i<num_exp; ++i) {
        if (connect_obj.GetClientConnStatus(i)) {
         client_iface=connect_obj.GetClientIface(i);
         client_iface.Shutdown();
        }
     }
     sim_state = SIM_SHUTDOWN;
     SendStateToDemon(sim_state);
     my_engine.ShutDown();
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
     return(true);
   }
   ///////////////////////////////////////////////////////////////////
   //
   // ClientQuit - tells the server a client is going away. The server
   // updates its Connections object of this.
   // 
   ///////////////////////////////////////////////////////////////////
   public void ClientQuit (int client_id, int password)
   throws RemoteException, Exception {
     try {
     if (connect_obj.RemoveClient(client_id, password)) {
        Print("Client "+client_id+" unregistered");
        SendConnectionsToDemon(connect_obj);
     }
     else {
        Print("Client "+client_id+" tried to unregister");
     }
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //
   // DemonQuit - tells the server the demon is going away. The server
   // updates its Connections object.
   // 
   ///////////////////////////////////////////////////////////////////
   public void DemonQuit (int password)
   throws RemoteException, Exception {
     try {
     if (connect_obj.RemoveDemon(DEMON_ID, password)) {
        Print("Demon unregistered");
     }
     else {
        Print("Demon tried to unregister");
     }
     }
     catch (Exception e) {
     my_engine.Error(e);
     throw (e);
     }
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendMapAll - invoked internally. It gets the full Map as a 
   // parameter and sends this map to all clients, and the demon. 
   // 
   ///////////////////////////////////////////////////////////////////
   public void SendMapAll(Map m)
   throws RemoteException, Exception {
     // send maps to clients, then demon(s)
     int num_exp = connect_obj.GetNumClients();
     for (int i=0; i<num_exp; ++i) {
        SendMapClient(m, i);
     }
     num_exp = connect_obj.GetNumDemons();
     for (int i=0; i<num_exp; ++i) {
        SendMapDemon(m, i);
     }
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendMapClient - invoked by ServerEngine. It gets the full Map and 
   // the number of a client to send this map to. An appropriate call is
   // made to ClientRMI.
   // 
   ///////////////////////////////////////////////////////////////////
   public int SendMapClient(Map m, int client_id)
   throws Exception, RemoteException {
     ClientRMIIface client_iface;
     if (connect_obj.GetClientConnStatus(client_id)) {
         client_iface = connect_obj.GetClientIface(client_id);
         client_iface.ReceiveMap (m);
          return(1);
     }
     else {
         return(0);
     }
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendMapDemon - invoked by ServerEngine. It gets the full Map and 
   // sends it to the demon (via DemonRMI). 
   // 
   ///////////////////////////////////////////////////////////////////
   public int SendMapDemon(Map m, int demon_id)
   throws RemoteException, Exception {
     DemonRMIIface demon_iface;
     if (connect_obj.GetDemonConnStatus(demon_id)) {
         demon_iface = connect_obj.GetDemonIface(demon_id);
         demon_iface.ReceiveMap (m);
         return(1);
     }
     else {
         return(0);
     }
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendMapDemon - invoked by ServerEngine. It gets the full Map and 
   // sends it to the main demon (via DemonRMI). 
   // 
   ///////////////////////////////////////////////////////////////////
   public int SendMapDemon(Map m)
   throws RemoteException, Exception {
     return(SendMapDemon(m, DEMON_ID));
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendStatsClient - invoked by ServerEngine. It gets the Stats object 
   // and sends it to the specified client (via ClientRMI). 
   // 
   ///////////////////////////////////////////////////////////////////
   public int SendStatsClient(Stats stats, int client_id)
   throws RemoteException, Exception {
     if (connect_obj.GetClientConnStatus(client_id)) {
         ClientRMIIface client_iface = connect_obj.GetClientIface(client_id);
         client_iface.ReceiveStats (stats);
         return(1);
     }
     else {
         return(0);
     }
   }
   ///////////////////////////////////////////////////////////////////
   // 
   // SendStatsDemon - invoked by ServerEngine. It gets the Stats object 
   // and sends it to the demon (via DemonRMI). 
   // 
   ///////////////////////////////////////////////////////////////////
   public int SendStatsDemon(Stats stats)
   throws Exception, RemoteException {
     if (connect_obj.GetDemonConnStatus(DEMON_ID)) {
         DemonRMIIface demon_iface = connect_obj.GetDemonIface(DEMON_ID);
         demon_iface.ReceiveStats (stats);
         return(1);
     }
     else {
         return(0);
     }
   }
   ///////////////////////////////////////////////////////////////////
   //   
   //   RunEnded - This method exists for the serverengine to notify 
   //   the ServerRMI when the simulation has ended and last maps 
   //   have been sent out.
   //   
   ///////////////////////////////////////////////////////////////////
   public void RunEnded ()
   throws Exception, RemoteException {
     // just in case, set everybody's submission to false
     connect_obj.SetAllSubmitFalse();
     sim_state = SIM_CHOOSEMODE;
     SendStateToDemon(sim_state);
     SendConnectionsToDemon(connect_obj);
   }
   ///////////////////////////////////////////////////////////////////
   //   
   //   SendConnectionsToDemon - sends the connection object 
   //   explicitly to the demon. 
   //   
   ///////////////////////////////////////////////////////////////////
   public void SendConnectionsToDemon(Connections obj)
   throws Exception, RemoteException {
     int num_demons = connect_obj.GetNumDemons();
     for (int i=0; i<num_demons; ++i) {
            if (connect_obj.GetDemonConnStatus(i)) {
         DemonRMIIface demon_rmi = connect_obj.GetDemonIface(i);
         demon_rmi.ReceiveClientConnectStatus(obj);
            }
     }
    }
   ///////////////////////////////////////////////////////////////////
   //   
   //   SendStateToDemon - sends the state of the simulation explicitly
   //   to the demon. This is done either on registration or on state
   //   change.
   //   
   ///////////////////////////////////////////////////////////////////
   private void SendStateToDemon (int state)
   throws RemoteException, Exception {
     int num_demons = connect_obj.GetNumDemons();
     for (int i=0; i<num_demons; ++i) {
            if (connect_obj.GetDemonConnStatus(i)) {
         DemonRMIIface demon_iface = connect_obj.GetDemonIface(i);
                demon_iface.ReceiveState (state);
            }
     }
   }
   ///////////////////////////////////////////////////////////////////
   //   
   //   Print - ServerRMI's own method, receiving a string from within
   //   ServerRMI, and in turn hands it off to ClientEngine for display.
   //   
   ///////////////////////////////////////////////////////////////////
   private void Print (String s) {
       my_engine.PrintMessage("\tRMI: "+s);
   }
 }
 ////////////////////////////////////////////////////////////////////////////