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

前端 未结 17 1784
长发绾君心
长发绾君心 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:31

    There are various ways to do this, but I'd consider using an Executor-- it just helps you encapsulate passing the exit value or exception from the thread back to the original caller.

        final Process p = ...        
        Callable<Integer> call = new Callable<Integer>() {
        public Integer call() throws Exception {
            p.waitFor();
            return p.exitValue();
          }
        };
        Future<Integer> ft = Executors.newSingleThreadExecutor().submit(call);
        try {
          int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
          return exitVal;
        } catch (TimeoutException to) {
          p.destroy();
          throw to;
        }
    

    I think you can't get round the race condition whereby the wait times out, and then process terminates just before you call destroy().

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

    Following the answer by erickson, I created a more generic way to do the same thing.

    public class ProcessWithTimeout extends Thread
    {
        private Process m_process;
        private int m_exitCode = Integer.MIN_VALUE;
    
        public ProcessWithTimeout(Process p_process)
        {
            m_process = p_process;
        }
    
        public int waitForProcess(int p_timeoutMilliseconds)
        {
            this.start();
    
            try
            {
                this.join(p_timeoutMilliseconds);
            }
            catch (InterruptedException e)
            {
                this.interrupt();
            }
    
            return m_exitCode;
        }
    
        @Override
        public void run()
        {
            try
            { 
                m_exitCode = m_process.waitFor();
            }
            catch (InterruptedException ignore)
            {
                // Do nothing
            }
            catch (Exception ex)
            {
                // Unexpected exception
            }
        }
    }
    

    Now, all you have to do is as follows:

    Process process = Runtime.getRuntime().exec("<your command goes here>");
    ProcessWithTimeout processWithTimeout = new ProcessWithTimeout(process);
    int exitCode = processWithTimeout.waitForProcess(5000);
    
    if (exitCode == Integer.MIN_VALUE)
    {
        // Timeout
    }
    else
    {
        // No timeout !
    }
    
    0 讨论(0)
  • 2020-11-27 12:36

    and here is the StreamThread

    public class CommandStreamThread extends Thread{
            private InputStream iStream;
            private String cPrompt;
    
            CommandStreamThread (InputStream is, String cPrompt)
            {
                this.iStream = is;
                this.cPrompt = cPrompt;
            }
    
            public void run()
            {
                try
                {
                    InputStreamReader streamReader= new InputStreamReader(this.iStream);
                    BufferedReader reader = new BufferedReader(streamReader);
    
    
                    String linesep=System.getProperty("line.separator");
                    String line=null;
                    while ((line=reader.readLine())!=null){
                        System.out.println(line);
                        //Process the next line seperately in case this is EOF is not preceded by EOL
                        int in;
                        char[] buffer=new char[linesep.length()];
                        while ( (in = reader.read(buffer)) != -1){
                            String bufferValue=String.valueOf(buffer, 0, in);
                            System.out.print(bufferValue);
                            if (bufferValue.equalsIgnoreCase(linesep))
                                break;
                        }
                    }
    
                    //Or the easy way out with commons utils!
                    //IOUtils.copy(this.iStream, System.out);
    
    
                  } catch (Exception e){
                        e.printStackTrace();  
                  }
            }
    
            public InputStream getIStream() {
                return iStream;
            }
    
            public void setIStream(InputStream stream) {
                iStream = stream;
            }
    
            public String getCPrompt() {
                return cPrompt;
            }
    
            public void setCPrompt(String prompt) {
                cPrompt = prompt;
            }
    
    
    }
    
    0 讨论(0)
  • 2020-11-27 12:39

    Apache Commons Exec can help you to do it.

    See http://commons.apache.org/proper/commons-exec/tutorial.html

    String line = "your command line";
    CommandLine cmdLine = CommandLine.parse(line);
    DefaultExecutor executor = new DefaultExecutor();
    ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
    executor.setWatchdog(watchdog);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
    executor.setStreamHandler(streamHandler);
    int exitValue = executor.execute(cmdLine);
    System.out.println(exitValue);
    System.out.println(outputStream.toString());
    
    0 讨论(0)
  • 2020-11-27 12:40

    If you're using Java 8 or later you could simply use the new waitFor with timeout:

    Process p = ...
    if(!p.waitFor(1, TimeUnit.MINUTES)) {
        //timeout - kill the process. 
        p.destroy(); // consider using destroyForcibly instead
    }
    
    0 讨论(0)
提交回复
热议问题