bodewig     01/12/11 06:05:43

  Modified:    .        build.xml
               src/main/org/apache/tools/ant/taskdefs Execute.java
  Added:       src/main/org/apache/tools/ant/taskdefs ProcessDestroyer.java
               src/testcases/org/apache/tools/ant/taskdefs TestProcess.java
  Log:
  If running on JDK 1.3 register an exit handler that kills spawned
  processes when the JVM exits.
  
  PR: 5125
  Submitted by: [EMAIL PROTECTED] (Michael Newcomb)
  
  Revision  Changes    Path
  1.214     +14 -0     jakarta-ant/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-ant/build.xml,v
  retrieving revision 1.213
  retrieving revision 1.214
  diff -u -r1.213 -r1.214
  --- build.xml 2001/12/11 13:54:35     1.213
  +++ build.xml 2001/12/11 14:05:43     1.214
  @@ -801,6 +801,9 @@
   
         <exclude name="${optional.package}/perforce/*.java"
                  unless="jakarta.oro.present" />
  +
  +      <exclude name="org/apache/tools/ant/taskdefs/TestProcess.java"
  +               unless="jdk1.3+" />
       </javac>
     </target>
   
  @@ -890,6 +893,9 @@
   
             <exclude name="${optional.package}/perforce/*.java"
                      unless="jakarta.oro.present" />
  +
  +          <!-- interactive test -->
  +          <exclude name="org/apache/tools/ant/taskdefs/TestProcess.java" />
           </fileset>
         </batchtest>
   
  @@ -916,6 +922,14 @@
         <formatter type="plain" usefile="false" />
         <test name="${testcase}" />
       </junit>
  +  </target>
  +
  +  <target name="interactive-tests" description="--> runs interactive tests"
  +          depends="compile-tests"
  +          if="jdk1.3+">
  +    <java classpathref="tests-classpath"
  +          classname="org.apache.tools.ant.taskdefs.TestProcess" 
  +          fork="true" />
     </target>
   
     <!--
  
  
  
  1.27      +56 -43    
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Execute.java
  
  Index: Execute.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Execute.java,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -r1.26 -r1.27
  --- Execute.java      2001/11/30 20:43:03     1.26
  +++ Execute.java      2001/12/11 14:05:43     1.27
  @@ -92,14 +92,17 @@
       private boolean newEnvironment = false;
   
       /** Controls whether the VM is used to launch commands, where possible */
  -    private boolean useVMLauncher = true;    
  -    
  +    private boolean useVMLauncher = true;
  +
       private static String antWorkingDirectory = 
System.getProperty("user.dir");
       private static CommandLauncher vmLauncher = null;
       private static CommandLauncher shellLauncher = null;
       private static Vector procEnvironment = null;
  +
  +    /** Used to destroy processes when the VM exits. */
  +    private static ProcessDestroyer processDestroyer = new 
