[JAVA] Spring Shell usage memo

What is Spring Shell?

--A framework that makes it easy to create REPL tools --Can be used when you don't need a rich UI like a web app and only an interactive CUI is enough. --JLine is used as the base (library also used by jshell)

environment

OS Windows 10

Java 1.8.0_162

Hello World

Implementation

build.gradle


buildscript {
	ext {
		springBootVersion = '2.0.0.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'

repositories {
	mavenCentral()
}

dependencies {
	compile('org.springframework.shell:spring-shell-starter:2.0.0.RELEASE')
}

bootJar {
	baseName = 'sample'
}

SampleApplication.java


package sample.spring.shell;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleApplication {

	public static void main(String[] args) {
		SpringApplication.run(SampleApplication.class, args);
	}
}

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello Spring Shell!!");
    }
}

Execution result

> gradle build

> java -jar build\libs\sample.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.8.RELEASE)

(Omitted)
shell:>【hello】
Hello Spring Shell!!
shell:>【exit】
(Omitted)

>

Description

--About Spring Boot dependency --Spring Shell, latest as of March 2018 is 2.0.0 --I don't know about ver 1, but it seems that integration with Spring Boot is progressing from ver 2, and the official document also explains how to create using Spring Boot. ――It seems that Spring Boot is not essential, but I couldn't find a way to not use Spring Boot, so I will make a note of how to build using Spring Boot here as well. --Running the created jar launches an interactive shell --You can use your own commands or commands provided by Spring Shell by default. --You can exit the shell with ʻexit`

Built-in commands

command Description
clear Erase what is currently displayed in the shell
exit, quit Exit the shell
help Get help
script Read and execute commands from a file
stacktrace View the stack trace of the last error that occurred

What it looks like when you run help


shell:>help
AVAILABLE COMMANDS

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

Sample Commands
        hello: Hello World

Basic operation of the shell

Shortcut

--You can use shortcuts similar to bash

Shortcut Operation details
Ctrl + u Delete left from cursor
Ctrl + k Remove right from cursor
Ctrl + a Move the cursor to the beginning of the line
Ctrl + e Move cursor to end of line
Ctrl + w Delete up to the previous word
Ctrl + d Delete the character at the cursor position
Ctrl + f Advance the cursor by one
Ctrl + b Move the cursor back one
Alt + f Move the cursor one word
Alt + b Move the cursor back one word

Pass a value that includes blank spaces

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class GreetingCommands {

    @ShellMethod("Hello World")
    public void hello(String text) {
        System.out.println(text);
    }
}

Execution result


shell:>【hello "foo bar"】
foo bar

shell:>【hello 'foo bar'】
foo bar

shell:>【hello "foo 'bar'"】
foo 'bar'

shell:>【hello 'foo "bar"'】
foo "bar"

shell:>【hello "foo \"bar\""】
foo "bar"

shell:>【hello 'foo \'bar\''】
foo 'bar'

--If you want to pass a string containing blank spaces as an argument, enclose the string in single quotes (') or double quotes (" ). --Double quotes can be used as they are in single quotes, and single quotes can be used as they are in double quotes. --If you want to use single quotes in single quotes and double quotes in double quotes, escape with a backslash (\)

Escape blank spaces


shell:>【hello foo\ bar】
foo bar

--There is also a way to escape the blank space itself without quoting it.

Multi-line input

Execution result


shell:>【hello "abc】
dquote> 【defg】
dquote> 【hijk"】
abc defg hijk

--If you insert a line break while quoting is started, you will continue to be prompted to enter characters. --Until you close the quotes, the input including the line break is treated as one input (the line break itself is finally replaced with a blank space).

Input completion by Tab

springshell.gif

--When you enter Tab, input completion works in various places. --When the candidates are displayed, continue to enter Tab to move the cursor to the options in order. --You can select candidates with ʻEnter` --Not only command completion but also argument completion is supported.

Include line breaks in one command input

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class GreetingCommands {

    @ShellMethod("Hello World")
    public void hello(int a, int b, int c) {
        System.out.println("a=" + a + ", b=" + b + ", c=" + c);
    }
}

Execution result


shell:>【hello \】
> 【--a 10 \】
> 【--b 20 \】
> 【--c 30】
a=10, b=20, c=30

--By separating with a backslash (\), you can describe the input of one command on multiple lines.

Command definition

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello Spring Shell!!");
    }
}

