Search code examples
javaiobufferedreaderprocessbuilder

Is there a better way to read from a process's inputstream and then handle using specified methods?


I am writing a program doing the following works:

  1. Run a command using ProcessBuilder (like "svn info" or "svn diff");
  2. Read the output of the command from the process's getInputStream();
  3. With the output of the command, I want either:
    • Parse the output and get what I want and use it later, OR:
    • Write the output directly to a specified file.

Now what I am doing is using BufferedReader to read whatever the command outputs by lines and save them to an ArrayList, and then decide if I would just scan the lines to find out something or write the lines to a file.

Obviously this is an ugly implement because the ArrayList should not be needed if I want a command's output to be saved to a file. So what will you suggest, to do it in a better way?

Here is some of my codes:

Use this to run command and read from the output of the process

private ArrayList<String> runCommand(String[] command) throws IOException {
    ArrayList<String> result = new ArrayList<>();
    _processBuilder.command(command);

    Process process = null;
    try {
        process = _processBuilder.start();
        try (InputStream inputStream = process.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                result.add(line);
            }
        }
    }
    catch (IOException ex) {
        _logger.log(Level.SEVERE, "Error!", ex);
    }
    finally {
        if (process != null) {
            try {
                process.waitFor();
    }
            catch (InterruptedException ex) {
                _logger.log(Level.SEVERE, null, ex);
            }
        }
    }

return result;
}

and in one method I may do like this:

ArrayList<String> reuslt = runCommand(command1);

for (String line: result) {
    // ...parse the line here...
}

and in another I may do like this:

ArrayList<String> result = runCommand(command2);
File file = new File(...filename, etc...);

try (PrintWriter printWriter = new PrintWriter(new FileWriter(file, false))) {
    for (String line: result) {
        printWriter.println(line);
    }
}

Solution

  • Returning the process output in an ArrayList seems like a fine abstraction to me. Then the caller of runCommand() doesn't need to worry about how the command was run or the output read. The memory used by the extra list is probably not significant unless your command is very prolix.

    The only time I could see this being an issue would be if the caller wanted to start processing the output while the command was still running, which doesn't seem to be the case here.

    For very big output that you don't want to copy into memory first, one option would be to have runCommand() take a callback like Guava's LineProcessor that it will call for each line of the output. Then runCommand() can still abstract away the whole deal of running the process, reading the output, and closing everything afterwards, but data can be passed out to the callback as it runs rather than waiting for the method to return the whole response in one array.