The command-line interface (CLI) is the inside world of software program improvement. From the shell, we’ve direct entry to all of the working system’s capabilities, and with that comes the ability to compose and orchestrate all facets of the software program. Many instruments and frameworks incorporate command traces. Not solely that, however the command immediate is the basis magic of working with software program methods; it is the house of close to limitless prospects.
On this article, we’ll take a tour of constructing subtle interactive command-line interface (CLI) functions and REPLs (learn–eval–print loops, or interactive shells) in Java. We’ll arrange a fundamental demo utility in Java and use the JLine and ConsoleUI libraries so as to add the options that we want.
The Java-based REPL
Our demonstration relies on a theoretical utility that examines a software program venture’s working listing and gathers details about the initiatives there. The applying additionally is ready to create new initiatives within the listing. The instance utility will begin a REPL that accepts two instructions, describe and create, which could be tab-completed. The describe command will listing the folder hierarchy of the working listing with shade coding (utilizing paging if mandatory), whereas create initiates an interactive menu that lets the person select what sort of venture to create—Java, JavaScript, or Python. If it’s a Java utility, we’ll enable a multi-select of further options the person can add (database or REST API) that may allow us to see a nested menu.
We’ll simply use these options to discover the JLine capabilities, slightly than really implementing them.
The demo utility
For this tour, you’ll want a Java JDK and Maven put in. We’ll begin by making a recent utility with a Maven archetype, like what’s proven in Itemizing 1.
Itemizing 1. Create a brand new utility with Maven
mvn archetype:generate -DgroupId=com.infoworld -DartifactId=jline3 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Maven will use these instructions to put out a brand new venture for us. Earlier than we go any additional, let’s additionally add all of the dependencies we’ll want, and in addition set the Java model to 11 (any model from Java 8 ahead ought to work), as I’ve carried out in Itemizing 2. This is applicable to the pom.xml file within the venture root (depart the remainder of the pom.xml as-is).
Itemizing 2. Add dependencies and set the Java model
<construct>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<model>3.8.1</model>
<configuration>
<supply>11</supply>
<goal>11</goal>
</configuration>
</plugin>
</plugins>
</construct>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<model>3.8.1</model>
<scope>check</scope>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<model>3.16.0</model>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<model>2.4.0</model>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jansi</artifactId>
<model>3.20.0</model>
</dependency>
</dependencies>
Subsequent, let’s modify the primary class in src/major/java/com/infoworld/App.java to start out a REPL loop. Modify App.java utilizing the code in Itemizing 3.
Itemizing 3. A easy REPL loop
package deal com.infoworld;
import org.jline.reader.*;
import org.jline.reader.impl.*;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.*;
import org.jline.terminal.TerminalBuilder;
import org.jline.terminal.Terminal;
import java.io.IOException;
import java.util.*;
public class App {
public static void major(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.terminal();
LineReader reader = LineReaderBuilder.builder()
.terminal(terminal)
.completer(new StringsCompleter("describe", "create"))
.construct();
whereas (true) {
String line = reader.readLine("> ");
if (line == null || line.equalsIgnoreCase("exit")) {
break;
}
reader.getHistory().add(line);
System.out.println("You mentioned: " + line);
}
}
}
Itemizing 3 creates a quite simple program that watches for traces of person enter and echoes them again. To that, I added a “completer,” which holds the 2 instructions we help, describe and create. Meaning when the person is typing on the immediate, they will tab to finish these instructions. Tabbing twice will supply a menu with the out there instructions. JLine has made this very straightforward with the fluent-style .completer(new StringsCompleter("describe", "create")) methodology name. JLine has a number of built-in completers, along with Strings, and you may as well construct your personal.
At coronary heart, the REPL is an infinite whereas loop, which breaks when the person enters exit.
You may try it out by operating the Maven exec:java command proven in Itemizing 4.
Itemizing 4. Run this system with Maven
mvn clear package deal exec:java -Dexec.mainClass=com.infoworld.App
You’ll get the carrot immediate, the echo response, and the tab-completion instructions.
Dealing with REPL instructions
Now that we’ve the echo REPL working with auto-complete, let’s really deal with the instructions. We’ll do that with typical Java, by evaluating the string entered with the instructions and calling strategies for every. For now, create received’t do something, however we’ll implement the logic to output the listing hierarchy, as proven in Itemizing 5.
Itemizing 5. REPL with the describe command
package deal com.infoworld;
import java.io.IOException;
import java.nio.file.Information;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.jline.reader.LineReader;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.DefaultParser;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
public class App {
public static void major(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.terminal();
LineReader reader =
LineReaderBuilder.builder()
.terminal(terminal)
.completer(new StringsCompleter("describe", "create"))
.parser(new DefaultParser())
.construct();
whereas (true) {
String line = reader.readLine("> ");
if (line == null || line.equalsIgnoreCase("exit")) {
break;
}
reader.getHistory().add(line);
if (line.equalsIgnoreCase("describe")) {
Path path = Paths.get(".");
System.out.println(getDirectoryHierarchy(path));
} else if (line.equalsIgnoreCase("create")) {
System.out.println(“TBD”);
} else {
System.out.println("Unknown command: " + line);
}
}
}
public static String getDirectoryHierarchy(Path path) {
StringBuilder sb = new StringBuilder();
strive (Stream<Path> paths = Information.stroll(path)) {
paths.sorted()
.forEach(
p -> {
int depth = path.relativize(p).getNameCount();
for (int i = 0; i < depth; i++) {
sb.append(" ");
}
if (p.toFile().isDirectory()) {
sb.append("https://www.infoworld.com/");
}
sb.append(p.getFileName()).append("n");
});
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
Now if you run the applying, if you happen to enter the describe command, you’ll see an indent-formatted listing of the working listing. The work of constructing that string up occurs in getDirectoryHierarchy(). That methodology makes use of regular Java from the java.nio.file package deal to stroll the listing and output every file and listing, indenting an area for every degree of listing we go down. This work is principally carried out with path.relativize(p).getNameCount(), which says: from my present path (.) give me the relative path to the present one (e.g., ./src/major/java). The getNameCount() simply counts the variety of names in that path—three, on this case. For every title, we add an area.

