如何从java.lang.Process关闭std -stream合适?

时间:2011-08-15 12:46:14

标签: java process stream resources

这个问题是关于java.lang.Process及其对stdin,stdout和stderr的处理。

我们的项目中有一个类,它是org.apache.commons.io.IOUtils的扩展。在那里,我们有一个安静的新方法来关闭Process-Object的std-streams?或者它不合适?

/**
 * Method closes all underlying streams from the given Process object.
 * If Exit-Code is not equal to 0 then Process will be destroyed after 
 * closing the streams.
 *
 * It is guaranteed that everything possible is done to release resources
 * even when Throwables are thrown in between.
 *
 * In case of occurances of multiple Throwables then the first occured
 * Throwable will be thrown as Error, RuntimeException or (masked) IOException.
 *
 * The method is null-safe.
 */
public static void close(@Nullable Process process) throws IOException {
    if(process == null) {
      return;
    }

    Throwable t = null;

    try {
      close(process.getOutputStream());
    }
    catch(Throwable e) {
      t = e;
    }

    try{
      close(process.getInputStream());
    }
    catch(Throwable e) {
      t = (t == null) ? e : t;
    }

    try{
      close(process.getErrorStream());
    }
    catch (Throwable e) {
      t = (t == null) ? e : t;
    }

    try{
      try {
        if(process.waitFor() != 0){
          process.destroy();
        }
      }
      catch(InterruptedException e) {
        t = (t == null) ? e : t;
        process.destroy();
      }
    }
    catch (Throwable e) {
      t = (t == null) ? e : t;
    }

    if(t != null) {
      if(t instanceof Error) {
        throw (Error) t;
      }

      if(t instanceof RuntimeException) {
        throw (RuntimeException) t;
      }

      throw t instanceof IOException ? (IOException) t : new IOException(t);
    }
}

public static void closeQuietly(@Nullable Logger log, @Nullable Process process) {
  try {
    close(process);
  }
  catch (Exception e) {
    //log if Logger provided, otherwise discard
    logError(log, "Fehler beim Schließen des Process-Objekts (inkl. underlying streams)!", e);
  }
}

public static void close(@Nullable Closeable closeable) throws IOException {
  if(closeable != null) {
    closeable.close();
  }
}

这些方法基本上用在finally块中。

我真正想知道的是,如果我对此实施安全吗?考虑以下事项:进程对象在其生命周期内是否始终返回相同的stdin,stdout和stderr流?或者我可能会错过先前由流程“getInputStream()getOutputStream()getErrorStream()方法返回的关闭流?”

StackOverflow.com上有一个相关问题:java: closing subprocess std streams?

修改

正如我和其他人所指出的那样:

  • 必须完全消耗InputStreams。如果不这样做,则子进程可能不会终止,因为其输出流中存在未完成的数据。
  • 必须关闭所有三个标准流。无论之前是否使用过。
  • 当子进程正常终止时,一切都应该没问题。如果没有,则必须强行终止。
  • 当子流程返回退出代码时,我们不需要destroy()它。它已经终止。 (即使不一定通过退出代码0正常终止,但终止。)
  • 我们需要监视waitFor()并在超时超时时中断,以使进程有机会正常终止,但在挂起时将其终止。

未答复的部分:

  • 考虑并行使用InputStream的优点和缺点。或者他们必须按特定顺序消费吗?

3 个答案:

答案 0 :(得分:2)

尝试简化代码:

public static void close(@Nullable Process process) throws IOException
{
    if(process == null) { return; }

    try
    {
        close(process.getOutputStream());
        close(process.getInputStream());
        close(process.getErrorStream());

        if(process.waitFor() != 0)
        {
            process.destroy();
        }
    }
    catch(InterruptedException e)
    {
        process.destroy();
    }
    catch (RuntimeException e)
    {
        throw (e instanceof IOException) ? e : new IOException(e);
    }
}

通过捕获Throwable,我假设您希望捕获所有未经检查的异常。这是RuntimeExceptionError的衍生物。但是永远不应该抓住Error,因此我已将Throwable替换为RuntimeException

(抓住所有RuntimeException仍然不是一个好主意。)

答案 1 :(得分:1)

作为与状态链接的问题,最好是读取并丢弃输出和错误流。如果你正在使用apache commons io,比如,

new Thread(new Runnable() {public void run() {IOUtils.copy(process.getInputStream(), new NullOutputStream());}}).start();
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getErrorStream(), new NullOutputStream());}}).start();

您希望在单独的线程中读取并丢弃stdout和stderr,以避免在向stderr或stdout写入足够的信息以填充缓冲区时出现阻塞等问题。

如果您担心有两个线程,请参阅此question

我不认为在将stdout,stdin复制到NullOutputStream时需要担心捕获IOExceptions,因为如果从进程stdout / stdin读取IOException,则可能是由于进程本身死了,并且写入到NullOutputStream永远不会抛出异常。

您无需检查waitFor()的返回状态。

您想等待该过程完成吗?如果是这样,你可以这样做,

while(true) {
     try
     {
         process.waitFor();
         break;
     } catch(InterruptedException e) {
         //ignore, spurious interrupted exceptions can occur
     }

}

查看您提供的链接,您需要在流程完成时关闭流,但destroy会为您执行此操作。

所以最后,方法变为,

public void close(Process process) {

    if(process == null) return;

    new Thread(new Runnable() {public void run() {IOUtils.copy(process.getInputStream(), new NullOutputStream());}}).start();
    new Thread(new Runnable() {public void run() {IOUtils.copy(process.getErrorStream(), new NullOutputStream());}}).start();
    while(true) {
        try
        {
            process.waitFor();
            //this will close stdin, stdout and stderr for the process
            process.destroy();
            break;
        } catch(InterruptedException e) {
            //ignore, spurious interrupted exceptions can occur
        }

   }
}

答案 2 :(得分:1)

只是为了让您知道我目前在代码库中的内容:

public static void close(@Nullable Process process) throws IOException {
  if (process == null) {
    return;
  }

  Throwable t = null;

  try {
    flushQuietly(process.getOutputStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    close(process.getOutputStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    skipAllQuietly(null, TIMEOUT, process.getInputStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    close(process.getInputStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    skipAllQuietly(null, TIMEOUT, process.getErrorStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    close(process.getErrorStream());
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  try {
    try {
      Thread monitor = ThreadMonitor.start(TIMEOUT);
      process.waitFor();
      ThreadMonitor.stop(monitor);
    }
    catch (InterruptedException e) {
      t = mostImportantThrowable(t, e);
      process.destroy();
    }
  }
  catch (Throwable e) {
    t = mostImportantThrowable(t, e);
  }

  if (t != null) {
    if (t instanceof Error) {
      throw (Error) t;
    }

    if (t instanceof RuntimeException) {
      throw (RuntimeException) t;
    }

    throw t instanceof IOException ? (IOException) t : new IOException(t);
  }
}

skipAllQuietly(...)使用完整的InputStreams。它在内部使用类似于org.apache.commons.io.ThreadMonitor的实现来在超出给定超时时中断消耗。

mostImportantThrowable(...)决定应该返回什么Throwable。一切都有错误。首次出现较高的prio而不是后来发生。这里没什么特别重要的,因为这些Throwable最有可能在以后丢弃。我们想继续在这里工作,我们只能扔一个,所以我们必须决定我们最后扔的东西,如果有的话。

close(...)是空的安全实现,用于关闭内容,但在出现问题时抛出异常。