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

java.lang.Runtime.exec: Output lost if subprocess exits quickly (Unix)

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • 1.3.0, 1.3.0_02, 1.3.1
    • core-libs



      Name: boT120536 Date: 03/06/2001


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

      Starting an external process using Runtime.exec() and trying to get the text written to stdout or stderr does not always work.

      In order to reproduce the problem try to compile and run the following test program using the following short sh-script for echo_cmd:

      #!/bin/sh
      echo Hello


      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.io.BufferedReader;
      import java.io.IOException;
       
      public class ExecProblem {
          private StreamReaderThread outReaderThread;
          private StreamReaderThread errReaderThread;
          private int num;
       
          public ExecProblem(int num) {
              this.num = num;
              outReaderThread = new StreamReaderThread();
              errReaderThread = new StreamReaderThread();
          }
       
          public void execute() {
              int retVal;
              Runtime rt = Runtime.getRuntime();
              try {
                  // Start threads storing the output of the macro
                  outReaderThread.start();
                  errReaderThread.start();
       
                  // Start external process
                  Process proc = rt.exec("/bin/sh ./echo_cmd");
                  //Thread.sleep(1000);
       
                  // Set input streams and wakeup reader threads
                  outReaderThread.setInputStream(proc.getInputStream());
                  errReaderThread.setInputStream(proc.getErrorStream());
       
                  // Wait for macro termination
                  retVal = proc.waitFor();
       
                  // Close streams (necessary ?)
                  proc.getInputStream().close();
                  proc.getErrorStream().close();
                  proc.getOutputStream().close();
       
              } catch (IOException ex) {
              } catch (InterruptedException ex) {
              }
              // Print result
              System.out.println(num+": Out = "+outReaderThread.getText());
              System.out.println(num+": Err = "+errReaderThread.getText());
          }
       
          private class StreamReaderThread extends Thread {
              private BufferedReader br;
              private StringBuffer sb;
              private String newline;
              private String line;
       
              public StreamReaderThread() {
                  br = null;
                  sb = null;
                  newline = null;
                  line = null;
              }
       
              public synchronized String getText() {
                  if (sb == null) return "";
                  return sb.toString();
              }
       
              public synchronized void setInputStream(InputStream is) {
                  br = new BufferedReader(new InputStreamReader(is));
                  notify();
              }
       
              public synchronized void run() {
                  try {
                      wait();
                      while ((line = br.readLine()) != null) {
                          if (sb == null) {
                              sb = new StringBuffer();
                              newline = System.getProperty("line.separator");
                          } else {
                              sb.append(newline);
                          }
                          sb.append(line);
                      }
                  } catch (IOException ex) {
                  } catch (InterruptedException ex) {
                  }
              }
          }
       
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  ExecProblem execProblem = new ExecProblem(i);
                  execProblem.execute();
              }
          }
      }

      Running the program with "java ExecProblem" will produce an output like the following:
      0: Out = Hello
      0: Err =
      1: Out = Hello
      1: Err =
      2: Out = Hello
      2: Err =
      3: Out = Hello
      3: Err =
      4: Out =
      4: Err =
      5: Out = Hello
      5: Err =
      6: Out = Hello
      6: Err =
      7: Out = Hello
      7: Err =
      8: Out = Hello
      8: Err =
      9: Out =
      9: Err =

      As you can see, not every execution of the sh-script produces the desired output. Things get worst if you uncomment the line Thread.sleep(1000);
      This will produce an additional delay between starting the external process and
      getting the streams.
      The resulting output is:
      0: Out =
      0: Err =
      1: Out =
      1: Err =
      2: Out =
      2: Err =
      3: Out =
      3: Err =
      4: Out =
      4: Err =
      5: Out =
      5: Err =
      6: Out =
      6: Err =
      7: Out =
      7: Err =
      8: Out =
      8: Err =
      9: Out =
      9: Err =

      The problem seams to be, that if the time difference between calling
      rt.exec("..."); and obtaining the input- and errorstreams is longer than the time the external process needs to complete the text written by the external process gets lost. This observation leads to my rather "difficult" solution for the StreamReaderThread class using wait() and notify() because I wanted to make the time between rt.exec() and reading from the streams as short as possible.

      I also tried the "solution" described in http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
      which does not use wait() and notify() but creates the reader threads after
      the call to rt.exec() providing the streams from process.getInputStream() and process.getErrorStream() in the threads constructors. This needs more time than my solution and for that reason does not work too.

      If my conclusions are correct a possible solution could be, that the caller of Runtime.exec() provides the streams for stdout/stderr in order to be connected to the external process in the exec() method.
      (Review ID: 117998)
      ======================================================================

            kkladkosunw Konstantin Kladko (Inactive)
            bonealsunw Bret O'neal (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: