Part 3: Developing CLI application with Spring Shell

Domagoj Madunić
Agency04
Published in
8 min readMay 14, 2019

Sooner or later every application had to perform an operation that could not be finished in an instant. In web applications we expect to be presented with a spinner (of one sort or another) during the execution of our requests, these prevents us from nervously pressing submit button again and again tell us that all is well and our request is being taken care of. On the other hand when we copy a large amount of files from one location to another we expect our OS GUI to provide us with some sort of progress bar displaying the progress of initiated operation. Similarly when we start a gradle build of our project we are presented with a progress bar and numeric information indicating percentage completion of our build.

In this post we will cover the topic of displaying a progress of command execution to a user with the use of counters, spinners and the progress bar similar to the one used by gradle build tool.

Implementing a simple spinner

Let us begin with implementing the simple spinner to be used by our clidemo application. As a first step, in the package com.ag04.clidemo.shell create a new ProgressCounter class:

As can be seen, this class exposes two public methods:

public void display():
public void reset();

The first one displays the next state of the rotating spinner, while the second one resets the spinner to its initial state.

To use this new class, we also need to configure it by adding the following configuration at the end of the SpringShellConfig class:

@Bean
public ProgressCounter progressCounter(@Lazy Terminal terminal) {
return new ProgressCounter(terminal);
}

To see it in action, we need to add another demo method in the existing echo command, as shown in the snippet bellow:

@ShellMethod("Displays progress spinner")
public void progressSpinner() throws InterruptedException {
for (int i = 1; i <=100; i++) {
progressCounter.display();
Thread.sleep(100);
}
progressCounter.reset();
}

Now, rebuild, run clidemo and execute this new progress-spinner command. If all goes well you should see 10 seconds long rotating sequence of these characters:

| / -\

NOTE: At the beginning of the ProgressCounter class, you might have notice a strange property:

private static final String CUU = "\u001B[A";

this is control character sequences “ESC A” known also as “CUU” command sequence instructing a terminal to move cursor up by one row. For more on control character sequences please see the following link.

Implementing a counter with spinner

Now lets improve ProgressCounter class by adding another method with the following signature:

public void display(int count, String message);

This method will display spinner, and both current progress count and information message.

To do so, change the ProgressCounter class with the additions as displayed in the code bellow:

ProgressCounter class property “pattern” defines how counter and message are displayed, and it can be passed either through constructor or by use of setter method.

To test this new functionality, add another method to EchoCommand class, as shown bellow:

@ShellMethod("Displays progress counter (with spinner)")
public void progressCounter() throws InterruptedException {
for (int i = 1; i <=100; i++) {
progressCounter.display(i, "Processing");
Thread.sleep(100);
}
progressCounter.reset();
}

The final output after running this new progress-counter command should be similar to the bellow:

CLI-DEMO:>progress-counter 
\ Processing: 100

This ProgressCounter implementation is best suited to keep the user informed of the progress in situations when we do not know the exact number of records/lines/entities that we need to process, that is when we are only aware of the current state of execution with no hindsight concerning the scope of the remaining work.

In the situations when we are aware of the scope of work ahead of us, more suited would be the use of the progress bar to convey the information on the current state of progress to the user. Therefore, as the last exercise in this post, we will create simple progress bar, similar to the one used by the popular gradle tool during build process:

<================----> 80%

Implementing a progress bar

We will begin with creation of a new ProgressBar class, with two public methods, similar as the ProgressCounter class:

public void display(int percentage);
public void reset();

The first method will be responsible for displaying progress bar that matches current state of execution as declared by percentage parameter passed to it, while the second one will be responsible for cleaning up and resetting the bar to its initial state.

In the package com.ag04.clidemo.shell create ProgressBar class with the following code:

As usual, this should be followed by adding bean configuration block to the SpringShellConfig class:

@Bean
public ProgressBar progressBar(ShellHelper shellHelper) {
return new ProgressBar(shellHelper);
}

Now, add new test method to the end of the echo command, as shown bellow, so we can see the new ProgressBar in action:

@ShellMethod("Displays progress bar")
public void progressBar() throws InterruptedException {
for (int i = 1; i <=100; i++) {
progressBar.display(i);
Thread.sleep(100);
}
progressBar.reset();
}

