package com.sun.launcher.tasks;

import java.io.*;
import java.util.*;
import com.sun.wizards.core.*;
import com.sun.install.tasks.*;
import com.sun.launcher.core.*;
import com.sun.install.products.*;


/** LaunchInstallTask: A task to launch an installer
*
* @version @(#)LaunchInstallTask.java	1.10 99/10/31 15:28:18  */

public class LaunchInstallTask extends Task implements Serializable, Runnable 
{
  private volatile transient Object inputThreadLock = null;
  private volatile transient Thread inputThread = null;
  private volatile transient Process runtimeProcess = null;

  /*
   * This happens at build time. 
   */
  public LaunchInstallTask()
  {
  }

  /** 
   *  Read the file
   */
  public void perform()
  {
    WizardState state = getSequence().getState();

    Hashtable launchTable = (Hashtable) state.getData("launchTable");
   
    /*
     * get the install command and run it
     */
    String launchCmd = ((String) state.getData("launchCmd")).trim();
    String launchLocation = (String) state.getData("launchLocation");

    /*
     * See if we can do an ls of the actual launch command. 
     * Doing this prevents a possible null process due to a 
     * race condition between when the CD is mounted and
     * when you can see the files.
     */
    int spaceIndex = launchCmd.indexOf(' ');
    String rawlaunchCmd = launchCmd;
    if (spaceIndex != -1) {
      rawlaunchCmd= launchCmd.substring(0,spaceIndex);
    } 

    /*
     * Check for install command with absolute path.
     */
    String filest = null;
    if (launchCmd.startsWith("/")) {
      filest = rawlaunchCmd;
    } else {
      filest = launchLocation + "/" + rawlaunchCmd;
    }
    File myfile = new File (filest);
    int i = 0;
    int maxattempts = 3;
    while ( !myfile.exists() && (i++ < maxattempts) ) {
      if (Def.DEBUG) System.out.println(i+". file doesn't exist::" + filest);
      try {
            Thread.sleep(2000);
          } catch (InterruptedException ex) {
            System.out.println("LaunchInstallTask: sleep: "+ex);
          }
    }

    if (myfile.exists()) {
      if (Def.DEBUG) System.out.println("LaunchInstallTask "+filest+" exists");
      /*
       * Run script 
       */
      String cmd = launchLocation + "/" + launchCmd;
      if (launchCmd.startsWith("/")) {
        cmd = launchCmd;
      } else {
        cmd = launchLocation + "/" + launchCmd;
      }

      if (Def.DEBUG) System.out.println ("\n==>LaunchInstallTask cmd: " + cmd ); 
      try {
          Runtime runtime = Runtime.getRuntime();
          runtimeProcess = runtime.exec(cmd);
      } catch(IOException e) {
          if (Def.DEBUG) System.out.println("LaunchInstallTask:" + e);
          if (Def.DEBUG) System.out.println("LaunchInstallTask - retrying " );
          //e.printStackTrace();
      }
    } else {
      if (Def.DEBUG) System.out.println("LaunchInstallTask "+filest+" MISSING");
    }

    /*
     * Check for null process and set flag for panel
     */
    if (runtimeProcess == null) {
      if (Def.DEBUG) System.out.println("LaunchInstallTask: NULL process!");
      state.setData(LaunchConstants.nullProcess, Boolean.TRUE);
    } else {
      state.setData(LaunchConstants.launchProcess, runtimeProcess);
      state.setData(LaunchConstants.nullProcess, Boolean.FALSE);

      /**
       * The following threads forward the output and error streams 
       * from the exec'd installer process through the output and 
       * error streams of the launcher's process.
       */

      if (inputThreadLock == null) {
	inputThreadLock = new Object();
      }

      synchronized(inputThreadLock) {
        if (inputThread == null) {
          inputThread = new Thread(this);
          inputThread.start();
          new ErrorMonitorThread(runtimeProcess);
        } else {
	  /* 
	   * If we get here, something has likely called this class's 
	   * perform method prior to the previous launched installer finishing.
	   * Since this class should be used with its paired WaitForProcessTask
 	   * class, this should not occur.  Print the following just
	   * in case it does, to call attention to the problem. 
	   */
	  System.out.println("LaunchInstallTask: " +
			     "an input thread already running!");
        }
      }
    }
  }

  /**
   * This thread forwards the outputSteam from the the exec'ed installer 
   * process to the launcher's outputStream. 
   */

    public void run() {
      boolean moreBytes = true;
      /*
       * Note: this launcher process's input stream is 
       * the exec'ed installer's output stream 
       */ 
      InputStream is = runtimeProcess.getInputStream();

      while(moreBytes) {
	try {
	  int nextByte = is.read();

	  if (nextByte >= 0) { 
	    System.out.write(nextByte);
	    System.out.flush();
	  } else {
	    moreBytes = false;
	  }
	} catch(IOException e) { 
	  moreBytes = false;
	}
      }

      /*
       * This thread's work is done.  Clear its saved id and return.
       */

      synchronized(inputThreadLock) {
	inputThread = null;
      }
    }


  /**
   * Add the runtime class requirements to the specified vector.
   */
  public void addRuntimeResources(Vector resourceVector)
  {
    resourceVector.addElement(new String[] {null, "com.sun.launcher.tasks.LaunchInstallTask"});
    resourceVector.addElement(new String[] {null, "com.sun.launcher.tasks.ErrorMonitorThread"});
  }



}
