Blogs

Developing CLI Application with Spring Shell (part 1)

Category
Software development
Developing CLI Application with Spring Shell (part 1)
Developing CLI application with Spring Shell

The first application I ever wrote was my personal library management software. I did it in the late ’90s. It was written as a terminal application in C (back then, for me, the most glamorous programming language — Unix was written in it!).

Since then, I wrote literary tens of thousands of lines of code (in dozen of languages). Today, I am mainly involved in the development of enterprise-grade IT solutions using languages and tools from the Java ecosystem (having Spring framework as the foundation), with some single-page-application (SPA) serving as the front-end.

That said, my heart still skips a beat when seeing a nice terminal application.

In this series of blog posts, I will walk you through the steps of creating a simple command line interpreter (CLI) application using one of the less-known Spring projects: Spring Shell.

About Spring Shell

As the authors of the Spring Shell library say on their website:

”Not all applications need a fancy web user interface! Sometimes, interacting with an application using an interactive terminal is the most appropriate way to get things done.”

Spring Shell aims to fill in this gap and provide Spring users with a library designed to support the development of CLI applications using all familiar concepts and design patterns (DI and IoC, for example) from the Spring framework.

At the time of writing this article, Spring Shell is in its second incarnation (version 2.0.1 is the latest release). This version represents a complete rewrite of the previous 1.2.x version, adjusted to the changes introduced in the Spring ecosystem with the release of Spring Boot 2.x.

About this blog posts series

This series of blog posts is an extension to the getting started guide, in which we go a step further and cover some of the base building blocks of CLI applications not addressed in the reference documentation. This first blog post will lead you through the process of setting up and configuring a simple demo application and will also cover the following topics:

  • base customization of the CLI application
  • conveying contextual information to the user through the use of colored messages

In the next posts we will tackle some of the more complex topics such as:

  • Capturing user input (free text, passwords, input from the list of select values, etc)
  • Displaying operation progress information (spinners, counter, progress bars)
  • Displaying data as tables using Spring Shell built-in util classes
  • Integrating Spring Shell and Spring Security

Generate and configure spring-boot/gradle project

Simplest way to start new Spring based project is to use Spring Initializr. To do so open a browser of your choice and navigate to: https://start.spring.io/

Fill in the form by entering the defaults, as displayed in the picture below:

  • Select: Gradle Project and Java as the project language
  • Choose: Spring Boot 2.1.3 (or the latest version at the moment)
  • Under dependencies enter “spring shell” and press enter
developing CLI application with spring shell

Finally, press “Generate Project”, download the zip file, unpack it and import the project into your favorite IDE (IntelliJ Idea, VSS Code, Eclipse,…).

Now we are ready to make some changes in the build file. First of all, lets upgrade Spring Shell library to its latest RELEASE (2.0.1).

In the generated build.gradle file find the following block of code:

