Using Spring Shell to quickly develop your own command interaction window

Keywords: Programming shell Spring Eclipse xml

Spring Shell

Sometimes, in order to facilitate the development and testing of the server, it does not need a beautiful user interface, just use a simple command window. As follows:

Here is a quick, convenient, easy-to-use and simple interactive command window development component Spring Shell Yes, it's in spring ecology again.

Source address

https://gitee.com/wgslucky/spring-shell-demo

Create project

This project uses Eclipse as the IDE of development. Similarly, it can be used to import directly into Idea. The JDK used needs to be version 1.8 or higher, which I tested can also be used on JDK11. Create maven project in eclipse: spring shell demo, and then pom.xml Add the following dependencies to:

   <parent>
	    <!-- add to spring boot father pom Dependence, this cannot be less, spring shell Not in the official documents -->
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.6.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.shell</groupId>
			<artifactId>spring-shell-starter</artifactId>
			<version>2.0.0.RELEASE</version>
		</dependency>
	</dependencies>

Add startup class

@SpringBootApplication
public class SpringShellDemo {

	public static void main(String[] args) {
		SpringApplication app = new SpringApplication(SpringShellDemo.class);
		app.setBannerMode(Mode.OFF);
		app.setWebApplicationType(WebApplicationType.NONE);
		app.run(args);
	}
}

At this point, the project creation is complete. Now is the time to witness the miracle.

Quickly add a command

Create a command class. The class name can be customized:

@ShellComponent
public class MyCommand {
	@ShellMethod("Connect to server, format: connect ip port")
	public String connect(String ip,int port) {
		return String.format("Successfully connected to service:%s:%s", ip,port);
	}
}

Then, run the main method of the project's startup class, and enter the command in the console: It's so simple!!! , just two steps:

  1. Add a comment on the command class: @ ShellComponent
  2. Add a comment on the method: @ ShellMethod

Project packaging and running

In the project pom.xml Execute on the directory

mvn clean package

Then the running package is generated under the directory of target: spring-shell-demo-0.0.1-SNAPSHOT.jar You can run this package directly using the following command:

java -jar spring-shell-demo-0.0.1-SNAPSHOT.jar

This way, you can use it at any time outside the IDE.

Use tips

Now you can easily add the interaction command you want. The most important responsibility of the interaction command is to receive the parameters entered by the user. How to execute the command is your business logic. Here are some tips.

Built in commands

Spring Shell contains some common commands, which can be used directly. You can enter help in the running command window to quickly view these commands:

shell:>help
AVAILABLE COMMANDS

Built-In Commands
        clear: Clear the shell screen. (Clear screen)
        exit, quit: Exit the shell.  (Exit)
        help: Display help about available commands. (Help, view all supported commands)
        script: Read and execute commands from a file. (Read command from file and execute)
        stacktrace: Display the full stacktrace of the last error. 
        (Display the exception stack. When an error is encountered, use it to quickly view the exception stack.)

Add command description

You can add a description of this command in the @ ShellMethod annotation, so that when you use the help command, you can see these descriptions and understand how to use the command. For example, if you enter help in the command window, you can see:

My Command
       connect: Connect to server, format: connect ip port

Name of custom command

By default, there is no need to customize the command name. It will use the method name to execute the command to convert the corresponding window command name. If it is more than one word, use separate. For example, the command name of connect method is connect. If the method name is say hello, the command name is say hello If you want to customize the command name, you can add it in the @ ShellMethod annotation:

	@ShellMethod(value = "Log in to the server, format: login-server playerId",key = "login-server")
	public String login(String playerId) {
		return "Login succeeded:" + playerId;
	}

Modify prompt

When the spring shell is running, the default command prompt is: shell: > sometimes, it looks awkward, if you can change it to a custom prompt. This can be modified. A new class needs to be added:

@Service
public class CustomPromptProvider implements PromptProvider{

	@Override
	public AttributedString getPrompt() {
		return new AttributedString("xinyues-client:>");
	}

}

In this way, repackage and run, and the command prompt entered becomes xinyues client: >

Input command parameters with parameter name

When entering a command, if you do not use the command parameter name, the order of the parameters must be the same as that defined in the method. If you want to make the order different, you can add parameter names as follows:

