Part 4: Developing CLI application with Spring Shell

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

In the previous posts we have tackled the topics of base customization of Spring Shell CLI applications, conveying contextual information to the user by the use of colored output and capturing user input (in the form of the free text, one value from the list of options etc).

In this post we will turn our attention to the task of displaying data as tables using built-in Spring Shell functionality.

Basic concepts

It is best to begin this post with a simple example. Open your favorite IDE and create a new sample-tables command with the code shown bellow:

After invoking this new sample-tables command you should be presented with the following output:

Demo of some of the available table border options.

As can be seen, this command displays the same set of data as tables, yet each with a different border style, illustrating the basic Spring Shell capabilities of displaying tabular data in the terminal.

At the core of this sample-tables command and Spring Shell support for display of tabular data is the TableBuilder class. This Spring Shell class, based on the supplied TableModel (we will cover it a little bit later), creates a Table object, that is a representation of our data (ie model). At the end what remains to be done is simply to print the Table object to the terminal.

The following lines of code do exactly that:

TableBuilder tableBuilder = new TableBuilder(someModel);
Table table = tableBuilder.build().render(width);
shellHelper.print(table);

In addition, the TableBuilder class has many other options. It allows us to: set border style (on all cells, only on the outline or header or just on some cells), set a wrapping strategy for an entire table or just a cell, set a cell alignment, configure a specific formatter class for a particular cell/or class, and finally to specify exact cell widths. More on these options will be said later, before that we will have to turn our attention to another Spring Shell core element- the TableModel interface.

The TableModel encapsulates our raw data and provides the Spring Shell Table class with a convenient interface for accessing it. The Spring Shell provides two TableModel implementations: ArrayTableModel and BeanListTableModel (this one will be discussed later).

The ArrayTableModel class is the most general implementation of the TableModel interface. Its constructor simply accept a two-dimensional array of Objects making it suitable for a wide range of cases:

ArrayTableModel(Object[][] data)

As can be seen from the code example above, constructing and displaying data in tabular form is very simple and boils down to these steps:

  1. Construct the TableModel around your actual data
  2. Construct the TableBuilder with the previously constructed model
  3. Add a border, formatter(s), set a wrapper and/or cell size …
  4. Render a Table from TableBuilder
  5. Print the Table to the terminal

In the table-samples command above we have adjusted the table border for each of the displayed tables, and in the last example we have shown how to set a different border for the header row:

TableModel model = new ArrayTableModel(sampleData);
TableBuilder tableBuilder = new TableBuilder(model);
....
tableBuilder.addInnerBorder(BorderStyle.fancy_light);
tableBuilder.addHeaderBorder(BorderStyle.fancy_double);
shellHelper.print(tableBuilder.build().render(80));

Fore more information on all available options please consult the official Javadoc for the BorderStyle enumeration and theTableBuilder class.

Data Formatters

So far we have displayed only Strings, and, as expected, all worked fine. However, in real life applications this is hardly going to be the case. We will most likely encounter the need to display other data types, as for example java.util.Date.

To display an element from the model within a cell, the Table class invokes configured Formatter, and the Spring shell provides a simple pre-configured DefaultFormatter class which simply invokes toString() method on the particular element being displayed. In the case of more complex objects, as for example java.util.Date, this is most likely going to produce unacceptable results.

In the next example we will create the model that will consist of both Strings and LocalDates and accordingly configure the TableBuilder class to use our own LocalDateFormatter class (see bellow) to display the LocalDate elements of the model.

In the TableExampleCommand class create a new method as shown bellow:

Method tableFormatterDemo() of TableExampleCommand

The sample code for the custom LocalDateFormatter can be found in the following code snippet:

After rebuilding the clidemo application and running of the new table-formatter-demo command you should see the following output:

As can be seen, all dates are formatted with “dd.MM.YYY” pattern, just as we wanted. Now that we have addressed the basics of using the Spring Shell support for the display of tabular data, it is time to turn to more advanced topics. That is, how to display a list of the (same) Java Beans or a single Java Bean as tabular data.

However, before proceeding any further we need to improve our MockUserService class to more reassemble a real life service and returns richer set of CliUser data.

Improved MockUserService

