Interactive Java consoles with JLine and ConsoleUI

Get inside the Java shell, with this hands-on demonstration of building advanced UI consoles and REPLs in Java.

1 2 Page 2
Page 2 of 2

When we run this code now, we’ll get output like what's shown in Listing 6.

Listing 6. Output of the describe command

> describe

That looks a little dull, so let's spruce it up, as shown in Listing 7. 

Listing 7. The getDirectoryHierarchy() with colors

public static String getDirectoryHierarchy(Path path) {
    StringBuilder sb = new StringBuilder();
    try (Stream<Path> paths = Files.walk(path)) {
              p -> {
                int depth = path.relativize(p).getNameCount();
                for (int i = 0; i < depth; i++) {
                  sb.append(" ");
                String fileName = p.getFileName().toString();
                if (p.toFile().isDirectory()) {
                  fileName = "\033[32m" + “/” + fileName + "\033[0m";
                } else {
                  fileName = "\033[34m" + fileName + "\033[0m";
    } catch (IOException e) {
    return sb.toString();

Now the directories will be green and files will be blue, as shown in Figure 1.

A Java-based REPL directory listing with color. IDG

Figure 1. A directory listing with color

The color is relying on the Jansi library dependency that we added in pom.xml, which supports escape codes to denote the font color at the terminal. We use the escape codes \033[32m and \033[34m to change colors for a moment, then reset to the default with \033[0m

The \033 is the “escape character,” which tells the system that a control code follows, which tells the terminal how to format the text. The [32m and [34m are green and blue colors, respectively, and [0m clears the formatting. The Jansi library lets your Java program use all these codes in a cross-platform way, without worrying about the underlying operating system. You can learn more about the history of ANSI escape codes here, and more about the Jansi library here. (Jansi's motto is “Eliminating boring console output.")

Create and add user menus

Now that we have the describe command working, let’s take a crack at create. The first thing we need to do is offer a choice to the user of what kind of project to create: Java, JavaScript, or Python. If the user picks Java, we’ll let them specify a couple of features to add: database and REST API. 

Listing 8 has a simple version of using JLine to handle the create command.

Listing 8. Modified REPL with the create command

package com.infoworld;

// ...other imports remain the same

// ...
      } else if (line.equalsIgnoreCase("create")) {
        String choice = reader.readLine("What kind of project do you want to create? (java, javascript, python) ");
        if (choice.equalsIgnoreCase("java")) {
        } else if (choice.equalsIgnoreCase("javascript")) {
        } else if (choice.equalsIgnoreCase("python")) {
        } else {
          System.out.println("Unsupported choice: " + choice);
      } else {
        System.out.println("Unknown command: " + line);
  public static void createJavaProject(LineReader reader) {
    boolean addDatabase = reader.readLine("Add Database Support? (y/n) ").equalsIgnoreCase("y");
    boolean addRest = reader.readLine("Add REST API? (y/n) ").equalsIgnoreCase("y");
    //TODO: Create project here
    System.out.println("Java project created with Database: " + addDatabase + ", REST: " + addRest);

Listing 8 is pretty straightforward. We prompt for the type of project to create, and if the user enters java we send them to the createJavaProject() method. Notice we share the reader object as an argument—the JLine project encourages reusing the reader (and in fact testing reveals that spawning a new reader while another is running causes a dumb terminal to be created, at least on Debian). 

The createJavaProject() chains together two questions, about adding a database and adding a REST API. We just output the user's selections. This use of the create command demonstrates just one approach to handling multi-select functionality. The interaction looks like what's shown in Listing 9.

Listing 9. Using the create command to chain together two questions

> create
What kind of project do you want to create? (java, javascript, python) java
Add Database Support? (y/n) y
Add REST API? (y/n) y
Java project created with Database: true, REST: true
> exit

This works, but it’s a bit primitive. With the ConsoleUI project, we can implement some much nicer CLI-style menus. ConsoleUI supports the whole range of UI widgets like select, multiselect, and radio buttons with arrow-key navigation and ANSI icons. We’ll just get a quick flavor of it here by implementing the create command with ConsoleUI.

First, we add the ConsoleUI dependency, as shown in Listing 10.

Listing 10. Adding the ConsoleUI dependency


Listing 11 gives a quick example of adding the create menus. ConsoleUI gives you a largely fluent API that you can chain together to form the prompts you need. To access the results of the user’s selections, you have to know how the framework models them (details here).

Listing 11. Fancy menus

package com.infoworld;

// consoleui
import de.codeshelf.consoleui.elements.ConfirmChoice;
import de.codeshelf.consoleui.prompt.ConfirmResult;
import de.codeshelf.consoleui.prompt.ConsolePrompt;
import de.codeshelf.consoleui.prompt.PromtResultItemIF;
import de.codeshelf.consoleui.prompt.builder.PromptBuilder;

import java.util.HashMap;

import static org.fusesource.jansi.Ansi.ansi;
import org.fusesource.jansi.AnsiConsole;
  // ...
  public static void createCommand() { 
    System.out.println(ansi().render("@|red,italic Hello|@ @|green World|@\n@|reset Welcome to the project creator.|@"));
    try {
      ConsolePrompt prompt = new ConsolePrompt();
      PromptBuilder promptBuilder = prompt.getPromptBuilder();

      promptBuilder.createListPrompt().name("projectType").message("What kind of project do you want to create? (java, javascript, python) ")         .newItem().text("java").add().newItem("javascript").text("javascript").add()

      HashMap<String, ? extends PromtResultItemIF> result = prompt.prompt(;

      String projectType = ((de.codeshelf.consoleui.prompt.ListResult)result.get("projectType")).getSelectedId(); 
      System.out.println("result = " + ((de.codeshelf.consoleui.prompt.ListResult)result.get("projectType")).getSelectedId());
      System.out.println("Building a new project: " + projectType);

      if (projectType== "java") {
        prompt = new ConsolePrompt();
        promptBuilder = prompt.getPromptBuilder();
          .message("Please select features to add:")
          .newItem("db2").text("DB2").disabledText("Sorry, discontinued.").add()

          .newSeparator().text("Rest API").add()
          .newItem("spring-rest").text("Spring REST").check().add()

        result = prompt.prompt(;
        System.out.println("result = " + result);
    } catch (IOException e) {
    } finally {
      try {
      } catch (Exception e) {

Now you’ll see a very nice console UI, like the one shown in Figure 2.

The updated ConsoleUI with menus. IDG

Figure 2. The updated console UI.


This was a quick tour demonstrating some of the features of the JLine and ConsoleUI libraries. Together, they make many powerful CLI and REPL features simple to implement in Java.

Copyright © 2023 IDG Communications, Inc.

1 2 Page 2
Page 2 of 2