我正在尝试为我的程序使用看门狗,但是如果我使用cmd.exe来启动它,它不起作用。如果进程是本机启动的(没有cmd.exe),则看门狗会终止该进程,但如果程序使用cmd.exe启动,则它不会执行任何操作。
工作代码示例:
CommandLine cmd1 = CommandLine.parse("mysql");
ExecuteWatchdog watchdog = new ExecuteWatchdog(3 * 1000); // wait for 3 sec
Executor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
try {
executor.execute(cmd1);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("DONE!");
将命令更改为此命令将永远阻塞线程("mysql"等待用户输入):
CommandLine cmd1 = CommandLine.parse("cmd /C start /wait cmd.exe /C 'mysql'");
你有什么想法,如何解决这个问题吗?"mysql"命令应该在一个新的cmd.exe窗口中运行。
我通过扩展 ExecuteWatchdog 解决了这个问题; 即:
static class DestroyDescendantsExecutorWatchdog extends ExecuteWatchdog {
private Process process = null;
/**
* Creates a new watchdog with a given timeout.
*
* @param timeout the timeout for the process in milliseconds. It must be
* greater than 0 or 'INFINITE_TIMEOUT'
*/
public DestroyDescendantsExecutorWatchdog(long timeout) {
super(timeout);
}
/**
* Overridden to capture and store the process to monitor.
* @param processToMonitor
* the process to monitor. It cannot be {@code null}
*/
@Override
public synchronized void start(Process processToMonitor) {
super.start(processToMonitor);
process = processToMonitor;
}
/**
* Overridden to clean up the process to monitor state as we stop monitoring.
*/
@Override
public synchronized void stop() {
super.stop();
process = null;
}
/**
* Overrides the default behavior to collect the descendants and kill them as well.
* @param w the watchdog that timed out.
*/
@Override
public synchronized void timeoutOccured(Watchdog w) {
// if the process is defined
if (process != null) {
boolean isProcessTerminated = true;
try {
// We must check if the process was not stopped before being here
process.exitValue();
} catch (final IllegalThreadStateException itse) {
// the process is not terminated, if this is really
// a timeout and not a manual stop then destroy it.
if (isWatching()) {
isProcessTerminated = false;
}
}
// if we haven't already started terminating the process
if (!isProcessTerminated) {
// get all the descendants before you destroy the root process
Stream<ProcessHandle> descendants = process.toHandle().descendants();
// now go destroy the root process
super.timeoutOccured(w);
// follow up by destroying the descendants as well
descendants.forEach(descendant -> {
try {
descendant.destroy();
} catch (Exception e) {
log.warn("pid={};info={}; Could not destroy descendant", descendant.pid(), descendant.info(), e);
}
});
// no longer watching this process as it's destroyed
process = null;
}
}
}
}
真正的魔法是摧毁后代以及根过程。
我有一个单元测试显示基本上是这样做的:
砰砰"script.sh"其中 script.sh 只是一个"睡眠5"
在使用 ExecuteWatchdog之前,ExecuteWatchdog 会让进程超时,退出代码为 143,但仅在 5 秒后超时。 在将 ExecuteWatchdog 替换为超时时间为 5ms 的 DestroyDescendantsExecutorWatchDog 后,单元测试几乎立即退出,预期返回代码为 143。