Search code examples
javanonblockingprocessbuilder

How to use exitValue() with parameter?


A very good article (When Runtime.exec() won't) says: The only possible time you would use exitValue() instead of waitFor() would be when you don't want your program to block waiting on an external process that may never complete. Instead of using the waitFor() method, I would prefer passing a boolean parameter called waitFor into the exitValue() method to determine whether or not the current thread should wait. A boolean would be more beneficial because exitValue() is a more appropriate name for this method, and it isn't necessary for two methods to perform the same function under different conditions. Such simple condition discrimination is the domain of an input parameter.

I have exactly same situation where my system call would start a process which will keep running until user decides to kill it. If I use '(process.waitFor() == 0)' it will block program there because process will not be completed. Author in article above suggest that exitValue() can be used with 'waitFor' parameter. Did anybody try it out ? Any example would be helpful.

Code:

// Start ProcessBuilder, 'str' contains a command

ProcessBuilder pbuilder = new ProcessBuilder(str);
pbuilder.directory(new File("/root/workspace/Project1"));
pbuilder.redirectErrorStream(true);
Process prcs = pbuilder.start();
AForm.execStatustext.append("\n=> Process is:" + prcs);

// Read output
StringBuilder out = new StringBuilder();
BufferedReader bfrd = new BufferedReader(new InputStreamReader(process.getInputStream()));
String current_line = null, previous_line = null;
while ((current_line = bfrd.readLine()) != null) {
    if (!line.equals(previous_line)) {
        previous_line = current_line;
        out.append(current_line).append('\n');
        //System.out.println(line);
    }
}
//process.getInputStream().close();
// Send 'Enter' keystroke through BufferedWriter to get control back
BufferedWriter bfrout = new BufferedWriter(new OutputStreamWriter(prcs.getOutputStream()));
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
//process.getOutputStream().close();*/

if (prcs.waitFor() == 0)
    System.out.println("Commands executed successfully");
System.exit(0); 

Solution

  • Before using waitFor in main thread, create another thread (child) and construct logic for your termination cases in this new thread. For example, wait for 10 secs. If the condition is fulfilled, then interrupt the main thread from the child thread ant handle the following logic on your main thread.

    The following code creates a child thread to invoke the process and the main thread does its work until the child finishes successfully.

        import java.io.IOException;
    
    
        public class TestExecution {
    
            public boolean myProcessState = false;
    
            class MyProcess implements Runnable {
    
                public void run() {
                    //------
                    Process process;
                    try {
                        process = Runtime.getRuntime().exec("your command");
                        process.waitFor();
                        int processExitValue = process.exitValue();
    
                        if(processExitValue == 0) {
                            myProcessState = true;
                        }
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
    
            }
    
            public void doMyWork() {
    
                MyProcess myProcess = new MyProcess();
    
                Thread myProcessExecuter = new Thread(myProcess);
                myProcessExecuter.start();
    
                while(!myProcessState) {
                    // do your job until the process exits with success
                }
            }
    
            public static void main(String[] args) {
    
                TestExecution testExecution = new TestExecution();
                testExecution.doMyWork();
            }
        }