HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Closing InputStream of a running process

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
runninginputstreamclosingprocess

Problem

Let's say you want to extract the output of a console application till you've found a certain keyword. If you have found the keyword the started console process still should be running, however the function which has found the keyword should return.

Here is a minimal application which simply creates an output of n lines (n is the first argument).

LineOutput.java

public class LineOutput {
    public static void main(String[] args) {
        int iterations = 10;
        if (args.length > 0) {
            iterations = Integer.valueOf(args[0]);
        }
        for (int i=0; i < iterations; i++) {
            System.out.println("Line " + i);
        }
    }
}


And here is the main application with the readStreamUntilFound method, which returns as soon as the keyword was found:

ReadTest.java

```
package org.test;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class ReadTest {
public static void main(String[] args) {
System.out.println("Result " + readStreamUntilFound(Arrays.asList("java", "-jar", "test.jar", "10000"), "Line 99"));
}

public static boolean readStreamUntilFound(List cmd, String keyword) {
boolean keywordFound = false;
ProcessBuilder pb = new ProcessBuilder();
pb.command(cmd);
pb.redirectErrorStream(true);
Process p;
try {
p = pb.start();

try {
BufferedInputStream bis = new BufferedInputStream(p.getInputStream());
BufferedReader r = new BufferedReader(
new InputStreamReader(bis));

String line;
while ((line = r.readLine()) != null) {
System.out.println(line);
if (line.equals(keyword)) {
keywordFound = true;
break;
}

Solution

To add to @ChrisWue's answer I would pull the Process away from the actual processing of it's data. IE: Make a separate class that takes a stream and does processing. Think about it this way: what is your code doing? It is taking data off of some InputStream and checking for a specific keyword. Does that processing code need a Process instance to do that? Nope! You could pass your code any InputStream and it would be able to look for a keyword. Making sure your methods are only given exactly what it needs dramatically increases testability, flexibility, and re-usability.

For the second part actually using the Process' Input/Output/Error streams. If you read the JavaDocs you will find this:


By default, the created subprocess does not have its own terminal or
console. All its standard I/O (i.e. stdin, stdout, stderr) operations
will be redirected to the parent process, where they can be accessed
via the streams obtained using the methods getOutputStream(),
getInputStream(), and getErrorStream(). The parent process uses these
streams to feed input to and get output from the subprocess. Because
some native platforms only provide limited buffer size for standard
input and output streams, failure to promptly write the input stream
or read the output stream of the subprocess may cause the subprocess
to block, or even deadlock.

Basically, what this is telling me is that you need to make sure you are managing all of these streams appropriately. If you close one of the processes streams prematurely you have to be sure that the Process can handle that case. Most applications will crash or exit with an error code. Unless you know you won't break the Process you're trying to run be sure to process all the data from these streams until that process is done. You could use a simple thread which just reads and discards the data if you don't need it.

Just a thought but if you are controlling the application you are running as a Process you could possibly tell the application to close gracefully by sending it something using the OutputStream.

Context

StackExchange Code Review Q#115294, answer score: 2

Revisions (0)

No revisions yet.