To do so, we will first expand the existing UserService interface to include the following methods:

public interface UserService {
CliUser findById(Long id);
CliUser findByUsername(String username);
List<CliUser> findAll();

boolean exists(String username);
CliUser create(CliUser user);
CliUser update(CliUser user);
long updateAll();
}

now, change the implementation of the MockUserService to match the following code snippet:

This implementation loads data from JSON file, store it locally and then performs all the operations on this local data. The location of JSON file should be in the src/main/resources folder and the example of JSON file can be found here:

For all this to work some additional configurations are needed. First we need to configure Jackson’s ObjectMapper bean. Now, create a new JacksonConfig configuration class, as shown in the next code snippet.

At the moment this class contains just a simple configuration of a single bean, but in a case we would later need to further customize default Jackson’s serialization and deserialization behavior, it is convenient to already have it in place.

Finally, we need to change the existing UserServiceConfig configuration class to match the changes in the MockUserService:

@Configuration
public class UserServiceConfig {

@Bean
public UserService userService(ProgressUpdateObserver observer, ObjectMapper objectMapper) throws IOException {
MockUserService userService = new MockUserService();
userService.setObserver(observer);
userService.setObjectMapper(objectMapper);
userService.init("cli-users.json");
return userService;
}
}

Now we are ready to further expand the existing UserCommand class by introducing new user related commands.

Display List of Java Beans/Entities

As mentioned before in addition to the ArrayTableModel, Spring Shell also provides the BeanListTableModel, a TableModel implementation suited for the display of the list of the same Java Beans/Entities. Using this TableModel implementation we will implement the user-list command that displays all available CliUsers. The BeanListTableModel class provides three constructors of which the most convenient for our purposes is the following one:

BeanListTableModel(Iterable<T> list, LinkedHashMap<String,Object> header);

As can be seen this constructor takes as its first parameter the iterable list of the beans/entities which were to be displayed. The second parameter (named header) is a Map in which keys represent the names of the bean properties we would like to display in the table and elements represent labels for those properties, which will accordingly be displayed in the header row of the table. One thing is worth noting here, omitting a property name from this list would result in exclusion of that property from the table. In this manner it is possible to, rather conveniently, filter the data that will be displayed in the table.

Now we can start working on the new user-list command. Add the following method to the UserCommand class:

Rebuilding the clidemo application and running the user-list command, now should produce the following output:

Output of the user-list command.

Display single Bean/Entity as tabular data

As the last exercise in this post we will address the issue of displaying a single Java Bean/Entity as tabular data. What we want to achieve is to have a table in which all rows consist of two columns, the first one containing the property label and the second one a property value, as shown in the image bellow:

Desired output of the user-details command.

Unfortunately the Spring Shell does not provide an out-of-box implementation of the TableModel that would suite our needs. However, the following can be achieved with the use of an existing ArrayTableModel. All we need to do is to create a two-dimensional array, in which the first column would contain a property label and a second property value.

In order not to have to repeat this procedure multiple times, we will create the BeanTableModelBuilder class that will also enable us to register property labels and a header row. The full listing of the BeanTableModelBuilder can be found in the snippet bellow:

As can be seen, based on the supplied Java bean, headers and labels, this class will construct an appropriate ArrayTableModel. If no labels are provided default property names will be used.

We will use this class to implement the new user-details command that will display details of a single CliUser as a table. To do so, add the following two methods to the UserCommand class:

As can be seen from the code snippet above, in this example we have also provided some additional table formatting. Namely, we have set the columns’ width to 20 and 30 character respectively.

Finally, add also the following property to the UserCommand class:

@Autowired
private ObjectMapper objectMapper;

Now, running of the command:

user-details lmodric

should display the details of my son’s favorite football player, as can be seen in the screenshot bellow ;-)

As an exercise, you could now refactor the existing create-user command so that it uses this new displayUser() method and by doing so remove some unnecessary/duplicate lines of code.

This concludes the fourth part of this series. In the next part we will address the problem of configuring Spring Security for the Spring Shell CLI applications.

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:

The entire source code for this tutorial is available at GitHub repository:

Additional resources:

The Spring Shell project site:

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

The Spring Shell official documentation:

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

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

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.