// file: $isip/util/demo/isip_comm_server_process_manager/DemoClientHandler.java
// version: $Id: DemoClientHandler.java 10102 2005-05-20 19:18:44Z elliott $
//

// import necessary java libraries
//
import java.net.*;
import java.io.*;
import java.util.*;

/** 
 * This class gets connected with client and communicate with it. The
 * main functions include: start a script to start all other support
 * servers for this client; kill all those support servers when it
 * gets signal from a client.
 */
public class DemoClientHandler implements DemoProtocol {

    //-------------------------------------------------------------------------
    //
    // public constants
    //
    //-------------------------------------------------------------------------
    
    /**
     * Default port number for data recorder server
     */ 
    public static final int DATA_RECORDER_PORT = 12346; 
    
    /**
     * Default port number for signal detector server
     */ 
    public static final int SIGNAL_DETECTOR_PORT = 12347;

    /**
     * Default port number for speech recognizer server
     */ 
    public static final int SPEECH_RECOGNIZER_PORT = 12348; 

    /**
     * Default port number for data recorder server
     */ 
    public static final int SPEAKER_VERIFIER_PORT = 12349; 

    /**
     * Default port number for dialog navigation manager server
     */ 
    public static final int DIALOG_NAVIGATION_MANAGER_PORT = 12350; 

    /**
     * Default port number for dialog nlp server
     */ 
    public static final int DIALOG_NLP_PORT = 12351;

    /**
     * Default port number for dialog navigation database server
     */ 
    public static final int DIALOG_NAVIGATION_DATABASE_PORT = 12352; 

    /**
     * A flag used when Speech Analysis demo is running
     */ 
    public static final int SPEECH_ANALYSIS = 1;
    
    /**
     * A flag used when Speech Recognition demo is running
     */ 
    public static final int SPEECH_RECOGNITION = 2;

     /**
     * A flag used when Speech Verification demo is running
     */ 
    public static final int SPEAKER_VERIFICATION = 3;

     /**
     * A flag used when Dialog Navigation demo is running
     */ 
    public static final int DIALOG_NAVIGATION = 4;

    /**
     * The number of ports to increment by each time a new client connects
     */
    public static final int PORT_INCREMENT = 15;

    /**
     * Default starting script file for speech analysis (csh)
     */
    public static String SPEECH_ANALYSIS_TEMPLATE_CSH;
    
    /**
     * Default hub server script file for speech analysis (pgm)
     */
    public static String SPEECH_ANALYSIS_SERVER_TEMPLATE_PGM;
    
    /**
     * Default hub client script file for speech analysis (pgm)
     */
    public static String SPEECH_ANALYSIS_CLIENT_TEMPLATE_PGM;

    /**
     * Default starting script file for speech analysis (csh)
     */
    public static String SPEECH_RECOGNITION_TEMPLATE_CSH;
    
    /**
     * Default hub server script file for speech analysis (pgm)
     */
    public static String SPEECH_RECOGNITION_SERVER_TEMPLATE_PGM;
    
    /** 
     * Default hub client script file for speech analysis (pgm)
     */
    public static String SPEECH_RECOGNITION_CLIENT_TEMPLATE_PGM;

    /**
     * Default starting script file for speech analysis (csh)
     */
    public static String SPEAKER_VERIFICATION_TEMPLATE_CSH;
    
    /**
     * Default hub server script file for speech analysis (pgm)
     */
    public static String SPEAKER_VERIFICATION_SERVER_TEMPLATE_PGM;
    
    /** 
     * Default hub client script file for speech analysis (pgm)
     */
    public static String SPEAKER_VERIFICATION_CLIENT_TEMPLATE_PGM;
    
    /**
     * Default starting script file for dialog navigation (csh)
     */
    public static String DIALOG_NAVIGATION_TEMPLATE_CSH;
    
    /**
     * Default hub server script file for dialog navigation (pgm)
     */
    public static String DIALOG_NAVIGATION_SERVER_TEMPLATE_PGM;
    
    /** 
     * Default hub client script file for dialog navigation (pgm)
     */
    public static String DIALOG_NAVIGATION_CLIENT_TEMPLATE_PGM;
     

    //------------------------------------------------------------------------
    //
    // protected data
    //
    //-----------------------------------------------------------------------

    /**
     * Unique ID to count how many clients connected up to now
     *
     * NOTE: Notice this is static so all DemoClientHandler's share it
     */
    protected static int unique_id_count_d = 0;

    /**
     * The group runner -- grouping of processings with their outputs
     */
    protected GroupRunner group_runner_d;

    /**
     * Socket for this client.
     */
    protected Socket socket_d; 

    /**
     * Process to start the child process.
     */
    protected Process process_d;  

    /**
     * Message handler for this client. Not used anymore
     */
    //protected MessageHandler message_handler_d;

    /**
     * Incoming message handler for this client.
     */
    protected IncomingMessageHandler incoming_message_handler_d;

    /**
     * Outgoing message handler for this client.
     */
    protected OutgoingMessageHandler outgoing_message_handler_d;
    
    /**
     * A boolean value to indicate if this client is connected with
     * the server.
     */
    protected boolean connected_d = false;
    
    /**
     * Map the server name with process id.
     */
    //protected HashMap server_name_map_d = new HashMap();
    
    /** 
     * A vector of Server list.
     */
    //protected Vector server_list_d = new Vector(1,1);
    
    /**
     * A player to represents this client.
     */
    protected DemoPlayer player_d;

    /**
     * The hub port given to this demo by the ProcessManager
     */
    protected int hub_port_d;

    /**
     * The int that contains the demo type.
     */
    protected int demo_type_d = DEMO_UNDEFINED; 

    /**
     * The string that contains the demo name.
     */
    protected String demo_name_string_d = "Undefined";

    /**
     * The string that contains the demo name.
     */
    protected static String temp_file_environment_d = "$ISIP_TMPDIR";

    /**
     * The string that contains the demo name.
     */
    protected String temp_file_outpath_d;

    /**
     * The Filename of the speech analysis' shell script.
     */
    protected File speech_analysis_csh_d; 

    /**
     * The Filename of the speech analysis' hub scripts.
     */
    protected File speech_analysis_pgm_d; 

    /**
     * The Filename of the speech recogniton's shell scripts.
     */
    protected File speech_recognition_csh_d; 

    /**
     * The Filename of the speech recognition's hub scripts.
     */
    protected File speech_recognition_pgm_d; 

    /**
     * The Filename of the speech verification's shell scripts.
     */
    protected File speaker_verification_csh_d; 

    /**
     * The Filename of the speech verification's hub scripts.
     */
    protected File speaker_verification_pgm_d; 

    /**
     * The Filename of the dialog navigation's shell scripts.
     */
    protected File dialog_navigation_csh_d; 

    /**
     * The Filename of the dialog navigation's hub scripts.
     */
    protected File dialog_navigation_pgm_d; 
    
    //-------------------------------------------------------------------------
    //
    // public methods
    //
    //-------------------------------------------------------------------------
       
