Issue with stdin after piped input
I'm having difficulty accessing Standard In after reading data piped in at run-time. The data file is loaded into my app by piping it in to stdin at runtime via:
java -jar ./myapp.jar < data.txt
I'm able to capture the piped data, however, if I attempt to have the user interact with the console via keyboard after the piped data has been read, the system blocks indefinitely.
Step-by-step, what I've done is when the program starts, I use a BufferedReader(InputStreamReader(System.in)) and while loop through the contents of the BufferedReader until I hit null: while( (line = in.readLine()) != null ). This is the end of the file that was piped in, so I move on to processing the file.
After processing the data, I prompt the user for a course of action. The user's response is read by another BufferedReader(InputStreamReader(System.in)) and the program is supposed to continue on. However, at this step, during this second access to standard in via readLine(), the program hangs. No matter what I enter, the program won't go anywhere.
I've attempted to use BufferedReader's "close()" method, but that closes the underlying stream (and throws an error the next time I create a BufferedReader to it and try to access the same underlying stream). I've tried using the same BufferedReader for both accesses to stdin, however, it seems that once I hit "null" from reading the piped data file, adding more to stdin via the keyboard doesn't refresh the buffer. It doesn't seem like "readLine()" closes the stream when you hit null (otherwise I'd get an IOException next time I try to access it), it simply just doesn't recognize when new data has arrived.
Any suggestions on the best course of action to read piped data via stdin, and still have access to the stream to allow me to read keyboard inputs after the data file has been loaded? I thought about putting a string terminator at the end of the file so I'd catch the input before I hit the null, but I don't control the data file, so someone would have to manually touch the file before it's processed.
Thanks!
What does the user input?
A simple "y" or "n" to continue.
After the data is loaded and processed, the user is presented some details on the data and they are asked if they want to continue.
Previously, this code read the data from a file that was identified within a configuration file. So it would read the data in via file and then prompt the user. It worked fine in this capacity. Only when the requirement came up that we be able to receive piped input did this stop working.
Have you read about the SequenceInputStream class? The firstInputStream of that stream could be that FileInputStream whilethe second one could be System.in itself.kind regards,Jos
I haven't. I'll take a look right now. Thanks for the reference...
The problem I see with SequenceInputStream is that when the data is piped in, it's coming from an InputStream via Standard In, and when the user is prompted, that comes from an InputStream via Standard In again. Only when the file is read via configuration file am I using a FileInputStream and then an InputStream from Standard In (for the prompt).
Can you using SequenceInputStream to stream System.in twice, and in doing so, still leave the second stream intact after the first is closed?
Maybe I just don't understand your problem but my idea was to open
a FileInputStream and do this:FileInputStream fis= new FileInputStream(args[1]);
SequenceInputStream sis= new SequenceInputStream(fis, System.in);
System.setIn(sis);
... and forget about all input redirection on the command line.
kind regards,
Jos
Let me try to clarify.
There are two scenarios for loading the data file. 1) Enter the filename in a configuration file. 2) Pipe the contents of a file from the command-line.
They are mutually exclusive. You will either do one, or the other. The piped file takes priority. So if the user doesn't pipe data from the command-line, my program loads the data from a flat-file as defined in the configuration. If the data is piped at the command-line, the configuration file is ignored and the data is loaded via stdin.
Once the data is loaded from one of the two sources, I process the data, and generate some info. That info is presented to the user so they can decide how to proceed. The user is expected to enter either "y" or "n" at the keyboard to continue or end the process. This is where it fails (or, more accurately, hangs when the data was read via pipe). If the data is read via FileReader, not a problem. Everything works. Only when I use an InputStreamReader to grab the contents of the data via input redirection, does it hang the next time I try to read something from stdin.
Look body
> java -jar ./myapp.jar < data.txt
You just redirected standard input to data.txt and have read everything from there.
Now, what do You expect? Of cause any attempt to read from stdin will block forever
or until something will drop into data.txt. Perhaps You can close and reopen the stream. But I would recommend You to open separate stream to read the data.txt.
Well, Halcyon, that isn't going to work. Your best bet is to get the requirements fixed. You can have piped input, or you can prompt the user, but you can't have both.
Here's a trimmed down version of what I'm trying...
BufferedReader in;
if(loadDataSrcStream){
in = new BufferedReader(new InputStreamReader(System.in));
} else{
in = new BufferedReader(new FileReader(dataSource));
}
String line;
while((line = in.readLine()) != null){
// Do something to load the data
}
// Do something to process the loaded data
if(skipCount > 0){
System.out.print("Entries in the source file have been skipped. Continue? (y/n): ");
BufferedReader prompt = new BufferedReader(new InputStreamReader(System.in));
while((line = prompt.readLine()) != null){
if("n".equalsIgnoreCase(line)){
return;
} else
break;
}
}
}
I've tried re-using "in" instead of "prompt" if the data was loaded via input redirection, however, I get the same result, so I tried something else.
In this example, after loading the data via stdin, when I get to the prompt, I've seem it to either hang or output garbage. If I load the data via the FileReader, it works fine.
> Well, Halcyon, that isn't going to work. Your best
> bet is to get the requirements fixed. You can have
> piped input, or you can prompt the user, but you
> can't have both.
Well ****.. that makes too much sense.. it seemed too obvious to be true, so I was hoping there was a possibility :)
I'm afraid you're overcomplicating things; when you allow a user to
redirect stdin to read a configuration file, how's you program to know
what it is reading? The keyboard? The config file? When is your program
ready to read that config file and how is it going to 'switch back' to
keyboard input?
Why don't you allow for an optional command line argument that
specifies the name of your config file? If the argument is supplied, use
it, if it isn't, read from a default config file and only then, read from a
non redirected stdin where your program will find the interactive user
input.
kind regards,
Jos
> Why don't you allow for an optional command line argumentYeh, I would do that way. If the last argument is present (You can check it with length) doread that file.
I do have a command-line argument. They supply one to tell it what configuration file to load, and within that configuration file they can supply the path to the data source. Piping data in to override the config file's data source was something that was discussed. The thought was that if this was part of a script, the data source file might change, and could be dynamically supplied by the script, however, it's obvious now that any script would need to run silently anyway, so it becomes a non-issue the more I think about it.