If your next mind blowing tool needs to get some user interaction, using command line arguments might not always be the best user experience. So if you want to provide a more convenient way for the user to interact with your program, then a shell can be a solution. Using a shell gives you the power to lead the user in the process of interaction by predefining commands and thereby giving a hint on what is possible. With Spring-Shell, it is pretty easy to create a shell with your own commands that gives you access to the functions of your program.
The shell is based on the Spring-Framework and already provides default built in commands for basic functions like exiting the shell, getting a help page or even using unix/windows commands. It also has some converters for reading in different types of input, like boolean or date. Besides that it contains a plugin model which can be used to customize the shell. Therefore to use your own commands, you need to write a plugin, that will be read in by the plugin model. Each plugin has to contain the file Meta-Inf/spring/spring-shell-plugin.xml. In this file you have to declare where to find the classes that define your custom commands, e.g. with the spring component-scanning functionality.
The command class itself needs to implement the CommandMarker interface and has to use the Spring @Component annotation. To define your custom commands in that class, annotations are used:
A simple example is shown below:
The class PrintMessageCommand implements the CommandMarker interface, so the shell core will detect it as a command class. The method annotated with @CliAvailabilityIndicator just gives back true, so the command “print” will be available in the shell. With @CliCommand the command “print” is defined and with it a help message, that can then be found in the help page later. To provide an input possibility for the user, the option “message” is defined, which in this case is mandatory to use the “print” function.
Besides the commands it is also possible to customize the banner text, the command prompt and the file for the command history. This is also done by creating classes that extend default classes of the core.
The simplest way to start the shell is to use the provided class org.springframework.shell.Bootstrap as your starter class. It starts the shell and uses the Meta-Inf/spring/spring-shell-plugin.xml to find the plugin and loads the custom classes into the application context. That’s it.
In our case, we used Spring-Shell for a tool called Cv Generator, to achieve more granularity in the command process for the user.
The Cv Generator is a tool for converting a cv into html or other formats. It uses the following technologies:
It reads in a json file wich contains the facts of the cv. Then it uses thymeleaf as a template engine to create a html file out of these files and layouts it with css & html. To provide single steps like syntax checking for a json file, the Spring-Shell is used.
Here you can see the Shell:
But in our case things were a bit more complicated since we wanted to use Spring-Boot as well. Therefore we had to create our own Application instead of using Bootstrap class to be able to start the application with SpringApplication.run(). In our ApplicationContext we load all Spring-Shell core components as well as the beans for the JLineShellComponent and the CommandLine. Then we can get the shell and run it. By using our own application we also get rid of the spring-shell-plugin.xml and instead use an annotation based variant to get all of our needed beans. Below you can see the code of the application class:
As you see, building your own shell with Spring-Shell is fast to realize, even if you don’t use the Bootstrap class. To get more details on how to use Spring-Shell you can have a look at the documentation.
Finally I would like to recommend a 2-days Training on Architecting for Continuous Delivery and Zero Downtime with Axel Fontaine, which will take place on 26th + 27th of January 2015 at comSysto. Read more about it by clicking at the link or sign up immediately HERE!