我正在尝试为我的程序使用看门狗,但如果我使用 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;
}
}
}
}
真正的魔法是连同根进程一起摧毁后代。
我有一个单元测试表明基本上是这样做的:
bash“script.sh” 其中 script.sh 只是一个“睡眠 5”
在使用 ExecuteWatchdog 之前,ExecuteWatchdog 会使进程超时,退出代码为 143,但仅在 5 秒后。在将 ExecuteWatchdog 替换为 DestroyDescendantsExecutorWatchDog 并设置 5 毫秒超时后,单元测试几乎立即退出并返回预期的代码 143。