--To define a command, first create an arbitrary class and annotate the class with @ ShellComponent. --Next, create a method and annotate it with @ ShellMethod --It is necessary to set the wording that explains the command in value of @ ShellMethod (otherwise an error will occur at startup). --The wording of the explanation should be written so as to satisfy the following conditions in order to be consistent with other commands.

  1. Short sentences (about 1 or 2 sentences)
  2. Start with a capital letter and end with a dot --Now, the method name becomes the command name as it is --If the method name is camel case with two or more words like helloWorld, it will be a hyphen-separated command name like hello-world. --When you execute the command, the corresponding method will be executed.

Specify the command name

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod(value="Hello World", key="hoge")
    public void hello() {
        System.out.println("Hello Spring Shell!!");
    }
}

Execution result


shell:>【hoge】
Hello Spring Shell!!

shell:>【hello】
No command found for 'hello'
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

--You can specify any name for the command name with key of @ ShellMethod.

Assign multiple names

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod(value="Hello World", key={"hoge", "fuga"})
    public void hello() {
        System.out.println("Hello Spring Shell!!");
    }
}

Execution result


shell:>【hoge】
Hello Spring Shell!!

shell:>【fuga】
Hello Spring Shell!!

shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

Sample Commands
        fuga, hoge: Hello World

--Since multiple names can be set for key, multiple names can be assigned to one command as aliases.

Argument specification

python


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello(int a, int b, int c) {
        System.out.println("a=" + a + ", b=" + b + ", c=" + c);
    }
}

Execution result


shell:>【hello 1 2 3】
a=1, b=2, c=3

shell:>【hello 1 2】
Parameter '--c int' should be specified
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【hello a 2 3】
Failed to convert from type [java.lang.String] to type [int] for value 'a'; nested exception is java.lang.NumberFormatException: For input string: "a"
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

--If you define an argument to the method, you can pass the argument to the command. --The arguments passed to the command are passed to the method arguments in the same order. --An error will occur if there are insufficient arguments or if you pass a value that cannot be type converted.

Give it a name and pass arguments

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello(int a, int b, int fooBar) {
        System.out.println("a=" + a + ", b=" + b + ", fooBar=" + fooBar);
    }
}

Execution result


shell:>【hello --a 1 --b 2 --foo-bar 3】
a=1, b=2, fooBar=3

shell:>【hello --foo-bar 3 --a 1 --b 2】
a=1, b=2, fooBar=3

shell:>【hello --b 2 1 3】
a=1, b=2, fooBar=3

--Arguments can also be specified with a name --Specify the name as -[argument name] [value] --By default, the argument name of the method is used as it is. --However, if the camel case is the same as the command name and has two or more words, it will be replaced with a hyphen delimiter (--foo-bar). --You can mix unnamed argument specifications and named argument specifications. --In that case, the named argument is assigned first to the method argument. --And the remaining method arguments are assigned unnamed arguments in order

Change the prefix

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod(value="Hello World", prefix="-")
    public void hello(int a) {
        System.out.println("a=" + a);
    }
}

Execution result


shell:>【hello -a 1】
a=1

--You can change the prefix (-) attached to the argument name when specifying the name with the prefix attribute of @ ShellMethod.

Change the argument name

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello(int a, @ShellOption("--foo") int b, @ShellOption({"-h", "--hoge"}) int c) {
        System.out.println("a=" + a + ", b=" + b + ", c=" + c);
    }
}

Execution result


shell:>【hello --a 1 --foo 2 -h 3】
a=1, b=2, c=3

shell:>【hello --a 1 --foo 2 --hoge 3】
a=1, b=2, c=3

--If you annotate a method argument with @ ShellOption, you can change the command argument name with the value attribute. --Since the value attribute can be specified as an array, multiple names can be assigned to the same argument.

Default value of argument

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

@ShellComponent
public class SampleCommands {
    
    @ShellMethod("Hello World")
    public void hello(@ShellOption(defaultValue="9") int a) {
        System.out.println("a=" + a);
    }
}

Execution result


shell:>【hello】
a=9

shell:>【hello 1】
a=1

--The defaultValue attribute of @ ShellOption can define the default value if that argument is not specified.

Receive multiple values with one argument

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import java.util.Arrays;

@ShellComponent
public class SampleCommands {

    @ShellMethod("Hello World")
    public void hello(@ShellOption(arity=3) int[] a, int b) {
        System.out.println("a=" + Arrays.toString(a) + ", b=" + b);
    }
}

