Runtime.exec(): run against time?

Hi all,

For the Java freaks among you, another case solved πŸ™‚

Problem: when calling an external command using Runtime.exec(), the method exec() doesn’t return.
Reason: on some operating systems (including Windows, which was my case), you need to manually read the output and error streams from the external process.Solution: see below.

Solution

To start the external command use the following code:

@Override
public void run() {
    try {
        Process p = Runtime.getRuntime().exec(command);
        BufferPoller bperr = new BufferPoller(p.getErrorStream());
        BufferPoller bpstd = new BufferPoller(p.getInputStream());
        bperr.start();
        bpstd.start();
        p.waitFor();

        err = bperr.getData();
        std = bpstd.getData();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

The first line starts the actual command in a new Process.
The Process object is used to get both streams. For now, consider the BufferPoller a way to keep emptying the streams in another thread.
The p.waitFor() method waits untill the process has finished. If it doesn’t finish, you’re in a mess anyway, so make sure it finishes!
Once the process is finished, we ask both BufferPollers for their data. If you’re not interested in this data, don’t ask for it πŸ˜‰
Note that the BufferPoller for the error stream is started first. I read somewhere that this has better results. Note also that both calls are start() and not run() methods. The start() is run in a separate thread whereas the run() would invoke it here. By invoking in their own thread, you are guaranteed that no deadlock between both streams will occur, for example if stderr is empty but stdin is giving lots of output, then you would never be able to get to the stdin data because stderr blocks due to no data being available, which in turn causes the process to block and never terminate. The only way to solve this is to run them in separate threads.

Now for the BufferPoller class:

public class BufferPoller extends Thread {
    private BufferedReader in;
    private StringBuffer sb;

    public BufferPoller(InputStream is) {
        in = new BufferedReader(new InputStreamReader(is));
        sb = new StringBuffer();
    }

    @Override
    public void run() {
        try {
            while(true) {
                String input = in.readLine();
                if(input != null) {
                    sb.append(input);
                    sb.append('\n');
                } else
                    break;
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public String getData() {
        return sb.toString();
    }
}

This class obviously extends Thread to be able to run in a separate thread. At construction time the class converts the InputStream to a BufferedReader to be able to read line per line.
The run() method just reads all lines into the StringBuffer as they become available, no tricks there.

That should do it folks πŸ™‚

A final remark: there are two ways of running the method in the first code snippet above, depending on if you want the output of the command or not.
If you DON’t want the output of the command, you can put the method in a Thread class and execute start() on it, the end time and handling will then be out of your control, but that doesn’t matter anyway then.
If you DO want the output, use the run() method directly, so that it blocks the calling method and you can get the data out of the buffers.

If you have any questions, mail me or put it in the comments.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s