8. Components

Components are a set of features which are either build-in or something you can re-use or extend for your own needs. Components in question are either built-in commands or UI side components providing higher level features within commands itself.spring-doc.cn

8.1. Built-In Commands

8.1.1. Help

Running a shell application often implies that the user is in a graphically limited environment. Also, while we are nearly always connected in the era of mobile phones, accessing a web browser or any other rich UI application (such as a PDF viewer) may not always be possible. This is why it is important that the shell commands are correctly self-documented, and this is where the help command comes in.spring-doc.cn

Typing help + ENTER lists all the commands known to the shell (including unavailable commands) and a short description of what they do, similar to the following:spring-doc.cn

my-shell:>help
AVAILABLE COMMANDS

Built-In Commands
       exit: Exit the shell.
       help: Display help about available commands
       stacktrace: Display the full stacktrace of the last error.
       clear: Clear the shell screen.
       quit: Exit the shell.
       history: Display or save the history of previously run commands
       completion bash: Generate bash completion script
       version: Show version info
       script: Read and execute commands from a file.

Typing help <command> shows more detailed information about a command, including the available parameters, their type, whether they are mandatory or not, and other details.spring-doc.cn

The following listing shows the help command applied to itself:spring-doc.cn

my-shell:>help help
NAME
       help - Display help about available commands

SYNOPSIS
       help --command String

OPTIONS
       --command or -C String
       The command to obtain help for.
       [Optional]

Help is templated and can be customized if needed. Settings are under spring.shell.command.help where you can use enabled to disable command, grouping-mode taking group or flat if you want to hide groups by flattening a structure, command-template to define your template for output of a command help, commands-template to define output of a command list.spring-doc.cn

If spring.shell.command.help.grouping-mode=flat is set, then help would show:spring-doc.cn

my-shell:>help help
AVAILABLE COMMANDS

exit: Exit the shell.
help: Display help about available commands
stacktrace: Display the full stacktrace of the last error.
clear: Clear the shell screen.
quit: Exit the shell.
history: Display or save the history of previously run commands
completion bash: Generate bash completion script
version: Show version info
script: Read and execute commands from a file.

Output from help and help <commmand> are both templated with a default implementation which can be changed.spring-doc.cn

Option spring.shell.command.help.commands-template defaults to classpath:template/help-commands-default.stg and is passed GroupsInfoModel as a model.spring-doc.cn

Option spring.shell.command.help.command-template defaults to classpath:template/help-command-default.stg and is passed CommandInfoModel as a model.spring-doc.cn

Table 2. GroupsInfoModel Variables
Key Description

showGroupsspring-doc.cn

true if showing groups is enabled. Otherwise, false.spring-doc.cn

groupsspring-doc.cn

The commands variables (see GroupCommandInfoModel Variables).spring-doc.cn

commandsspring-doc.cn

The commands variables (see CommandInfoModel Variables).spring-doc.cn

hasUnavailableCommandsspring-doc.cn

true if there is unavailable commands. Otherwise, false.spring-doc.cn

Table 3. GroupCommandInfoModel Variables
Key Description

groupspring-doc.cn

The name of a group, if set. Otherwise, empty.spring-doc.cn

commandsspring-doc.cn

The commands, if set. Otherwise, empty. Type is a multi value, see CommandInfoModel Variables.spring-doc.cn

Table 4. CommandInfoModel Variables
Key Description

namespring-doc.cn

The name of a command, if set. Otherwise, null. Type is string and contains full command.spring-doc.cn

namesspring-doc.cn

The names of a command, if set. Otherwise, null. Type is multi value essentially name splitted.spring-doc.cn

aliasesspring-doc.cn

The possible aliases, if set. Type is multi value with strings.spring-doc.cn

descriptionspring-doc.cn

The description of a command, if set. Otherwise, null.spring-doc.cn

parametersspring-doc.cn

The parameters variables, if set. Otherwise empty. Type is a multi value, see CommandParameterInfoModel Variables.spring-doc.cn

availabilityspring-doc.cn

The availability variables (see CommandAvailabilityInfoModel Variables).spring-doc.cn

Table 5. CommandParameterInfoModel Variables
Key Description

typespring-doc.cn

The type of a parameter if set. Otherwise, null.spring-doc.cn

argumentsspring-doc.cn