    /**
     * Constructs a new client handler using the socket specified.
     *
     * @param socket_a socket it get connected to.
     */
    public DemoClientHandler(Socket socket_a, int hub_port_a) {

        try {
	    // get the Gui path
	    //
	    String GUI_PATH = getGuiPath();

	    SPEECH_ANALYSIS_SERVER_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speech_analysis/pgm/speech_analysis_server.pgm");
	    SPEECH_ANALYSIS_CLIENT_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speech_analysis/pgm/speech_analysis_client.pgm");
	    SPEECH_RECOGNITION_SERVER_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speech_recognition/pgm/" + 
		       "speech_recognition_server.pgm");
	    SPEECH_RECOGNITION_CLIENT_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speech_recognition/pgm/speech_recognition_client.pgm");
	    SPEAKER_VERIFICATION_SERVER_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speaker_verification/pgm/" +
		       "speaker_verification_server.pgm");
	    SPEAKER_VERIFICATION_CLIENT_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "speaker_verification/pgm/" +
		       "speaker_verification_client.pgm");
	    DIALOG_NAVIGATION_SERVER_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "dialog_navigation/pgm/" +
		       "dialog_navigation_server.pgm");
	    DIALOG_NAVIGATION_CLIENT_TEMPLATE_PGM = new 
		String(GUI_PATH + 
		       "/util/demo/isip_demo_data/" +
		       "dialog_navigation/pgm/" +
		       "dialog_navigation_client.pgm");

	    SPEECH_ANALYSIS_TEMPLATE_CSH = new 
		String(GUI_PATH + 
		       "/util/demo/isip_comm_server_process_manager/" +
		       "scripts/" +
		       "speech_analysis.csh");
	    SPEECH_RECOGNITION_TEMPLATE_CSH = new
		String(GUI_PATH + 
		       "/util/demo/isip_comm_server_process_manager/" +
		       "scripts/" +
		       "speech_recognition.csh");
	    SPEAKER_VERIFICATION_TEMPLATE_CSH = new 
		String(GUI_PATH + 
		       "/util/demo/isip_comm_server_process_manager/" +
		       "scripts/" +
		       "speaker_verification.csh");
	    DIALOG_NAVIGATION_TEMPLATE_CSH = new 
		String(GUI_PATH + 
		       "/util/demo/isip_comm_server_process_manager/" +
		       "scripts/" +
		       "dialog_navigation.csh");
	    
	    // accepts the socket
	    //
            this.socket_d = socket_a;

	    // get the in and out stream for this socket
	    //
            DataInputStream in = new DataInputStream
		(socket_d.getInputStream());
	    
            DataOutputStream out = new DataOutputStream
                (socket_d.getOutputStream());
            
	    // set up the in and out message handler
	    //
            incoming_message_handler_d = new IncomingMessageHandler(in);
            outgoing_message_handler_d = new OutgoingMessageHandler(out);
            
	    // set the connected is true
	    //
            connected_d = true;
            
	    // create a player to represent the client's information
	    //
            player_d = new DemoPlayer(unique_id_count_d); 
	    
	    // add one more client
	    //
            unique_id_count_d++;

	    // Find out the temporary path from the environment variables
	    //
	    temp_file_outpath_d = replaceEnv(temp_file_environment_d);

	    // If replaceEnv returns current directory, make it equal to /tmp/
	    //
	    if (temp_file_outpath_d.equals("."))
		temp_file_outpath_d = "/tmp/";

	    // If the environment path doesn't in with a /, add one
	    //
	    if (temp_file_outpath_d.endsWith("/") == false)
		temp_file_outpath_d = temp_file_outpath_d + "/";
	    
	    // send message to client and tell its id
	    //   
            String init_line = MSG_INIT_PLAYER + "|" + player_d.getID();
	    sendMessage(init_line);
	    
	    // print out debug message
	    //
	    System.out.println("init message sent as [" + init_line + "]");
	    
	    // send message to client and tell it the hub port
	    //
            String hub_line = MSG_SET_HUB_PORT + "|" + hub_port_a;
	    sendMessage(hub_line);

	    // set the hub port for further reference
	    //
	    hub_port_d = hub_port_a;

	    // print out debug message
	    //
	    System.out.println("hub port message sent as [" + hub_line + "]");

	    // Create a new group for this user
	    group_runner_d = new GroupRunner(player_d);
	    
        }
        catch(IOException e) {
	    System.out.println("DemoClientHandler Constructor: " +
			       "Unable to connect. ");
	    e.printStackTrace();
	}
    }
    

    //-------------------------------------------------------------------------
    //
    // class-specific other public methods:
    //  
    //
    //-------------------------------------------------------------------------

    /**
     * Returns back the port number for the data recorder.
     *
     * @return an integer for port of the data recorder server.
     */
    public int getDataRecorderPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = DATA_RECORDER_PORT + PORT_INCREMENT * player_d.getID();

	// return the data recorder port number
	//
	return port;
    } 
    
    /**
     * Returns back the port number for the data recorder.
     *
     * @return an integer for port of the signal detector server.
     */
    public int getSignalDetectorPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = SIGNAL_DETECTOR_PORT + PORT_INCREMENT * player_d.getID();

	// return the data recorder port number
	//
	return port;
    } 


    /**
     * Returns back the port number for the speech recognition.
     *
     * @return an integer for port of the speech recognition server.
     */
    public int getSpeechRecognizerPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = SPEECH_RECOGNIZER_PORT + PORT_INCREMENT * player_d.getID();

	// return the data recorder port number
	//
	return port;
    } 


    /**
     * Returns back the port number for the speaker verifier.
     *
     * @return an integer for port of the speaker verifier server.
     */
    public int getSpeakerVerifierPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = SPEAKER_VERIFIER_PORT + PORT_INCREMENT * player_d.getID();
	
	// return the data recorder port number
	//
	return port;
    } 

    /**
     * Returns back the port number for the speaker verifier.
     *
     * @return an integer for port of the speaker verifier server.
     */
    public int getDialogNLPPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = DIALOG_NLP_PORT + PORT_INCREMENT * player_d.getID();
	
	// return the data recorder port number
	//
	return port;
    } 

    /**
     * Returns back the port number for the speaker verifier.
     *
     * @return an integer for port of the speaker verifier server.
     */
    public int getDialogNavigationManagerPort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = DIALOG_NAVIGATION_MANAGER_PORT + 
	    PORT_INCREMENT * player_d.getID();
	
	// return the data recorder port number
	//
	return port;
    } 

    /**
     * Returns back the port number for the speaker verifier.
     *
     * @return an integer for port of the speaker verifier server.
     */
    public int getDialogNavigationDatabasePort() {

	// the port number of data recorder is increased by 4 each
	// time when a new connection is established. The 4 is magic
	// number, you can use any number you like.
	//
	int port = DIALOG_NAVIGATION_DATABASE_PORT 
	    + PORT_INCREMENT * player_d.getID();
	
	// return the data recorder port number
	//
	return port;
    } 

    /**
     * Get the GUI path.
     *
     * @return a string representing  the GUI path.
     */
    public static String getGuiPath() {
	
	EnvProperties props = new EnvProperties();
	
	String path = props.run("ISIP_DEVEL");
	if (path.equals("null")) {
	    path = ".";
	}
	return path;
    }

    /**
     * Replace rnvironment variables with their values.
     *
     * @return a string representing path with all $VAR replaced.
     */
    public static String replaceEnv(String in) {
	
	if (in == null)
	    return "";

	EnvProperties props = new EnvProperties();

	String out = in;
	int loc = 0;

	//System.out.println("Entered replaceEnv with in[" + in + "]");
	while ((loc = out.indexOf('$', loc)) != -1) {

	    //System.out.println("Entered while loop with loc[" + loc + "]");
	    // found a $ so now find the next '/'
	    //
	    int end = out.indexOf('/', loc);

	    // So now the thing is
	    // /some/path/$VARIABLE/rest_of_it/
	    //            |        |
	    //          loc        end
	    // or 
	    // /some/path/$VARIABLE   (-1)
	    //            |           |
	    //          loc           end
	    //

	    String path;
	    // If there isn't one then its from loc->end of string 
	    if (end == -1) {
		path = out.substring(loc);
	    }
	    else {
		path = out.substring(loc, end);
	    }

	    // Now path contains "$VARIABLE", so give the props
	    // everything but the $. The "substring(1)" says
	    // everything but the $.
	    //
	    String result = props.run(path.substring(1));
	    if (result.equals("null")) {
		result = ".";
	    }
	    //System.out.println("Found [" + path + "] having value [" + 
	    //       result + "]"); 

	    // So now, need to replace it so
	    if (end == -1) {
		out = out.substring(0,loc) + result;
	    }
	    else {
		out = out.substring(0,loc) + result + out.substring(end);
	    }

	    //System.out.println("After replacing is [" + out + "] loc[" +
	    //       loc + "]");

	    // Find out if I am at end of string yet or else move up
	    // a spot
	    //
	    /*if (end == -1)
		break;
	    else
		loc = loc + 1;
	    */
	}

	return out;
    }

    public void error_happened_cleanup_client(String error_a) {
	// Null pointer check
	//
	if (error_a == null) {
	    error_a = "[No reason given]";
	}

	// Since pipes are the "divider" character, please attempt
	// to not write any pipes in there or else the client may
	// not parsed as expected
	//

	// Send the message
	//
	System.out.println("Entered error_happened()");
	sendMessage(MSG_SERVER_FAILURE + "|" + error_a);
	
	// But don't immediately exit because we have to wait for
	// the outgoing_message_handler to send it
	//
	while (outgoing_message_handler_d != null &&
	       outgoing_message_handler_d.isEmpty() == false) {
	    // While loop has no purpose except sleeping
	    //
	    try {
		Thread.sleep(500);
	    }
	    catch (Exception e) {
		// don't care, it's a sleep
	    }
	}

	// Put this socket with garbage because nothing
	// else *should* be referencing it.
	//
	disconnect();

	return;
    }

   /**
     * Disconnect and cleanup.
     *
     * @return a boolean value indicating status.
     */
    public synchronized boolean disconnect() {
     
	//if (message_handler_d != null) 
	//message_handler_d.destroy();
	
	if (incoming_message_handler_d != null) {
	    incoming_message_handler_d.destroy();
	    incoming_message_handler_d = null;
	}
	
	if (outgoing_message_handler_d != null) {
	    outgoing_message_handler_d.destroy();
	    outgoing_message_handler_d = null;
	}
        
	try {
	    socket_d.close();
	}
	catch (Exception e) {}
	socket_d = null;
	
	if (group_runner_d != null) {
	    group_runner_d.killAll();
	    group_runner_d = null;
	}

	// Remove all the temporary files. There may be null pointers
	// but it doesn't matter.
	//	
	try { speech_analysis_csh_d.delete(); }
	catch(Exception e) { /* don't care */ }
	try { speech_analysis_pgm_d.delete(); }
	catch(Exception e) { /* don't care */ }

	try { speaker_verification_csh_d.delete(); }
	catch(Exception e) { /* don't care */ }
	try { speaker_verification_pgm_d.delete(); }
	catch(Exception e) { /* don't care */ }

	try { speech_recognition_csh_d.delete(); }
	catch(Exception e) { /* don't care */ }
	try { speech_recognition_pgm_d.delete(); }
	catch(Exception e) { /* don't care */ }

	try { dialog_navigation_csh_d.delete(); }
	catch(Exception e) { /* don't care */ }
	try { dialog_navigation_pgm_d.delete(); }
	catch(Exception e) { /* don't care */ }

	
	
       
	// TODO, note should I make sure all file handles and everything
	// from the class is closed?
	//

	/*
	// kill all servers in the server list one by one
	//

	System.out.println("===============================");

	// Changed this so that it attempts to close all the servers
	// moved the "try\catch" inside the 'for' instead of outside it
	//
	for (int i = 0 ; i < server_list_d.size(); i++) {
		
	    try {
		String s = "kill " + server_name_map_d.get(
					     (String)server_list_d.get(i));

		// This runs [kill 1182] as a process
		//
		Process p = Runtime.getRuntime().exec(s);

		// Prints [server 1182 (Hub) has just been killed]
		//
		System.out.println("server " + (String)server_list_d.get(i) + 
				   " (" +
				   server_name_map_d.get(
					      (String)server_list_d.get(i))
				   + ") " + 
				   "has just been killed");

	    }
	    catch (Exception ex) {
		System.out.println("Exception has been caught trying to " +
				   "a kill server. On number " + i + " of " +
				   server_list_d.size() + ".\nStack:");
		ex.printStackTrace();
	    }

	    System.out.println("===============================");
        }
	*/
	    
        System.out.println("-> Client Disconnected");

	// exit gracefully
	//
	return true;
    }

    /**
     * Send message using outgoing message handler.
     *
     * @param message_a a message intended to send.
     *
     * @return a boolean value indicating status.
     */
    public boolean sendMessage(String message_a) {
	if (outgoing_message_handler_d == null || socket_d == null || 
	    message_a == null) {
	    System.out.println("sendMessage: entered with something null!");
	    return false;
	}
	
        outgoing_message_handler_d.addMessage(message_a);
	
	// exit gracefully
	//
	return true;
    }
    
    /**
     * Handle message when the message coming.
     *
     * @param message_a a messageintended to handle.
     *
     * @return a boolean value indicating status.
     */
    public boolean handleMessage(String message_a) {

	if (message_a == null || socket_d == null)
	    return false;

	// parse the message
	//
        StringTokenizer st = new StringTokenizer(message_a, "|");
	
	// first token is message type
	//
        int type = -1;
	try {
	    type = Integer.parseInt(st.nextToken());
	}
	catch (Exception e) {
	    System.out.println("handleMessage: Error parsing message[" +
			       message_a + "]. Should begin with an 'int'. " +
			       "Not sure what sort of message client " + 
			       "is trying to send");
	    return false;
	}
        
	System.out.println("DemoClientHandler.handleMessage: messagetok[" 
			   + type + "] message[" + message_a + "]");
	
        switch (type) {
	    
	case DEMO_SPEECH_RECOGNITION: {

	    demo_type_d = SPEECH_RECOGNITION;
	    demo_name_string_d = "Speech Recognition";
	    break;
	}
	    
	case DEMO_SPEECH_ANALYSIS: {
	    
	    demo_type_d = SPEECH_ANALYSIS;
	    demo_name_string_d = "Speech Analysis";
	    break;
	}
	    
	case DEMO_SPEAKER_VERIFICATION: {
	    
	    demo_type_d = SPEAKER_VERIFICATION;
	    demo_name_string_d = "Speech Verification";
	    break;
	}

	case DEMO_DIALOG_NAVIGATION: {
	    
	    demo_type_d = DIALOG_NAVIGATION;
	    demo_name_string_d = "Dialog Navigation";
	    break;
	}
	    
	    // This message isn't being used by the client at all right now
	    //
	case MSG_KILL_SERVER: {
	    System.out.println("MSG_KILL_SERVER received but it doesn't "
			       + "do anything.");
	    //player_d.setName(st.nextToken());
	    //sendMessage(MSG_SET_NAME + "|" + player_d.getName());
	    
	    break;
	}
	    
	    // set name
	    //
	case MSG_SET_NAME: {	    
	    player_d.setName(st.nextToken());
	    // The client just sent us it's name. We don't need to send back.
	    //sendMessage(MSG_SET_NAME + "|" + player_d.getName());	    
	    break;
	}
	    
	    // set client ip address
	    //
	case MSG_CLIENT_IP_ADDRESS: {
	    
	    player_d.setIPAddress(st.nextToken());
	    // The client just sent us it's IP. We don't need to send back.
	    //sendMessage(MSG_CLIENT_IP_ADDRESS + "|" + 
	    //		player_d.getIPAddress());
	    break;
	}
	    
	    // set client port number
	    //
	case MSG_CLIENT_PORT: {
	    
	    try {
		player_d.setPortNumber((Integer.decode(
				        st.nextToken())).intValue());
	    }
	    catch (Exception ex) {
		System.out.println("Caught exception while Integer.decode() " +
				   "on player_d setPortNumber");
		ex.printStackTrace();
		System.out.println("Continuing...results are not reliable\n");
	    }
	    break;
	}

	case MSG_DATA:

	    // nothing is in here yet
	    break;

	    // This grouping here assumes that the player information has
	    // already been set and the type of the demo set and everything.
	    // If it hasn't, it won't find the correct 'demo_type_d' and it'll
	    // merely error out.
	    //
	case MSG_START_DEMO:
	case MSG_START_SPEECH_ANALYSIS:
	case MSG_START_SPEECH_RECOGNITION:
	case MSG_START_DIALOG_NAVIGATION:
	case MSG_START_SPEAKER_VERIFICATION: {
	    
	    System.out.println("begin DemoClientHandler." + 
			       "handleMessage:");
	    System.out.println("name: " + player_d.getName());
	    System.out.println("id: " + player_d.getID());
	    System.out.println("ip: " + player_d.getIPAddress());
	    System.out.println("port: " + player_d.getPortNumber());
	    System.out.println("end MSG_CLIENT_PORT");
	    
	    String template = null;
	    File inFile;
	    
	    // Error check first to make sure it is defined
	    if (demo_type_d == DEMO_UNDEFINED) {
		//error, send msg back saying server problems
		error_happened_cleanup_client
		    ("Type of demo remains unset. Failing out.");
		return false;	
	    }

	    // SPEECH ANALYSIS - Reading the file and executing script here
	    //
	    else if (demo_type_d == SPEECH_ANALYSIS) {
     
		//find template name

		// Change the PGM file
		//
		if (player_d.getName().equals("false")) {
		    template = SPEECH_ANALYSIS_SERVER_TEMPLATE_PGM;
		}
		else {
		    template = SPEECH_ANALYSIS_CLIENT_TEMPLATE_PGM;
		}

		try {
		    speech_analysis_pgm_d = File
			.createTempFile("speech_analysis_" + "0", ".pgm");
		    speech_analysis_pgm_d.deleteOnExit();
		}
		catch (Exception e) {
		    e.printStackTrace();
		    error_happened_cleanup_client
			("Unable to find Speech Analysis template");
		    return false;
		}

		String[] keywords = new String[] {
		    "DATA_RECORDER_PORT", 
		    "SIGNAL_DETECTOR_PORT", 
		    "CLIENT_IPADDRESS",
		    "CLIENT_PORTNUMBER",
		    "HUB_PORTNUMBER",
		    "START_SERVERS_PGM"
		};
		String replacements[] = new String[] {
		    getDataRecorderPort() + " ",
		    getSignalDetectorPort() + " ",
		    player_d.getIPAddress(),
		    player_d.getPortNumber() + " ",
		    hub_port_d + " ",
		    temp_file_outpath_d + speech_analysis_pgm_d.getName()
		};

		changeScriptFile(template,
				 speech_analysis_pgm_d,
				 keywords,
				 replacements
				 );

		// Change the CSH
		//
		try {
		    speech_analysis_csh_d = File
			.createTempFile("speech_analysis_" + "0", ".csh");
		    speech_analysis_csh_d.deleteOnExit();
		}
		catch(Exception e) {
		    e.printStackTrace();
		    System.out.println("Continuing running...");
		}

		changeScriptFile(SPEECH_ANALYSIS_TEMPLATE_CSH,
				 speech_analysis_csh_d,
				 keywords,
				 replacements
				 );

		// Create this group object
		//
		if (loadServersFromCSH
		    (speech_analysis_csh_d, group_runner_d) == false) {
		    group_runner_d.killAll();
		    error_happened_cleanup_client
			("Unable to load servers from CSH file");
		    return false;
		}

		System.out.println("script modified");
		// Now the demo should be all setup and ready to execute
	    }

	    // SPEECH_RECOGNITION - Reading the file and executing script here
	    //	    
	    else if(demo_type_d == SPEECH_RECOGNITION) {
	       
		if (player_d.getName().equals("false")) {
		    template = SPEECH_RECOGNITION_SERVER_TEMPLATE_PGM;
		}
		else {
		    template = SPEECH_RECOGNITION_CLIENT_TEMPLATE_PGM;
		}


		try {
		    speech_recognition_pgm_d = File
			.createTempFile("speech_recognition_" + "0", ".pgm");
		    speech_recognition_pgm_d.deleteOnExit();
		}
		catch (Exception e) {
		    e.printStackTrace();
		    error_happened_cleanup_client
			("Unable to find Speech Recognition template");
		    return false;
		}

		String[] keywords = new String[] {		
		    "DATA_RECORDER_PORT", 
		    "SIGNAL_DETECTOR_PORT", 
		    "SPEECH_RECOGNIZER_PORT", 
		    "CLIENT_IPADDRESS", 
		    "CLIENT_PORTNUMBER", 
		    "HUB_PORTNUMBER", 
		    "START_SERVERS_PGM"
		};

		String replacements[] = new String[] {
		    getDataRecorderPort() + " ",
		    getSignalDetectorPort() + " ",
		    getSpeechRecognizerPort() + " ",
		    player_d.getIPAddress(),
		    player_d.getPortNumber() + " ",
		    hub_port_d + " ",
		    temp_file_outpath_d + speech_recognition_pgm_d.getName()
		};

		changeScriptFile(template,
				 speech_recognition_pgm_d,
				 keywords,
				 replacements
				 );		
		try {
		    speech_recognition_csh_d = File
			.createTempFile("speech_recognition_" + "0", ".csh");
		    speech_recognition_csh_d.deleteOnExit();
		}
		catch(Exception e) { 
		    e.printStackTrace();
		    error_happened_cleanup_client
			("Unable to find Speech Recognition template");
		    return false;
		}
		changeScriptFile(
				 SPEECH_RECOGNITION_TEMPLATE_CSH,
				 speech_recognition_csh_d,
				 keywords,
				 replacements
				 );

		// Create this group object
		//
		if (loadServersFromCSH
		    (speech_recognition_csh_d, group_runner_d) == false) {
		    group_runner_d.killAll();
		    error_happened_cleanup_client
			("Unable to load servers from CSH file");
		    return false;
		}
		 
		System.out.println("script modified");
		// Now the demo should be all setup and ready to execute
	    }	    

	    else if(demo_type_d == SPEAKER_VERIFICATION) {
	       
		if (player_d.getName().equals("false")) {
		    template = SPEAKER_VERIFICATION_SERVER_TEMPLATE_PGM;
		}
		else {
		    template = SPEAKER_VERIFICATION_CLIENT_TEMPLATE_PGM;
		}


		try {
		    speaker_verification_pgm_d = File.createTempFile(
				       "speaker_verification_" + "0", ".pgm");
		    speaker_verification_pgm_d.deleteOnExit();
		}
		catch (Exception e) { 
		    e.printStackTrace();
		    System.out.println("Continuing running...");
		}
		
		String[] keywords = new String[] {
		    "DATA_RECORDER_PORT", 
		    "SIGNAL_DETECTOR_PORT", 
		    "SPEAKER_VERIFICATION_PORT", 
		    "CLIENT_IPADDRESS", 		   
		    "CLIENT_PORTNUMBER", 
		    "HUB_PORTNUMBER", 
		    "START_SERVERS_PGM"
		};
		String replacements[] = new String[] {
		    getDataRecorderPort() + " ",
		    getSignalDetectorPort() + " ",
		    getSpeakerVerifierPort() + " ",
		    player_d.getIPAddress() + " ",
		    player_d.getPortNumber() + " ",
		    hub_port_d + " ",
		    temp_file_outpath_d + speaker_verification_pgm_d.getName()
		};

		changeScriptFile(template,
				 speaker_verification_pgm_d,
				 keywords,
				 replacements
				 );		

		try {
		    speaker_verification_csh_d = File.createTempFile
			("speaker_verification" + "0", ".csh");
		    speaker_verification_csh_d.deleteOnExit();
		}
		catch(Exception e) {
		    e.printStackTrace();
		    error_happened_cleanup_client
			("Unable to find Speaker Verification template");
		    return false;
		 }

		changeScriptFile(
				 SPEAKER_VERIFICATION_TEMPLATE_CSH,
				 speaker_verification_csh_d,
				 keywords,
				 replacements
				 );

		// Create this group object
		//
		if (loadServersFromCSH
		    (speaker_verification_csh_d, group_runner_d) == false) {
		    group_runner_d.killAll();
		    error_happened_cleanup_client
			("Unable to load servers from CSH file");
		    return false;
		}

		System.out.println("script modified");
	    }

	    else if(demo_type_d == DIALOG_NAVIGATION) {

		// Change the PGM file
		if (player_d.getName().equals("false")) {
		    template = DIALOG_NAVIGATION_SERVER_TEMPLATE_PGM;
		}
		else {
		    template = DIALOG_NAVIGATION_CLIENT_TEMPLATE_PGM;
		}

		try {
		    dialog_navigation_pgm_d = File.createTempFile(
					  "dialog_navigation_" + "0", ".pgm");
		    dialog_navigation_pgm_d.deleteOnExit();
		}
		catch (Exception e) {
		    e.printStackTrace();
		    error_happened_cleanup_client
			("Unable to find Dialog Navigation template");
		    return false;
		}

		String[] dialog_keywords = new String[] {
		    "DATA_RECORDER_PORT", 
		    "SIGNAL_DETECTOR_PORT", 
		    "SPEECH_RECOGNIZER_PORT",
		    "DIALOG_NAVIGATION_MANAGER_PORT",
		    "DIALOG_NLP_PORT",
		    "DIALOG_NAVIGATION_DATABASE_PORT",
		    "CLIENT_IPADDRESS", 
		    "CLIENT_PORTNUMBER",
		    "HUB_PORTNUMBER",
		    "START_SERVERS_PGM",
		    "UNIQUE_PLAYER_ID"
		};
		String [] dialog_replacements =  new String[] {
		    getDataRecorderPort() + " ",
		    getSignalDetectorPort() + " ",
		    getSpeechRecognizerPort() + " ",
		    getDialogNavigationManagerPort() + " ",
		    getDialogNLPPort() + " ",
		    getDialogNavigationDatabasePort() + " ",
		    player_d.getIPAddress() + " ",
		    player_d.getPortNumber() + " ",
		    hub_port_d + " ",
		    temp_file_outpath_d + dialog_navigation_pgm_d.getName(),
		    player_d.getID() + ""
		};


		changeScriptFile(template,
				 dialog_navigation_pgm_d,
				 dialog_keywords,
				 dialog_replacements
				 );

		// Change the CSH
		try {
		    dialog_navigation_csh_d = File.createTempFile(
					   "dialog_navigation_" + "0", ".csh");
		    dialog_navigation_csh_d.deleteOnExit();
		}
		catch(Exception e) {
		    e.printStackTrace();
		    System.out.println("Continuing running...");
		}

		changeScriptFile(
				 DIALOG_NAVIGATION_TEMPLATE_CSH,
				 dialog_navigation_csh_d,
				 dialog_keywords,
				 dialog_replacements
				 );

		// Create this group object
		//
		if (loadServersFromCSH
		    (dialog_navigation_csh_d, group_runner_d) == false) {
		    group_runner_d.killAll();
		    error_happened_cleanup_client
			("Unable to load servers from CSH file");
		    return false;
		}

	    }
	    else {
		System.out.println("handleMessage(): MSG_START_DEMO: Unable " +
				   "to find type[" + demo_type_d + "]. " +
				   "Sending error to client to exit.");
		error_happened_cleanup_client
		    ("Unable to find template requested. " +
		     "Please try demo again.");
		return false;
	    }

	    // When we get here, the group runner has the servers all in it
	    // so now just run it
	    boolean ret = group_runner_d.startAllServers();
	    System.out.println("startAllServers command returned[" +
			       ret + "]");
	    if (ret == false) {
		error_happened_cleanup_client
		    ("Error: Group Runner failed to start all servers.");
		return false;
	    }

	    break;
	} //end MSG_START_DEMO

	    
	case MSG_SET_SERVER: {
	    System.out.println("Received a MSG_SET_SERVER command. This is " +
			       "some old code that is not " +
			       "supposed to be used!");
	    break;
	}
	    /*
	    // set the server
	    //
	case MSG_SET_SERVER: {
	    
	    String server_name = st.nextToken();
	    String server_pid = st.nextToken();
	    
	    server_list_d.add(server_name);
	    
	    server_name_map_d.put(server_name, server_pid);
	    
	    System.out.println("===============================");

	    for (int i = 0 ; i < server_list_d.size(); i++){
		
		System.out.println("(" +
				   server_name_map_d.get(
						(String)server_list_d.get(i))
				   + ") " 
				   + server_list_d.get(i) 
				   + " has been started");
	    }
	    
	    System.out.println("===============================");

	    break;
	}
	    */
	    /*	    
	    // start the speech analysis server
	    //
	case MSG_START_SPEECH_ANALYSIS: {
	    
	    try {
		
		//DEBUG
		System.out.println("Entering start_speech_analysis. Calling "+
				   "GroupRunner.start()");
		
		// Check to make sure the obj doesnt equal null anymore
		if (group_runner_d == null) {
		    System.out.println("Unable to find GroupRunner object");
		    //TODO, return server malfunction
		    return false;
		}		    

		boolean ret = group_runner_d.startAllServers();
		System.out.println("startAllServers command returned[" +
				   ret + "]");
		if (ret == false) {
		    // TODO
		    // here need to now return "DEMO START FAILED TO START
		    return false;
		}
	    */	    //HERE
		/*String command;
		
		command = new String(" sh /tmp/" +
				     speech_analysis_csh_d.getName());
		System.out.println("this is the command " + command);
		
		// start a process
		//
		process_d = Runtime.getRuntime().exec(command);
		
		// get the input for this process
		//		
		BufferedReader std_input_d = new BufferedReader(new 
				InputStreamReader(process_d.getInputStream()));
		
		// get the output for this process
		//
		BufferedReader std_error_d = new BufferedReader(new 
		    InputStreamReader(process_d.getErrorStream()));
		
		// ask the message to handle the message
		// 
		message_handler_d = new MessageHandler(std_input_d, 
						       std_error_d);
		*/
	    /* HERE
	    }
	    catch(Exception e) {
		System.out.println("-> Could not start the server ");
		e.printStackTrace();
		System.exit(1);
	    }
	    
	    System.out.println("speech analysis demo started!");
	    
	    break;
	}


	    // start the speech analysis server
	    //
	case MSG_START_SPEECH_RECOGNITION: {
	    
	    try {
		
		String command;
		
		command = new String(" sh /tmp/" + 
				     speech_recognition_csh_d.getName());
		System.out.println("this is the command " + command);
		
		// start a process
		//
		process_d = Runtime.getRuntime().exec(command);
		
		// get the input for this process
		//		
		BufferedReader std_input_d = new BufferedReader(new 
			InputStreamReader(process_d.getInputStream()));
		
		// get the output for this process
		//
		BufferedReader std_error_d = new BufferedReader(new 
			InputStreamReader(process_d.getErrorStream()));
		
		// ask the message to handle the message
		// 
		message_handler_d = new MessageHandler(std_input_d, 
						       std_error_d);
	    }
	    catch(IOException e) {
		System.out.println("-> Could not start the server ");
		System.exit(1);
	    }
	    
	    System.out.println("speech recognition demo started!");
	    
	    break;
	}

	    
	    // start a dialog server
	    //
	case MSG_START_DIALOG_NAVIGATION: {

	    try {
		
		String command;
		
		command = new String(" sh /tmp/" + 
				     dialog_navigation_csh_d.getName());

		System.out.println("this is the command " + command);
		
		// start a process
		//
		process_d = Runtime.getRuntime().exec(command);
		
		// get the input for this process
		//		
		BufferedReader std_input_d = new BufferedReader(new 
				InputStreamReader(process_d.getInputStream()));
		
		// get the output for this process
		//
		BufferedReader std_error_d = new BufferedReader(new 
		    InputStreamReader(process_d.getErrorStream()));
		
		// ask the message to handle the message
		// 
		message_handler_d = new MessageHandler(std_input_d, 
						       std_error_d);
	    }
	    catch(IOException e) {
		System.out.println("-> Could not start the server ");
		e.printStackTrace();
		System.exit(1);
	    }
	    
	    System.out.println("dialog navigation demo started!");
	    
	    break;
	}
	
	case MSG_START_SPEAKER_VERIFICATION: {
	    
	     try {
		
		 String command;
		 
		 command = new String(" sh /tmp/" +
				      speaker_verification_csh_d.getName());
		 System.out.println("this is the command " + command);
		 
		 // start a process
		 //
		 process_d = Runtime.getRuntime().exec(command);
		 
		 // get the input for this process
		 //		
		 BufferedReader std_input_d = new BufferedReader(new 
		     InputStreamReader(process_d.getInputStream()));
		 
		 // get the output for this process
		 //
		 BufferedReader std_error_d = new BufferedReader(new 
		     InputStreamReader(process_d.getErrorStream()));
		 
		 // ask the message to handle the message
		 // 
		 message_handler_d = new MessageHandler(std_input_d, 
							std_error_d);
	     }
	     catch(IOException e) {
		 System.out.println("-> Could not start the server ");
		 e.printStackTrace();
		 System.exit(1);
	     }
	     
	     System.out.println("speaker verification demo started!");
	     
	     break;
	}
	    */ //HERE
	default: {
		System.out.println("Unknown message in handleMessage() [" +
				   type + "] message[" + message_a + "]");
	    }
	    
        } // end of switch
	
	// exit gracefully
	//
	return true;
    }

    /**
     * This function takes in the following parameters. It will open the
     * "in" file and read it line by line. It will replace all the "keywords"
     * with the data in the "replacements" array of the same array index. In
     * other words, they are parallel arrays of key\replacement. The result
     * is written to the open file pointer.
     *
     * @param in_filename_a String name of file to read script\program from.
     * @param out_file_a File object of temporary file to write
     * @param keywords_a Parallel Array of keywords to locate and change
     * @param replacements_a Parallel Array of replacements for the keywords
     *
     * @return boolean value with result. if bad result will send msg to client
     */
    private boolean changeScriptFile(
			     String in_filename_a,
			     File out_file_a,
			     String[] keywords_a,
			     String[] replacements_a) {

	// Error checking
	//
	if (in_filename_a == null || out_file_a == null || 
	    keywords_a == null || replacements_a == null) {

	    System.out.println("Null variables passed into changeScriptFile");
	    return false;
	}
	if (keywords_a.length != replacements_a.length) {
	    
	    System.out.println("The keywords and replacement arrays are not " +
			       "the same length. Error!\n");
	    return false;
	}

	// Since the in-file is given as a string, make it a file object
	//
	File in_file_d = new File(in_filename_a);
	try {
	    // Open read and write files
	    //
	    BufferedReader bufRdr  = new BufferedReader(
				       new FileReader(in_file_d)); 
	    BufferedWriter bufWrtr = new BufferedWriter(
				       new FileWriter(out_file_a)); 
	    String line = null;

	    // For each line in the file
	    while ((line = bufRdr.readLine()) != null) {

		// Replace the keywords with respective replacements
		for (int i = 0; i < keywords_a.length ; i++) {
		    line = line.replaceAll(keywords_a[i],
					   replacements_a[i]);
		}

		bufWrtr.write(line); 
		bufWrtr.newLine(); 
	    }
	    
	    bufRdr.close(); 
	    bufWrtr.close(); 
	}
	catch(IOException e) {
	    System.out.println("-> [changeScript] Could not create the " +
			       "parameter files. Exiting DemoClientHandler.");

	    e.printStackTrace();
	    //System.exit(1);
	    error_happened_cleanup_client
		("changeScript(): Error when writing out files.");
	    return false;
	}
	return true;
    }


    /**
     * This function takes in the following parameters. It will open the
     * "in" file and read it line by line. It will replace all the "keywords"
     * with the data in the "replacements" array of the same array index. In
     * other words, they are parallel arrays of key\replacement. The result
     * is written to the open file pointer.
     *
     * @param csh_file_d File object of an open CSH file.
     * @param gr_a GroupRunner object that will store all the Processes
     *
     * @return boolean value with result. if bad result will send msg to client
     */
    boolean loadServersFromCSH(File csh_file_d, GroupRunner gr_a) {

	// Error checking
	//
	if (csh_file_d == null || gr_a == null) {
	    System.out.println("loadServersFromCSH: Entered with null params");
	    return false;
	}

	// The input buffer for the File object
	//
	BufferedReader bufRdr;
	
	// the line of the file itself. Will end up containing just the command
	//
	String line = null;

	// Will contain the filename of the output file
	//
	String stdout_log;
	
	//	Vector csh_server_lines = new Vector(5,1);
	//	String hub_server_line = null;
	    
	try {
	    // Open read and write files
	    //
	    bufRdr  = new BufferedReader(new FileReader(csh_file_d)); 

	    // For each line in the file
	    while ((line = bufRdr.readLine()) != null) {

		//System.out.println("Found line of CSH as [" + line + "]");
		stdout_log = "";

		// If line is blank or comment, keep going...
		if (line.equals("") || line.charAt(0) == '#')
		    continue;
		
		// dont want to save the "sleep" line command
		if (line.startsWith("sleep"))
		    continue;

		// This will be a good server line. Replace all $VAR's
		//
		line = replaceEnv(line);

		// Lets see if it ends with a " &" because we don't need them
		// to do that anymore
		//
		if (line.indexOf('&') != -1) {
		    line = line.substring(0,line.indexOf('&'));
		}

		// Lets see if we are piping to a file. If we are, we need
		// to extract it
		if (line.indexOf('>') != -1) {
		    // There are 3 ways of doing this.
		    // -a- This method puts stdout to out1.txt
		    //    command > out1.txt
		    //
		    // -b- This method puts stdout to out2.txt
		    //    command 1> out2.txt
		    //
		    // -c- This method puts stderr to err1.txt
		    //    command 2> err1.txt
		    //
		    // There are also other ways of piping redirection
		    // but other than worrying, I am just going to handle
		    // stdout going to a file and stderr going to screen. If
		    // 2> is present, that is going to be an error!
		   
		    //int stderr_test = line.indexOf("2>");
		    if (line.indexOf("2>") != -1) {
			// TODO, no piping to stderr allowed, or else it
			// causes some complex redirection stuff
			error_happened_cleanup_client
			    ("Found a stderr redirect in the CSH file");
			return false;
		    }

		    // Now I don't want to have any 1> present because that
		    // will mess things up. Replace them with just >
		    //		    
		    line = line.replaceAll(">1", ">");

		    // Now we shouldn't have any "2>" or "1>" so i am going to
		    // assume there is at most one single > and everything to
		    // the right of it is the filename to write to
		    //
		    int begin = line.indexOf(">");

		    // Now everything from > to the end of string is the file
		    // name
		    //
		    stdout_log = line.substring(begin+1).trim();

		    // And everything from beginning to the > is the command
		    //
		    line = line.substring(0,begin).trim();
		}

		// DEBUG
		//System.out.println("Found [" + line + "] having stdout " +
		//	   "log of [" + stdout_log + "]");
		// ENDDEBUG

		// See if "/bin/hub" is written there. if it is, it is
		// the "hub" server and is kept separate from the rest
		//
		if (line.indexOf("/bin/hub") != -1) {
		    
		    // Save hub right here
		    if (gr_a.setHubLine(line, stdout_log) == false) {
			gr_a.killAll();
			error_happened_cleanup_client
			    ("Error when saving the hub execution line to GR");
			System.out.println("Calling killAll() on GR #3");
			return false;
		    }
		}

		// Else the only thing left when I get here should be
		// "isip_comm_*" commands. I am not going to make sure it
		// starts with "isip_comm" but instead going to just assume
		// it is correct.
		//
		else {
		    if (gr_a.addServerLine(line, stdout_log) == false) {
			gr_a.killAll();
			error_happened_cleanup_client
			    ("Error when saving a server execution " +
			     "line to GR");
			System.out.println("Calling killAll() on GR #2");
			return false;
		    }			
		}
	    }
	    
	    bufRdr.close(); 
	}
	catch(IOException e) {
	    System.out.println("-> [loadServersFromCSH] Could not read the " +
			       "CSH file. Sending message to client telling " +
			       "there is an error.");
	    e.printStackTrace();

	    error_happened_cleanup_client
		("loadServersFromCSH: unable to read CSH file");

	    return false;
	}

	// Ok, this GR is filled up and ready to go!

	return true;
    }

    /**
     * This class wraps up the following stuff:
     * - Process
     * - Stdout output
     * - Stderr output
     *
     * The choices for stdout output are
     *     - standard, the screen
     *     - file, a file must be passed in
     *
     * stderr will always be written to the screen
     */
    public class ProcessAndOutput implements Runnable {

	/**
	 * Process name as a String
	 * eg "/cavs/bin/isip_comm_signal detector -khz 16000 -option"
	 */
	protected String process_name_d;

	/**
	 * Name of outfile if there is to be one
	 * Null value means write to stdout
	 */
	protected String outfile_name_d;

	/**
	 * Once the process is executed, this is the process pointer
	 */
	protected Process proc_d;

	/**
	 * Determines if this thread has exited. The thread is the piece
	 * of that that polls the stdout and stderr BufferedReader's to see
	 * if this server or hub has written something out
	 *
	 * false - It hasn't run or is current running
	 * true  - It has run and has exited
	 */       
	protected boolean thread_exited = false;

	/**
	 * Determines if this thread has started. 
	 *
	 * false - It hasn't run
	 * true  - It is running or has run and has exited
	 */       
	protected boolean thread_started = false;

	/**
	 * The thread for this output monitoring. It polls proc_d's
	 * stdout\stderr and prints out the output to the respective location
	 */
	protected Thread thread_d;

	/**
	 * The Readers for the respective stdout and stderr streams of the
	 * process being executed by this ProcessAndOutput object
	 */
	protected BufferedReader stdout_in_d = null;
	//protected InputStreamReader stdout_in_d2 = null;
	protected BufferedReader stderr_in_d = null;

	/**
	 * The logfile object that will be NOT null if there is an open file
	 * that stdout is to be written to
	 */
	protected BufferedWriter logfile_out_d = null;

	/**
	 * Constructs a new ProcessAndOutput object
	 *
	 * @param proc_a string that is the line to execute
	 */	
	public ProcessAndOutput(String proc_a) {
	    process_name_d = proc_a;
	    outfile_name_d = null;
	} // end constructor

	/**
	 * Constructs a new ProcessAndOutput object
	 *
	 * @param proc_a string that is the line to execute
	 * @param stdout_filename_a string that contains the filename to write
	 */	
	public ProcessAndOutput(String proc_a, String stdout_filename_a) {
	    process_name_d = proc_a;

	    // Empty filename is the same as null
	    //
	    if (stdout_filename_a == null || stdout_filename_a.equals("")) {
		outfile_name_d = null;
	    }
	    else {
		outfile_name_d = stdout_filename_a;
	    }
	} // end constructor

	/**
	 * Returns the name of the process currently stored in this object
	 *
	 * @return a guaranteed non-null, possibly empty, server name string
	 */
	public String getServerString() {
	    if (process_name_d == null)
		return "";
	    return process_name_d;
	}

	/**
	 * Returns the name of the stdout file currently stored in this object
	 *
	 * @return a guaranteed non-null, possibly empty, server name string
	 */
	public String getStdOutputString() {
	    if (outfile_name_d == null)
		return "";
	    return outfile_name_d;
	}

	/**
	 * Returns the process object of this object
	 *
	 * @return process object of the process in this object. may be null or
	 * uninitalized
	 */
	public Process getProcess() {
	    return proc_d;
	}

	/**
	 * Gives the class-member outfile_name_d a valid file pointer if
	 * there has been a stdout outfile string set
	 *
	 * @return a boolean value indicating status.
	 */
	public boolean makeOutFile() {
	    if (outfile_name_d == null)
		return true;
	    
	    try {
		// Create the file object
		//
		File out_file_a = new File(outfile_name_d);
		
		// Now open it and save object pointer
		//
		logfile_out_d = new
		    BufferedWriter(new FileWriter(out_file_a)); 
	    }
	    catch (Exception e) {
		System.out.println("ProcessAndOutput: Failed attempting to " +
				   "open the file [" + outfile_name_d + "] " +
				   "for writing.");
		e.printStackTrace();
		logfile_out_d = null;
		return false;
	    }
	    return true;

	}
	/**
	 * Will take the process string and attempt to create it. Then it will
	 * take that object and give it stream readers that either redirect
	 * to stdout\stderr or file\stderr
	 *
	 * @return a boolean value indicating status.
	 */
	public boolean startRunning() {
	    
	    // These are one-time starts. Make sure this hasn't been started
	    // before
	    if (thread_started == true) {
		return false;
	    }

	    Runtime staticRuntime = Runtime.getRuntime();
	    //runtime cannot be null;

	    if (process_name_d == null || process_name_d.equals(""))
		return false;
		
	    try {
		// Run the process
		//
		proc_d = staticRuntime.exec((String)process_name_d);
		if (proc_d == null)
		    return false;

		// If we get here, we have a successful process running!
		// time to setup the stdout\stderr listeners. Also open
		// up the outfile if we need to.
	
		// get the input for this process
		//		
		stdout_in_d = new BufferedReader
		    (new InputStreamReader(proc_d.getInputStream()));
		//stdout_in_d2 
		//  = new InputStreamReader(proc_d.getInputStream());
		
		// get the output for this process
		//
		stderr_in_d = new BufferedReader
		    (new InputStreamReader(proc_d.getErrorStream()));
	       
		// This will either make the output file or get stdout ready.
		// It will do everything internally
		//
		if (makeOutFile() == false)
		    return false;
		
		// Finally need to start a thread to do all the processing
		// of these streams.
		//
		thread_d = new Thread(this);
		thread_d.start();
	    }
	    catch (Exception e) {
		System.out.println("Caught exception in startRunning() [" +
				   e.getClass().getName() + "] Killing obj");
		e.printStackTrace();
		destroy();
		return false;	
	    }
	    
	    return true;

	} // end startRunning	    

	/**
	 * Looks at the stdout stream from the process and the stderr stream
	 * from the process. If either are ready, it reads a byte and then
	 * writes it to where it needs to be written. The final_read_a=true
	 * case is happier about catching exceptions since it won't be coming
	 * into this loop again and if the streams are closed, it's not a big
	 * deal.
	 *
	 * @param final_read_a boolean true if this is the final read
	 *
	 * @return a boolean value indicating status.
	 */	
	protected synchronized boolean do_process_IO_read(boolean
							  final_read_a) {

	    String s;

	    try {
		/*
		  System.out.println("name[" + 
		  process_name_d.substring(0,35) + "] in[ " 
		  + stdout_in_d.ready() + "] err[" + 
		  stderr_in_d.ready() + "]");
		*/
		/*
		  Usage:
		  bufWrtr.write(line); 
		  bufWrtr.newLine(); 
		*/
		// print out the in stream message
		//
		//char[] buf = new char[11];
		char c;

		// I tried different ways of reading the stream. The best way
		// I found was to check to see if it was ready, then get a byte
		// and write it out.
		//
		while (stdout_in_d.ready() && 
		       (c = (char)stdout_in_d.read()) != -1) {

		    s = "" + c;			
		    
		    // We have the stdout line, so now check to see if
		    // a outfile exists. If it doesn't we just write to
		    // the screen
		    //
		    if (logfile_out_d == null) {
			//System.out.print("Out[" 
			//+ process_name_d.substring(0,35) + "] ["+  s);
			System.out.print(s);
			System.out.flush();
		    }
		    else {
			logfile_out_d.write(s);
			// Only writing a character at a time so no newline
			// anymore
			//
			//logfile_out_d.newLine();
			logfile_out_d.flush();
		    }
		}

		// Still do stderr this way because it would mix lines very
		// badly if it was not. I didn't have any issues using this
		// but if someone finds something wrong and needs to do a
		// a character at a time, just follow the above example.
		//
		// print out error message in case of error
		//
		while (stderr_in_d.ready() &&
		       (s = stderr_in_d.readLine()) != null) {
		    // This is where we would write stderr to a file
		    // but we don't, so just write to screen.
		    //
		    System.out.println("StdErr:" + s);
		    System.out.flush();
		}
	    }

	    catch (Exception e) {
		// If this is a final read, I don't care about exceptions
		// because it is about to exit anyway
		//
		if (final_read_a) {
		    System.out.println("Caught exception in " +
				       "FINAL READ process IO function. " +
				       "Not a big deal");
		    e.printStackTrace();
		    return true;
		}
		    
		System.out.println("Caught exception in process IO function");
		e.printStackTrace();
		return false;
	    }		

	    return true;
	}
	
	
        /** 
	 * When this thread is started, this method is called. This is a
	 * polling thread that will read from this process's stdout and stderr
	 * streams and write to specified place. Sets thread_started on enter
	 * and thread_exited on end
	 */
        public void run() {
	    
	    // local variable
	    //		
	    String s;

	    // Make sure this hasn't been run once before. These are one-time
	    // use.
	    synchronized(this) {
		if (thread_started == true) {
		    return;
		}
	    }

	    // Set saying this has started
	    synchronized(this) {
		thread_started = true;
	    }
	     
	    try {
		// While the main class does not have the thread pointer set
		// to null. Run this infinite loop
		//
		while (thread_d != null) {

		    // Attempt to read the i\o streams and write them out to
		    // disk or screen
		    //
		    if (do_process_IO_read(false) == false) {
			System.out.println("ProcessAndOutput: Error " +
					   "occurred in run() " +
					   "so exiting this thread.");
			destroy();			
			break;
		    }

		    try {
			Thread.sleep(600);
		    }
		    catch (Exception e) { 
			// dont care about exceptions here
		    };

		} // end while
	    } // end try
	    catch (Exception e) {
		System.out.println("ProcessAndOutput: Error occurred in run() "
				   + "so exiting this thread.");
		e.printStackTrace();
		destroy();
	    }
	    synchronized(this) {
		thread_exited = true;
	    }    
        }
	
	/**
	 * Destroys this thread and kill process and close all I/O streams
	 */
        public void destroy() {
	    synchronized(this) {
		thread_d = null;
	    }
	    
	    try {
		System.out.println("forcefully killing[" + 
				   process_name_d + "]");
	    }
	    catch (Exception e) {
		// don't care
	    }

	    // While the server is still being monitored by the listening
	    // thread, wait for it to die
	    //
	    if (thread_started == true) {
		while (thread_exited == false) {
		    try {
			Thread.sleep(200);
		    }
		    catch(Exception e) {
			// Don't need to catch anything here
		    }
		}
	    }

	    // Ok, now that we are here, the thead is exited, so can
	    // now kill all the files that are open
	    //
	    try { proc_d.destroy(); }
	    catch (Exception e) {
		// don't care
	    }

	    // Try and read rest of the buffers
	    //
	    do_process_IO_read(true);

	    // Now the process is closed, so close all the file I/O
	    //
	    try { stdout_in_d.close(); }
	    catch (Exception e) {
		// don't care
	    }
	    try { stderr_in_d.close(); }
	    catch (Exception e) {
		// don't care
	    }
	    try { logfile_out_d.close(); }
	    catch (Exception e) {
		// don't care
	    }

        }// end destroy()

    } // end ProcessAndOutput class

    /**
     * An inner class for running the "group" of servers
     */
    public class GroupRunner implements Runnable {

	// Variables read by both threads but written only
	// by base (eg, non-run() thread)
	//
	//protected Vector csh_server_pao_d = new Vector(5,1);
	protected Vector process_vector_d = new Vector(5,1);
	protected ProcessAndOutput hub_server_pao_d = null;
	protected int group_number_d = -1;

	// Variables written and read by both threads
	//
	protected boolean executingThread = false;
	protected boolean stayAlive = true;

	/**
	 * Constructs a new GroupRunner object.
	 *
	 */
	public GroupRunner() {
	    // Synchronized not needed here since no chance of thread-issues
	    //
	    executingThread = false;
	    stayAlive = true;
	}

	/**
	 * Constructs a new GroupRunner object with a unique number.
	 *
	 * @param player_a Player object that getID() is called on
	 */
	public GroupRunner(DemoPlayer player_a) {
	    // Synchronized not needed here since no chance of thread-issues
	    //
	    executingThread = false;
	    stayAlive = true;
	    group_number_d = player_a.getID();
	}

	/**
	 * NOT IMPLEMENTED-- Eventually may check to make sure there is at
	 * least one server and one hub.
	 *
	 * @return a boolean value indicating status.
	 */
	public boolean checkIntegrity() {
	    if (executingThread)
		return false;

	    // TODO, make sure we have a hub and at least one server
	    return true;
	}

	/**
	 * Adds a server to the list of servers in this Group
	 *
	 * @param server_a string that is the line to execute
	 * @param outfile_a string that contains the filename to write
	 *
	 * @return a boolean value indicating status.
	 */	
	public boolean addServerLine(String server_a, String outfile_a) {
	    try {
		// Create a new PaO object
		//
		ProcessAndOutput pao 
		    = new ProcessAndOutput(server_a, outfile_a);

		// Attempt to add this PaO to the process_vector
		//
		return addServerPaO(pao);
	    }
	    catch (Exception e) {
		return false;
	    }
	    // Unreachable down here
	}

	/**
	 * Adds a server to the list of servers in this Group. Returns false
	 * if the thread is executing or the storage devices aren't present.
	 *
	 * @param server_pao_a string that is the line to execute
	 *
	 * @return a boolean value indicating status.
	 */	
	public boolean addServerPaO(ProcessAndOutput server_pao_a) {
	    //String server_line_a, String outfile_a) {
	    if (executingThread)
		return false;

	    if (process_vector_d == null || server_pao_a == null)
		return false;

	    // Attempt to add this PaO to the process_vector
	    //
	    try {
		process_vector_d.add(server_pao_a);
	    }
	    catch (Exception e) {
		return false;
	    }

	    // DEBUG
	    System.out.println("Added server [" +
			       server_pao_a.getServerString() + "] out[" + 
			       server_pao_a.getStdOutputString()+"] to array");
	    // ENDDEBUG

	    return true;
	}


	public boolean setHubLine(String hub_a, String outfile_a) {
	    try {
		ProcessAndOutput pao 
		    = new ProcessAndOutput(hub_a, outfile_a);
		return setHubPaO(pao);
	    }
	    catch (Exception e) {
		return false;
	    }
	}

	/**
	 * Saves this as the hub in this Group. Returns false if the thread
	 * is executing or a hub has already been set.
	 *
	 * @param hub_pao_a string that is the line to execute
	 *
	 * @return a boolean value indicating status.
	 */	
	public boolean setHubPaO(ProcessAndOutput hub_pao_a) {
	    //String hub_line_a, String outfile_a) {
	    if (executingThread)
		return false;

	    //if (hub_server_line != null || hub_line_a == null) {
	    if (hub_server_pao_d != null || hub_pao_a == null) {
		// This is an error because we have a hub line set already
		return false;
	    }

	    hub_server_pao_d = hub_pao_a;

	    // DEBUG
	    System.out.println("Added hub [" + 
			       hub_pao_a.getServerString() + "] out[" + 
			       hub_pao_a.getStdOutputString()+"] to array");

	    // ENDDEBUG

	    return true;
	}

	/**
	 * This goes through all the servers and starts them. Then a few secs
	 * are waited and the hub is started. The hub is then saved into
	 * the same process_vector list as the servers. On failure calls
	 * killAll() so that any servers that were started are killed.
	 *
	 * @return a boolean value indicating status.
	 */	
	public boolean startAllServers() {
	    if (executingThread)
		return false;

	    executingThread = true;

	    System.out.println("Entering startAllServers()");
	    if (process_vector_d == null || hub_server_pao_d == null)
		return false;

	    //System.out.println("process_vector_d.size()[" + 
	    //	       process_vector_d.size() + "]");

	    // First go through the listing of all servers and start them
	    //
	    for (int i = 0; i < process_vector_d.size(); i++) {

		// Get the next ProcessAO and let it start itself
		//
		ProcessAndOutput process_ao =
		    (ProcessAndOutput)process_vector_d.elementAt(i);

		boolean result = process_ao.startRunning();

		if (result == false) {
		    // Then we failed to start this PaO
		    //
		    System.out.println("Failed to start [" +
				       process_ao.getServerString() + "]");
		    System.out.println("Killing all servers.");
		    killAll();
		    error_happened_cleanup_client("it failed");
		    return false;
		}
		
		// Give it a second to finish...
		try {
		    Thread.sleep(100);
		}
		catch(Exception e) {
		    //dont care about exceptions with the Sleep
		}	    
		
	    }// end for

	    // If we get here then we have all the servers started so now wait
	    // another second and start the hub
	    //
	    try {
		Thread.sleep(1000); // 1 seconds
	    }
	    catch(Exception e) {
		//dont care about exceptions with the Sleep
	    }
	    
	    try {

		boolean result = hub_server_pao_d.startRunning();
		if (result == false) {
		    // Then we failed to start the PaO
		    //
		    System.out.println("Failed to start hub [" +
				       hub_server_pao_d.getServerString() +
				       "]");
		    error_happened_cleanup_client
			("Process failed to start when starting hub");
		    return false;
		}
		//if we get here, then the process is running. Otherwise
		//it would have thrown an exception
		
		// So that everything is in one vector, add the hub to
		// the process vector
		//
		process_vector_d.add(hub_server_pao_d);
	    }
	    catch (Exception e) {
		System.out
		    .println("Caught exception [ " + e.getClass().getName()
			     + "] when starting the hub. Killing any " +
			     "servers already started.");
		e.printStackTrace();
		killAll();
		error_happened_cleanup_client
		    ("Caught exception when starting hub");
		System.out.println("DEBUG: killAll() called #5");
		return false;
	    } //end try\catch
	
	    // If we get here then all of servers + hub are successfully
	    // running and we need to continue to monitor them...so start
	    // up our thread and keep going
	    
	    Thread monitorServersThread = new Thread(this);
	    monitorServersThread.start();
	    
	    return true; 
	}
	
	public void run() {
	    
	    while (true) {
		//System.out.println("GR: Running thread now. Server count: " +
		//	   process_vector.size());

		//look to see if I am suppose to be alive
		//
		synchronized(this) {
		    if (stayAlive == false) {
			//whoops, I am suppose to exit.
			System.out.println("DEBUG: GR [" + group_number_d +
					   "] Thread() exited...");
			return;
		    }
		}

		// look at all to make sure all the servers are alive
		//
		// The way this works is that I call "exitValue()" on
		// each process. If it has NOT exited (Eg, still running)
		// it will throw a 
		// Throws:
		// IllegalThreadStateException - if the subprocess represented
		//              by this Process object has not yet terminated.
		// So I need to make sure I get that exception everytime
		//
		// Done backwards so I kill the hub first (hopefully)
		//
		for (int i = process_vector_d.size()-1; i >= 0 ; i--) {
		//for (int i = 0; i < process_vector.size(); i++) {
		    ProcessAndOutput pao_a = null;
		    try {
			pao_a = (ProcessAndOutput)process_vector_d.get(i);
		    }
		    catch (Exception e) {
			// if i get here then I got outofbounds
			// kill all servers cause this is bad
			//
			killAll();
			error_happened_cleanup_client
			    ("Caught exception when traversing process list");
			System.out.println("DEBUG: killAll() called #1");
			e.printStackTrace();
			return;
		    }

		    try {
			// exitValue() either throws an exception or returns
			// a number.
			// If it throws an exception that means I got a null
			// ptr or it hasn't exited. Hopefully it hasn't exited!
			//
			int ret = pao_a.getProcess().exitValue();
			System.out.println("Found server[" +
					   pao_a.getServerString() +
					   "] had a return value of [" +
 					   ret + "]");
		    }
		    catch (NullPointerException npe) {
			// This one isn't good.
			System.out.println("Got a null pointer exception" +
					   "Ending demo and exiting.");
			npe.printStackTrace();
			killAll();
			error_happened_cleanup_client
			    ("Got a null pointer error when checking on " +
			     "all processes");
			return;
		    }
		    catch (Exception e) {
			// Good! I hope to catch an exception. Now continue
			// to next element in array
			//System.out.println("Entered good catch place!");
			continue;
		    }

		    // If I get here, that means I missed the catch() above
		    // so one of the servers have exited. Now kill them all
		    killAll();
		    error_happened_cleanup_client
			("A server in process list ended execution. " +
			 "Ending demo");
		    System.out.println("DEBUG: killAll() called #2");
		    return;

		}//end for
		
		// Ok, all the servers were still running this loop. Wait
		// 10 seconds and then try again
		//
		try {
		    Thread.sleep(5000); // 5 seconds
		}
		catch(Exception e) {
		    //dont care about exceptions with the Sleep
		}	    
	    }
	}

	/**
	 * Kill all processes currently running. This will end the polling
	 * thread and kill all servers. May get some exceptions thrown
	 * but shouldn't be fatal at all. Returns false if thread isn't
	 * being executed right now or there aren't any processes.
	 *
	 * @return a boolean value indicating status.
	 */
       	public boolean killAll() {
	    // Check to make sure we are executing or else it won't do
	    // much good
	    System.out.println("Entering GroupRunner.killAll()\n");

	    synchronized(this) {
		stayAlive = false;
	    }

	    if (executingThread == false)
		return false;

	    if (process_vector_d == null)
		return false;

	    // Now go through the process array and kill them all.
	    //
	    for (int i = 0; i < process_vector_d.size(); i++) {
		try {
		    ProcessAndOutput pao_d =
			(ProcessAndOutput)process_vector_d.get(i);

		    pao_d.destroy();
		}
		catch (Exception e) {
		    System.out
			.println("Caught exception [ " + e.getClass().getName()
				 + "] in KillAll(). (May not be fatal) " +
				 "Continuing to kill them.");
		    //e.printStackTrace();
		}
	    }

	    return true;
	}// end killAll()

    } // end GroupRunner
    
    //-------------------------------------------------------------------------
    //
    // inner classes for this class
    //  
    //
    //-------------------------------------------------------------------------
       
    /**
     * An inner class for MessageHandler.
     *
     * THIS CLASS IS NO LONGER USED
     *
     */
    public class MessageHandler implements Runnable {

	/**
	 * In stream for this handler.
	 */
	protected BufferedReader in_stream_d;

	/**
	 * Out stream for this handler.
	 */
	protected BufferedReader in_error_d;
	
	/**
	 * The thread for this handler
	 */
	protected Thread receiver_d;

	/**
	 * Constructor.
	 */
        public MessageHandler(BufferedReader in_a, 
			      BufferedReader std_error_a) {
            in_stream_d = in_a;
            in_error_d = std_error_a;
            receiver_d = new Thread(this);
            receiver_d.start();
        }

        /** 
	 * When this thread is started, this method is called.  
	 */
        public void run() {

	    try {
		while (receiver_d != null) {

		    // local variable
		    //		
		    String s;
		    
		    // print out the in stream message
		    //
		    while (in_stream_d.ready() &&
			   (s = in_stream_d.readLine()) != null) {
			//TODO
			//System.out.println("Out:" + s);
		    }

		    // print out error message in case of error
		    //
		    while (in_error_d.ready() &&
			   (s = in_error_d.readLine()) != null) {
			System.out.println("StdErr:" + s);
		    }

		    try {
			Thread.sleep(600);
		    }
		    catch (Exception e) { 
			// dont care about exceptions here
		    };
		}
	    }
	    catch (IOException e) {
		//TODO, catch me!
	    }
        }
	
	/**
	 * Destroys this thread.
	 */
        public void destroy() {
            synchronized(this) {
		receiver_d = null;
	    }
        }
    }//end of MessageHandler
    
    /**
     * A inner class for IncomingMessageHandler.
     */
    public class IncomingMessageHandler implements Runnable {
	
	/**
	 * This handler's thread.
	 */
	protected Thread receiver_d;
	
	/**
	 * In stream.
	 */
	protected DataInputStream in_stream_d;
	
	/**
	 * Constructor.
	 */
	public IncomingMessageHandler(DataInputStream in_a) {
	    in_stream_d = in_a;
	    receiver_d = new Thread(this);
	    receiver_d.start();
	}
	
	/** 
	 * When this thread is started, this method is called.  
	 */    
	public void run() {
	    
	    Thread thisThread = Thread.currentThread();
	    
	    while (receiver_d == thisThread) {
		
		try {
		    String message = in_stream_d.readUTF();
		    handleMessage(message);
		}
		catch(IOException e) {
		    disconnect();
		}
	    }
	}
	
	/**
	 * Destroys this thread.
	 */
	public void destroy() {
	    receiver_d = null;
	}
    }
    
    
    /**
     * A inner class for OutgoingMessageHandler
     */
    public class OutgoingMessageHandler implements Runnable {
	
	/** 
	 * This handler's thread.
	 */
	protected Thread sender_d;
	
	/**
	 * Linked list for messages needed to handle.
	 */
	protected LinkedList message_list_d;
	
	/**
	 * Out stream for this handler.
	 */
	protected DataOutputStream out_stream_d;
		
	/**
	 * Constructor.
	 */
	public OutgoingMessageHandler(DataOutputStream out_a) {
	    
	    out_stream_d = out_a;
	    message_list_d = new LinkedList();
	    
	    sender_d = new Thread(this);
	    sender_d.start();
	}
	
	/**
	 * Add the message to message holder.
	 */
	public void addMessage(String message_a) {
	    
	    synchronized(message_list_d) {
		message_list_d.add(message_a);
		message_list_d.notify();
	    }
	}
	
	/**
	 * Returns true if no messages are waiting to be sent
	 * returns true if message_list is null or socket is null
	 */
	public boolean isEmpty() {
	    // If any of these are null, doesnt matter if anything is
	    // waiting because they wont be able to be sent
	    //
	    if (message_list_d == null || out_stream_d == null ||
		sender_d == null) {
		return true;
	    }
	    synchronized(message_list_d) {
		return message_list_d.isEmpty();
	    }
	}
	
	/** 
	 * When this thread is started, this method is called.  
	 */    
	public void run() {
	    
	    String message;
	    
	    Thread thisThread = Thread.currentThread();
	    
	    while (sender_d == thisThread) {

		// Take control and see if any messages are waiting.
		// If they aren't, calls wait() which wait's for
		// addMessage()'s notify() command
		//
		synchronized(message_list_d) {
		    if(message_list_d.isEmpty() && sender_d != null) {
			
			try {
			    message_list_d.wait();
			}
			catch(InterruptedException e) { }
		    }
		}
		
		synchronized(message_list_d) {
		    while (message_list_d.size() > 0) {
			message = (String)message_list_d.removeFirst();
			
			
			try {
			    out_stream_d.writeUTF(message);
			}
			catch(IOException e) {
			    System.out.println("Outgoing Message writeUTF: " +
					       "exception catched " + e);
			    disconnect();
			}
		    }// end while
		}// end synchro
	    }
	}
	
	/**
	 * Destroy this thread, and cleanup.
	 */
	public void destroy() {
	    
	    sender_d = null;
	    synchronized(message_list_d) {
		
		// wake up if stuck in waiting stage
		//
		message_list_d.notify(); 
	    }
	}
    } // end OutgoingMessageHandler

} // end DemoClientHandler
