Java Apache Commons Exec Watchdog and Windows cmd.exe issues



我正在尝试为我的程序使用看门狗,但是如果我使用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。

最新更新