/*
 * Aleph Toolkit
 *
 * Copyright 1997,1998 Brown University, Providence, RI.
 * 
 *                         All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose other than its incorporation into a
 * commercial product is hereby granted without fee, provided that the
 * above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Brown University not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.
 * 
 * BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE FOR
 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package aleph.comm;

import aleph.Aleph;
import aleph.AlephDebug;
import aleph.Config;
import aleph.meter.Meter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

/**
 * The Aleph server must run at every host that participates in an Aleph
 * computation.  The server uses RMI to communicate with the console.
 **/ 
public class Server extends UnicastRemoteObject implements AlephServer {

  private PrintWriter log = null;
  private Runtime runtime = Runtime.getRuntime(); // get runtime environment
  private Vector childProcesses = new Vector();
  private int counter = 0;      // number of processes started
  private String longHostName;
  private String hostName;
  private static final boolean DEBUG = false; // Mayday, mayday!
  
  /**
   * name used in RMI registry
   **/
  private static final String NAME = "aleph/comm/Server";

  /**
   * Top-level code: creates and registers a Server.
   **/
  public static void main (String[] args) {
    // Create and install the security manager
    //    System.setSecurityManager(new RMISecurityManager());
    try {
      Aleph.setProperty("aleph.label", name());
      Server server = new Server();
    } catch (Exception e) {
      System.err.println("Exception starting Server: " + e);
      e.printStackTrace();
    }
  }

  /**
   * The only constructor.
   **/
  public Server () throws java.rmi.RemoteException {
    super();
    try {
      AlephDebug.setTitle(name());
      log = new PrintWriter(new FileWriter(new File(Config.logDir, name() + ".log")));
      log.println(Config.banner);
      log.println("Server started at " + new Date());
      sanityCheck();
      log.println("System properties:");
      System.getProperties().list(log);	// System properties
      log.println("Aleph properties:");
      Aleph.getProperties().list(log); // Aleph properties
      log.flush();
      Registry registry = null;
      try {
        registry = LocateRegistry.createRegistry(Config.PORT);
        log.println("Started rmiregistry at port " + Config.PORT);
      } catch (java.rmi.server.ExportException e) {
        registry = LocateRegistry.getRegistry(Config.PORT);
        log.println("Found running rmiregistry at port " + Config.PORT);
      }
      registry.rebind(NAME, this);
      log.println("Registered as\t" + NAME);
      log.flush();
    } catch (Exception e) {
      log.println(new Date() + "During Server intialization : " + e);
      log.close();              // cry like a rainstorm ...
      System.exit(-1);          // howl like the wind.
    }
  }

  /**
   * Shut down the server.
   * @see aleph.comm.message.ShutdownServer
   **/
  public synchronized void shutdown (String who)
                             throws java.rmi.RemoteException {
    // kill all child processes
    for (Enumeration e = childProcesses.elements(); e.hasMoreElements();)
      ((Process) e.nextElement()).destroy();
    log.println(new Date() + "\tShutdown requested by " + who);
    Meter.report(log);
    log.close();
    System.exit(0);
  }

  /**
   * Start up new PEs.
   * @see aleph.comm.message.StartPE
   **/
  public synchronized void startPE (Address parent,
                                    Address console,
                                    int index,
                                    int numPEs,
                                    String label,
                                    String[] args,
                                    Properties properties)
                             throws java.rmi.RemoteException {
    try {
      // start the specified number of PEs for this server
      Vector line = new Vector(16);// accumulate command here
      // profiling?
      if (properties.getProperty("aleph.profile") != null) {
        line.addElement(Config.pathJava_g); // profiling requires java_g
        File prof = new File(Config.logDir, label + ".prof");
        line.addElement("-prof:" + prof.toString());
      } else {			// java_g requested?
        if (properties.getProperty("aleph.java_g") != null) 
          line.addElement(Config.pathJava_g);
        else                    // regular java
          line.addElement(Config.pathJava);
      }
      /* pass on run-time environment from Client */
      for (Enumeration enum = properties.propertyNames(); enum.hasMoreElements();) {
        String key = (String) enum.nextElement();
        line.addElement("-D" + key + "=" + properties.getProperty(key));
      }

      /* console and parent addresses */
      line.addElement("-Daleph.console=" + console.toString());
      line.addElement("-Daleph.parent="  + parent.toString());

      /* index and group size */
      line.addElement("-Daleph.index="          + String.valueOf(index));
      line.addElement("-Daleph.numPEs="         + String.valueOf(numPEs));

      line.addElement("-Daleph.label="          + label);
      // Special JIT?
      String jit = properties.getProperty("aleph.compiler");
      if (jit != null)
        line.addElement("-Dcompiler=" + jit);
      /* our class name */
      line.addElement("aleph.PE");

      /* add user's args for good measure */
      for (int i = 0; i < args.length; i++)
        line.addElement(args[i]);

      String[] command = new String[line.size()];
      for (int i = 0; i < line.size(); i++)
        command[i] = (String) line.elementAt(i);

      log.print(new Date() + "\texec");
      for (int i = 0; i < command.length; i++)
        log.print(" " + command[i]);
      log.println();
      log.flush();

      // run the command and place it in our list of processes we started
      childProcesses.addElement(runtime.exec(command)); // exec the process

      log.println ("Exec complete");
      log.flush();
    } catch (Exception e) {
      log.println(e);
      e.printStackTrace();
      log.close();
      System.exit(-1);
    }
  }

  /**
   * Check that we are running on an approved host
   **/
  private void sanityCheck () {
    try {
      InetAddress localHost = InetAddress.getLocalHost();
      for (int i=0; i < Config.hosts.length; i++) {
        if (localHost.equals(InetAddress.getByName(Config.hosts[i])))
          return;
      }
      String warning = "WARNING: server not on host list";
      System.err.println(warning);
      log.println(warning);
      log.flush();
    } catch (Exception e) {
      Aleph.panic(e);
    }
  }

  /**
   * Return suggestive name for log and debugging files.
   **/
  private static String name () {
    String name = "server";
    try {
      name = "server@" +
        InetAddress.getLocalHost().getHostName() + // full host name
        "." + Config.PORT;	// server socket
    } catch (Exception e) {
      Aleph.panic(e);
    }
    return name;
  }

}

  
