Java STUFF
Java STUFF
Handling Console Input with Auto completion in Java using Jline
mandag den 2. november 2009
Today I had to create a small command line driven Test driver, which can send XML messages to a JMS queue as well as register as JMS listener to receive messages send to a given queue.
To make it nicer to work with, I decided to make the configuration and all the commands available from within the console application, but with auto completion, as well as available from the command line using program arguments.
After having looked around I decided on two Java libraries to help me with this task.
1.JLine for handling the Console Input
2.JArgs for handling all commands and their arguments (either based on the console input or based on parameters given directly on the command line when executing the program)
I can say if JArgs is the best for the job, and even though its old and hasn’t been updated in a while it does its job well.
For handling console input JLine was the only contestant so there was little choice there. The documentation is sparse to therefore this blog post, so others can save some learning time.
JLine basically works by creating an instance of the ConsoleReader class.
To make it more fun and to allow content/keyword/command completion it features a Completion interface of which several can be registered with the ConsoleReader. For my usage I basically settled for a nested setup, which features only one root Completion instead, which the nests, in a tree structure, the context sensitive completions.
The small application only features a small set of root commands, such as “addConnectionFactory”, “addQueue”, “addDestination”, “sendMessage”, “listConnectionFactories”, “quit”, etc.
The basic code for this is listed below:
Note: All completions are setup in createRootCompletor().
The code above basically runs in a loop until the program terminates, which is when the user either enters “quit”, “exit” or “q”.
The command entered is typically either a single command and no arguments (hence the check for ‘ ‘) or a single command with several arguments like:
> listDestinations
> addDestination MyDestination --connectionFactory CF1 --queue Queue1
At the first level (e.g. “listDestinations”, “addDestination”) you freely choose which command to issue I’m going to use a JLine MultiCompletor.
So a simple MultiCompletor setup such as this one:
Will offer you a tabbed (default key for completion) choice of “addDestination”, “listDestinations”, “removeDestination”
But since you want something a bit more intelligent and context sensitive, you can instead of “new SimpleCommand(“addDestination”) use the ArgumentCompletor which gives you a use sequential guided list of arguments and completions:
Note: getCommand() returns “addDestination” - Also the reason why the “connectionFactory” and “queue” arguments are using “- -” is because JArgs follow GNU style arguments where you can have a single character argument using one “-” and a long argument name using “- -”
The trick (or the only way I could figure out) to allow a user to enter a value, for which we have no completion (since the user can enter anything he or she likes), was to create a SimpleValueCompletor, which basically just returns what the user has entered as the only suggestion:
The final piece in making this work is to add JArgs into the equation - here everything nicely encapsulated in a SimulationCommand:
All that’s left to cover is how a custom completor which returns choices is written:
That’s basically it - I hope this helps others venturing out into this area :)
View from Gross Glockner Strasse in Austria - Summer 2009