The arguments, if set. Otherwise, null. Type is multi value with strings.spring-doc.cn

requiredspring-doc.cn

true if required. Otherwise, false.spring-doc.cn

descriptionspring-doc.cn

The description of a parameter, if set. Otherwise, null.spring-doc.cn

defaultValuespring-doc.cn

The default value of a parameter, if set. Otherwise, null.spring-doc.cn

hasDefaultValuespring-doc.cn

true if defaultValue exists. Otherwise, false.spring-doc.cn

Table 6. CommandAvailabilityInfoModel Variables
Key Description

availablespring-doc.cn

true if available. Otherwise, false.spring-doc.cn

reasonspring-doc.cn

The reason if not available if set. Otherwise, null.spring-doc.cn

8.1.2. Clear

The clear command does what you would expect and clears the screen, resetting the prompt in the top left corner.spring-doc.cn

8.1.3. Exit

The quit command (also aliased as exit) requests the shell to quit, gracefully closing the Spring application context. If not overridden, a JLine History bean writes a history of all commands to disk, so that they are available again on the next launch.spring-doc.cn

8.1.4. Stacktrace

When an exception occurs inside command code, it is caught by the shell and a simple, one-line message is displayed so as not to overflow the user with too much information. There are cases, though, when understanding what exactly happened is important (especially if the exception has a nested cause).spring-doc.cn

To this end, Spring Shell remembers the last exception that occurred, and the user can later use the stacktrace command to print all the details on the console.spring-doc.cn

8.1.5. Script

The script command accepts a local file as an argument and replays commands found there, one at a time.spring-doc.cn

Reading from the file behaves exactly like inside the interactive shell, so lines starting with // are considered to be comments and are ignored, while lines ending with \ trigger line continuation.spring-doc.cn

8.1.6. History

The history command shows the history of commands that has been executed.spring-doc.cn

There are a few configuration options that you can use to configure behavior of a history. History is kept in a log file, which is enabled by default and can be turned off by setting spring.shell.history.enabled. The name of a log file is resolved from spring.application.name and defaults to spring-shell.log, which you can change by setting spring.shell.history.name.spring-doc.cn

By default, a log file is generated to a current working directory, which you can dictate by setting spring.shell.config.location. This property can contain a placeholder ({userconfig}), which resolves to a common shared config directory.spring-doc.cn

Run the Spring Shell application to see how the sample application works as it uses these options.

8.1.7. Completion

The completion command set lets you create script files that can be used with am OS shell implementations to provide completion. This is very useful when working with non-interactive mode.spring-doc.cn

Currently, the only implementation is for bash, which works with bash sub-command.spring-doc.cn

8.1.8. Version

The version command shows existing build and git info by integrating into Boot’s BuildProperties and GitProperties if those exist in the shell application. By default, only version information is shown, and you can enable other information through configuration options.spring-doc.cn

The relevant settings are under spring.shell.command.version, where you can use enabled to disable a command and, optionally, define your own template with template. You can use the show-build-artifact, show-build-group, show-build-name, show-build-time, show-build-version, show-git-branch, show-git-commit-id, show-git-short-commit-id and show-git-commit-time commands to control fields in a default template.spring-doc.cn

The template defaults to classpath:template/version-default.st, and you can define your own, as the following example shows:spring-doc.cn

<buildVersion>

This setting would output something like the following:spring-doc.cn

X.X.X

You can add the following attributes to the default template rendering: buildVersion, buildGroup, buildGroup, buildName, buildTime, gitShortCommitId, gitCommitId, gitBranch, and gitCommitTime.spring-doc.cn

8.2. Flow

When you use Flow Components to build something that involves use of a multiple components, your implementation may become a bit cluttered. To ease these use cases, we added a ComponentFlow that can hook multiple component executions together as a “flow”.spring-doc.cn

The following listings show examples of flows and their output in a shell:spring-doc.cn

static class FlowSampleComplex {

	@Autowired
	private ComponentFlow.Builder componentFlowBuilder;

