Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4770092

(process) Process.destroy does not kill multiple child processes

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Won't Fix
    • Icon: P4 P4
    • None
    • 1.3.1
    • core-libs
    • x86
    • windows_nt

      Name: nt126004 Date: 10/28/2002


      FULL PRODUCT VERSION :
        Bug reproduced in 1.4, 1.3.1_01,_03, and _04.

      java version "1.3.1_04"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_04-b02)
      Java HotSpot(TM) Client VM (build 1.3.1_04-b02, mixed mode)


      FULL OPERATING SYSTEM VERSION :

      Windows NT 4.00.1381

      A DESCRIPTION OF THE PROBLEM :

      My basic requirement is to run two process sequentially in
      the same environment, for instance to run a setenv.bat
      script and followed by a java app. The reason for this is
      to set up the system environment variables in a script
      (using Runtime.exec() passing environment array is not an
      option).

      I have tried the following

      Runtime.exec("setenv.bat && java ...");
      Runtime.exec("cmd.exe /K setenv.bat && java ...");
      Runtime.exec("run.bat"); // Where run.bat is "setenv.bat &&
      java ..."

      I have a thread waiting on the return process with
      Process.waitFor. In my main thread, I call
      Process.destroy(). Process.waitFor() returns with exit code
      1, but the java process continues.

      If I kill the java process externally, Process.waitFor()
      will also return.

      I can not find any workaround for this, and it seriously
      limits my ability to launch external applications.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Prepare by creating the batch files specified in the source
      section and modifying the path to the batch files both
      within the batch files and in the java source file.

      1. java ExecTest
      The system.out should stop after 5 seconds.

      2. java ExecTest and
      The system.out will continue after the destroy.

      3. java ExecTest script
      The system.out will continue after the destroy.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      The child processes should be killed, just as if you had hit
      control-c. Instead, it continues (as evidenced by the
      printing to the command line). However, Process.waitFor
      DOES return. I can find no way to successfully kill the
      child processes.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      // FILE: foo.bat
      rem This just makes sure the batch file returns 0 exit code
      // END OF FILE: foo.bat

      // FILE: test.bat - contains a few alternatives, none of which work
      rem Application is not properly killed in any of these cases:

      rem Option 1
      c:\foo.bat && java com.bluemartini.dev.ExecTestWait

      rem Option 2
      rem java com.bluemartini.dev.ExecTestWait

      rem Option 3
      rem call c:\foo.bat
      rem java com.bluemartini.dev.ExecTestWait
      // END OF FILE: test.bat

      // FILE: ExecTest.java
      import java.util.*;
      import java.io.*;

      public class ExecTest {
          private static final int RUN_JAVA = 0;
          private static final int RUN_SCRIPT = 1;
          private static final int RUN_AND = 2;

          private static int state = RUN_JAVA;

          public static void main(String[] args) throws Exception {
              if (args.length > 0) {
                  String sState = args[0];
                  if (sState.equals("and")) {
                      state = RUN_AND;
                  } else if (sState.equals("script")) {
                      state = RUN_SCRIPT;
                  } else if (sState.equals("java")) {
                      state = RUN_JAVA;
                  }
              }
              new ExecTest();
          }

          public ExecTest() throws Exception {
              // This behaves properly
              Process p;
              if (state == RUN_SCRIPT) {
                  // ExecTestWait continues to run after destroy called
                  p = Runtime.getRuntime().exec("c:\\test.bat");
              } else if (state == RUN_AND) {
                  // ExecTestWait continues to run after destroy called
                  p = Runtime.getRuntime().exec("c:\\foo.bat && java
      com.bluemartini.dev.ExecTestWait");
              } else {
                  // This behaves properly
                  p = Runtime.getRuntime().exec("java com.bluemartini.dev.ExecTestWait");
              }

              ProcessWatcher pw = new ProcessWatcher(p) {
                  public void outPrint(String s) {
                      System.out.println("[" + Thread.currentThread().getName() +
      ":out] " + s);
                  }
                  public void errPrint(String s) {
                      System.out.println("[" + Thread.currentThread().getName() +
      ":err] " + s);
                  }

                  public void processTerminated(int code) {
                      System.out.println("[" + Thread.currentThread().getName() + "]
      Process ended");
                  }
              };

              Thread.sleep(5000);
              System.out.println("Killing process");
              p.destroy();
          }

          public interface TailPipeListener {
              void tailPipeOutput(String s);
          }

          public class ProcessWatcher extends Thread {

              Process proc_;
              Thread outputThread_;
              Thread errorThread_;
              boolean bInterrupt_ = false;

              TailPipe tailPipeOut_;
              TailPipe tailPipeErr_;

              public ProcessWatcher(Process p) {
                  proc_ = p;

                  tailPipeOut_ = new TailPipe(proc_, false);
                  tailPipeOut_.addListener(new TailPipeListener() {
                      public void tailPipeOutput(String s) {
                          outPrint(s);
                      }
                  });

                  tailPipeErr_ = new TailPipe(proc_, true);
                  tailPipeErr_.addListener(new TailPipeListener() {
                      public void tailPipeOutput(String s) {
                          errPrint(s);
                      }
                  });

                  setName("ProcessWatcher");
                  start();
              }

              public void run() {
                  try {
                      tailPipeOut_.follow(200);
                      tailPipeErr_.follow(200);
                      int exitValue = proc_.waitFor();
                      processTerminated(exitValue);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  } catch (IOException e) {
                      e.printStackTrace();
                  } finally {
      // bInterrupt_ = true;
      // tailPipeOut_.stopFollow();
      // tailPipeErr_.stopFollow();
                  }
              }

              protected void processTerminated(int code) {
              }

              protected void outPrint(String s) {
              }

              protected void errPrint(String s) {
              }
          }

          public class TailPipe {
              private Vector vListeners_ = new Vector();
              private boolean bFollowing_ = false;
              private PipeThread followThread_;

              File file_;
              Process process_;
              boolean bErrorStream_;

              public TailPipe(File file) {
                  file_ = file;
              }

              public TailPipe(Process process, boolean errorStream) {
                  process_ = process;
                  bErrorStream_ = errorStream;
              }

          // public void tail(int lines) {
          // }

          // public void follow(int lines, int latency) {
          // bFollowing_ = true;
          // PipeThread thread = new PipeThread(latency);
          // thread.start();
          // }

              public void follow(int latency) throws IOException {
                  try {
                      followThread_ = new PipeThread(latency);
                  } catch (Exception e) {
                      if (e instanceof IOException) {
                          throw (IOException)e;
                      } else {
                          e.printStackTrace();
                          throw new IOException(e.getMessage());
                      }
                  }
                  bFollowing_ = true;
                  followThread_.start();
              }

              public void stopFollow() {
                  bFollowing_ = false;
                  followThread_.interrupt(); // ### Do I really have to do this?
              }

              private class PipeThread extends Thread {
                  int latency_ = -1;
                  BufferedReader pipeIn_;
                  char[] buf_ = new char[32768];

                  public PipeThread(int latency) throws Exception {
                      latency_ = latency;
                      if (file_ != null) {
                          pipeIn_ = new BufferedReader(new FileReader(file_));
                      } else if (process_ != null) {
                          if (bErrorStream_) {
                              pipeIn_ = new BufferedReader(new
      InputStreamReader(process_.getErrorStream()));
                          } else {
                              pipeIn_ = new BufferedReader(new
      InputStreamReader(process_.getInputStream()));
                          }
                      }
                  }
                  public void run() {
                      int charsRead;
                      while (bFollowing_) {
                          try {
                              charsRead = pipeIn_.read(buf_);
                              fireTailPipeOutput(new String(buf_, 0, charsRead));
                          } catch(Exception e) {
                              try {
                                  pipeIn_.close();
                              } catch (IOException ioe) {
                                  ioe.printStackTrace();
                              }
                              bFollowing_ = false;
                          }
                          try {
                              Thread.sleep(latency_);
                          } catch (InterruptedException e) {
                          }
                      }
                      try {
                          pipeIn_.close();
                      } catch (IOException ioe) {
                      }
                  }
              }

              private void fireTailPipeOutput(String s) {
                  Enumeration enum = vListeners_.elements();
                  while (enum.hasMoreElements()) {
                      TailPipeListener ln = (TailPipeListener)enum.nextElement();
                      ln.tailPipeOutput(s);
                  }
              }

              public void addListener(TailPipeListener lnr) {
                  vListeners_.addElement(lnr);
              }
          }
      }
      // END OF FILE: ExecTest.java

      // FILE: ExecTestWait.java
      public class ExecTestWait {
          public static void main(String[] args) throws Exception {
              System.out.println("Sleep process started ...");
              while(true) {
                  System.out.println("sleeping");
                  Thread.sleep(1000);
                  Thread.yield();
              }
          }
      }
      // END OF FILE: ExecTestWait.java

      ---------- END SOURCE ----------

      CUSTOMER WORKAROUND :
      None found.
      (Review ID: 165865)
      ======================================================================

            Unassigned Unassigned
            nthompsosunw Nathanael Thompson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: