RemoteExperiment.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 25k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    RemoteExperiment.java
  18.  *    Copyright (C) 2000 Mark Hall
  19.  *
  20.  */
  21. package weka.experiment;
  22. import weka.core.SerializedObject;
  23. import weka.core.OptionHandler;
  24. import weka.core.Utils;
  25. import weka.core.Option;
  26. import weka.core.FastVector;
  27. import weka.core.Queue;
  28. import java.rmi.*;
  29. import java.io.Serializable;
  30. import java.io.File;
  31. import java.io.IOException;
  32. import java.io.Reader;
  33. import java.io.FileReader;
  34. import java.io.BufferedReader;
  35. import java.lang.reflect.Array;
  36. import java.util.Enumeration;
  37. import java.util.Vector;
  38. import java.beans.PropertyDescriptor;
  39. import javax.swing.DefaultListModel;
  40. import java.io.FileInputStream;
  41. import java.io.ObjectInputStream;
  42. import java.io.BufferedInputStream;
  43. import java.io.FileOutputStream;
  44. import java.io.BufferedOutputStream;
  45. import java.io.ObjectOutputStream;
  46. import java.io.BufferedOutputStream;
  47. /**
  48.  * Holds all the necessary configuration information for a distributed
  49.  * experiment. This object is able to be serialized for storage on disk.<p>
  50.  * 
  51.  * This class is experimental at present. Has been tested using 
  52.  * CSVResultListener (sending results to standard out) and 
  53.  * DatabaseResultListener (InstantDB + RmiJdbc bridge). <p>
  54.  *
  55.  * Getting started:<p>
  56.  *
  57.  * Start InstantDB (with the RMI bridge) on some machine. If using java2
  58.  * then specify -Djava.security.policy=db.policy to the
  59.  * virtual machine. Where db.policy is as follows: <br>
  60.  * <pre>
  61.  * grant {
  62.  *   permission java.security.AllPermission;
  63.  * };
  64.  * </pre><p>
  65.  *
  66.  * Start RemoteEngine servers on x machines as per the instructons in the
  67.  * README_Experiment_Gui file. There must be a 
  68.  * DatabaseUtils.props in either the HOME or current directory of each
  69.  * machine, listing all necessary jdbc drivers.<p>
  70.  *
  71.  * The machine where a RemoteExperiment is started must also have a copy
  72.  * of DatabaseUtils.props listing the URL to the machine where the 
  73.  * database server is running (RmiJdbc + InstantDB). <p>
  74.  *
  75.  * Here is an example of starting a RemoteExperiment: <p>
  76.  *
  77.  * <pre>
  78.  *
  79.  * java -Djava.rmi.server.codebase=file:/path to weka classes/ 
  80.  * weka.experiment.RemoteExperiment -L 1 -U 10 
  81.  * -T /home/ml/datasets/UCI/iris.arff 
  82.  * -D "weka.experiment.DatabaseResultListener" 
  83.  * -P "weka.experiment.RandomSplitResultProducer" 
  84.  * -h rosebud.cs.waikato.ac.nz -h blackbird.cs.waikato.ac.nz -r -- 
  85.  * -W weka.experiment.ClassifierSplitEvaluator -- 
  86.  * -W weka.classifiers.NaiveBayes
  87.  *
  88.  * </pre> <p>
  89.  * The "codebase" property tells rmi where to serve up weka classes from.
  90.  * This can either be a file url (as long as a shared file system is being
  91.  * used that is accessable by the remoteEngine servers), or http url (which
  92.  * of course supposes that a web server is running and you have put your
  93.  * weka classes somewhere that is web accessable). If using a file url the
  94.  * trailing "/" is *most* important unless the weka classes are in a jar
  95.  * file. <p>
  96.  *
  97.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  98.  * @version $Revision: 1.9 $
  99.  */
  100. public class RemoteExperiment extends Experiment {
  101.   /** The list of objects listening for remote experiment events */
  102.   private FastVector m_listeners = new FastVector();
  103.   /** Holds the names of machines with remoteEngine servers running */
  104.   protected DefaultListModel m_remoteHosts = new DefaultListModel();
  105.   
  106.   /** The queue of available hosts */
  107.   private Queue m_remoteHostsQueue = new Queue();
  108.   /** The status of each of the remote hosts */
  109.   private int [] m_remoteHostsStatus;
  110.   /** The number of times tasks have failed on each remote host */
  111.   private int [] m_remoteHostFailureCounts;
  112.   protected static final int AVAILABLE=0;
  113.   protected static final int IN_USE=1;
  114.   protected static final int CONNECTION_FAILED=2;
  115.   protected static final int SOME_OTHER_FAILURE=3;
  116. //    protected static final int TO_BE_RUN=0;
  117. //    protected static final int PROCESSING=1;
  118. //    protected static final int FAILED=2;
  119. //    protected static final int FINISHED=3;
  120.   /** allow at most 3 failures on a host before it is removed from the list
  121.       of usable hosts */
  122.   protected static final int MAX_FAILURES=3;
  123.   /** Set to true if MAX_FAILURES exceeded on all hosts or connections fail 
  124.       on all hosts or user aborts experiment (via gui) */
  125.   private boolean m_experimentAborted = false;
  126.   /** The number of hosts removed due to exceeding max failures */
  127.   private int m_removedHosts;
  128.   /** The count of failed sub-experiments */
  129.   private int m_failedCount;
  130.   /** The count of successfully completed sub-experiments */
  131.   private int m_finishedCount;
  132.   /** The base experiment to split up into sub experiments for remote
  133.       execution */
  134.   private Experiment m_baseExperiment = null;
  135.   /** The sub experiments */
  136.   protected Experiment [] m_subExperiments;
  137.   /** The queue of sub experiments waiting to be processed */
  138.   private Queue m_subExpQueue = new Queue();
  139.   /** The status of each of the sub-experiments */
  140.   protected int [] m_subExpComplete;
  141.   /**
  142.    * If true, then sub experiments are created on the basis of data sets
  143.    * rather than run number.
  144.    */
  145.   protected boolean m_splitByDataSet = true;
  146.   /**
  147.    * Returns true if sub experiments are to be created on the basis of
  148.    * data set..
  149.    *
  150.    * @return a <code>boolean</code> value indicating whether sub
  151.    * experiments are to be created on the basis of data set (true) or
  152.    * run number (false).
  153.    */
  154.   public boolean getSplitByDataSet() {
  155.     return m_splitByDataSet;
  156.   }
  157.   /**
  158.    * Set whether sub experiments are to be created on the basis of
  159.    * data set.
  160.    *
  161.    * @param sd true if sub experiments are to be created on the basis
  162.    * of data set. Otherwise sub experiments are created on the basis of
  163.    * run number.
  164.    */
  165.   public void setSplitByDataSet(boolean sd) {
  166.     m_splitByDataSet = sd;
  167.   }
  168.     
  169.   /**
  170.    * Construct a new RemoteExperiment using a base Experiment
  171.    * @param base the base experiment to use
  172.    * @exeception Exception if the base experiment is null
  173.    */
  174.   public RemoteExperiment(Experiment base) throws Exception {
  175.     setBaseExperiment(base);
  176.   }
  177.   /**
  178.    * Add an object to the list of those interested in recieving update
  179.    * information from the RemoteExperiment
  180.    * @param r a listener
  181.    */
  182.   public void addRemoteExperimentListener(RemoteExperimentListener r) {
  183.     m_listeners.addElement(r);
  184.   }
  185.   /**
  186.    * Get the base experiment used by this remote experiment
  187.    * @return the base experiment
  188.    */
  189.   public Experiment getBaseExperiment() {
  190.     return m_baseExperiment;
  191.   }
  192.   /**
  193.    * Set the base experiment. A sub experiment will be created for each
  194.    * run in the base experiment.
  195.    * @param base the base experiment to use.
  196.    * @exception Exception if supplied base experiment is null
  197.    */
  198.   public void setBaseExperiment(Experiment base) throws Exception {
  199.     if (base == null) {
  200.       throw new Exception("Base experiment is null!");
  201.     }
  202.     m_baseExperiment = base;
  203.     setRunLower(m_baseExperiment.getRunLower());
  204.     setRunUpper(m_baseExperiment.getRunUpper());
  205.     setResultListener(m_baseExperiment.getResultListener());
  206.     setResultProducer(m_baseExperiment.getResultProducer());
  207.     setDatasets(m_baseExperiment.getDatasets());
  208.     setUsePropertyIterator(m_baseExperiment.getUsePropertyIterator());
  209.     setPropertyPath(m_baseExperiment.getPropertyPath());
  210.     setPropertyArray(m_baseExperiment.getPropertyArray());
  211.     setNotes(m_baseExperiment.getNotes());
  212.     m_ClassFirst = m_baseExperiment.m_ClassFirst;
  213.     m_AdvanceDataSetFirst = m_baseExperiment.m_AdvanceDataSetFirst;
  214.   }
  215.   
  216.   /**
  217.    * Set the user notes.
  218.    *
  219.    * @param newNotes New user notes.
  220.    */
  221.   public void setNotes(String newNotes) {
  222.     
  223.     super.setNotes(newNotes);
  224.     m_baseExperiment.setNotes(newNotes);
  225.   }
  226.   /**
  227.    * Set the lower run number for the experiment.
  228.    *
  229.    * @param newRunLower the lower run number for the experiment.
  230.    */
  231.   public void setRunLower(int newRunLower) {
  232.     
  233.     super.setRunLower(newRunLower);
  234.     m_baseExperiment.setRunLower(newRunLower);
  235.   }
  236.   /**
  237.    * Set the upper run number for the experiment.
  238.    *
  239.    * @param newRunUpper the upper run number for the experiment.
  240.    */
  241.   public void setRunUpper(int newRunUpper) {
  242.     
  243.     super.setRunUpper(newRunUpper);
  244.     m_baseExperiment.setRunUpper(newRunUpper);
  245.   }
  246.   /**
  247.    * Sets the result listener where results will be sent.
  248.    *
  249.    * @param newResultListener the result listener where results will be sent.
  250.    */
  251.   public void setResultListener(ResultListener newResultListener) {
  252.     
  253.     super.setResultListener(newResultListener);
  254.     m_baseExperiment.setResultListener(newResultListener);
  255.   }
  256.   /**
  257.    * Set the result producer used for the current experiment.
  258.    *
  259.    * @param newResultProducer result producer to use for the current 
  260.    * experiment.
  261.    */
  262.   public void setResultProducer(ResultProducer newResultProducer) {
  263.     
  264.     super.setResultProducer(newResultProducer);
  265.     m_baseExperiment.setResultProducer(newResultProducer);
  266.   }
  267.   /**
  268.    * Set the datasets to use in the experiment
  269.    * @param ds the list of datasets to use
  270.    */
  271.   public void setDatasets(DefaultListModel ds) {
  272.     super.setDatasets(ds);
  273.     m_baseExperiment.setDatasets(ds);
  274.   }
  275.   /**
  276.    * Sets whether the custom property iterator should be used.
  277.    *
  278.    * @param newUsePropertyIterator true if so
  279.    */
  280.   public void setUsePropertyIterator(boolean newUsePropertyIterator) {
  281.     
  282.     super.setUsePropertyIterator(newUsePropertyIterator);
  283.     m_baseExperiment.setUsePropertyIterator(newUsePropertyIterator);
  284.   }
  285.   /**
  286.    * Sets the path of properties taken to get to the custom property
  287.    * to iterate over.
  288.    *
  289.    * @newPropertyPath an array of PropertyNodes
  290.    */
  291.   public void setPropertyPath(PropertyNode [] newPropertyPath) {
  292.     
  293.     super.setPropertyPath(newPropertyPath);
  294.     m_baseExperiment.setPropertyPath(newPropertyPath);
  295.   }
  296.   /**
  297.    * Sets the array of values to set the custom property to.
  298.    *
  299.    * @param newPropArray a value of type Object which should be an
  300.    * array of the appropriate values.
  301.    */
  302.   public void setPropertyArray(Object newPropArray) {
  303.     super.setPropertyArray(newPropArray);
  304.     m_baseExperiment.setPropertyArray(newPropArray);
  305.   }
  306.     
  307.   /**
  308.    * Prepares a remote experiment for running, creates sub experiments
  309.    *
  310.    * @exception Exception if an error occurs
  311.    */
  312.   public void initialize() throws Exception {
  313.     if (m_baseExperiment == null) {
  314.       throw new Exception("No base experiment specified!");
  315.     }
  316.     m_experimentAborted = false;
  317.     m_finishedCount = 0;
  318.     m_failedCount = 0;
  319.     m_RunNumber = getRunLower();
  320.     m_DatasetNumber = 0;
  321.     m_PropertyNumber = 0;
  322.     m_CurrentProperty = -1;
  323.     m_CurrentInstances = null;
  324.     m_Finished = false;
  325.     if (m_remoteHosts.size() == 0) {
  326.       throw new Exception("No hosts specified!");
  327.     }
  328.     // initialize all remote hosts to available
  329.     m_remoteHostsStatus = new int [m_remoteHosts.size()];    
  330.     m_remoteHostFailureCounts = new int [m_remoteHosts.size()];
  331.     m_remoteHostsQueue = new Queue();
  332.     // prime the hosts queue
  333.     for (int i=0;i<m_remoteHosts.size();i++) {
  334.       m_remoteHostsQueue.push(new Integer(i));
  335.     }
  336.     // set up sub experiments
  337.     m_subExpQueue = new Queue();
  338.     int numExps;
  339.     if (getSplitByDataSet()) {
  340.       numExps = m_baseExperiment.getDatasets().size();
  341.     } else {
  342.       numExps = getRunUpper() - getRunLower() + 1;
  343.     }
  344.     m_subExperiments = new Experiment[numExps];
  345.     m_subExpComplete = new int[numExps];
  346.     // create copy of base experiment
  347.     SerializedObject so = new SerializedObject(m_baseExperiment);
  348.     if (getSplitByDataSet()) {
  349.       for (int i = 0; i < m_baseExperiment.getDatasets().size(); i++) {
  350. m_subExperiments[i] = (Experiment)so.getObject();
  351. // one for each data set
  352. DefaultListModel temp = new DefaultListModel();
  353. temp.addElement(m_baseExperiment.getDatasets().elementAt(i));
  354. m_subExperiments[i].setDatasets(temp);
  355. m_subExpQueue.push(new Integer(i));
  356.       }
  357.     } else {
  358.       for (int i = getRunLower(); i <= getRunUpper(); i++) {
  359. m_subExperiments[i-getRunLower()] = (Experiment)so.getObject();
  360. // one run for each sub experiment
  361. m_subExperiments[i-getRunLower()].setRunLower(i);
  362. m_subExperiments[i-getRunLower()].setRunUpper(i);
  363. m_subExpQueue.push(new Integer(i-getRunLower()));
  364.       }    
  365.     }
  366.   }
  367.   /**
  368.    * Inform all listeners of progress
  369.    * @param status true if this is a status type of message
  370.    * @param log true if this is a log type of message
  371.    * @param finished true if the remote experiment has finished
  372.    * @param message the message.
  373.    */
  374.   private synchronized void notifyListeners(boolean status, 
  375.     boolean log, 
  376.     boolean finished,
  377.     String message) {
  378.     if (m_listeners.size() > 0) {
  379.       for (int i=0;i<m_listeners.size();i++) {
  380. RemoteExperimentListener r = 
  381.   (RemoteExperimentListener)(m_listeners.elementAt(i));
  382. r.remoteExperimentStatus(new RemoteExperimentEvent(status,
  383.    log,
  384.    finished,
  385.    message));
  386.       }
  387.     } else {
  388.       System.err.println(message);
  389.     }
  390.   }
  391.   /**
  392.    * Set the abort flag
  393.    */
  394.   public void abortExperiment() {
  395.     m_experimentAborted = true;
  396.   }
  397.   /**
  398.    * Increment the number of successfully completed sub experiments
  399.    */
  400.   protected synchronized void incrementFinished() {
  401.     m_finishedCount++;
  402.   }
  403.   /**
  404.    * Increment the overall number of failures and the number of failures for
  405.    * a particular host
  406.    * @param hostNum the index of the host to increment failure count
  407.    */
  408.   protected synchronized void incrementFailed(int hostNum) {
  409.     m_failedCount++;
  410.     m_remoteHostFailureCounts[hostNum]++;
  411.   }
  412.   /**
  413.    * Push an experiment back on the queue of waiting experiments
  414.    * @param expNum the index of the experiment to push onto the queue
  415.    */
  416.   protected synchronized void waitingExperiment(int expNum) {
  417.     m_subExpQueue.push(new Integer(expNum));
  418.   }
  419.   /**
  420.    * Check to see if we have failed to connect to all hosts
  421.    */
  422.   private boolean checkForAllFailedHosts() {
  423.     boolean allbad = true;
  424.     for (int i = 0; i < m_remoteHostsStatus.length; i++) {
  425.       if (m_remoteHostsStatus[i] != CONNECTION_FAILED) {
  426. allbad = false;
  427. break;
  428.       }
  429.     }
  430.     if (allbad) {
  431.       abortExperiment();
  432.       notifyListeners(false,true,true,"Experiment aborted! All connections "
  433.       +"to remote hosts failed.");
  434.     }
  435.     return allbad;
  436.   }
  437.   /**
  438.    * Returns some post experiment information.
  439.    * @return a String containing some post experiment info
  440.    */
  441.   private String postExperimentInfo() {
  442.     StringBuffer text = new StringBuffer();
  443.     text.append(m_finishedCount+(m_splitByDataSet 
  444.  ? " data sets" 
  445.  : " runs") + " completed successfully. "
  446. +m_failedCount+" failures during running.n");
  447.     System.err.print(text.toString());
  448.     return text.toString();
  449.   }
  450.   /**
  451.    * Pushes a host back onto the queue of available hosts and attempts to
  452.    * launch a waiting experiment (if any).
  453.    * @param hostNum the index of the host to push back onto the queue of
  454.    * available hosts
  455.    */
  456.   protected synchronized void availableHost(int hostNum) {
  457.     if (hostNum >= 0) { 
  458.       if (m_remoteHostFailureCounts[hostNum] < MAX_FAILURES) {
  459. m_remoteHostsQueue.push(new Integer(hostNum));
  460.       } else {
  461. notifyListeners(false,true,false,"Max failures exceeded for host "
  462. +((String)m_remoteHosts.elementAt(hostNum))
  463. +". Removed from host list.");
  464. m_removedHosts++;
  465.       }
  466.     }
  467.     // check for all sub exp complete or all hosts failed or failed count
  468.     // exceeded
  469.     if (m_failedCount == (MAX_FAILURES * m_remoteHosts.size())) {
  470.       abortExperiment();
  471.       notifyListeners(false,true,true,"Experiment aborted! Max failures "
  472.       +"exceeded on all remote hosts.");
  473.       return;
  474.     }
  475.     if ((getSplitByDataSet() && 
  476.  (m_baseExperiment.getDatasets().size() == m_finishedCount)) ||
  477. (!getSplitByDataSet() && 
  478.  ((getRunUpper() - getRunLower() + 1) == m_finishedCount))) {
  479.       notifyListeners(false,true,false,"Experiment completed successfully.");
  480.       notifyListeners(false,true,true,postExperimentInfo());
  481.       return;
  482.     }
  483.     
  484.     if (checkForAllFailedHosts()) {
  485.       return;
  486.     }
  487.     if (m_experimentAborted && 
  488. (m_remoteHostsQueue.size() + m_removedHosts) == m_remoteHosts.size()) {
  489.       notifyListeners(false,true,true,"Experiment aborted. All remote tasks "
  490.       +"finished.");
  491.     }
  492.         
  493.     if (!m_subExpQueue.empty() && !m_experimentAborted) {
  494.       if (!m_remoteHostsQueue.empty()) {
  495. int availHost, waitingExp;
  496. try {
  497.   availHost = ((Integer)m_remoteHostsQueue.pop()).intValue();
  498.   waitingExp = ((Integer)m_subExpQueue.pop()).intValue();
  499.   launchNext(waitingExp, availHost);
  500. } catch (Exception ex) {
  501.   ex.printStackTrace();
  502. }
  503.       }
  504.     }    
  505.   }
  506.   /**
  507.    * Launch a sub experiment on a remote host
  508.    * @param wexp the index of the sub experiment to launch
  509.    * @param ah the index of the available host to launch on
  510.    */
  511.   public void launchNext(final int wexp, final int ah) {
  512.     
  513.     Thread subExpThread;
  514.     subExpThread = new Thread() {
  515. public void run() {       
  516.   m_remoteHostsStatus[ah] = IN_USE;
  517.   m_subExpComplete[wexp] = TaskStatusInfo.PROCESSING;
  518.   RemoteExperimentSubTask expSubTsk = new RemoteExperimentSubTask();
  519.   expSubTsk.setExperiment(m_subExperiments[wexp]);
  520.   String subTaskType = (getSplitByDataSet())
  521.     ? "dataset :" + ((File)m_subExperiments[wexp].getDatasets().
  522.      elementAt(0)).getName()
  523.     : "run :" + m_subExperiments[wexp].getRunLower();
  524.   try {
  525.     String name = "//"
  526.       +((String)m_remoteHosts.elementAt(ah))
  527.       +"/RemoteEngine";
  528.     Compute comp = (Compute) Naming.lookup(name);
  529.     // assess the status of the sub-exp
  530.     notifyListeners(false,true,false,"Starting "
  531.     +subTaskType
  532.     +" on host "
  533.     +((String)m_remoteHosts.elementAt(ah)));
  534.     Object subTaskId = comp.executeTask(expSubTsk);
  535.     boolean finished = false;
  536.     TaskStatusInfo is = null;
  537.     while (!finished) {
  538.       try {
  539. Thread.sleep(2000);
  540. TaskStatusInfo cs = (TaskStatusInfo)comp.
  541.   checkStatus(subTaskId);
  542. if (cs.getExecutionStatus() == TaskStatusInfo.FINISHED) {
  543.   // push host back onto queue and try launching any waiting 
  544.   // sub-experiments
  545.   notifyListeners(false, true, false,  cs.getStatusMessage());
  546.   m_remoteHostsStatus[ah] = AVAILABLE;
  547.   incrementFinished();
  548.   availableHost(ah);
  549.   finished = true;
  550. } else if (cs.getExecutionStatus() == TaskStatusInfo.FAILED) {
  551.   // a non connection related error---possibly host doesn't have
  552.   // access to data sets or security policy is not set up
  553.   // correctly or classifier(s) failed for some reason
  554.   notifyListeners(false, true, false,  cs.getStatusMessage());
  555.   m_remoteHostsStatus[ah] = SOME_OTHER_FAILURE;
  556.   m_subExpComplete[wexp] = TaskStatusInfo.FAILED;
  557.   notifyListeners(false,true,false,subTaskType
  558.   +" "+cs.getStatusMessage()
  559.   +". Scheduling for execution on another host.");
  560.   incrementFailed(ah);
  561.   // push experiment back onto queue
  562.   waitingExperiment(wexp);
  563.   // push host back onto queue and try launching any waiting 
  564.   // sub-experiments. Host is pushed back on the queue as the
  565.   // failure may be temporary---eg. with InstantDB using the
  566.   // RMI bridge, two or more threads may try to create the
  567.   // experiment index or results table simultaneously; all but
  568.   // one will throw an exception. These hosts are still usable
  569.   // however.
  570.   availableHost(ah);
  571.   finished = true;
  572. } else {
  573.   if (is == null) {
  574.     is = cs;
  575.     notifyListeners(false, true, false, cs.getStatusMessage());
  576.   } else {
  577.     if (cs.getStatusMessage().
  578. compareTo(is.getStatusMessage()) != 0) {
  579.      
  580.       notifyListeners(false, true, false,  
  581.       cs.getStatusMessage());
  582.     }
  583.     is = cs;
  584.   }  
  585. }
  586.       } catch (InterruptedException ie) {
  587.       }
  588.     }       
  589.   } catch (Exception ce) {
  590.     m_remoteHostsStatus[ah] = CONNECTION_FAILED;
  591.     m_subExpComplete[wexp] = TaskStatusInfo.TO_BE_RUN;
  592.     System.err.println(ce);
  593.     ce.printStackTrace();
  594.     notifyListeners(false,true,false,"Connection to "
  595.     +((String)m_remoteHosts.elementAt(ah))
  596.     +" failed. Scheduling "
  597.     +subTaskType
  598.     +" for execution on another host.");
  599.     checkForAllFailedHosts();
  600.     waitingExperiment(wexp);
  601.   } finally {
  602.     if (isInterrupted()) {
  603.       System.err.println("Sub exp Interupted!");
  604.     }
  605.   }
  606. }    
  607.       };
  608.     subExpThread.setPriority(Thread.MIN_PRIORITY);
  609.     subExpThread.start();
  610.   }
  611.   /**
  612.    * Overides the one in Experiment
  613.    * @exception Exception
  614.    */
  615.   public void nextIteration() throws Exception {
  616.   }
  617.   /** 
  618.    * overides the one in Experiment
  619.    */
  620.   public void advanceCounters() {
  621.   }
  622.   /** 
  623.    * overides the one in Experiment
  624.    */
  625.   public void postProcess() {
  626.    
  627.   }
  628.   /**
  629.    * Add a host name to the list of remote hosts
  630.    * @param hostname the host name to add to the list
  631.    */
  632.   public void addRemoteHost(String hostname) {
  633.     m_remoteHosts.addElement(hostname);
  634.   }
  635.   /**
  636.    * Get the list of remote host names
  637.    * @return the list of remote host names
  638.    */
  639.   public DefaultListModel getRemoteHosts() {
  640.     return m_remoteHosts;
  641.   }
  642.   /**
  643.    * Overides toString in Experiment
  644.    * @return a description of this remote experiment
  645.    */
  646.   public String toString() {
  647.     String result = m_baseExperiment.toString();
  648.     result += "nRemote Hosts:n";
  649.     for (int i=0;i<m_remoteHosts.size();i++) {
  650.       result += ((String)m_remoteHosts.elementAt(i)) +'n';
  651.     }
  652.     return result;
  653.   }
  654.   /**
  655.    * Overides runExperiment in Experiment
  656.    */
  657.   public void runExperiment() {
  658.     int totalHosts = m_remoteHostsQueue.size();
  659.     // Try to launch sub experiments on all available hosts
  660.     for (int i = 0; i < totalHosts; i++) {
  661.       availableHost(-1);
  662.     }
  663.   }
  664.   /**
  665.    * Configures/Runs the Experiment from the command line.
  666.    *
  667.    * @param args command line arguments to the Experiment.
  668.    */
  669.   public static void main(String[] args) {
  670.     try {
  671.       RemoteExperiment exp = null;
  672.       Experiment base = null;
  673.       String expFile = Utils.getOption('l', args);
  674.       String saveFile = Utils.getOption('s', args);
  675.       boolean runExp = Utils.getFlag('r', args);
  676.       FastVector remoteHosts = new FastVector();
  677.       String runHost = " ";
  678.       while (runHost.length() != 0) {
  679. runHost = Utils.getOption('h', args);
  680. if (runHost.length() != 0) {
  681.   remoteHosts.addElement(runHost);
  682. }
  683.       }
  684.       if (expFile.length() == 0) {
  685. base = new Experiment();
  686. try {
  687.   base.setOptions(args);
  688.   Utils.checkForRemainingOptions(args);
  689. } catch (Exception ex) {
  690.   ex.printStackTrace();
  691.   String result = "Usage:nn"
  692.     + "-l <exp file>n"
  693.     + "tLoad experiment from file (default use cli options)n"
  694.     + "-s <exp file>n"
  695.     + "tSave experiment to file after setting other optionsn"
  696.     + "t(default don't save)n"
  697.     + "-h <remote host name>n"
  698.     +"tHost to run experiment on (may be specified more than oncen"
  699.     +"tfor multiple remote hosts)n"
  700.     + "-r n"
  701.     + "tRun experiment on (default don't run)nn";
  702.   Enumeration enum = ((OptionHandler)base).listOptions();
  703.   while (enum.hasMoreElements()) {
  704.     Option option = (Option) enum.nextElement();
  705.     result += option.synopsis() + "n";
  706.     result += option.description() + "n";
  707.   }
  708.   throw new Exception(result + "n" + ex.getMessage());
  709. }
  710.       } else {
  711. FileInputStream fi = new FileInputStream(expFile);
  712. ObjectInputStream oi = new ObjectInputStream(
  713.        new BufferedInputStream(fi));
  714. Object tmp = oi.readObject();
  715. if (tmp instanceof RemoteExperiment) {
  716.   exp = (RemoteExperiment)tmp;
  717. } else {
  718.   base = (Experiment)tmp;
  719. }
  720. oi.close();
  721.       }
  722.       if (base != null) {
  723. exp = new RemoteExperiment(base);
  724.       }
  725.       for (int i=0;i<remoteHosts.size();i++) {
  726. exp.addRemoteHost((String)remoteHosts.elementAt(i));
  727.       }
  728.       System.err.println("Experiment:n" + exp.toString());
  729.       if (saveFile.length() != 0) {
  730. FileOutputStream fo = new FileOutputStream(saveFile);
  731. ObjectOutputStream oo = new ObjectOutputStream(
  732. new BufferedOutputStream(fo));
  733. oo.writeObject(exp);
  734. oo.close();
  735.       }
  736.       
  737.       if (runExp) {
  738. System.err.println("Initializing...");
  739. exp.initialize();
  740. System.err.println("Iterating...");
  741. exp.runExperiment();
  742. System.err.println("Postprocessing...");
  743. exp.postProcess();
  744.       }      
  745.     } catch (Exception ex) {
  746.       ex.printStackTrace();
  747.       System.err.println(ex.getMessage());
  748.     }
  749.   }
  750. }