viphe
viphe

Reputation: 699

Handling output/error of Process separately in Java without extra-threads

I know that the canonical way of handling the output/error streams of an external Process in Java, is to use two extra-threads in order to pull the data from the output and error streams lest the process might be blocked.

Now what about the following?

public static void main(String[] args) throws IOException, InterruptedException {
  ProcessBuilder processBuilder = new ProcessBuilder(args);
  Process process = processBuilder.start();

  InputStream outputStream = null, errorStream = null;
  ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
  ByteArrayOutputStream errorBuffer = new ByteArrayOutputStream();
  try {
    outputStream = process.getInputStream();
    errorStream = process.getErrorStream();

    byte[] tmp = new byte[1024];

    while (true) {
      int outputBytes = readAvailablOnce(outputStream, outputBuffer, tmp);
      int errorBytes = readAvailablOnce(errorStream, errorBuffer, tmp);
      if (outputBytes == 0 && errorBytes == 0) {
        try {
          process.exitValue();
          break;
        } catch (IllegalThreadStateException e) {
          // keep on looping
        }
      }
    }
    readAvailableAll(outputStream, outputBuffer, tmp);
    readAvailableAll(errorStream, errorBuffer, tmp);

  } finally {
    closeQuietly(outputStream);
    closeQuietly(errorStream);
  }

  System.out.println(outputBuffer.toString("ASCII"));
  System.err.println(errorBuffer.toString("ASCII"));
  System.err.println("exit code: " + process.exitValue());
}

private static void closeQuietly(InputStream in) {
  if (in != null) {
    try {
      in.close();
    } catch (IOException e) {
      // ignored
    }
  }
}

private static int readAvailablOnce(
  InputStream inputStream, OutputStream outputStream, byte[] buffer)
throws IOException {
  int bytesRead = 0;
  if (inputStream.available() > 0) {
    bytesRead = inputStream.read(buffer);
    outputStream.write(buffer, 0, bytesRead);
  }
  return bytesRead;
}

private static void readAvailableAll(
  InputStream inputStream, OutputStream outputStream, byte[] buffer)
throws IOException {
  if (inputStream.available() > 0) {
    int bytesRead = 0;
    while ((bytesRead = inputStream.read(buffer)) >= 0) {
      outputStream.write(buffer, 0, bytesRead);
    }
  }
}

This actually runs fine in the few examples I tried (running "dir", "ps aux", etc).

It also has the disadvantage of not allowing you to easily handle the outputs line by line (here you buffer everything before doing anything), unless you start doing some more or less complicated stuff with Buffers and CharsetDecoders.

Still, it looks useful for anything without unacceptably big output (although nothing forces us to buffer the whole output before using it).

I only tried the 1.5 and 1.6 JVM (Windows XP and Linux).

Also, this code makes the assumption that the final output bits of the process will be readily available for reading (InputStream.available() > 0).

Anyone would know what (or if something) is wrong with this code (or have a better idea)?

Upvotes: 1

Views: 2108

Answers (1)

Peter Lawrey
Peter Lawrey

Reputation: 533660

I would use ProcessBuilder.redirectErrorStream(true) which allows you to read one stream in the current thread. You wouldn't need any background threads.

Upvotes: 1

Related Questions