/*
 * Aleph Toolkit
 *
 * Copyright 1997, 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.thread;

import aleph.Aleph;
import aleph.AlephDebug;
import aleph.Config;
import aleph.Message;
import aleph.PE;
import aleph.comm.Address;
import aleph.comm.CommunicationManager;
import aleph.dir.DirectoryManager;
import aleph.trans.TransactionManager;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.IllegalArgumentException;
import java.lang.Thread;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.Vector;

/**
 * This class assigns worker threads to low-priority jobs, and hunts down user
 * applications.
 *
 * @author  Maurice Herlihy
 * @date    September 1998
 **/

public class Scheduler {

  private static final boolean DEBUG = false; // mistakes were made ...

  private static Scheduler theScheduler = null;	// singleton

  private MessageQueue messageQueue;

  public static Scheduler getScheduler() {
    if (theScheduler == null)
      theScheduler = new Scheduler();
    return theScheduler;
  }

  /**
   * Constructor.
   **/
  private Scheduler () {
    messageQueue = new MessageQueue();
    // Start worker threads
    for (int i = 0; i < Config.numWorkers; i++) {
      Worker worker = new Worker();
      worker.setDaemon(true);
      worker.start();
    }
  }

  /**
   * Execute user's app.
   * @param args user's command.
   **/
  public void exec (String[] args){
    (new UserApp(args)).start();
  }

  /**
   * Schedules Asynchronous messages for execution.
   * @param message schedule this message
   **/
  public void schedule (Message message) {
    messageQueue.enq(message);
  }

  /**
   * Inner class representing user's app.
   **/
  private class UserApp extends Thread {
    String[] args;
    // constructor
    UserApp(String[] args) {
      this.args = args;
    }
    // do it
    public void run () {
      // Did user forget to specify a program?
      if (args.length == 0) {
	System.err.println("No user program specified");
	Aleph.exit();
      }
      if (DEBUG)
	AlephDebug.println("Scheduler looking for " + args[0]);
      Class user_class = null;
      try {
	// Java convention: arguments start at position 0.
	String[] userArgs = new String[args.length-1];
	// Shift args left by one to eliminate program name.
	System.arraycopy(args, 1, userArgs, 0, userArgs.length);
	// Look for user's class.
	user_class = Class.forName(args[0]);// args[0] is name of user's program.
	// Look for user's main method.
	Class[]  arg_classes = {userArgs.getClass()};
	Object[] arg_objects = {userArgs};
	// call the method
	(user_class.getMethod("main", arg_classes)).invoke(null, arg_objects);

      } catch (NoClassDefFoundError e) {
	System.err.println("Error: could not find class " + args[0]);
      } catch (ClassNotFoundException e) {
	System.err.println("Error: could not find class " + args[0]);
      } catch (NoSuchMethodException e) { // no main program
	System.err.println("In " + user_class +
			   ": void main(String[] argv) is not defined");
      } catch (IllegalAccessException e) { // malformed main program
	System.err.println("In " + user_class +
			   ": main must be public and static");
      } catch (InvocationTargetException e) {
	System.err.println(args[0] + ".main() throws exception " + e.getTargetException());
      } catch (IllegalArgumentException e) {
	System.err.println("Could not make sense out of " + args[0]);
      } catch (Exception e) {
	Aleph.panic("Exception in Scheduler: " + e);
      } catch (Error e) {
	Aleph.panic("Error in Scheduler: " + e);
      } finally {		// quit when user returns
	Aleph.exit();
      }
    }
  }

  /**
   * Inner class that implements worker thread.
   **/
  private class Worker extends Thread {
   public void run () {
     while (true) {
       messageQueue.deq().run();
     }
   }
  }
}
