System Console Support — JDK 1.6

I’m sure some of you have found the need occasionally to use the “console” input/output support provided by the JDK via System.in and System.out — if nothing else just to implement an occasional more complicated command-line “scripty” app to be run in a Linux environment and piped input in or out from/to another program.

The problem so far has been the fact that while System.out is a PrintStream instance — System.in is an InputStream implementation! So while the output deals with characters, the input deals with bytes — and you have to start wrapping it up (through a few layers!) into a BufferedReader so you can read input line by line.

Luckily, JDK 1.6 introduces a nice function on the System class : console() — which does solve these problems.

Even more, the Console class actually offers a nice printf() function for the C nostalgics — which, as you would expect, acts exactly like printf() in C! The only thing that is lacking, if you ask me, is a scanf() equivalent — that can still be achieved via a Scanner instance, however, it obviously requires more coding. I’ll come back to the whole scanf() discussion though, for now let’s have a look at printf().

Prior to this, you had a few ways of printing something like “User {username} is {years} old”:

  • Call System.out.print a few times:
String username = ...;
int age = ...;
System.print( "User " );
System.print( username );
System.print( " is " );
System.print( age );
System.println( " old" );
  • Simply use string concatenation in a call to System.out.println:
String username = ...;
int age = ...;
System.out.println( "User " + username + " is " + age + " old" );
  • Use String.format():
String username = ...;
int age = ...;
String output = String.format( "User %s is %d old", username, age );
System.out.println( output );

With the introduction of the Console class you can now also mix String.format and outputting this to the console in one line like this:

String username = ...;
int age = ...;
System.console().printf( "User %s is %d old", username, age );

As to be expected with something like this, there is a readLine() method, which to be honest saves anyone who’s be dealing with console-based programs the whole spaghetti code of creating a BufferedReader etc — just so you can read everything the user types till they press ENTER. Even more, you can now show a prompt and read user input in one line — System.console().readLine() can be passed the same set of parameters as printf() does: it formats the output, presenting the user with the prompt then waits for user input.

I haven’t personally ever had to use this, but for those of you who need it, there is even a readPassword() function, which obviously doesn’t display the output :)

I’ve put together a (very) short piece of code to exemplify this — see below (also you can download the full source code at the bottom of this post):

if (System.console() == null) {
  System.out.println("No console!");
  System.exit(1);
}
System.console().printf("The current timestamp is %d\n", System.currentTimeMillis());
String line = System.console().readLine();
System.console().printf("Line read was: %s\n", line);
line = System.console().readLine("Username:");
System.console().printf("Your username is '%s'\n", line);
line = new String(System.console().readPassword("Password:"));
System.console().printf("Your password is '%s'\n", line);

Now, as I said, one thing that I reckon Oracle needs to add to this is an scanf() equivalent — this is the kind of code that you have to write to read a String and an int:

/*
 * Scanf-like
 */
line = System.console().readLine("Username and age:");
Scanner scan = new Scanner(line);
// %s%d
String name = scan.next();
int age = scan.nextInt();
System.console().printf("%s is %d y/o\n", name, age);

Not ideal, huh? Imagine how cool it would be if you could write this though:

StringBuilder name = new StringBuilder();
MutableInt age = new MutableAge();
System.console.scanf( "Username and age:", "%s%d", name, age );

Note that I’m suggesting using the likes of StringBuilder and MutableInt (you will need Apache Commons Lang for this) — since String is not mutable, and the parameters passing in Java is by value (so passing an int means we pass a copy of the value, not a reference to the original int variable, so we can’t modify that!). This, I think, would help everyone who does any Java/Groovy system scripting — as it saves a bit of coding around the whole Scanner interface, as you can see above.

One last thing about Console — and it’s a sad one to be honest: you might have noticed in the above code the following construct:

if (System.console() == null) {
  System.out.println("No console!");
  System.exit(1);
}

That is because according to the System.console() JavaDoc: Returns the unique Console object associated with the current Java virtual machine, if any. So there is no guarantee that there will be a Console instance in the system :( You could think “ok, fair enough, this makes sense for server apps which run in a nohup environment and log everything etc”, and in those circumstances I would guess it’s acceptable not to have a Console instance. What I don’t see as acceptable, is the current set of IDE’s (Eclipse included — as much as I rate it) all return a null for System.console()! That, annoyingly, makes the testing of any console-based Java program awkward in Eclipse: rather than simply starting the code in debug, you have to put together a bit command-line, execute it in terminal, attach Eclipse to the process… and so on — eeeewwww! So Eclipse guys, if you’re reading this, please give us a Console implementation under JDK 1.6!

Attached, the code for the short test program mentioned above: ConsoleTest.java.bz2

As per usual, would love to hear some comments — or drop me a line — in fact would love to hear when there is an IDE there which provides a Console implementation. And more to the point, would love to find out when Eclipse does that too!

Tags: , , , , , , , , ,

Leave a Comment