/*
 * Aleph Toolkit
 *
 * Copyright 1998,1999 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;

import aleph.Aleph;
import aleph.comm.Address;
import aleph.comm.AlephServer;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.NoSuchElementException;
import java.util.Vector;


/**
 * A Host is a machine running the Aleph Server.  Multiple PEs can coexist on a
 * host.
 *
 * @author Maurice Herlihy
 * @date August 1998
 * @see aleph.CommunicationManager
 * @see java.io.serializable
 **/

public class Host implements Serializable {

  private static Vector hosts = new Vector();
  private static Host thisHost;

  private String label;         // human-readable
  private String name;          // for rmiregistry

  /**
   * Constructor.
   * @param hostName string name for host
   * @exception UnknownHostException if hostName can't be resolved
   **/
  public Host (String hostName) throws UnknownHostException {
    label = hostName + ":" + Config.PORT;
    name = "rmi://" +
      InetAddress.getByName(hostName).getHostName() + // long host name
      ":" +
      Config.PORT +
      "/aleph/comm/Server";
  }

  /**
   * start a new PE
   * @param parent Parent PE's address
   * @param console Console's address
   * @param index index of this PE in group
   * @param numPEs total number in group
   * @param label suggestive label
   * @param args user's args
   * @param properties caller's environment
   **/
  public void startPE (Address parent,
                       Address console,
                       int index,
                       int numPEs,
                       String label,
                       String[] args,
                       Properties properties) {
    try {
    AlephServer server = getServer();
    if (server != null)
      server.startPE(parent, console, index, numPEs, label, args, properties);
    else
      Aleph.panic("No server running at " + name);
    } catch (java.rmi.RemoteException e) {
      Aleph.panic("No server running at " + name);
    }
  }


  /**
   * @return whether a host seems to be running the server.
   **/
  public boolean ping () {
    try {
      Naming.lookup(name);
      return true;
    } catch (Exception e) {
      return false;
    }
  }

  /**
   * Shuts down a server.
   * @return whether shutdown is confirmed.
   * @exception SecurityException If caller is not allowed to kill this server.
   **/
  public boolean stop () throws SecurityException {
    try {			// croak on unexpected exceptions
      AlephServer server = getServer();
      if (server != null) {
        server.shutdown(InetAddress.getLocalHost().getHostName());
        return true;            // no problem
      } else {
        return false;           // something happened
      }
    } catch (java.rmi.UnmarshalException e) { // server shut down too fast
      return true;
    } catch (SecurityException e) {
      throw e;
    } catch (Exception e) {
      return false;
    }
  }

  public String toString () {
    return label;
  }
    
  public int hashCode () {
    return name.hashCode();
  }

  public boolean equals (Object anObject) {
    if ((anObject != null) && (anObject instanceof Host)) {
      Host other = (Host) anObject;
      return (other.name.equals(this.name));
    } else {
      return false;
    }
  }

  /**
   * @return the host on which we are runnin
   **/
  public static Host thisHost () {
    return thisHost;
  }

  public static Enumeration allHosts () {
    return hosts.elements();
  }

  /**
   * @return Enumerate hosts in round-robin order.
   **/
  public static Enumeration roundRobin () {
    return new Enumeration() {	// Anonymous enumerator class
      Enumeration e = hosts.elements();
      public boolean hasMoreElements() {
	return true;
      }
      public Object nextElement() throws NoSuchElementException {
	if (! e.hasMoreElements()) // got milk?
	  e = hosts.elements();
	return e.nextElement();
      }
    };
  }

  private AlephServer getServer () throws java.rmi.RemoteException {
    try {
      return (AlephServer) Naming.lookup(name);
    } catch (Exception e) {
      return null;
    }
  }

  static {
    // transform Config.hosts String[] into Vector of Host
    for (int i = 0; i < Config.hosts.length; i++) {
      try {
        Host host = new Host(Config.hosts[i]);
        hosts.addElement(host);
      } catch (UnknownHostException e) {
        System.err.println("Unknown host " + Config.hosts[i] + " in Config.hosts");
      }
    }
    /* compute my host */
    try {
      thisHost = new Host(InetAddress.getLocalHost().getHostName());
    } catch (UnknownHostException e) {
      System.err.println(e);
      System.exit(-1);		// too early for Aleph.panic
    }

    /* Does user want own host to be first? */
    try {
      if (Config.localHostFirst) {
        hosts.removeElement(thisHost); // remove if present
        hosts.insertElementAt(thisHost, 0); // make it first
      }
    } catch (Exception e) {
      System.err.println(e);
      System.exit(-1);
    }
  }

}

