Runtime.getRuntime().exec(cmd) hanging

21,892

Solution 1

I guess the issue is that you are only reading InputStream and not reading ErrorStream. You also have to take care that both the streams are read in parallel. It may so happen that currently the data piped from the output stream fills up the OS buffer, your exec command will be automatically be suspended to give your reader a chance to empty the buffer. But the program will still be waiting for the output to process. Hence, the hang occurs.

You can create a separate class to handle both the Input and Error Stream as follows,

public class ReadStream implements Runnable {
    String name;
    InputStream is;
    Thread thread;      
    public ReadStream(String name, InputStream is) {
        this.name = name;
        this.is = is;
    }       
    public void start () {
        thread = new Thread (this);
        thread.start ();
    }       
    public void run () {
        try {
            InputStreamReader isr = new InputStreamReader (is);
            BufferedReader br = new BufferedReader (isr);   
            while (true) {
                String s = br.readLine ();
                if (s == null) break;
                System.out.println ("[" + name + "] " + s);
            }
            is.close ();    
        } catch (Exception ex) {
            System.out.println ("Problem reading stream " + name + "... :" + ex);
            ex.printStackTrace ();
        }
    }
}

The way you use it is as follows,

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ;  
s1 = new ReadStream("stdin", p.getInputStream ());
s2 = new ReadStream("stderr", p.getErrorStream ());
s1.start ();
s2.start ();
p.waitFor();        
} catch (Exception e) {  
e.printStackTrace();  
} finally {
    if(p != null)
        p.destroy();
}

Solution 2

This code is based on the same idea Arham's answer, but is implemented using a java 8 parallel stream, which makes it a little more concise.

public static String getOutputFromProgram(String program) throws IOException {
    Process proc = Runtime.getRuntime().exec(program);
    return Stream.of(proc.getErrorStream(), proc.getInputStream()).parallel().map((InputStream isForOutput) -> {
        StringBuilder output = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(isForOutput))) {
            String line;
            while ((line = br.readLine()) != null) {
                output.append(line);
                output.append("\n");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return output;
    }).collect(Collectors.joining());
}

You can call the method like this

getOutputFromProgram("cmd /C si viewhistory --fields=revision --project="+fileName);

Note that this method will hang if the program you are calling hangs, which will happen if it requires input.

Share:
21,892
user1688404
Author by

user1688404

Updated on August 09, 2020

Comments

  • user1688404
    user1688404 over 3 years

    I am executing a command which returns me the Revision number of a file; 'fileName'. But if there is some problem executing the command, then the application hangs up. What can I do to avoid that condition? Please find below my code.

    String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
    Process p = Runtime.getRuntime().exec(cmd) ;  
    BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));  
    String line = null; 
    while ((line = in.readLine()) != null) {  
    System.out.println(line);  
    } 
    
    } catch (Exception e) {  
    e.printStackTrace();  
     }
    
  • Akyo
    Akyo over 7 years
    Yep that block of code also save me from being ignorant hehe :)
  • John
    John about 6 years
    Works great running mvn verify and I'm not wrapping it with cmd /C -- do you know what is the benefit of wrapping a second shell?
  • mikeyreilly
    mikeyreilly about 6 years
    I copied the OP's command string exactly and it started with "cmd /C". I don't think there is any benefit to wrapping it in this case.
  • chilleo
    chilleo almost 6 years
    This is a fantastic code block and works as is but theres one issue I have noticed. While this works to prevent hanging in a process that used to hang for me, in another process that is very fast and that creates files to be used quickly after by other things, the files are not getting written fast enough before an access is attempted. I assumed a proc.waitfor would fix it but no luck so far, you may want to edit the code block to address this issue that can arise in not having sufficient waiting
  • Abhishek K
    Abhishek K over 5 years
    +1 for the perfect example, /C is an important argument which we should pass. Without passing this argument the code won't run. URL for cmd arguments: docs.microsoft.com/en-us/windows-server/administration/…
  • Sturm
    Sturm almost 5 years
    I would like to use this, however, I need to return a value from ReadStream, rather than just dumping the stream to System.out. I know this can be done with Callable rather than Runnable, but it's a bit more complicated. Is there a practical example I can follow? Preferably following the same sort of structure shown in these examples.