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

前端 未结 17 1783
长发绾君心
长发绾君心 2020-11-27 12:08

I have a method I am using to execute a command on the local host. I\'d like to add a timeout parameter to the method so that if the command being called doesn\'t finish in

相关标签:
17条回答
  • 2020-11-27 12:15

    For those who can't use the new Java 8 method waitFor(long timeout, TimeUnit unit) (because they are on Android or simply can't upgrade) you can simply rip it from the JDK source code and add it somewhere in your utils file :

    public boolean waitFor(long timeout, TimeUnit unit, final Process process)
                throws InterruptedException
        {
            long startTime = System.nanoTime();
            long rem = unit.toNanos(timeout);
    
            do {
                try {
                    process.exitValue();
                    return true;
                } catch(IllegalThreadStateException ex) {
                    if (rem > 0)
                        Thread.sleep(
                                Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
                }
                rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
            } while (rem > 0);
            return false;
        }
    

    The only change I've made to the original one from JDK8 source code is the addition of the Process parameter so that we can call the exitValue method from the process.

    The exitValue method will directly try to return or throw an IllegalThreadStateException if the process has not yet terminated. In that case, we wait the received timeout and terminate.

    The method return a boolean, so if it return false then you know you need to manually kill the process.

    This way seems simplier than anything posted above (expect the direct call to waitFor for sure).

    0 讨论(0)
  • 2020-11-27 12:17

    Implement as a delegate and fail the call if it takes above your threshold to complete.

    0 讨论(0)
  • 2020-11-27 12:17

    If using Java 8 I'd go with Aleksander Blomskøld answer i.e. p.waitFor(1, TimeUnit.MINUTE)

    else if Java 6/7 and using Swing, then you can use a SwingWorker:

       final Process process = ...
       SwingWorker<Integer, Integer> sw = new SwingWorker<>() {
           @Override
           protected Integer doInBackground() throws Exception {
              process.waitFor();
              return process.exitValue();
           }
       };
       sw.execute();                
       int exitValue = sw.get(1, TimeUnit.SECONDS);
       if (exitValue == 0) {
           //everything was fine
       } else {
           //process exited with issues
       }
    
    0 讨论(0)
  • 2020-11-27 12:17

    I know this is really old post; i needed some help with a similar project so I thought I might give some of my code that I worked and ones that work.

    long current = System.currentTimeMillis();
    
    ProcessBuilder pb  = new ProcessBuilder(arguments);
    try{
        pb.redirectErrorStream(true);
        process = pb.start();
        int c ;
        while((c = process.getInputStream().read()) != -1 )
            if(System.currentTimeMillis() - current < timeOutMilli) 
                result += (char)c;
            else throw new Exception();
        return result.trim();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result;
    

    Hope this helps the future :D

    0 讨论(0)
  • 2020-11-27 12:21

    A light-weight solution for small apps:

    public class Test {
        public static void main(String[] args) throws java.io.IOException, InterruptedException {   
            Process process = new ProcessBuilder().command("sleep", "10").start();
    
            int i=0;
            boolean deadYet = false;
            do {
                Thread.sleep(1000);
                try {
                    process.exitValue();
                    deadYet = true;
                } catch (IllegalThreadStateException e) {
                    System.out.println("Not done yet...");
                    if (++i >= 5) throw new RuntimeException("timeout");
                }
            } while (!deadYet);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 12:21

    First some background info, I came across the issue to have a timeout while running a command because the program that I tried to execute would never print any debug or error information incase of error and would just keep on retrying internally by itself resulting in process stuck because there was never an error or output stream when it was retrying.

    So after process.exec() or process.start() ,

    It would be stuck forever at this line,

    BufferedReader input = new BufferedReader(newInputStreamReader(process.getInputStream()));

    As per java 1.8 with public boolean waitFor(long timeout,TimeUnit unit) method it should have "ideally" timed out after the specified timeout but in my case for some reason it never timed out may be because I was running application as a windows service (I have checked the user permissions and everything on the account but it didn't help).

    So I tried to implement it with the below logic, where we would keep checking the input stream with input.ready() and a timeout flag.This simple solution worked like a charm compared to all other that existed.

    Code:

    public boolean runCommand() throws IOException, InterruptedException, Exception {
        StringBuilder rawResponse = new StringBuilder();
        System.out.println("Running Command " + Arrays.toString(command));
        ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList(command));
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start(); //Executing the process
        BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
        waitForTimeout(input, process); //Waiting for Timout
        String line;
        while ((line = input.readLine()) != null) {
            rawResponse.append(line).append("\n");
        }
        return true;
    }
    
    
    //Timeout method 
    private void waitForTimeout(BufferedReader input, Process process) throws InterruptedException, Exception {
        int timeout = 5;
        while (timeout > 0) {
            if (!process.isAlive() || input.ready()) {
                break;
            } else {
                timeout--;
                Thread.sleep(1000);
                if (timeout == 0 && !input.ready()) {
                    destroyProcess(process);
                    throw new Exception("Timeout in executing the command "+Arrays.toString(command));
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题