Reading STDIN from Java

Micro-blog here, just to note this for my benefit and others. It started as a joke tweet:

I was going to tweet the coolest thing I learned about Java, but the line was too long.

Turns out, the minimized line would have fit in twitter's 140 character textbox, but wouldn't have allowed any context. It was actually a rather useful few lines of code, and I couldn't find the info in my own Googling so I'll share it in full.

The goal: read optional data from a pipe in Java/Rhino. There was a code snippet on the Rhino WikiPedia page doing just this, but it was a generic "read line by line" input prompt. It worked when you piped data to it, but would halt execution waiting for an EOF if nothing was in the pipe. I wanted this to run like any standard Unix shell program where pipe data is optional.

After a little doc searching, and head scratching I found the .ready() method of BufferedReader. Initially, I thought it was just a property ... print(!!stream.ready) said true, both when pipe data was available and without. Head-slapping moment: typeof ready == function. stream.ready() is false if no stream data is present:

 
importPackage(java.io);
importPackage(java.lang);
(function(args){
	// setup the input buffer and output buffer
	var stdin = new BufferedReader(new InputStreamReader(System['in'])),
	    lines = [];
 
	// read stdin buffer until EOF (or skip)
	while(stdin.ready()){
		lines.push(stdin.readLine());
	}
 
	if(lines.length){ /* handle buffer data */ }
 
})(arguments);
 

Then, we make a little shell script to run this via Java / Rhino. Call it stdin.sh:

 
#!/bin/sh
java -jar js.jar stdin.js "$@"
 

Now, we can pipe data to the script and process it in JavaScript later:

 
cat file.txt | ./stdin.sh
./stdin.sh < file.txt
find *.html | ./stdin.sh --filelist
 

In the last example, we passed an argument to the stdin.sh script, which is passed along to the arguments of the bootstrap .js file (stdin.js) ... Checking for that is trivial, as Rhino implements a sane modern version of JavaScript (the one with Array.forEach and Array.indexOf):

 
(function(args){
     var useFileList = args.indexOf("--filelist") > -1;
})(arguments);
 

Then we'd just make whatever code uses this treat the stdin data as a list of files rather than a raw dump of data. Magic.