commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From patri...@apache.org
Subject cvs commit: jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher ChildMain.java LaunchTask.java Launcher.java ParentListener.java
Date Fri, 06 Sep 2002 02:03:46 GMT
patrickl    2002/09/05 19:03:46

  Modified:    daemon/src/java/org/apache/commons/launcher ChildMain.java
                        LaunchTask.java Launcher.java ParentListener.java
  Log:
  Implement a more orderly termination of any synchronous child processes. Previously, the
code used the Process.destroy() method which, on Windows, forcefully terminates the child
process. As a result, the child process' shutdown hooks were never executed.
  
  With this revision, the Launcher will delete a "heartbeat" file and the child process, once
it senses that the file has been deleted, will invoke System.exit() to shut itself down. This
new approach will all the child process' shutdown hooks to execute.
  
  Revision  Changes    Path
  1.11      +11 -3     jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/ChildMain.java
  
  Index: ChildMain.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/ChildMain.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- ChildMain.java	30 Aug 2002 00:15:17 -0000	1.10
  +++ ChildMain.java	6 Sep 2002 02:03:46 -0000	1.11
  @@ -63,6 +63,7 @@
   import java.awt.Toolkit;
   import java.awt.event.WindowAdapter;
   import java.awt.event.WindowEvent;
  +import java.io.File;
   import java.io.FileOutputStream;
   import java.io.PrintStream;
   import java.lang.reflect.Method;
  @@ -109,6 +110,12 @@
           "org.apache.commons.launcher.executableName";
   
       /**
  +     * The heartbeatFile system property name.
  +     */
  +    public final static String HEARTBEAT_FILE_PROP_NAME =
  +        "org.apache.commons.launcher.heartbeatFile";
  +
  +    /**
        * The miminizedWindowTitle system property name.
        */
       public final static String MINIMIZED_WINDOW_TITLE_PROP_NAME =
  @@ -196,14 +203,15 @@
               // Start the thread to check if the parent JVM exits.
               boolean waitForChild = false;
               if (System.getProperty(ChildMain.WAIT_FOR_CHILD_PROP_NAME) != null) {
  +                waitForChild = true;
                   // Swap in a non-blocking input stream for System.in since
                   // reading System.in can block the entire process on some
                   // Windows platforms
                   if (windows)
                       System.setIn(new NonBlockingInputStream(System.in));
  -
  -                waitForChild = true;
  -                ParentListener heartbeat = new ParentListener();
  +                // Create the ParentListener thread
  +                File heartbeatFile = new File(System.getProperty(ChildMain.HEARTBEAT_FILE_PROP_NAME)).getCanonicalFile();
  +                ParentListener heartbeat = new ParentListener(heartbeatFile);
                   // Make the thread a daemon thread so that it does not
                   // prevent this process from exiting when all of the
                   // application's threads finish.
  
  
  
  1.26      +54 -15    jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/LaunchTask.java
  
  Index: LaunchTask.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/LaunchTask.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- LaunchTask.java	30 Aug 2002 00:15:17 -0000	1.25
  +++ LaunchTask.java	6 Sep 2002 02:03:46 -0000	1.26
  @@ -58,6 +58,7 @@
   package org.apache.commons.launcher;
   
   import java.io.File;
  +import java.io.FileOutputStream;
   import java.io.IOException;
   import java.net.URL;
   import java.net.URLClassLoader;
  @@ -114,9 +115,10 @@
       public final static String TASK_NAME = "launch";
   
       /**
  -     * Cached synchronous child processes for all instances of this class.
  +     * Cached instances of this class that are executing synchronous child
  +     * processes.
        */
  -    private static ArrayList childProcesses = new ArrayList();
  +    private static ArrayList executingTasks = new ArrayList();
   
       //------------------------------------------------------------------ Fields
   
  @@ -126,11 +128,6 @@
       private boolean appendOutput = false;
   
       /**
  -     * Cached synchronously executing child process.
  -     */
  -    private Process childProc = null;
  -
  -    /**
        * Cached classpath.
        */
       private Path classpath = null;
  @@ -171,6 +168,11 @@
       private Path filterClasspath = null;
   
       /**
  +     * Cached heartbeatFile.
  +     */
  +    private File heartbeatFile = null;
  +
  +    /**
        * Cached main class name.
        */
       private String mainClassName = null;
  @@ -238,13 +240,14 @@
       //---------------------------------------------------------- Static Methods
   
       /**
  -     * Get the synchronous child processes for all instances of this class.
  +     * Get the instances of this class that are executing synchronous child
  +     * processes.
        *
        * @return the instances of this class.
        */
  -    public static Process[] getChildProcesses() {
  +    public static LaunchTask[] getExecutingTasks() {
   
  -        return (Process[])childProcesses.toArray(new Process[childProcesses.size()]);
  +        return (LaunchTask[])executingTasks.toArray(new LaunchTask[executingTasks.size()]);
   
       }
   
  @@ -577,6 +580,18 @@
                   }
               }
   
  +            // Create the heartbeatFile. This file is used by to request the
  +            // child JVM to terminate itself.
  +            heartbeatFile = null;
  +            if (filteredWaitForChild) {
  +                File tmpDir = null;
  +                String tmpDirName = (String)sysProps.get("java.io.tmpdir");
  +                if (tmpDirName != null)
  +                    tmpDir = new File(tmpDirName);
  +                heartbeatFile = File.createTempFile(ChildMain.HEARTBEAT_FILE_PROP_NAME
+ ".", "", tmpDir);
  +                sysProps.put(ChildMain.HEARTBEAT_FILE_PROP_NAME, heartbeatFile.getCanonicalPath());
  +            }
  +
               // Assemble child command
               String[] cmd = new String[5 + jvmArgs.size() + sysProps.size() + appArgs.size()];
               int nextCmdArg = 0;
  @@ -654,12 +669,11 @@
               if (Launcher.isStopped())
                   throw new BuildException();
               Process proc = null;
  -            synchronized (LaunchTask.childProcesses) {
  +            synchronized (LaunchTask.executingTasks) {
                   proc = Runtime.getRuntime().exec(cmd);
                   // Add the synchronous child process
                   if (filteredWaitForChild) {
  -                    childProc = proc;
  -                    LaunchTask.childProcesses.add(proc);
  +                    LaunchTask.executingTasks.add(this);
                   }
               }
               if (filteredWaitForChild) {
  @@ -679,6 +693,7 @@
                   stdout.join();
                   stderr.join();
                   int exitValue = proc.exitValue();
  +                Launcher.setExitValue(exitValue);
                   if (filteredFailOnError && exitValue != 0)
                       throw new BuildException(Launcher.getLocalizedString("child.failed",
this.getClass().getName()) + " " + exitValue);
               }
  @@ -694,11 +709,35 @@
                   throw new BuildException(Launcher.getLocalizedString("launch.task.stopped",
this.getClass().getName()));
               else 
                   throw new BuildException(e);
  +        } finally {
  +            synchronized (LaunchTask.executingTasks) {
  +                // Remove the synchronous child process
  +                LaunchTask.executingTasks.remove(this);
  +            }
  +            if (heartbeatFile != null)
  +                heartbeatFile.delete();
  +            heartbeatFile = null;
           }
   
       }
   
       /**
  +     * Kill the currently executing synchronous child process. This is done
  +     * by deleting the heartbeat file. The child process, in its
  +     * {@link ParentListener#run()} method, will invoke
  +     * {@link System#exit(int)}. This approach is used because it allows the
  +     * child process to execute its shutdown hooks. {@link Process#destroy()}
  +     * is not used because it will forcefully terminate the child process on
  +     * Windows without executing child process' shutdown hooks.
  +     */
  +    public void kill() {
  +
  +        if (heartbeatFile != null)
  +            heartbeatFile.delete();
  +
  +    }
  +
  +    /**
        * Set the useArgs flag. Setting this flag to true will cause this
        * task to append all of the command line arguments used to start the
        * {@link Launcher#start(String[])} method to the arguments
  @@ -801,7 +840,7 @@
        */
       public void setDisplayminimizedwindow(boolean displayMinimizedWindow) {
   
  -        this.displayMinimizedWindow = displayMinimizedWindow;
  +        this.disposeMinimizedWindow = disposeMinimizedWindow;
   
       }
   
  @@ -823,7 +862,7 @@
        */
       public void setDisposeminimizedwindow(boolean disposeMinimizedWindow) {
   
  -        this.disposeMinimizedWindow = disposeMinimizedWindow;
  +        this.disposeMinimizedWindow = displayMinimizedWindow;
   
       }
   
  
  
  
  1.18      +40 -22    jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/Launcher.java
  
  Index: Launcher.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/Launcher.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- Launcher.java	31 Jul 2002 02:04:48 -0000	1.17
  +++ Launcher.java	6 Sep 2002 02:03:46 -0000	1.18
  @@ -112,13 +112,17 @@
   
       //----------------------------------------------------------- Static Fields
   
  -
       /**
        * Cached bootstrap file.
        */
       private static File bootstrapFile = null;
   
       /**
  +     * Cached exit value
  +     */
  +    private static int exitValue = 0;
  +
  +    /**
        * Cached java command
        */
       private static String javaCmd = null;
  @@ -198,7 +202,6 @@
   
       //---------------------------------------------------------- Static Methods
   
  -
       /**
        * Get the started flag.
        *
  @@ -222,6 +225,18 @@
       }
   
       /**
  +     * Set the exit value of the last synchronous child process that finished
  +     * executing.
  +     *
  +     * @param exitValue the exit value of the last synchronous child process
  +     *  that finished executing
  +     */
  +    protected static synchronized void setExitValue(int exitValue) {
  +
  +        Launcher.exitValue = exitValue;
  +    }
  +
  +    /**
        * Start the launching process. This method is essential the
        * <code>main(String[])<code> method for this class except that this method
        * never invokes {@link System#exit(int)}. This method is designed for
  @@ -393,14 +408,21 @@
               if (!project.getTargets().containsKey(target))
                   throw new IllegalArgumentException(target + " " + Launcher.getLocalizedString("invalid.target"));
   
  -            // Execute the target
  +            // Add the shutdown hook
               try {
                   runtime.addShutdownHook(shutdownHook);
  -            } catch (NoSuchMethodError nsme) {
  +            } catch (Throwable t) {
                   // Early JVMs do not support this method
               }
  +            // Reset the exit value
  +            Launcher.setExitValue(returnValue);
  +
  +            // Execute the target
               project.executeTarget(target);
   
  +            // Get the exit value of last synchronous child JVM
  +            returnValue = Launcher.exitValue;
  +
           } catch (Throwable t) {
               // Log any errors
               returnValue = 1;
  @@ -418,7 +440,7 @@
                   // Remove the shutdown hook
                   try {
                       runtime.removeShutdownHook(shutdownHook);
  -                } catch (NoSuchMethodError nsme) {
  +                } catch (Throwable t) {
                       // Early JVMs do not support this method
                   }
                   // Reset the class loader after running Ant
  @@ -430,11 +452,6 @@
               }
           }
   
  -        // Override return value with exit value of last synchronous child JVM
  -        Process[] childProcesses = LaunchTask.getChildProcesses();
  -        if (childProcesses.length > 0)
  -            returnValue = childProcesses[childProcesses.length - 1].exitValue();
  -
           return returnValue;
   
       }
  @@ -476,7 +493,7 @@
   
           try {
   
  -            // Kill all of the synchronous child processes
  +            // Kill all of tasks that are executing synchronous child processes
               killChildProcesses();
   
               // Wait for the start() method to reset the start flag
  @@ -928,26 +945,27 @@
       }
   
       /**
  -     * Iterate through the list of synchronous child process launched by
  -     * all of the {@link LaunchTask} instances.
  -     */
  -    public static void killChildProcesses() {
  -
  -        Process[] procs = LaunchTask.getChildProcesses();
  -        for (int i = 0; i < procs.length; i++)
  -            procs[i].destroy();
  +     * Iterate through the {@link LaunchTask} instances that are executing
  +     * synchronous child processes and invokes the {@link LaunchTask#kill()}
  +     * method on each.
  +     */
  +    private static void killChildProcesses() {
  +
  +        LaunchTask[] tasks = LaunchTask.getExecutingTasks();
  +        for (int i = 0; i < tasks.length; i++)
  +            tasks[i].kill();
   
       }
   
       //----------------------------------------------------------------- Methods
   
       /**
  -     * Wrapper to allow the {@link #killChildProcesses()} method to be
  -     * invoked in a shutdown hook.
  +     * Wrapper to allow the call the {@link #stop()} method to gracefully kill
  +     * the synchronous child processes.
        */
       public void run() {
   
  -        Launcher.killChildProcesses();
  +        Launcher.stop();
   
       }
   
  
  
  
  1.6       +28 -4     jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/ParentListener.java
  
  Index: ParentListener.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/daemon/src/java/org/apache/commons/launcher/ParentListener.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ParentListener.java	27 Aug 2002 21:11:28 -0000	1.5
  +++ ParentListener.java	6 Sep 2002 02:03:46 -0000	1.6
  @@ -57,6 +57,7 @@
   
   package org.apache.commons.launcher;
   
  +import java.io.File;
   import java.io.PrintStream;
   
   /**
  @@ -67,6 +68,26 @@
    */
   public class ParentListener extends Thread {
   
  +    //------------------------------------------------------------------ Fields
  +
  +    /**
  +     * Cached heartbeat file.
  +     */
  +    private File heartbeatFile = null;
  +
  +    //------------------------------------------------------------ Constructors
  +
  +    /**
  +     * Validates and caches a lock file created by the parent JVM.
  +     *
  +     * @param heartbeatFile the lock file that the parent JVM will delete when
  +     *  it wants this process to exit
  +     */
  +    public ParentListener(File heartbeatFile) {
  +
  +        this.heartbeatFile = heartbeatFile;
  +
  +    }
   
       //----------------------------------------------------------------- Methods
   
  @@ -77,22 +98,25 @@
        * invoke {@link System#exit(int)}. This method <b>must</b> be executed
        * <b>before</b> {@link System#setErr(PrintStream)} is ever invoked.
        * <p>
  -     * TODO: {@link System#err} never seems to close while System.in is
  -     * being read on Windows platforms. Need to find a workaround for this
  -     * case.
  +     * In addition, we check if the heartbeat file has been deleted. If so,
  +     * we assume the parent is requesting that this process terminate itself.
        */ 
       public void run() {
   
           // Save System.err in case the application invokes System.setErr()
           // later
  +        PrintStream out = System.out;
           PrintStream err = System.err;
  -        while (!err.checkError()) {
  +        while (!out.checkError() && !err.checkError() && heartbeatFile.exists())
{
               // Wait a while before the next loop
               yield();
               try {
                   sleep(3000);
               } catch (Exception e) {}
           }
  +
  +        // Clean up before exiting
  +        heartbeatFile.delete();
   
           // Exit this process since the parent JVM has exited
           System.exit(0);
  
  
  

--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message