Process Builder and Process in Java - how to execute a process with a timeout :?

14,894

Solution 1

It is a bug in Java's Process.destroy() implementation on Windows. The problem is that the batch-script (or its executing shell) is killed, but does not kill its own child processes (the ping here). Thus, ping is still running after the .destroy(), and also after the .waitFor(). But somehow the VM still waits for the ping to finish before finishing itself.

It seems there is nothing you can do here from the Java side to really kill the ping reliably.

You may think about using start (in your batch script or outside) to invoke your ping as a separate process.

(See also this previous discussion.)

Or change to a unix-like operation system.

Solution 2

If you use Unix/Linux, then write a wrapper bash shell script to interrupt an external command by timeout, then call the wrapper from Java.

The wrapper script looks like

#!/bin/bash
timeout 60 <your command>

You can detect if timeout expired by checking the script exit code which is 124 in case of timeout

See

man timeout

Share:
14,894
mhshams
Author by

mhshams

Software Architect / Java Developer.

Updated on June 17, 2022

Comments

  • mhshams
    mhshams almost 2 years

    I need to execute an external batch file in java with a specific timeout. which means that if the batch execution take longer than specified timeout, i need to cancel the execution.

    here is a sample code that i wrote:

    public static void main(String[] args) throws IOException, InterruptedException {
        ProcessBuilder p = new ProcessBuilder("c:\\wait.bat", "25");  // batch file execution will take 25 seconds.
        final long l = System.currentTimeMillis();
        System.out.println("starting..." + (System.currentTimeMillis() - l));
        final Process command = p.start();
        System.out.println("started..." + (System.currentTimeMillis() - l));
    
        Timer t = new Timer();
        t.schedule(new TimerTask() {
    
            @Override
            public void run() {
                command.destroy();
            }
        }, 5000);   // it will kill the process after 5 seconds (if it's not finished yet).
        int i = command.waitFor();
        t.cancel();
        System.out.println("done..." + (System.currentTimeMillis() - l));
        System.out.println("result : " + i);
    
        System.out.println("Really Done..." + (System.currentTimeMillis() - l));
    }
    

    the batch file "wait.bat" is something like this:

    @echo off
    echo starting the process...
    @ping 127.0.0.1 -n 2 -w 1000 > nul
    @ping 127.0.0.1 -n %1% -w 1000> nul
    echo process finished succesfully
    @echo on
    

    As you see in the code, batch file will take 25 seconds to finish (first line in main method) and the Timer will destroy the command after 5 seconds.

    here is the output of my code:

    starting...0
    started...0
    done...5000
    result : 1
    Really Done...5000
    BUILD SUCCESSFUL (total time: 25 seconds)
    

    as you see in output, the last line ("Really Done...") is executed in 5th second but the application is finished after 25 seconds.

    my question is that : even though i called the destroy method in my timer, why jvm still waiting for the process to be finished ?