Execution result


shell:>【hello 1 2 3 4】
a=[1, 2, 3], b=4

shell:>【hello --a 1 2 3 --b 4】
a=[1, 2, 3], b=4

shell:>【hello 1 --b 4 2 3】
a=[1, 2, 3], b=4

shell:>【hello --a 1 2 --b 4】
Failed to convert from type [java.lang.String] to type [@org.springframework.shell.standard.ShellOption int] for value '--b'; nested exception is java.lang.NumberFormatException: For input string: "--b"
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【hello --a 1 2 3 4 --b 5】
Too many arguments: the following could not be mapped to parameters: '4'
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

--You can receive multiple values with one command argument --Method arguments are defined as collection type or array type --Also, annotate the argument with @ ShellOption and specify the number of values to receive with the ʻarity attribute. --An error will occur if you try to pass a number different from the number specified by ʻarity. ――It seems that you can't limit the number now (the document says TO BE IMPLEMENTED)

boolean argument

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {

    @ShellMethod("Hello World")
    public void hello(boolean a) {
        System.out.println("a=" + a);
    }
}

Execution result


shell:>【hello】
a=false

shell:>【hello --a】
a=true

--If the argument type is boolean, the specification method in the command changes a little. --If no command argument is specified, it will be false. --When specifying command arguments, specify only the name and do not pass a value (an error will occur if you try to pass a value like --a true) --If you specify an argument, it becomes true by itself.

When a default value is specified for the boolean argument

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

@ShellComponent
public class SampleCommands {

    @ShellMethod("Hello World")
    public void hello(@ShellOption(defaultValue="true") boolean a, @ShellOption(defaultValue="false") boolean b) {
        System.out.println("a=" + a + ", b=" + b);
    }
}

Execution result


shell:>【hello】
a=true, b=false

shell:>【hello --a --b】
a=false, b=true

--The behavior when " false " is set to the default value is the same as when nothing is set. --If no argument is specified false --If you specify an argument, true --If you set the default value to " true ", --If no argument is specified, it will be true, --If you specify an argument, it becomes false

Use Bean Validation

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;

@ShellComponent
public class SampleCommands {

    @ShellMethod("Hello World")
    public void hello(@Min(0) @Max(100) int a) {
        System.out.println("a=" + a);
    }
}

Execution result


shell:>【hello -1】
The following constraints were not met:
        --a int : must be greater than or equal to 0 (You passed '-1')

shell:>【hello 0】
a=0

shell:>【hello 100】
a=100

shell:>【hello 101】
The following constraints were not met:
        --a int : must be less than or equal to 100 (You passed '101')

