Java Runtime.exec() asynchronous output
Asked Answered
D

5

6

I'd like to get the output from a long running shell command as it is available instead of waiting for the command to complete. My code is run in a new thread

Process proc = Runtime.getRuntime().exec("/opt/bin/longRunning");
InputStream in = proc.getInputStream();
int c;
while((c = in.read()) != -1) {
    MyStaticClass.stringBuilder.append(c);
}

The problem with this is that my program in /opt/bin/longRunning has to complete before the InputStream gets assigned and read. Is there any good way to do this asynchronously? My goal is that an ajax request will return the current value MyStaticClass.stringBuilder.toString() every second or so.

I'm stuck on Java 5, fyi.

Thanks! W

Drugstore answered 13/10, 2011 at 17:23 Comment(1)
Thanks all! Apache Commons' Exec is the way I went. @Paul Cager, you also provided an excellent idea. The script that was being called was in fact detecting that it was being piped and blocking. However the stream that you get from the Runtime.exec is synchronized, so if you try to read it, it is blocking.Drugstore
T
8

Try with Apache Common Exec. It has the ability to asynchronously execute a process and then "pump" the output to a thread. Check the Javadoc for more info

Tergum answered 13/10, 2011 at 17:29 Comment(0)
G
2

Runtime.getRuntime().exec does not wait for the command to terminate, so you should be getting the output straight away. Maybe the output is being buffered because the command knows it is writing to a pipe rather than a terminal?

Gula answered 13/10, 2011 at 17:29 Comment(0)
B
1

Put the reading in a new thread:

new Thread() {
    public void run() {
        InputStream in = proc.getInputStream();
        int c;
        while((c = in.read()) != -1) {
            MyStaticClass.stringBuilder.append(c);
        }
    }
}.start();
Barby answered 13/10, 2011 at 17:32 Comment(1)
I might be being a little daft here, but how would you then get your variable c out of the thread? Besides, shouldn't c be a string?Vaughan
M
1

Did you write the program you're calling? If so try flushing your output after writing. The text could be stuck in a buffer and not getting to your java program.

I use this code to do this and it works:

    Runtime runtime = Runtime.getRuntime();
    Process proc = runtime.exec(command);
    BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));

    while (true) {

        // enter a loop where we read what the program has to say and wait for it to finish
        // read all the program has to say
        while (br.ready()) {
            String line = br.readLine();
            System.out.println("CMD: " + line);
        }

        try {
            int exitCode = proc.exitValue();
            System.out.println("exit code: " + exitCode);
            // if we get here then the process finished executing
            break;
        } catch (IllegalThreadStateException ex) {
            // ignore
        }

        // wait 200ms and try again
        Thread.sleep(200);

    }
Motel answered 13/10, 2011 at 17:33 Comment(2)
Does BufferedReader.ready() also cover the state of BufferedReader.readLine()? The documentation only mentions that ready() indicates when read() can be used, readLine() will probably be a couple of read() operations, therefore this approach might still block as I understand it.Diocesan
You're right, it will block and stay in this loop until the sub-process exits. This is correct in my opinion. If you want to do something else before this program finishes running then put this code in a separate thread and do the other work in a different thread. I don't see a way to make the InputStream returned from Process non-blocking like you can do with network sockets. In that case it would then throw a timeout exception when you try to read from the stream.Motel
M
0

Try :

   try {  
         Process p = Runtime.getRuntime().exec("/opt/bin/longRunning");  
         BufferedReader in = new BufferedReader(  
                                    new InputStreamReader(p.getInputStream()));  
         String line = null;  
         while ((line = in.readLine()) != null) {  
         System.out.println(line);  }
Meistersinger answered 13/10, 2011 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.