xinyues-client:>connect --port 8888  --ip localhost
 Successfully connected to service: localhost:8888
xinyues-client:>

The parameter requires two shoulder (- -).

Add default parameter value

Sometimes, some commands can be simplified to use the default value of the parameter if no parameter is entered. As follows:

	@ShellMethod("Connect to server, format: connect ip port")
	public String connectDefault(@ShellOption(defaultValue = 
"localhost")String ip,@ShellOption(defaultValue = "8080")int port) {
		return String.format("Successfully connected to service:%s:%s", ip,port);
	}

A new annotation is used on the parameter: @ ShellOption When using the command, you can directly enter the command name without entering parameters:

xinyues-client:>connect-default
 Successfully connected to service: localhost:8080
xinyues-client:>

Using array parameters

Sometimes, for the convenience of input, if you don't want to define too many parameter variables, or if the parameter is an array of data, you can use the following method:

        @ShellMethod("Add Numbers.")
        public float add(@ShellOption(arity=3) float[] numbers) {
                return numbers[0] + numbers[1] + numbers[2];
        }

When the input parameter is blank

By default, the spring shell distinguishes multiple parameters by spaces. If a parameter has multiple words and spaces, it cannot be entered directly. You can use double quotation marks or single quotation marks, such as the following command:

        @ShellMethod("Prints what has been entered.")
        public String echo(String what) {
                return "You said " + what;
        }

Enter as follows:

shell:>echo Hello
You said Hello
shell:>echo 'Hello'
You said Hello
shell:>echo 'Hello World'
You said Hello World
shell:>echo "Hello World"
You said Hello World

You can also use this to avoid escape characters:

shell:>echo "I'm here!"
You said I'm here!
shell:>echo 'He said "Hi!"'
You said He said "Hi!"

Command auto completion function

You can use the tab key to automatically fill in the command name. Similar to the linux command, you can also use the tab to automatically fill in the parameter name.

Command line feed

Sometimes, there are too many command parameters and one line may not be completed. You can add \ "at the end of one line and then enter the command from another line:

shell:>register module --type source --name foo   
> --uri file:///tmp/bar
Successfully registered module 'source:foo'

Shortcut key use

  1. ctrl + r searches for historically executed commands. Reduce repeated input and improve operation efficiency.
  2. ctrl + a jump to line header input
  3. ctrl + e jump to end of line input
  4. After the up and down arrows input the command name, press the up and down arrows to view the previously entered parameter commands.

Parameter limit

You can use annotations to restrict parameter entry to prevent user input errors, as follows:

        @ShellMethod("Change password.")
        public String changePassword(@Size(min = 8, max = 40) String password) {
                return "Password successfully set to " + password;
        }

If the input does not meet the requirements, you will be prompted:

shell:>change-password hello
The following constraints were not met:
	--password string : size must be between 8 and 40 (You passed 'hello')

For more notes to use, see: https://beanvalidation.org/2.0/spec/#builtinconstraints

Command validity detection

Sometimes, there may be some dependency between multiple commands. For example, in such a scenario, the client has a download command, but when using the download command, you must first connect successfully. You can check whether the download command is available as follows:

@ShellComponent
public class MyCommands {

    private boolean connected;

    @ShellMethod("Connect to the server.")
    public void connect(String user, String password) {
        [...]
        connected = true;
    }

    @ShellMethod("Download the nuclear codes.")
    public void download() {
        [...]
    }
   // Note that the name of the method here is command name + Availability
    public Availability downloadAvailability() {
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }
}

In this way, if the user does not use the connect command and directly uses the download command, he will be prompted:

xinyues-client:>download
[31mCommand 'download' exists but is not currently available because you are not connected[0m
[31mDetails of the error have been omitted. You can use the [1mstacktrace[22m command to print the full stacktrace.[0m
xinyues-client:>

The above detection method is that the naming rule must be command name + Availability; another way is to use annotation to specify the detection method name:

    @ShellMethod("Download the nuclear codes.")
    @ShellMethodAvailability("availabilityCheck") 
    public void download() {
        [...]
    }

    public Availability availabilityCheck() { 
        return connected
            ? Availability.available()
            : Availability.unavailable("you are not connected");
    }

Posted by AlexP on Wed, 27 May 2020 21:09:04 -0700