--Spring Shell supports Bean Validation, and input checking can be performed by adding Bean Validation constraint annotation to method arguments. --Implementation library is Hibernate Validator --For Japanese localization of messages, [this method](https://qiita.com/opengl-8080/items/3926fbde5469c0b330c2#%E3%83%93%E3%83%AB%E3%83%88% E3% 82% A4% E3% 83% B3% E3% 81% AE% E5% 88% B6% E7% B4% 84% E3% 82% A2% E3% 83% 8E% E3% 83% 86% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E3% 81% AE% E3% 83% 87% E3% 83% 95% E3% 82% A9% E3% 83% AB% E3% 83% 88% E3% 82% A8% E3% 83% A9% E3% 83% BC% E3% 83% A1% E3% 83% 83% E3% 82% BB% E3% 83% BC% E3% 82% B8% E3% 82% 92% E4% B8% 8A% E6% 9B% B8% E3% 81% 8D% E3% 81% 99% E3% 82% 8B) --However, it remains in English except for the Bean Validation error message.

Convert to any type

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {

    @ShellMethod("Hello World")
    public void hello(Hoge hoge) {
        hoge.hello();
    }
}

Hoge.java


package sample.spring.shell;

public class Hoge {
    
    private final String value;

    public Hoge(String value) {
        this.value = value;
    }
    
    public void hello() {
        System.out.println("Hoge(" + this.value + ")");
    }
}

HogeConverter.java


package sample.spring.shell;

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class HogeConverter implements Converter<String, Hoge> {
    
    @Override
    public Hoge convert(String source) {
        return new Hoge(source);
    }
}

Execution result


shell:>【hello Hey】
Hoge(Hey)

--If you want to receive any type other than the basic type as an argument of the command method, you can do it by defining your own Converter. --Create a class that implements ʻorg.springframework.core.convert.converter.Converter <S, T> --Execute theT convert (S)method and return the result of converting a value of typeS (mostly String) to type T. --Add @Component` to make it registered in the container

Dynamically enable / disable commands

package sample.spring.shell;

import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    private boolean greeted;

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
        this.greeted = true;
    }
    
    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }
    
    public Availability byeAvailability() {
        return this.greeted
                ? Availability.available()
                : Availability.unavailable("you does not greet yet.");
    }
}

Execution result


shell:>【bye】
Command 'bye' exists but is not currently available because you does not greet yet.
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

Sample Commands
      * bye: Good Bye
        hello: Hello World

Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

shell:>【hello】
Hello!!

shell:>【bye】
Bye!!

--Some commands may make you want to be unable to execute until a certain state is reached. --For example, if you are trying to create a shell that connects to a server and issues some command You may want to disable subsequent commands until the command to connect is successful. --Spring Shell provides a mechanism to dynamically enable / disable commands. --Invalid command will result in an error when executed --In the above example, the bye command is disabled until the hello command is executed. --The bye command is judged to be valid or invalid by thebyeAvailability ()method. --A method with the suffix ʻAvailabilityadded to the name of the method for which you want to dynamically switch between valid and invalid is automatically identified as a judgment method. -bye()->byeAvailability() --Implement this method to return an object called ʻAvailability --The ʻAvailability class provides two factory methods, ʻavailable () and ʻunavailable (String). --If valid, return the object created by the ʻavailable () method --If invalid, return the object created by the ʻunavailable ()method --At this time, pass the reason why it is invalid as an argument in a short sentence. --Then, it is embedded in the error messageThis command is currently not available because [here] ――So, if you write it so that it starts with a lowercase letter and ends with a dot, it feels good. --Information on whether the command is valid or invalid is reflected in the information when you seehelp`.

Make the name of the valid / invalid judgment method arbitrary

package sample.spring.shell;

import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellMethodAvailability;

@ShellComponent
public class SampleCommands {
    
    private boolean greeted;

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
        this.greeted = true;
    }
    
    @ShellMethod("Good Bye")
    @ShellMethodAvailability("checkByeAvailability")
    public void bye() {
        System.out.println("Bye!!");
    }
    
    public Availability checkByeAvailability() {
        return this.greeted
                ? Availability.available()
                : Availability.unavailable("you does not greet yet.");
    }
}

--If you don't like the naming convention "[Target method name] Availability, you can change it to a method with any name. --Add the @ShellMethodAvailability annotation to the method of the dynamic command, and specify the name of the method that determines whether it is valid or invalid in the value` attribute.

Control the validity / invalidity of multiple commands at once

package sample.spring.shell;

import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellMethodAvailability;

@ShellComponent
public class SampleCommands {
    
    private boolean greeted;

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
        this.greeted = true;
    }
    
    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }
    
    @ShellMethod("lol")
    public void laugh() {
        System.out.println("HAHAHAHA!!");
    }
    
    @ShellMethodAvailability({"bye", "laugh"})
    public Availability checkAvailability() {
        return this.greeted
                ? Availability.available()
                : Availability.unavailable("you does not greet yet.");
    }
}

Execution result


shell:>【laugh】
Command 'laugh' exists but is not currently available because you does not greet yet.
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【bye】
Command 'bye' exists but is not currently available because you does not greet yet.
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【hello】
Hello!!

shell:>【laugh】
HAHAHAHA!!

shell:>【bye】
Bye!!

--If you want to control the enable / disable of multiple commands under the same conditions, you do not need to annotate each method with @ShellMethodAvailability. --Instead, annotate the valid / invalid judgment method with @ShellMethodAvailability. --And specify the ** command name ** of the array in the value attribute --Note that this is a command name, not a method name. --In other words, if the method name is lotsOfLaugh, the command name will be lots-of-laugh, so it is the lots-of-laugh that is specified for @ ShellMethodAvailability.

Controls the validity / invalidity of all commands in the class at once

SampleCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class SampleCommands {
    
    private boolean greeted;

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
        this.greeted = true;
    }
    
    public boolean isGreeted() {
        return this.greeted;
    }
}

SomeCommands.java


package sample.spring.shell;

import org.springframework.shell.Availability;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellMethodAvailability;

@ShellComponent
public class SomeCommands {
    
    private final SampleCommands sampleCommands;