	public void runFlow() {
		Map<String, String> single1SelectItems = new HashMap<>();
		single1SelectItems.put("key1", "value1");
		single1SelectItems.put("key2", "value2");
		List<SelectItem> multi1SelectItems = Arrays.asList(SelectItem.of("key1", "value1"),
				SelectItem.of("key2", "value2"), SelectItem.of("key3", "value3"));
		ComponentFlow flow = componentFlowBuilder.clone().reset()
				.withStringInput("field1")
					.name("Field1")
					.defaultValue("defaultField1Value")
					.and()
				.withStringInput("field2")
					.name("Field2")
					.and()
				.withConfirmationInput("confirmation1")
					.name("Confirmation1")
					.and()
				.withPathInput("path1")
					.name("Path1")
					.and()
				.withSingleItemSelector("single1")
					.name("Single1")
					.selectItems(single1SelectItems)
					.and()
				.withMultiItemSelector("multi1")
					.name("Multi1")
					.selectItems(multi1SelectItems)
					.and()
				.build();
		flow.run();
	}

}
text input

Normal execution order of a components is same as defined with a builder. It’s possible to conditionally choose where to jump in a flow by using a next function and returning target component id. If this returned id is aither null or doesn’t exist flow is essentially stopped right there.spring-doc.cn

static class FlowSampleConditional {

	@Autowired
	private ComponentFlow.Builder componentFlowBuilder;

	public void runFlow() {
		Map<String, String> single1SelectItems = new HashMap<>();
		single1SelectItems.put("Field1", "field1");
		single1SelectItems.put("Field2", "field2");
		ComponentFlow flow = componentFlowBuilder.clone().reset()
				.withSingleItemSelector("single1")
					.name("Single1")
					.selectItems(single1SelectItems)
					.next(ctx -> ctx.getResultItem().get().getItem())
					.and()
				.withStringInput("field1")
					.name("Field1")
					.defaultValue("defaultField1Value")
					.next(ctx -> null)
					.and()
				.withStringInput("field2")
					.name("Field2")
					.defaultValue("defaultField2Value")
					.next(ctx -> null)
					.and()
				.build();
		flow.run();
	}

}
text input
The result from running a flow returns ComponentFlowResult, which you can use to do further actions.

8.3. Flow Components

Starting from version 2.1.x, a new component model provides an easier way to create higher-level user interaction for the usual use cases, such as asking for input in various forms. These usually are just plain text input or choosing something from a list.spring-doc.cn

Templates for built-in components are in the org/springframework/shell/component classpath.spring-doc.cn

Built-in components generally follow this logic:spring-doc.cn

  1. Enter a run loop for user input.spring-doc.cn

  2. Generate component-related context.spring-doc.cn

  3. Render the runtime status of a component state.spring-doc.cn

  4. Exit.spring-doc.cn

  5. Render the final status of a component state.spring-doc.cn

Flow gives better interface for defining the flow of components that are better suited for defining interactive command flows.

8.3.1. Component Render

You can implement component rendering in either of two ways: fully programmatically or by using a ANTLR Stringtemplate. Strictly speaking, there is a simple Function renderer interface that takes Context as an input and outputs a list of AttributedString. This lets you choose between templating and code.spring-doc.cn

Templating is a good choice if you do not need to do anything complex or you just want to slightly modify existing component layouts. Rendering through code then gives you flexibility to do whatever you need.spring-doc.cn

The programmatic way to render is to create a Function:spring-doc.cn

class StringInputCustomRenderer implements Function<StringInputContext, List<AttributedString>> {
	@Override
	public List<AttributedString> apply(StringInputContext context) {
		AttributedStringBuilder builder = new AttributedStringBuilder();
		builder.append(context.getName());
		builder.append(" ");
		if (context.getResultValue() != null) {
			builder.append(context.getResultValue());
		}
		else  {
			String input = context.getInput();
			if (StringUtils.hasText(input)) {
				builder.append(input);
			}
			else {
				builder.append("[Default " + context.getDefaultValue() + "]");
			}
		}
		return Arrays.asList(builder.toAttributedString());
	}
}

Then you can hook it to a component:spring-doc.cn

@ShellMethod(key = "component stringcustom", value = "String input", group = "Components")
public String stringInputCustom(boolean mask) {
	StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue",
			new StringInputCustomRenderer());
	component.setResourceLoader(getResourceLoader());
	component.setTemplateExecutor(getTemplateExecutor());
	if (mask) {
		component.setMaskCharacter('*');
	}
	StringInputContext context = component.run(StringInputContext.empty());
	return "Got value " + context.getResultValue();
}