Rebuilding clidemo application and running this new progress-bar command should now result in the output similar to the one in the image bellow:

Progress bar in action.

If we would like our progress bar to reassemble the one used by gradle even more, we need to add the capability to pass optional “status message” parameter to the display() method. To do so, we need to expand the ProgressBar by adding a new display() method with the following signature:

display(int percentage, String statusMessage); 

Accordingly, we will change the ProgressBar class to match the code snippet bellow:

With these additions we are able to enrich our progress bar with display of additional status message, for example with info on duration of execution so far, or something similar.

Putting it all together: displaying progress of UserService method

Testing our progress bar and progress counter components with echo command is fine, but to make everything learned in this post so far less abstract we will close this post with the demonstration of progress bar component usage in a “real life” example.

“Real life” example: massive update of (all) users

Lets assume that UserService also defines the following method:

long updateAll();

This method is intended to synchronise and update all users in local database with some external source of data, and returns the number of updated users. Also, let us assume that there is a large number of users in our database and that this operation will take some time to finish.

For the purposes of this post we will modify MockUserService and add mock implementation of this method, one that will took time some to finish. Thus, add the method updateAll() to the UserService interface and add the following block of code to the MockUserService:

@Override
public long updateAll() {
long numberOfUsers = 2000;
for (long i = 1; i <= numberOfUsers; i++) {
// do some operation ...
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return numberOfUsers;
}

Now modify UserCommand by adding a method (see bellow) that invokes this new UserService method:

@ShellMethod("Update and synchronize all users in local database with external source")
public void updateAllUsers() {
shellHelper.printInfo("Starting local user db update");
long numOfUsers = userService.updateAll();
String successMessage = shellHelper.getSuccessMessage("SUCCESS >>");
successMessage = successMessage + String.format(" Total of %d local db users updated!", numOfUsers);
shellHelper.print(successMessage);
}

Rebuilding and running of clidemo update-all-users command will now produce the following output:

User update without progress bar.

As you could see, by running this example, after the first message “Starting local user db update” is printed out, we are left to wait until user update operation has finished. What we would like to achieve is to present user with the progress bar displaying the progress of user update operation.

Moreover we would like to achieve this in a least intrusive way, thus we will implement it with the use of Observer pattern.

Observer pattern in action

Wikipedia defines Observer pattern as follows:

“The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.”

In our case Observable object will be MockUserService class and we will also implement custom Observer class that will display operation progress at the terminal.

Detailed information concerning the Java support for the Observer pattern can be found on the following link. Our solution, one that follows bellow, is based on the use of Observer/Observable classes as described in the previously linked post.

First we will create new ProgressUpdateObserver class, one that will be responsible for updating user’s terminal based on the the progress information.

In the same package we will now create ProgressUpdateEvent class that this Observer reacts on.

What remains to be done is to modify MockUserService so that it fires up this event as the update operation progresses. Modify MockUserService class in accordance with the code bellow, add Observer property, modify updateAll() method and do not forget to remove @Service annotation, since we will configure this class later manually.

Finally, add UserServiceConfig class which contains method to create and configure MockUserService bean as shown in the code snippet bellow:

Whit this we are all set! Running clidemo update-all-user command now, should produce the output as shown in the picture bellow:

update-all-users command final output

This concludes the third part of this series. In the next part we will focus on advanced built-in Spring Shell support for data display as tables within terminal.

Rest of the series:

Part 1: Conveying contextual messages to the users in the CLI application

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

Sample Code:

Entire source code for this tutorial is available at GitHub repository:

Additional resources:

Spring Shell project site:

https://projects.spring.io/spring-shell/

Spring Shell official documentation:

https://docs.spring.io/spring-shell/docs/current-SNAPSHOT/reference/htmlsingle/

https://docs.spring.io/spring-shell/docs/current/api/

JLine project site:

[https://jline.github.io/](https://jline.github.io/)

Good overview of the Terminal control sequences:

https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences

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

--

--

Domagoj Madunić
Agency04

CEO and founder of Agency04 — Software development company based in Zagreb. Passionate about software development and 17th century Mediterranean history.