如何适当地从 java.lang.Process 关闭 std-streams?
这个问题是关于 java.lang.Process 及其对 stdin、stdout 和 stderr 的处理。
我们的项目中有一个类是 org.apache.commons.io.IOUtils 的扩展。我们有一个安静的新方法来关闭进程对象的标准流吗?还是不合适?
/**
* 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 流?
< strong>编辑
正如我和其他人在这里指出的:
- InputStreams 必须被完全消耗。如果不完成,则子进程可能不会终止,因为其输出流中存在未完成的数据。
- 所有三个标准流都必须关闭。不管以前用过还是没用过。
- 当子进程正常终止时,一切都应该没问题。如果不是,则必须强制终止。
- 当子进程返回退出代码时,我们不需要
destroy()
它。它已经终止了。 (即使不一定以退出代码 0 正常终止,但它还是终止了。) - 我们需要监视
waitFor()
并在超时时中断,以便让进程有机会正常终止,但在挂起时杀死它。
未回答的部分:
- 考虑并行使用 InputStreams 的优点和缺点。或者它们必须按特定顺序消耗?
This question is about java.lang.Process
and its handling of stdin, stdout and stderr.
We have a class in our project that is an extension to org.apache.commons.io.IOUtils
. There we have a quiet new method for closing the std-streams of a Process-Object appropriate? Or is it not appropriate?
/**
* 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();
}
}
Methods like these are basically used in finally-blocks.
What I really want to know is if I am safe with this implementation? Considering things like: Does a process object always return the same stdin, stdout and stderr streams during its lifetime? Or may I miss closing streams previously returned by process' getInputStream()
, getOutputStream()
and getErrorStream()
methods?
There is a related question on StackOverflow.com: java: closing subprocess std streams?
Edit
As pointed out by me and others here:
- InputStreams have to be totally consumed. When not done then the subprocess may not terminate, because there is outstanding data in its output streams.
- All three std-streams have to be closed. Regardless if used before or not.
- When the subprocess terminates normally everything should be fine. When not then it have to be terminated forcibly.
- When an exit code is returned by subprocess then we do not need to
destroy()
it. It has terminated. (Even when not necessarily terminated normally with Exit Code 0, but it terminated.) - We need to monitor
waitFor()
and interrupt when timeout exceeds to give process a chance to terminate normally but killing it when it hangs.
Unanswered parts:
- Consider Pros and Cons of consuming the InputStreams in parallel. Or must they be consumed in particular order?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
尝试简化代码:
通过捕获 Throwable 我假设您希望捕获所有未经检查的异常。它是
RuntimeException
或Error
的派生物。但是Error
永远不应该被捕获,因此我用RuntimeException
替换了Throwable
。(捕获所有 RuntimeException 仍然不是一个好主意。)
An attempt at simplifying your code:
By catching
Throwable
I assume you wish to catch all unchecked exceptions. That is either a derivative ofRuntimeException
orError
. HoweverError
should never be catched, so I have replacedThrowable
withRuntimeException
.(It is still not a good idea to catch all
RuntimeException
s.)正如您链接到的问题所述,最好读取并丢弃输出和错误流。如果您使用 apache commons io,例如,
您希望在单独的线程中读取并丢弃 stdout 和 stderr,以避免出现问题,例如当向 stderr 或 stdout 写入足够的信息以填充缓冲区时进程阻塞。
如果您担心有两个线程,请参阅此 问题
我认为在将 stdout、stdin 复制到 NullOutputStream 时,您不需要担心捕获 IOExceptions,因为如果从进程 stdout/stdin 读取 IOException,则可能是由于进程本身已死亡,并且写入 NullOutputStream 永远不会抛出异常。
您不需要检查 waitFor() 的返回状态。
您想等待该过程完成吗?如果是这样,您可以这样做,
查看您提供的链接,您确实需要在过程完成时关闭流,但 destroy 会为您完成此操作。
所以最终,该方法变成了,
As the question you linked to states, it is better to read and discard the output and error streams. If you are using apache commons io, something like,
You want to read and discard stdout and stderr in a separate thread to avoid problems such as the process blocking when it writes enough info to stderr or stdout to fill the buffer.
If you are worried about having two many threads, see this question
I don't think you need to worry about catching IOExceptions when copying stdout, stdin to NullOutputStream, since if there is an IOException reading from the process stdout/stdin, it is probably due to the process being dead itself, and writing to NullOutputStream will never throw an exception.
You don't need to check the return status of waitFor().
Do you want to wait for the process to complete? If so, you can do,
Looking at the link you provided you do need to close the streams when the process is complete, but destroy will do that for you.
So in the end, the method becomes,
只是为了让您知道我目前在代码库中的内容:
skipAllQuietly(...)
消耗完整的输入流。它在内部使用类似于 org.apache.commons.io.ThreadMonitor 的实现来在超出给定超时时中断消耗。mostImportantThrowable(...)
决定应返回什么 Throwable。凡事都有错误。首先发生的优先级高于后来发生的优先级。这里没有什么非常重要的,因为这些 Throwable 很可能稍后会被丢弃。我们想继续在这里工作,但我们只能扔一个,所以我们必须决定最后扔什么(如果有的话)。close(...)
是空安全实现,用于关闭内容,但在出现问题时抛出异常。Just to let you know what I have currently in our codebase:
skipAllQuietly(...)
consumes complete InputStreams. It uses internally an implementation similar toorg.apache.commons.io.ThreadMonitor
to interrupt consumption if a given timeout exceeded.mostImportantThrowable(...)
decides over what Throwable should be returned. Errors over everything. First occured higher prio than later occured. Nothing very important here since these Throwable are most probably discarded anyway later. We want to go on working here and we can only throw one, so we have to decide what we throw at the end, if ever.close(...)
are null-safe implementations to close stuff but throwing Exception when something went wrong.