Components have their own context but usually share some functionality from a parent component types. The following tables show those context variables:spring-doc.cn

Table 7. TextComponentContext Template Variables
Key Description

resultValuespring-doc.cn

The value after a component renders its result.spring-doc.cn

namespring-doc.cn

The name of a component — that is, its title.spring-doc.cn

messagespring-doc.cn

The possible message set for a component.spring-doc.cn

messageLevelspring-doc.cn

The level of a message — one of INFO, WARN, or ERROR.spring-doc.cn

hasMessageLevelInfospring-doc.cn

Return true if level is INFO. Otherwise, false.spring-doc.cn

hasMessageLevelWarnspring-doc.cn

Return true if level is WARN. Otherwise, false.spring-doc.cn

hasMessageLevelErrorspring-doc.cn

Return true if level is ERROR. Otherwise, false.spring-doc.cn

inputspring-doc.cn

The raw user input.spring-doc.cn

Table 8. SelectorComponentContext Template Variables
Key Description

namespring-doc.cn

The name of a component — that is, its title.spring-doc.cn

inputspring-doc.cn

The raw user input — mostly used for filtering.spring-doc.cn

itemStatesspring-doc.cn

The full list of item states.spring-doc.cn

itemStateViewspring-doc.cn

The visible list of item states.spring-doc.cn

isResultspring-doc.cn

Return true if the context is in a result mode.spring-doc.cn

cursorRowspring-doc.cn

The current cursor row in a selector.spring-doc.cn

8.3.2. String Input

The string input component asks a user for simple text input, optionally masking values if the content contains something sensitive. The following listing shows an example:spring-doc.cn

@ShellComponent
public class ComponentCommands extends AbstractShellComponent {

	@ShellMethod(key = "component string", value = "String input", group = "Components")
	public String stringInput(boolean mask) {
		StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue");
		component.setResourceLoader(getResourceLoader());
		component.setTemplateExecutor(getTemplateExecutor());
		if (mask) {
			component.setMaskCharacter('*');
		}
		StringInputContext context = component.run(StringInputContext.empty());
		return "Got value " + context.getResultValue();
	}
}

The following image shows typical output from a string input component:spring-doc.cn

text input

The context object is StringInputContext. The following table lists its context variables:spring-doc.cn

Table 9. StringInputContext Template Variables
Key Description

defaultValuespring-doc.cn

The default value, if set. Otherwise, null.spring-doc.cn

maskedInputspring-doc.cn

The masked input valuespring-doc.cn

maskedResultValuespring-doc.cn

The masked result valuespring-doc.cn

maskCharacterspring-doc.cn

The mask character, if set. Otherwise, null.spring-doc.cn

hasMaskCharacterspring-doc.cn

true if a mask character is set. Otherwise, false.spring-doc.cn

modelspring-doc.cn

The parent context variables (see TextComponentContext Template Variables).spring-doc.cn

8.3.3. Path Input

The path input component asks a user for a Path and gives additional information about a path itself.spring-doc.cn

@ShellComponent
public class ComponentCommands extends AbstractShellComponent {

	@ShellMethod(key = "component path", value = "Path input", group = "Components")
	public String pathInput() {
		PathInput component = new PathInput(getTerminal(), "Enter value");
		component.setResourceLoader(getResourceLoader());
		component.setTemplateExecutor(getTemplateExecutor());
		PathInputContext context = component.run(PathInputContext.empty());
		return "Got value " + context.getResultValue();
	}
}

The following image shows typical output from a path input component:spring-doc.cn

text input

The context object is PathInputContext. The following table describes its context variables:spring-doc.cn

Table 10. PathInputContext Template Variables
Key Description

modelspring-doc.cn

The parent context variables (see TextComponentContext Template Variables).spring-doc.cn

8.3.4. Confirmation

The confirmation component asks a user for a simple confirmation. It is essentially a yes-or-no question.spring-doc.cn

@ShellComponent
public class ComponentCommands extends AbstractShellComponent {

	@ShellMethod(key = "component confirmation", value = "Confirmation input", group = "Components")
	public String confirmationInput(boolean no) {
		ConfirmationInput component = new ConfirmationInput(getTerminal(), "Enter value", !no);
		component.setResourceLoader(getResourceLoader());
		component.setTemplateExecutor(getTemplateExecutor());
		ConfirmationInputContext context = component.run(ConfirmationInputContext.empty());
		return "Got value " + context.getResultValue();
	}
}