dependencies {
    implementation 'org.springframework.shell:spring-shell-starter:2.0.0.RELEASE'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

and change it to look like this:

dependencies {
	implementation 'org.springframework.shell:spring-shell-starter:2.0.1.RELEASE'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Finally, delete the integration tests found in `src/test/java/` folder generated by Spring Initializr (see this link for a detailed explanation). Then execute the following command from your terminal inside the root clidemo/ folder to compile and build the application:

./gradlew clean build

If all goes well, start the application by running:

java -jar build/libs/clidemo-0.0.1-SNAPSHOT.jar

And soon, you should see the familiar output of a SpringBoot application starting:

developing CLI application with spring shell
CLI application started

The last line says that our application is now ready to accept our input. At this point, just type in ”help” to see the list of predefined commands available by default in a Spring Shell application. You should see an output similar to this one:

shell:>help
AVAILABLE COMMANDS
Built-In Commands
 clear: Clear the shell screen.
 exit, quit: Exit the shell.
 help: Display help about available commands.
 history: Display or save the history of previously run commands
 script: Read and execute commands from a file.
 stacktrace: Display the full stacktrace of the last error.
shell:>

Voila, we have a nice CLI application running with set of available (built-in) commands.

Customizing our application

Banner and log levels can be customized to suit our specific needs same as in any other Spring Boot application (please refer to the official Spring Boot docs for this). Spring Shell CLI applications, however, offer some specific customization options, the most notable one being the ability to adjust the shell prompt. As can be seen from the examples above, the application greets the users with a default prompt message: “shell:>

We will change this message into “cli-demo:>

To do so, we need to configure our Spring application with a Bean implementing the PromptProvider interface:

public interface PromptProvider {
 AttributedString getPrompt();
}

In the package com.ag04.clidemo.shell simply add a Java class named ClidemoPromptProvider with the following content:

package com.ag04.clidemo.shell;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.shell.jline.PromptProvider;
import org.springframework.stereotype.Component;
@Component
public class ClidemoPromptProvider implements PromptProvider {
    @Override
    public AttributedString getPrompt() {
        return new AttributedString("CLI-DEMO:>", 
            AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE)
        );
    }
}

In this simple implementation, we have changed the prompt from shell:>to CLI-DEMO:> and have also changed its color to blue. This class is annotated with the @Component annotation so Spring can discover and register this bean during start up. No further changes are needed. Simply build and rerun the application and you should be greeted with a new custom prompt.

Real-life PromptProvider implementations can be far more complex than this simple example. They can contain complex application logic that changes the prompt to reflect, for example, user status (signed in or not), application state, current folder path etc.

Now its time to add some functionality to our CLI application.

Hello world: implement your first command

As always, it‘s good to start with a simple Hello World example. In our case, it will be an ”echo” command that prints a greeting message to a person whose name was supplied as a parameter to the command.

Commands are the core of the Spring Shell library. They are equivalent to the Controller object in the classical Web or Rest API application. In short, Commands are the higher-level components responsible for processing user input.

Start by creating the following class named EchoCommand.java in the package: com.ag04.clidemo.command

package com.ag04.clidemo.command;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
@ShellComponent
public class EchoCommand {
    @ShellMethod("Displays greeting message to the user whose name is supplied")
    public String echo(@ShellOption({"-N", "--name"}) String name) {
        return String.format("Hello %s! You are running spring shell cli-demo.", name);
    }
}

Try it out! Build and rerun your app and type: “echo Domagoj”. You should see the following output.

shell:>echo Domagoj
Hello Domagoj! You are running spring shell cli-demo.
shell:>

HINT: Execute the help command again, and you should see our new echocommand listed with the appropriate command description.

shell:>help
AVAILABLE COMMANDS
Built-In Commands
 clear: Clear the shell screen.
 exit, quit: Exit the shell.
 help: Display help about available commands.
 history: Display or save the history of previously run commands
 script: Read and execute commands from a file.
 stacktrace: Display the full stacktrace of the last error.
Echo Command
 echo: Displays greeting message to the user whose name is supplied

This is a result of the @ShellMethod and @ShellOption annotations added to the EchoCommand, which provide the necessary information to Spring Shell. For more information concerning the options for customizing your commands, please consult the official Spring Shell documentation.

Now that we have our first command, it’s time to make our application output a little bit more attractive/engaging by adding some colors to it.

ShellHelper: adding some color to the output

At the moment, our application uses the same (standard) terminal color for displaying the entire echo message. What if we would like to display the first part of the message ”Hello %s!” in a different color, say green?

One of the great novelties made by Bootstrap was the introduction of the practice of conveying meaning to users by using colors. All with a handful of color utility CSS classes available to the developers. Thus came into existence the use of success, info, danger, and other familiar Bootstrap CSS styles. Let’s attempt to emulate this practice in our CLI application.

Contact

Looking for Java experts?

To do so, we will create a ShellHelper util class that we will use to color the output of our commands. First, in the package com.ag04.clidemo.shell create a new Java enumeration of the name PromptColor:

package com.ag04.clidemo.shell;
public enum PromptColor {
    BLACK(0),
    RED(1),
    GREEN(2),
    YELLOW(3),
    BLUE(4),
    MAGENTA(5),
    CYAN(6),
    WHITE(7),
    BRIGHT(8);
    private final int value;
    PromptColor(int value) {
        this.value = value;
    }
    public int toJlineAttributedStyle() {
        return this.value;
    }
}

With this enumeration in place, we can proceed and define which colors will be used for which “style” of message in our application. In the application.properties file (empty at the moment) found in the src/resources/ folder, add the following lines:

# — contextual colors — — — — — — — — — — — — — — — — — — — — 
shell.out.info=CYAN
shell.out.success=GREEN
shell.out.warning=YELLOW
shell.out.error=RED

Now, in the same package crate a Java class named ShellHelper, with the following code:

package com.ag04.clidemo.shell;
import org.jline.terminal.Terminal;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.springframework.beans.factory.annotation.Value;
public class ShellHelper {
    @Value("${shell.out.info}")
    public String infoColor;
    @Value("${shell.out.success}")
    public String successColor;
    @Value("${shell.out.warning}")
    public String warningColor;
    @Value("${shell.out.error}")
    public String errorColor;
    private Terminal terminal;
    public ShellHelper(Terminal terminal) {
        this.terminal = terminal;
    }
    public String getColored(String message, PromptColor color) {
        return (new AttributedStringBuilder()).append(message, AttributedStyle.DEFAULT.foreground(color.toJlineAttributedStyle())).toAnsi();
    }
    public String getInfoMessage(String message) {
        return getColored(message, PromptColor.valueOf(infoColor));
    }
    public String getSuccessMessage(String message) {
        return getColored(message, PromptColor.valueOf(successColor));
    }
    public String getWarningMessage(String message) {
        return getColored(message, PromptColor.valueOf(warningColor));
    }
    public String getErrorMessage(String message) {
        return getColored(message, PromptColor.valueOf(errorColor));
    }
}

Now we can use this class in our commands to display the entire, or just part of the message in a particular style. Before we do this, we need to make this class available to our application. Add Spring configuration class SpringShellConfig in the package com.ag04.clidemo.config:

package com.ag04.clidemo.config;
import com.ag04.clidemo.shell.ShellHelper;
import org.jline.terminal.Terminal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class SpringShellConfig {
    @Bean
    public ShellHelper shellHelper(@Lazy Terminal terminal) {
            return new ShellHelper(terminal);
    }
}

Finally, change EchoCommand so that it uses this util class to construct the returning String:

package com.ag04.clidemo.command;
import com.ag04.clidemo.shell.ShellHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
@ShellComponent
public class EchoCommand {
    @Autowired
    ShellHelper shellHelper;
    @ShellMethod("Displays greeting message to the user whose name is supplied")
    public String echo(@ShellOption({"-N", "--name"}) String name) {
        String output = shellHelper.getSuccessMessage(String.format("Hello %s!", name));
        return output.concat(" You are running spring shell cli-demo.");
    }
}

To see all the applied changes in action, rebuild and execute clidemo again. The echo command output should now consist of the message displayed in two colors, as visible in the image below.

Echo command with colored output

In the current implementation, our echo command returns a String (as a result of command execution), which is then also displayed in the terminal. Yet, there are cases when we would also like to print some informational messages to the user during the execution of our commands.

To support this let’s expand ShellHelper class and add several print methods, as shown in the code snippet below.

/**
     * Print message to the console in the default color.
     *
     * @param message message to print
     */
    public void print(String message) {
        print(message, null);
    }
    /**
     * Print message to the console in the success color.
     *
     * @param message message to print
     */
    public void printSuccess(String message) {
        print(message, PromptColor.valueOf(successColor));
    }
    /**
     * Print message to the console in the info color.
     *
     * @param message message to print
     */
    public void printInfo(String message) {
        print(message, PromptColor.valueOf(infoColor));
    }
    /**
     * Print message to the console in the warning color.
     *
     * @param message message to print
     */
    public void printWarning(String message) {
        print(message, PromptColor.valueOf(warningColor));
    }
    /**
     * Print message to the console in the error color.
     *
     * @param message message to print
     */
    public void printError(String message) {
        print(message, PromptColor.valueOf(errorColor));
    }
    /**
     * Generic Print to the console method.
     *
     * @param message message to print
     * @param color   (optional) prompt color
     */
    public void print(String message, PromptColor color) {
        String toPrint = message;
        if (color != null) {
            toPrint = getColored(message, color);
        }
        terminal.writer().println(toPrint);
        terminal.flush();
    }

With these tools at hand, we can now modify our echo command and demonstrate the capability to print contextual messages we just added. Thus change the body of the echo method to look as in the code snippet below.

 @ShellMethod("Displays greeting message to the user whose name is supplied")
    public String echo(@ShellOption({"-N", "--name"}) String name) {
        String message = String.format("Hello %s!", name);
        shellHelper.print(message.concat(" (Default style message)"));
        shellHelper.printError(message.concat(" (Error style message)"));
        shellHelper.printWarning(message.concat(" (Warning style message)"));
        shellHelper.printInfo(message.concat(" (Info style message)"));
        shellHelper.printSuccess(message.concat(" (Success style message)"));
        String output = shellHelper.getSuccessMessage(message);
        return output.concat(" You are running spring shell cli-demo.");
    }

Finally, rebuild and execute clidemo once more, and you should see the following output:

With this we conclude the first part. In the next part we will expand this sample skeleton application with examples of how to capture user input.

HINT: Both PromptColor and ShellHelper classes are written in such a way that they can be easily taken out of this project and put in a separate shell-utils library (jar) for re-use in various different CLI projects.

The entire source code for this tutorial is available at the following GitHub repository.

Additional resources:

Spring Shell project site:

Spring Shell official documentation:

Thank you for reading! I do hope you enjoyed it and please share if you did.

The rest of the series:

Part 2: Capturing user’s input in the CLI application

Part 3: Displaying the progress of CLI command execution with the use of counters, spinners and progress bars

Part 4: Displaying the data with the use of tables in a Spring Shell based CLI application

Part 5: Securing CLI application with Spring Security

Next

Blog

Estimating and Planning Is in the Heart of Agile Software Development

Estimating and Planning Agile Software Development

Company

Our people really love it here

Evolution of expertise

The agency was founded in 2014 by seasoned industry veterans with experience from large enterprises. A group of Java engineers then evolved into a full-service company that builds complex web and mobile applications. 

Flexibility is our strong suit — both large enterprises and startups use our services. We are now widely recognized as the leading regional experts in the Java platform and Agile approach.

We make big things happen and we are proud of that.

Personal development

If you join our ranks you’ll be able hone your coding skills on relevant projects for international clients.

Our diverse client portfolio enables our engineers to work on complex long term projects like core technologies for Delivery Hero, Blockchain and NFTs for Fantasy Football or cockpit interface for giants like Strabag. 

Constant education and knowledge sharing are part of our company culture. Highly experienced mentors will help you out and provide real-time feedback and support.

Contact

We’d love to hear from you