这个问题是关于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?
修改
正如我和其他人所指出的那样:
destroy()
它。它已经终止。 (即使不一定通过退出代码0正常终止,但终止。)waitFor()
并在超时超时时中断,以使进程有机会正常终止,但在挂起时将其终止。未答复的部分:
答案 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
,我假设您希望捕获所有未经检查的异常。这是RuntimeException
或Error
的衍生物。但是永远不应该抓住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(...)
是空的安全实现,用于关闭内容,但在出现问题时抛出异常。