The following image shows the typical output from a confirmation component:spring-doc.cn

text input

The context object is ConfirmationInputContext. The following table describes its context variables:spring-doc.cn

Table 11. ConfirmationInputContext Template Variables
Key Description

defaultValuespring-doc.cn

The default value — either true or false.spring-doc.cn

modelspring-doc.cn

The parent context variables (see TextComponentContext Template Variables).spring-doc.cn

8.3.5. Single Select

A single select component asks a user to choose one item from a list. It is similar to a simple dropbox implementation. The following listing shows an example:spring-doc.cn

@ShellComponent
public class ComponentCommands extends AbstractShellComponent {

	@ShellMethod(key = "component single", value = "Single selector", group = "Components")
	public String singleSelector() {
		SelectorItem<String> i1 = SelectorItem.of("key1", "value1");
		SelectorItem<String> i2 = SelectorItem.of("key2", "value2");
		List<SelectorItem<String>> items = Arrays.asList(i1, i2);
		SingleItemSelector<String, SelectorItem<String>> component = new SingleItemSelector<>(getTerminal(),
				items, "testSimple", null);
		component.setResourceLoader(getResourceLoader());
		component.setTemplateExecutor(getTemplateExecutor());
		SingleItemSelectorContext<String, SelectorItem<String>> context = component
				.run(SingleItemSelectorContext.empty());
		String result = context.getResultItem().flatMap(si -> Optional.ofNullable(si.getItem())).get();
		return "Got value " + result;
	}
}

The following image shows typical output for a single select component:spring-doc.cn

text input

The context object is SingleItemSelectorContext. The following table describes its context variables:spring-doc.cn

Table 12. SingleItemSelectorContext Template Variables
Key Description

valuespring-doc.cn

The returned value when the component exists.spring-doc.cn

rowsspring-doc.cn

The visible items, where rows contains maps of name and selected items.spring-doc.cn

modelspring-doc.cn

The parent context variables (see SelectorComponentContext Template Variables).spring-doc.cn

You can pre-select an item by defining it to get exposed. This is useful if you know the default and lets the user merely press Enter to make a choice. The following listing sets a default:spring-doc.cn

SelectorItem<String> i1 = SelectorItem.of("key1", "value1");
SelectorItem<String> i2 = SelectorItem.of("key2", "value2");
List<SelectorItem<String>> items = Arrays.asList(i1, i2);
SingleItemSelector<String, SelectorItem<String>> component = new SingleItemSelector<>(getTerminal(),
		items, "testSimple", null);
component.setDefaultExpose(i2);

8.3.6. Multi Select

The multi select component asks a user to select multiple items from a list. The following listing shows an example:spring-doc.cn

@ShellComponent
public class ComponentCommands extends AbstractShellComponent {

	@ShellMethod(key = "component multi", value = "Multi selector", group = "Components")
	public String multiSelector() {
		List<SelectorItem<String>> items = new ArrayList<>();
		items.add(SelectorItem.of("key1", "value1"));
		items.add(SelectorItem.of("key2", "value2", false, true));
		items.add(SelectorItem.of("key3", "value3"));
		MultiItemSelector<String, SelectorItem<String>> component = new MultiItemSelector<>(getTerminal(),
				items, "testSimple", null);
		component.setResourceLoader(getResourceLoader());
		component.setTemplateExecutor(getTemplateExecutor());
		MultiItemSelectorContext<String, SelectorItem<String>> context = component
				.run(MultiItemSelectorContext.empty());
		String result = context.getResultItems().stream()
				.map(si -> si.getItem())
				.collect(Collectors.joining(","));
		return "Got value " + result;
	}
}

The following image shows a typical multi-select component:spring-doc.cn

text input

The context object is MultiItemSelectorContext. The following table describes its context variables:spring-doc.cn

Table 13. MultiItemSelectorContext Template Variables
Key Description

valuesspring-doc.cn

The values returned when the component exists.spring-doc.cn

rowsspring-doc.cn

The visible items, where rows contain maps of name, selected, on-row, and enabled items.spring-doc.cn

modelspring-doc.cn

The parent context variables (see SelectorComponentContext Template Variables).spring-doc.cn