    public SomeCommands(SampleCommands sampleCommands) {
        this.sampleCommands = sampleCommands;
    }

    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }

    @ShellMethod("lol")
    public void laugh() {
        System.out.println("HAHAHAHA!!");
    }

    @ShellMethodAvailability
    public Availability checkAvailability() {
        return this.sampleCommands.isGreeted()
                ? Availability.available()
                : Availability.unavailable("you does not greet yet.");
    }
}

Execution result


shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

Sample Commands
        hello: Hello World

Some Commands
      * bye: Good Bye
      * laugh: lol

Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

--If you want to control all the commands in the class at once, annotate the control method with @ ShellMethodAvailability and do not set anything in the value attribute. --This takes advantage of the fact that the default value of the value attribute is*, which is a special wildcard for all commands. --This will control all commands in the class.

Command grouping

--As the number of commands increases, it is better to group the commands to make it easier to see help etc. --Spring Shell provides a mechanism to group commands into arbitrary groups.

Default grouping

GreetingCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class GreetingCommands {

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
    }

    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }
}

CalcCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class CalcCommands {
    
    @ShellMethod("a + b")
    public int add(int a, int b) {
        return a + b;
    }
    
    @ShellMethod("a - b")
    public int minus(int a, int b) {
        return a - b;
    }
}

Execution result


shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

Calc Commands
        add: a + b
        minus: a - b

Greeting Commands
        bye: Good Bye
        hello: Hello World

--If you do not specify a group, a group is defined for each class annotated with @ShellComponent, and the commands defined in it are assigned to the group corresponding to that class. --The group name will be the class name separated by words.

Specify a group for each command

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class GreetingCommands {

    @ShellMethod(value="Hello World", group="Hello")
    public void hello() {
        System.out.println("Hello!!");
    }

    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }
}

Execution result


shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

Greeting Commands
        bye: Good Bye

Hello
        hello: Hello World

--If you specify the group name in the group attribute of @ ShellMethod, you can specify the group for each command.

Specify a group for each class

GreetingCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellCommandGroup;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
@ShellCommandGroup("My Commands")
public class GreetingCommands {

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
    }

    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }
}

CalcCommands.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellCommandGroup;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
@ShellCommandGroup("My Commands")
public class CalcCommands {
    
    @ShellMethod("a + b")
    public int add(int a, int b) {
        return a + b;
    }
    
    @ShellMethod("a - b")
    public int minus(int a, int b) {
        return a - b;
    }
}

Execution result


shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

My Commands
        add: a + b
        bye: Good Bye
        hello: Hello World
        minus: a - b

--Annotate the class that defines the command with @ShellCommandGroup --And if you specify a group name with the value attribute, the commands defined in that class will belong to that group. --If the above-mentioned allocation for each command is specified, that will take precedence.

Specify a group for each package

package-info.java


@ShellCommandGroup("my commands")
package sample.spring.shell;

import org.springframework.shell.standard.ShellCommandGroup;

Execution result


shell:>【help】
AVAILABLE COMMANDS

Built-In Commands
        ...

my commands
        add: a + b
        bye: Good Bye
        hello: Hello World
        minus: a - b

--Create package-info.java and annotate the package with @ShellCommandGroup --Then, the commands defined under that package will belong to the group specified there. --If there is an allocation for each class mentioned above, that will take precedence.

See command help

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

@ShellComponent
public class GreetingCommands {

    @ShellMethod(value="Hello World")
    public void hello(int a, @ShellOption(defaultValue="9", help="help text") int b) {
        System.out.println("Hello!!");
    }
}

Execution result


shell:>【help hello】


NAME
        hello - Hello World

SYNOPSYS
        hello [--a] int  [[--b] int]

OPTIONS
        --a  int

                [Mandatory]

        --b  int
                help text
                [Optional, default = 9]

--help [command name], you can check the detailed explanation of the specified command. ――Your own command also builds a nice help based on the definition information.

Change prompt

package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@ShellComponent
public class GreetingCommands {
    
    private boolean greeted;

    @ShellMethod("Hello World")
    public void hello() {
        System.out.println("Hello!!");
        this.greeted = true;
    }

    @ShellMethod("Good Bye")
    public void bye() {
        System.out.println("Bye!!");
    }

    public boolean isGreeted() {
        return greeted;
    }
}

MyPromptProvider.java


