How to Add a Timeout Value When Using Java's Runtime.Exec()

How to add a timeout value when using Java's Runtime.exec()?

public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError,
final long timeout)
throws IOException, InterruptedException, TimeoutException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);
/* Set up process I/O. */
...
Worker worker = new Worker(process);
worker.start();
try {
worker.join(timeout);
if (worker.exit != null)
return worker.exit;
else
throw new TimeoutException();
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
process.destroyForcibly();
}
}

private static class Worker extends Thread {
private final Process process;
private Integer exit;
private Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
}

How to add a timeout to Runtime.exec() but checking exit value?

You can use p.exitValue() to get hold of the exit value, but note that you will get an IllegalThreadStateException if the subprocess represented by this Process object has not yet terminated so don't use this method if the waitFor() times out.

Process p = ...
if(!p.waitFor(1, TimeUnit.MINUTES)) {
//timeout - kill the process.
p.destroyForcibly();
} else {
int exitValue = p.exitValue();
// ...
}

How to get the output from a process and set a timeout?

As @EJP suggested, You can use different threads to capture the streams or use ProcessBuilder or redirect to a file from your command.

Here are 3 approaches that I feel you can use.

  1. Using different threads for Streams.

    Process process = Runtime.getRuntime().exec("cat ");

    ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);

    Future<String> output = newFixedThreadPool.submit(() -> {
    return IOUtils.toString(process.getInputStream());
    });
    Future<String> error = newFixedThreadPool.submit(() -> {
    return IOUtils.toString(process.getErrorStream());
    });

    newFixedThreadPool.shutdown();

    // process.waitFor();
    if (!process.waitFor(3, TimeUnit.SECONDS)) {
    System.out.println("Destroy");
    process.destroy();
    }

    System.out.println(output.get());
    System.out.println(error.get());
  2. Using ProcessBuilder

    ProcessBuilder processBuilder = new ProcessBuilder("cat")
    .redirectError(new File("error"))
    .redirectOutput(new File("output"));

    Process process = processBuilder.start();

    // process.waitFor();
    if (!process.waitFor(3, TimeUnit.SECONDS)) {
    System.out.println("Destroy");
    process.destroy();
    }

    System.out.println(FileUtils.readFileToString(new File("output")));
    System.out.println(FileUtils.readFileToString(new File("error")));
  3. Use a redirection operator in your command to redirect Output & Error to a file and then Read from File.

Here is very good blog which explains different ways of handling Runtime.Exec

Java native process timeout

This is how the Plexus CommandlineUtils does it:

Process p;

p = cl.execute();

...

if ( timeoutInSeconds <= 0 )
{
returnValue = p.waitFor();
}
else
{
long now = System.currentTimeMillis();
long timeoutInMillis = 1000L * timeoutInSeconds;
long finish = now + timeoutInMillis;
while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) )
{
Thread.sleep( 10 );
}
if ( isAlive( p ) )
{
throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" );
}
returnValue = p.exitValue();
}

public static boolean isAlive( Process p ) {
try
{
p.exitValue();
return false;
} catch (IllegalThreadStateException e) {
return true;
}
}

Java: set timeout on a certain block of code?

Yes, but its generally a very bad idea to force another thread to interrupt on a random line of code. You would only do this if you intend to shutdown the process.

What you can do is to use Thread.interrupt() for a task after a certain amount of time. However, unless the code checks for this it won't work. An ExecutorService can make this easier with Future.cancel(true)

Its much better for the code to time itself and stop when it needs to.



Related Topics



Leave a reply



Submit