Обработка вывода / ошибки процесса отдельно в Java без лишних потоков - PullRequest
1 голос
/ 06 января 2012

Я знаю, что канонический способ обработки потоков вывода / ошибок внешнего Процесса в Java - это использование двух дополнительных потоков для извлечения данных из потоков вывода и ошибок, чтобы процесс не мог быть заблокирован.

А что насчет следующего?

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);
    }
  }
}

Это на самом деле работает нормально в тех нескольких примерах, которые я пробовал (запуск "dir", "ps aux" и т. Д.).

У него также есть недостаток, заключающийся в том, что вы не можете легко обрабатывать выходные данные построчно (здесь вы буферизуете все перед тем, как что-либо делать), если только вы не начинаете делать более или менее сложные вещи с помощью Buffers и CharsetDecoders.это выглядит полезным для всего без недопустимо большого вывода (хотя ничто не заставляет нас буферизовать весь вывод перед его использованием).

Я пробовал только 1.5 и 1.6 JVM (Windows XP и Linux).

Кроме того, этот код предполагает, что последние выходные биты процесса будут легко доступны для чтения (InputStream.available ()> 0).

Кто-нибудь может знать, что (или если что-то) не так с этим кодом (или есть идея получше)?

1 Ответ

1 голос
/ 06 января 2012

Я бы использовал ProcessBuilder.redirectErrorStream (true) , который позволяет читать один поток в текущем потоке. Вам не нужны фоновые темы.

...