class RunProcess {

    private static final long JUST_ENDED_LIMIT = 500;

    private final File workingDirectory;

    private final String[] command;

    private volatile Process process;

    private volatile long endTime;

    /**
     * Creates new {@link RunProcess} instance for the specified command.
     *
     * @param command the program to execute and its arguments
     */
    public RunProcess(String... command) {
        this(null, command);
    }

    /**
     * Creates new {@link RunProcess} instance for the specified working directory and
     * command.
     *
     * @param workingDirectory the working directory of the child process or {@code null}
     *                         to run in the working directory of the current Java process
     * @param command          the program to execute and its arguments
     */
    public RunProcess(File workingDirectory, String... command) {
        this.workingDirectory = workingDirectory;
        this.command = command;
    }

    public int run(boolean waitForProcess, String... args) throws IOException {
        return run(waitForProcess, Arrays.asList(args), Collections.emptyMap());
    }

    public int run(boolean waitForProcess, Collection<String> args, Map<String, String> environmentVariables)
            throws IOException {
        ProcessBuilder builder = new ProcessBuilder(this.command);
        builder.directory(this.workingDirectory);
        builder.command().addAll(args);
        builder.environment().putAll(environmentVariables);
        builder.redirectErrorStream(true);
        builder.inheritIO();
        try {
            Process process = builder.start();
            this.process = process;
            SignalUtils.attachSignalHandler(this::handleSigInt);
            if (waitForProcess) {
                try {
                    return process.waitFor();
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    return 1;
                }
            }
            return 5;
        } finally {
            if (waitForProcess) {
                this.endTime = System.currentTimeMillis();
                this.process = null;
            }
        }
    }

    /**
     * Return the running process.
     *
     * @return the process or {@code null}
     */
    public Process getRunningProcess() {
        return this.process;
    }

    /**
     * Return if the process was stopped.
     *
     * @return {@code true} if stopped
     */
    public boolean handleSigInt() {
        if (allowChildToHandleSigInt()) {
            return true;
        }
        return doKill();
    }

    private boolean allowChildToHandleSigInt() {
        Process process = this.process;
        if (process == null) {
            return true;
        }
        long end = System.currentTimeMillis() + 5000;
        while (System.currentTimeMillis() < end) {
            if (!process.isAlive()) {
                return true;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }

    /**
     * Kill this process.
     */
    public void kill() {
        doKill();
    }

    private boolean doKill() {<FILL_FUNCTION_BODY>}

    public boolean hasJustEnded() {
        return System.currentTimeMillis() < (this.endTime + JUST_ENDED_LIMIT);
    }

}

        // destroy the running process
        Process process = this.process;
        if (process != null) {
            try {
                process.destroy();
                process.waitFor();
                this.process = null;
                return true;
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
        return false;
    
<no_super_class>