package sample.spring.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 MyPromptProvider implements PromptProvider {
    
    private final GreetingCommands greetingCommands;

    public MyPromptProvider(GreetingCommands greetingCommands) {
        this.greetingCommands = greetingCommands;
    }

    @Override
    public AttributedString getPrompt() {
        return this.greetingCommands.isGreeted()
                ? new AttributedString("greeted > ", AttributedStyle.DEFAULT.foreground(AttributedStyle.WHITE))
                : new AttributedString("not greeted > ", AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
    }
}

Execution result


not greeted > 【hello】
Hello!!

greeted >

--To change the prompt, create a class that implements PromptProvider and register it in the container. --Implement the getPrompt () method to return an instance of ʻAttributedString --ʻAttributedString is a (ʻAttributed`) string with attribute information, and you can add character styles (bold, color, etc.).

Customize built-in commands

Disable built-in commands

build.gradle


dependencies {
	compile('org.springframework.shell:spring-shell-starter:2.0.0.RELEASE') {
		exclude module: 'spring-shell-standard-commands'
	}
}

Execution result


shell:>【help】
No command found for 'help'

--Excluding spring-shell-standard-commands from the dependencies will remove all built-in commands ―― ʻexit` also disappears, so you have to make your own command to terminate the shell.

Disable only certain commands

package sample.spring.shell;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.StringUtils;

@SpringBootApplication
public class SampleApplication {

	public static void main(String[] args) {
		String[] disabledCommands = {"--spring.shell.command.help.enabled=false"};
		String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands);
		SpringApplication.run(SampleApplication.class, fullArgs);
	}
}

Execution result


shell:>【help】
No command found for 'help'
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.

shell:>【stacktrace】
org.springframework.shell.CommandNotFound: No command found for 'help'
        at org.springframework.shell.Shell.evaluate(Shell.java:180)
        at org.springframework.shell.Shell.run(Shell.java:134)
        ...

--By specifying spring.shell.command. [Command name] .enabled = [true | false] as an argument at startup, you can control the enable / disable of built-in commands. --In the above example, the image is as specified by the command line argument at startup, but it can also be specified by ʻapplication.properties` (I have not tried it, but it seems that it can be specified by environment variables)

Override built-in command behavior

SampleApplication.java


package sample.spring.shell;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.StringUtils;

@SpringBootApplication
public class SampleApplication {

	public static void main(String[] args) {
		String[] disabledCommands = {"--spring.shell.command.help.enabled=false"};
		String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands);
		SpringApplication.run(SampleApplication.class, fullArgs);
	}
}

MyHelpCommand.java


package sample.spring.shell;

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.commands.Help;

@ShellComponent
public class MyHelpCommand implements Help.Command {
    
    @ShellMethod("My help command.")
    public void help() {
        System.out.println("HELP ME!!!!");
    }
}

Execution result


shell:>【help】
HELP ME!!!!

--If you want to change the behavior of the built-in command, make the change by following the steps below.

  1. Disable the built-in command you want to overwrite
  2. Define your own command with the same name --At this time, the document says that the class of the self-made command implements the interface [command name] .Command. --But it worked without implementing this interface (I'm not sure what it's used for)

reference

Recommended Posts

Spring Shell usage memo
Spring Security usage memo CSRF
Spring Security usage memo Run-As
Spring Security Usage memo Method security
Spring Security usage memo Remember-Me
Spring Security usage memo CORS
Spring Security usage memo test
Spring Security usage memo response header
Spring Security usage memo session management
Spring Security usage memo Basic / mechanism
Spring retrospective memo
JavaParser usage memo
WatchService usage memo
PlantUML usage memo
JUnit5 usage memo
Spring Security Usage Memo Domain Object Security (ACL)
Spring boot memo writing (1)
Spring boot memo writing (2)
Spring Security usage memo: Cooperation with Spring MVC and Boot
[Personal memo] About Spring framework
JJUG CCC 2018 Spring participation memo
Spring Framework self-study memo series_1
Dependency Management Plugin Usage memo
Spring boot controller method memo
JCA (Java Cryptography Architecture) Usage Memo
spring framework Simple study memo (2): AOP
Memo after the first Spring project-MVC-
A memo that touched Spring Boot
Spring thorough introduction version upgrade memo
Memo after the first Spring project-Database-
Thymeleaf usage notes in Spring Boot
Automatically generated memo from swagger condegen (spring) -1
Spring Boot environment construction memo on mac
Memo after the first Spring project-What is Spring-
Spring Boot + Thymeleaf BootStrap installation method memo