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

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

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

 /** * 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:关闭子进程std流?

编辑

正如我和其他人在此指出:

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

未答复的部分:

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

试图简化您的代码:

 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我假设您希望捕获所有未经检查的exception。 这可能是RuntimeExceptionError的衍生物。 但是永远不应该捕获Error ,所以我用RuntimeException替换了Throwable

(捕获所有RuntimeException仍然不是一个好主意。)

作为与状态链接的问题,最好是读取并丢弃输出和错误流。 如果你正在使用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写入足够的信息以填充缓冲区时出现阻塞等问题。

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

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

您不需要检查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 } } } 

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

 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(...)是空的安全实现来关闭东西,但在出错时抛出exception。