Daniel has posted 4 posts at DZone. View Full User Profile

Console applications with Java 6

05.06.2008
| 15817 views |
  • submit to reddit

In Java 6 a better way of interacting with the command prompt was introduced, the java.io.Console class. Together with the utility class java.util.Scanner introduced in Java 5 this new API can be used to develop more advanced Java console applications.
The creation of Java console applications was severely crippled up to the current version of Java (6) and things are now better but not perfect. The example presented in this tutorial implements a shell type application using various features of the Console and Scanner APIs.

package com.littletutorials.console;

import java.io.*;
import java.util.*;

public class Shell
{
private static final String NO_CONSOLE = "Error: Console unavailable";
private static final String GREETINGS = "Welcome to the System. Please login.%n";
private static final String DENIED_ATTEMPT = "Wrong user name or password [%1$d]%n";
private static final String ACCESS_DENIED = "Access denied%n";
private static final String ACCESS_GRANTED = "Access granted%n";
private static final String UNKNOWN_COMMAND = "Unknown command [%1$s]%n";
private static final String COMMAND_ERROR = "Command error [%1$s]: [%2$s]%n";

private static final String TIME_FORMAT = "%1$tH:%1$tM:%1$tS";
private static final String PROMPT = TIME_FORMAT + " $ ";
private static final String USER_PROMPT = TIME_FORMAT + " User: ";
private static final String PASS_PROMPT = TIME_FORMAT + " Password [%2$s]: ";

private static final String USER = "john";
private static final String PASS = "secret";

public static void main(String[] args)
{
Console console = System.console();
if (console != null)
{
if (login(console))
{
execCommandLoop(console);
}
}
else
{
throw new RuntimeException(NO_CONSOLE);
}
}

private static boolean login(Console console)
{
console.printf(GREETINGS);

boolean accessGranted = false;
int attempts = 0;
while (!accessGranted && attempts < 3)
{
String name = console.readLine(USER_PROMPT, new Date());
char[] passdata = console.readPassword(PASS_PROMPT, new Date(), name);
if (USER.equals(name) && PASS.equals(new String(passdata)))
{
attempts = 0;
accessGranted = true;
break;
}

console.printf(DENIED_ATTEMPT, ++attempts);
}

if (! accessGranted)
{
console.printf(ACCESS_DENIED);
return false;
}

console.printf(ACCESS_GRANTED);
return true;
}

private static void execCommandLoop(final Console console)
{
while (true)
{
String commandLine = console.readLine(PROMPT, new Date());
Scanner scanner = new Scanner(commandLine);

if (scanner.hasNext())
{
final String commandName = scanner.next().toUpperCase();

try
{
final Command cmd = Enum.valueOf(Command.class, commandName);
String param = scanner.hasNext() ? scanner.next() : null;
cmd.exec(console, new String[]{param}, new Command.Listener()
{
@Override
public void exception(Exception e)
{
console.printf(COMMAND_ERROR, cmd, e.getMessage());
}
});
}
catch (IllegalArgumentException e)
{
console.printf(UNKNOWN_COMMAND, commandName);
}
}

scanner.close();
}
}
}

The class defined above needs the definition of the com.littletutorials.console.Command enumeration where the shell commands are defined:

package com.littletutorials.console;

import java.io.Console;

public enum Command
{
BYE(new Action()
{
@Override
public void exec(Console c, String[] params)
{
c.printf("Bye%n");
System.exit(0);
}
}),
DETAILS(new Action()
{
@Override
public void exec(Console c, String[] params) throws Exception
{
int detailsLevel = 1;
try
{
detailsLevel = Integer.parseInt(params[0]);
}
catch (NumberFormatException e)
{
// ignore
}

for (int i = 1; i <= detailsLevel; i++)
{
c.printf("Detail number %1$X%n", i);
}
}
});

private interface Action
{
public void exec(Console c, String[] params) throws Exception;
}

public interface Listener
{
public void exception(Exception e);
}

private Action action;

private Command(Action a)
{
this.action = a;
}

public void exec(final Console c, final String[] params, final Listener l)
{
try
{
action.exec(c, params);
}
catch (Exception e)
{
l.exception(e);
}
}
}

The application has to be executed from a console/command prompt with a command like this:

java -cp . com.littletutorials.console.Shell

There is at most one Console instance associated with a instance of the JVM. If we get a console instance from the System.console() call then a session of our shell could look like this:

Welcome to the System. Please login.
01:13:02 User: john
01:13:08 Password [john]:
Access granted
01:13:12 $ details 3
Detail number 1
Detail number 2
Detail number 3
01:13:26 $ bye
Bye
 
These APIs have more functionality than used in this example. For example the Scanner can use regular expressions to locate tokens in the input stream. Reasonably rich console applications can now be implemented in Java and this covers a small but annoying gap in functionality.
 
This article was originally posted at http://littletutorials.com/2008/03/14/console-applications-with-java-6/ 
References
Published at DZone with permission of its author, Daniel Pietraru. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Collin Fagan replied on Tue, 2008/05/06 - 4:05pm

Console is a nice addition to Java. If you are interested in really advanced text based user interfaces take a look at charva. This guy actually implemented a text terminal version of Swing.

Daniel Pietraru replied on Tue, 2008/05/06 - 4:23pm in response to: Collin Fagan

Indeed it looks pretty amazing. On the other hand it uses JNI to get to ncurses. I would prefer if possible a pure Java solution... hard, I know :)

Giriraj Bhojak replied on Wed, 2008/10/15 - 6:09am

I ran this sample program from DOS prompt and it worked fine.

But when i tried from eclipse it threw runtime exception: "Console unavailable" .

Any clue why it works through command line and not through eclipse.

Or is there a flaw in the implementation of Console?

 Somebody out there please help me with this.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.