ProcessDestroyer();
   
  -    /** 
  +    /**
        * Builds a command launcher for the OS and JVM we are running under
        */
       static {
  @@ -133,7 +136,7 @@
               }
   
               // Determine if we're running under 2000/NT or 98/95
  -            String osname = 
  +            String osname =
                   System.getProperty("os.name").toLowerCase(Locale.US);
   
               if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 ) {
  @@ -183,7 +186,7 @@
                   // Just try to use what we got
               }
   
  -            BufferedReader in = 
  +            BufferedReader in =
                   new BufferedReader(new StringReader(out.toString()));
               String var = null;
               String line, lineSep = System.getProperty("line.separator");
  @@ -208,7 +211,7 @@
               }
               // Since we "look ahead" before adding, there's one last env var.
               procEnvironment.addElement(var);
  -        } 
  +        }
           catch (java.io.IOException exc) {
               exc.printStackTrace();
               // Just try to see how much we got
  @@ -224,7 +227,7 @@
               return cmd;
           }
           else if ( Os.isFamily("windows") ) {
  -            String osname = 
  +            String osname =
                   System.getProperty("os.name").toLowerCase(Locale.US);
               // Determine if we're running under 2000/NT or 98/95
               if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 ) {
  @@ -243,7 +246,7 @@
               // Alternatively one could use: /bin/sh -c env
               String[] cmd = {"/usr/bin/env"};
               return cmd;
  -        } 
  +        }
           else if ( Os.isFamily("netware") ) {
               String[] cmd = {"env"};
               return cmd;
  @@ -332,7 +335,7 @@
        * Sets the environment variables for the subprocess to launch.
        *
        * @param commandline array of Strings, each element of which has
  -     * an environment variable settings in format <em>key=value</em> 
  +     * an environment variable settings in format <em>key=value</em>
        */
       public void setEnvironment(String[] env) {
           this.env = env;
  @@ -366,17 +369,17 @@
   
       /**
        * Launch this execution through the VM, where possible, rather than 
through
  -     * the OS's shell. In some cases and operating systems using the shell 
will 
  -     * allow the shell to perform additional processing such as associating 
an 
  +     * the OS's shell. In some cases and operating systems using the shell 
will
  +     * allow the shell to perform additional processing such as associating 
an
        * executable with a script, etc
        *
  -     * @param vmLauncher true if exec should launch through thge VM, 
  +     * @param vmLauncher true if exec should launch through thge VM,
        *                   false if the shell should be used to launch the 
command.
        */
       public void setVMLauncher(boolean useVMLauncher) {
           this.useVMLauncher = useVMLauncher;
       }
  -    
  +
       /**
        * Runs a process defined by the command line and returns its exit 
status.
        *
  @@ -389,7 +392,7 @@
           if (!useVMLauncher) {
               launcher = shellLauncher;
           }
  -        
  +
           final Process process = launcher.exec(project, getCommandline(), 
getEnvironment(), workingDirectory);
           try {
               streamHandler.setProcessInputStream(process.getOutputStream());
  @@ -400,8 +403,18 @@
               throw e;
           }
           streamHandler.start();
  +
  +        // add the process to the list of those to destroy if the VM exits
  +        //
  +        processDestroyer.add(process);
  +
           if (watchdog != null) watchdog.start(process);
           waitFor(process);
  +
  +        // remove the process to the list of those to destroy if the VM exits
  +        //
  +        processDestroyer.remove(process);
  +
           if (watchdog != null) watchdog.stop();
           streamHandler.stop();
           if (watchdog != null) watchdog.checkException();
  @@ -434,9 +447,9 @@
        * @since 1.5
        */
       public boolean killedProcess() {
  -        return watchdog!=null && watchdog.killedProcess();    
  +        return watchdog!=null && watchdog.killedProcess();
       }
  -    
  +
       /**
        * Patch the current environment with the new values from the user.
        * @return the patched environment
  @@ -474,7 +487,7 @@
       {
           try {
               task.log(Commandline.toString(cmdline), Project.MSG_VERBOSE);
  -            Execute exe = new Execute(new LogStreamHandler(task, 
  +            Execute exe = new Execute(new LogStreamHandler(task,
                                                              Project.MSG_INFO,
                                                              Project.MSG_ERR));
               exe.setAntRun(task.getProject());
  @@ -483,7 +496,7 @@
               if ( retval != 0 ) {
                   throw new BuildException(cmdline[0] + " failed with return 
code " + retval, task.getLocation());
               }
  -        } 
  +        }
           catch (java.io.IOException exc) {
               throw new BuildException("Could not launch " + cmdline[0] + ": " 
+ exc, task.getLocation());
           }
  @@ -496,7 +509,7 @@
        */
       private static class CommandLauncher
       {
  -        /** 
  +        /**
            * Launches the given command in a new process.
            *
            * @param project       The project that the command is part of
  @@ -509,11 +522,11 @@
               if (project != null) {
                   project.log("Execute:CommandLauncher: " +
                               Commandline.toString(cmd), Project.MSG_DEBUG);
  -            }                            
  +            }
               return Runtime.getRuntime().exec(cmd, env);
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory.
            *
  @@ -544,7 +557,7 @@
            * Launches the given command in a new process.  Needs to quote
            * arguments
            */
  -        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException 
  +        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException
           {
               // Need to quote arguments with spaces, and to escape quote 
characters
               String[] newcmd = new String[cmd.length];
  @@ -554,7 +567,7 @@
               if (project != null) {
                   project.log("Execute:Java11CommandLauncher: " +
                               Commandline.toString(newcmd), Project.MSG_DEBUG);
  -            }                            
  +            }
               return Runtime.getRuntime().exec(newcmd, env);
           }
       }
  @@ -571,44 +584,44 @@
               _execWithCWD = Runtime.class.getMethod("exec", new Class[] 
{String[].class, String[].class, File.class});
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory
            */
  -        public Process exec(Project project, String[] cmd, String[] env, 
File workingDir) 
  +        public Process exec(Project project, String[] cmd, String[] env, 
File workingDir)
               throws IOException
           {
               try {
                   if (project != null) {
                       project.log("Execute:Java13CommandLauncher: " +
                                   Commandline.toString(cmd), 
Project.MSG_DEBUG);
  -                }                                
  +                }
                   Object[] arguments = { cmd, env, workingDir };
                   return (Process)_execWithCWD.invoke(Runtime.getRuntime(), 
arguments);
  -            } 
  +            }
               catch (InvocationTargetException exc) {
                   Throwable realexc = exc.getTargetException();
                   if ( realexc instanceof ThreadDeath ) {
                       throw (ThreadDeath)realexc;
  -                } 
  +                }
                   else if ( realexc instanceof IOException ) {
                       throw (IOException)realexc;
  -                } 
  +                }
                   else {
                       throw new BuildException("Unable to execute command", 
realexc);
                   }
  -            } 
  +            }
               catch (Exception exc) {
                   // IllegalAccess, IllegalArgument, ClassCast
                   throw new BuildException("Unable to execute command", exc);
               }
           }
  -        
  +
           private Method _execWithCWD;
       }
  -    
  +
       /**
  -     * A command launcher that proxies another command launcher.  
  +     * A command launcher that proxies another command launcher.
        *
        * Sub-classes override exec(args, env, workdir)
        */
  @@ -619,7 +632,7 @@
               _launcher = launcher;
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process.  Delegates this
            * method to the proxied launcher
            */
  @@ -643,7 +656,7 @@
               super(launcher);
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory.
            */
  @@ -685,7 +698,7 @@
               super(launcher);
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory
            */
  @@ -698,7 +711,7 @@
               System.getProperties().put("user.dir", 
workingDir.getAbsolutePath());
               try {
                   return exec(project, cmd, env);
  -            } 
  +            }
               finally {
                   System.getProperties().put("user.dir", antWorkingDirectory);
               }
  @@ -717,7 +730,7 @@
               _script = script;
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory
            */
  @@ -729,7 +742,7 @@
                   }
                   throw new IOException("Cannot locate antRun script: No 
project provided");
               }
  -            
  +
               // Locate the auxiliary script
               String antHome = project.getProperty("ant.home");
               if ( antHome == null ) {
  @@ -747,7 +760,7 @@
               newcmd[0] = antRun;
               newcmd[1] = commandDir.getAbsolutePath();
               System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
  -            
  +
               return exec(project, newcmd, env);
           }
   
  @@ -766,7 +779,7 @@
               _script = script;
           }
   
  -        /** 
  +        /**
            * Launches the given command in a new process, in the given working
            * directory
            */
  @@ -778,7 +791,7 @@
                   }
                   throw new IOException("Cannot locate antRun script: No 
project provided");
               }
  -            
  +
               // Locate the auxiliary script
               String antHome = project.getProperty("ant.home");
               if ( antHome == null ) {
  @@ -797,7 +810,7 @@
               newcmd[1] = antRun;
               newcmd[2] = commandDir.getAbsolutePath();
               System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
  -            
  +
               return exec(project, newcmd, env);
           }
   
  
  
  
  1.1                  
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
  
  Index: ProcessDestroyer.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.tools.ant.taskdefs;
  
  import java.lang.reflect.Method;
  import java.util.Enumeration;
  import java.util.Vector;
  
  /**
   * Destroys all registered <code>Process</code>es when the VM exits.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]">Michael Newcomb</a>
   */
  class ProcessDestroyer
      extends Thread
  {
  
      private Vector processes = new Vector();
  
      /**
       * Constructs a <code>ProcessDestroyer</code> and registers it as
       * a shutdown hook.
       */
      public ProcessDestroyer()
      {
          try
          {
              // check to see if the method exists (support pre-JDK 1.3 VMs)
              //
              Class[] paramTypes = {Thread.class};
              Method addShutdownHook =
                  Runtime.class.getMethod("addShutdownHook", paramTypes);
  
              // add the hook
              //
              Object[] args = {this};
              addShutdownHook.invoke(Runtime.getRuntime(), args);
          }
          catch (Exception e)
          {
              // it just won't be added as a shutdown hook... :(
          }
      }
  
      /**
       * Returns <code>true</code> if the specified <code>Process</code> was
       * successfully added to the list of processes to destroy upon VM exit.
       *
       * @param   process the process to add
       * @return  <code>true</code> if the specified <code>Process</code> was
       *          successfully added
       */
      public boolean add(Process process)
      {
          processes.addElement(process);
          return processes.contains(process);
      }
  
      /**
       * Returns <code>true</code> if the specified <code>Process</code> was
       * successfully removed from the list of processes to destroy upon VM 
exit.
       *
       * @param   process the process to remove
       * @return  <code>true</code> if the specified <code>Process</code> was
       *          successfully removed
       */
      public boolean remove(Process process)
      {
          return processes.removeElement(process);
      }
  
      /**
       * Invoked by the VM when it is exiting.
       */
      public void run()
      {
          synchronized(processes)
          {
              Enumeration e = processes.elements();
              while (e.hasMoreElements())
              {
                  ((Process) e.nextElement()).destroy();
              }
          }
      }
  }
  
  
  
  1.1                  
