0

I have a GUI program written in Java which outputs data to the command line using System.out.println. The data is intended to be piped into another program. As an example, I'll pipe the program through head:

$ java MyProgram | head -n10

I want my program to exit when the pipe is broken: in this case, this should happen after MyProgram has printed out ten lines of text.

There is a similar question on this site, and the solution given there works fairly well; for example, I could have the following running in its own thread:

while(!System.out.checkError()) {
    // sleep 100ms
}
System.exit(0);

The problem is that PrintStream.checkError() seems to return true only after you have tried to print to the stream and failed. For that reason, my program does not in fact exit until it has printed out eleven lines of text: the pipe is broken after the first ten, but System.out continues to return true until I try to pipe through the eleventh line.

Printing out extra 'junk' lines in order to trigger an error on the PrintStream is out of the question, since the program on the right hand side of the pipe may be very sensitive to the data it receives.

Calling System.out.flush() inside the loop has no effect on PrintStream.checkError(), even though the source code for PrintStream indicates that it ought to call the private ensureOpen method in that class.

How can I reliably test whether System.out is open without printing anything to it?


'Real world' example: suppose that I have some program consumer that takes in command line input and does something with it. Certain pieces of input will call consumer to fail silently. Since the input to consumer is sometimes long and abstruse, I write a GUI program InputProvider in Java where I can click buttons and have the corresponding commands printed out to stdout. If I pipe the output of InputProvider into consumer, then I am able to control consumer graphically.

Unfortunately, there seems to be no way for InputProvider to notify the user when consumer has shut down, except by attempting to write to consumer and getting an exception of some kind.

John Gowers
  • 2,646
  • 2
  • 24
  • 37
  • This sounds like an [XY Problem](http://xyproblem.info). You need to clarify why you think it's not OK to fail on the 11th line of output. The underlying mechanism by which all this happens is in the OS, not in Java, and the broken pipe indication doesn't happen until you try to write. – Jim Garrison Jan 05 '18 at 19:16
  • @JimGarrison I don't think it's an XY problem. I want my input program to shut down when the program on the other side of the pipe shuts down. Perhaps the `head` example is misleading. At the very least, I want my input program to be able to tell whether or not the other program has quit. The only mechanism of communication between the two is the pipe, which corresponds to the state of `System.out`. – John Gowers Jan 05 '18 at 19:23
  • Then as I said, that is not possible without some other communication channel. The pipe is handled by the OS and the error cannot occur until you attempt to write to the pipe. In other words, attempting flow control by using a broken pipe is an incorrect approach that cannot be made to work. – Jim Garrison Jan 05 '18 at 19:27
  • Could you elaborate how can you say that the pipe is broken after 10 lines ? It seems you have a way to know if the pipe is broken by looking at the output lines. The fact that 11th line appeared in output means the IO is intact until then. – SomeDude Jan 05 '18 at 19:27
  • @svasa The 11th line does not appear in the output, but `MyProgram` does not quit. In this case, this is not a problem, since I can tell that `head` has quit since it is no longer producing output. But, for another program that does not print its input straight out, I might not be able to tell that that program had quit. – John Gowers Jan 05 '18 at 19:29
  • By looking at your latest description , I think you should do something like : `while( consumer.ready() && printStream.checkError() ) { printStream.print(...) )`. Now if you don't have some means like `ready()`, you cannot say upfront that the pipe is in tact or not. – SomeDude Jan 05 '18 at 19:36

2 Answers2

2

I don't think you can fix this in Java. There's nothing wrong with System.out until you write to it and it fails. Another process (head) ended but the Java process can't know about that.

So I think that you have two options.

  1. don't use pipe to head but limit the output in your Java code - then you'll know when to stop
  2. accept that the last line will fail and handle the exception appropriately
Jonathan
  • 349
  • 1
  • 9
1

Try to set a new stream for the system that way you'll be able to check if that stream is closed or not: OutputStream output = new FileOutputStream("c:\\data\\system.out.txt"); and PrintStream printOut = new PrintStream(output);. Then set it here: System.setOut(printOut); and inside the if you can check if(printOut.checkError())

Alex Cuadrón
  • 638
  • 12
  • 19