jakarta-ant/src/testcases/org/apache/tools/ant/taskdefs/TestProcess.java
  
  Index: TestProcess.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Ant", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.tools.ant.taskdefs;
  
  /**
   * Interactive Testcase for Processdestroyer.
   *
   * @author <a href="mailto:[EMAIL PROTECTED]">Michael Newcomb</a>
   */
  public class TestProcess
      implements Runnable
  {
      private boolean run = true;
      private boolean done = false;
  
      public void shutdown()
      {
          if (!done)
          {
              System.out.println("shutting down TestProcess");
              run = false;
              
              synchronized(this)
              {
                  while (!done)
                  {
                      try { wait(); } catch (InterruptedException ie) {}
                  }
              }
              
              System.out.println("TestProcess shut down");
          }
      }
  
      public void run()
      {
          for (int i = 0; i < 5 && run; i++)
          {
              System.out.println(Thread.currentThread().getName());
              
              try { Thread.sleep(2000); } catch (InterruptedException ie) {}
          }
  
          synchronized(this)
          {
              done = true;
              notifyAll();
          }
      }
  
      public Thread getShutdownHook()
      {
          return new TestProcessShutdownHook();
      }
  
      private class TestProcessShutdownHook
          extends Thread
      {
          public void run()
          {
              shutdown();
          }
      }
  
      public static void main(String[] args)
      {
          TestProcess tp = new TestProcess();
          new Thread(tp, "TestProcess thread").start();
          Runtime.getRuntime().addShutdownHook(tp.getShutdownHook());
      }
  }
  
  
  

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to