@@ -1,14 +0,0 @@
|
||||
[[extending-spring-shell]]
|
||||
== Extending Spring Shell
|
||||
|
||||
[[support-for-shell-1-and-jcommander]]
|
||||
=== Support for Spring Shell 1 and JCommander
|
||||
|
||||
|
||||
=== Discovering Methods that Can Act as Commands
|
||||
|
||||
=== Resolving Parameter Values
|
||||
|
||||
==== Supporting kbd:[TAB] Completion
|
||||
|
||||
|
||||
120
spring-shell-docs/src/main/asciidoc/getting-started.adoc
Normal file
120
spring-shell-docs/src/main/asciidoc/getting-started.adoc
Normal file
@@ -0,0 +1,120 @@
|
||||
== Getting Started
|
||||
To see what Spring Shell has to offer, we can write a trivial shell application that
|
||||
has a simple command to add two numbers.
|
||||
|
||||
=== Writing a Simple Boot Application
|
||||
|
||||
Starting with version 2, Spring Shell has been rewritten from the ground up with various
|
||||
enhancements in mind, one of which is easy integration with Spring Boot, although it is
|
||||
not a strong requirement.
|
||||
For the purpose of this tutorial, we create a simple Boot application by
|
||||
using https://start.spring.io. This minimal application depends only on `spring-boot-starter`
|
||||
and configures the `spring-boot-maven-plugin` to generate an executable über-jar:
|
||||
|
||||
====
|
||||
[source, xml]
|
||||
----
|
||||
...
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
...
|
||||
</dependencies>
|
||||
----
|
||||
====
|
||||
|
||||
[[using-spring-shell-add-dependency]]
|
||||
=== Adding a Dependency on Spring Shell
|
||||
|
||||
The easiest way to get going with Spring Shell is to depend on the `{starter-artifactId}` artifact.
|
||||
This comes with everything one needs to use Spring Shell and plays nicely with Boot,
|
||||
configuring only the necessary beans as needed:
|
||||
|
||||
====
|
||||
[source, xml, subs=attributes+]
|
||||
----
|
||||
...
|
||||
<dependency>
|
||||
<groupId>org.springframework.shell</groupId>
|
||||
<artifactId>{spring-shell-starter}</artifactId>
|
||||
<version>{project-version}</version>
|
||||
</dependency>
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
CAUTION: Given that Spring Shell starts the REPL by virtue of this dependency being present,
|
||||
you need to either skip tests when you build (`-DskipTests`) throughout this tutorial or remove the sample integration test
|
||||
that was generated by https://start.spring.io. If you do not remove it, the integration test creates
|
||||
the Spring `ApplicationContext` and, depending on your build tool, stays stuck in the eval loop or crashes with a NPE.
|
||||
|
||||
[[using-spring-shell-your-first-command]]
|
||||
=== Your First Command
|
||||
|
||||
Now we can add our first command. To do so, create a new class (named whatever you want) and
|
||||
annotate it with `@ShellComponent` (a variation of `@Component` that is used to restrict
|
||||
the set of classes that are scanned for candidate commands).
|
||||
|
||||
Then create an `add` method that takes two ints (`a` and `b`) and returns their sum. Annotate it
|
||||
with `@ShellMethod` and provide a description of the command in the annotation (the only piece of
|
||||
information that is required):
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
package com.example.demo;
|
||||
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
|
||||
@ShellComponent
|
||||
public class MyCommands {
|
||||
|
||||
@ShellMethod("Add two integers together.")
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[using-spring-shell-try-application]]
|
||||
=== Trying the Application
|
||||
|
||||
To build the application and run the generated jar, run the following command:
|
||||
|
||||
====
|
||||
[source, bash]
|
||||
----
|
||||
./mvnw clean install -DskipTests
|
||||
[...]
|
||||
|
||||
java -jar target/demo-0.0.1-SNAPSHOT.jar
|
||||
----
|
||||
====
|
||||
|
||||
You are greeted by the following screen (the banner comes from Spring Boot and can be
|
||||
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-banner[customized]:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>
|
||||
----
|
||||
====
|
||||
|
||||
A yellow `shell:>` prompt invites you to type commands. Type `add 1 2`, press `ENTER`, and admire the magic:
|
||||
|
||||
====
|
||||
[source, bash]
|
||||
----
|
||||
shell:>add 1 2
|
||||
3
|
||||
----
|
||||
====
|
||||
|
||||
Try to play with the shell (hint: there is a `help` command). When you are done, type `exit` and press `ENTER`.
|
||||
|
||||
The rest of this document delves deeper into the whole Spring Shell programming model.
|
||||
@@ -1,11 +1,11 @@
|
||||
= Spring Shell Reference Documentation
|
||||
Eric Bottard; Janne Valkealahti; Jay Bryant;
|
||||
Eric Bottard; Janne Valkealahti; Jay Bryant, Corneil du Plessis;
|
||||
:doctype: book
|
||||
:hide-uri-scheme:
|
||||
:icons: font
|
||||
:experimental: // For kbd: macro
|
||||
:spring-shell-starter: spring-shell-starter
|
||||
|
||||
ifdef::backend-html5[]
|
||||
*{projectVersion}*
|
||||
|
||||
(C) 2017 - 2022 VMware, Inc.
|
||||
@@ -15,10 +15,8 @@ others, provided that you do not charge any fee for such copies and further
|
||||
provided that each copy contains this Copyright Notice, whether distributed in
|
||||
print or electronically._
|
||||
|
||||
// ======================================================================================
|
||||
include::introduction.adoc[]
|
||||
|
||||
include::what-is-spring-shell.adoc[]
|
||||
include::getting-started.adoc[]
|
||||
|
||||
include::using-spring-shell.adoc[]
|
||||
|
||||
// include::extending-spring-shell.adoc[]
|
||||
include::using-shell.adoc[]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
== What is Spring Shell?
|
||||
== Introduction
|
||||
|
||||
=== What is Spring Shell?
|
||||
Not all applications need a fancy web user interface!
|
||||
Sometimes, interacting with an application through an interactive terminal is
|
||||
the most appropriate way to get things done.
|
||||
@@ -0,0 +1,147 @@
|
||||
[[dynamic-command-availability]]
|
||||
=== Dynamic Command Availability
|
||||
|
||||
Registered commands do not always make sense, due to the internal state of the application.
|
||||
For example, there may be a `download` command, but it only works once the user has used `connect` on a remote
|
||||
server. Now, if the user tries to use the `download` command, the shell should gracefully explain that
|
||||
the command exist but that it is not available at the time.
|
||||
Spring Shell lets you do that, even letting you provide a short explanation of the reason for
|
||||
the command not being available.
|
||||
|
||||
There are three possible ways for a command to indicate availability.
|
||||
They all leverage a no-arg method that returns an instance of `Availability`.
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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() {
|
||||
[...]
|
||||
}
|
||||
|
||||
public Availability downloadAvailability() {
|
||||
return connected
|
||||
? Availability.available()
|
||||
: Availability.unavailable("you are not connected");
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `connect` method is used to connect to the server (details omitted), altering the state
|
||||
of the command through the `connected` boolean when done.
|
||||
The `download` command as marked as unavailable until the user has connected, thanks to the presence
|
||||
of a method named exactly as the `download` command method with the `Availability` suffix in its name.
|
||||
The method returns an instance of `Availability`, constructed with one of the two factory methods.
|
||||
If the command is not available, an explanation has to be provided.
|
||||
Now, if the user tries to invoke the command while not being connected, here is what happens:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>download
|
||||
Command 'download' exists but is not currently available because you are not connected.
|
||||
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
|
||||
----
|
||||
====
|
||||
|
||||
Information about currently unavailable commands is also used in the integrated help. See <<help-command>>.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The reason provided when the command is not available should read nicely if appended after "`Because`".
|
||||
|
||||
You should not start the sentence with a capital or add a final period
|
||||
====
|
||||
|
||||
If naming the availability method after the name of the command method does not suit you, you
|
||||
can provide an explicit name by using the `@ShellMethodAvailability` annotation:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Download the nuclear codes.")
|
||||
@ShellMethodAvailability("availabilityCheck") // <1>
|
||||
public void download() {
|
||||
[...]
|
||||
}
|
||||
|
||||
public Availability availabilityCheck() { // <1>
|
||||
return connected
|
||||
? Availability.available()
|
||||
: Availability.unavailable("you are not connected");
|
||||
}
|
||||
----
|
||||
<1> the names have to match
|
||||
====
|
||||
|
||||
Lastly, it is often the case that several commands in the same class share the same internal state and, thus,
|
||||
should all be available or unavailable as a group. Instead of having to stick the `@ShellMethodAvailability`
|
||||
on all command methods, Spring Shell lets you flip things around and put the `@ShellMethodAvailabilty`
|
||||
annotation on the availability method, specifying the names of the commands that it controls:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Download the nuclear codes.")
|
||||
public void download() {
|
||||
[...]
|
||||
}
|
||||
|
||||
@ShellMethod("Disconnect from the server.")
|
||||
public void disconnect() {
|
||||
[...]
|
||||
}
|
||||
|
||||
@ShellMethodAvailability({"download", "disconnect"})
|
||||
public Availability availabilityCheck() {
|
||||
return connected
|
||||
? Availability.available()
|
||||
: Availability.unavailable("you are not connected");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
The default value for the `@ShellMethodAvailability.value()` attribute is `*`. This special
|
||||
wildcard matches all command names. This makes it easy to turn all commands of a single class on or off
|
||||
with a single availability method:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@ShellComponent
|
||||
public class Toggles {
|
||||
@ShellMethodAvailability
|
||||
public Availability availabilityOnWeekdays() {
|
||||
return Calendar.getInstance().get(DAY_OF_WEEK) == SUNDAY
|
||||
? Availability.available()
|
||||
: Availability.unavailable("today is not Sunday");
|
||||
}
|
||||
|
||||
@ShellMethod
|
||||
public void foo() {}
|
||||
|
||||
@ShellMethod
|
||||
public void bar() {}
|
||||
}
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
TIP: Spring Shell does not impose many constraints on how to write commands and how to organize classes.
|
||||
However, it is often good practice to put related commands in the same class, and the availability indicators
|
||||
can benefit from that.
|
||||
139
spring-shell-docs/src/main/asciidoc/using-shell-built-in.adoc
Normal file
139
spring-shell-docs/src/main/asciidoc/using-shell-built-in.adoc
Normal file
@@ -0,0 +1,139 @@
|
||||
[[built-in-commands]]
|
||||
=== Built-In Commands
|
||||
|
||||
Any application built by using the `{starter-artifactId}` artifact
|
||||
(or, to be more precise, the `spring-shell-standard-commands` dependency) comes with a set of built-in commands.
|
||||
You can override or disable these commands individually (see <<overriding-or-disabling-built-in-commands>>).
|
||||
However, if they are not overridden or disabled, this section describes their behavior.
|
||||
|
||||
[[help-command]]
|
||||
==== 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.
|
||||
|
||||
Typing `help` + `ENTER` lists all the commands known to the shell (including <<dynamic-command-availability,unavailable>> commands)
|
||||
and a short description of what they do, similar to the following:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>help
|
||||
AVAILABLE COMMANDS
|
||||
add: Add numbers together.
|
||||
* authenticate: Authenticate with the system.
|
||||
* blow-up: Blow Everything up.
|
||||
clear: Clear the shell screen.
|
||||
connect: Connect to the system
|
||||
disconnect: Disconnect from the system.
|
||||
exit, quit: Exit the shell.
|
||||
help: Display help about available commands.
|
||||
register module: Register a new module.
|
||||
script: Read and execute commands from a file.
|
||||
stacktrace: Display the full stacktrace of the last error.
|
||||
|
||||
Commands marked with (*) are currently unavailable.
|
||||
Type `help <command>` to learn more.
|
||||
----
|
||||
====
|
||||
|
||||
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.
|
||||
|
||||
The follwoing listing shows the `help` command applied to itself:
|
||||
|
||||
====
|
||||
----
|
||||
shell:>help help
|
||||
|
||||
|
||||
NAME
|
||||
help - Display help about available commands.
|
||||
|
||||
SYNOPSYS
|
||||
help [[-C] string]
|
||||
|
||||
OPTIONS
|
||||
-C or --command string
|
||||
The command to obtain help for. [Optional, default = <none>]
|
||||
----
|
||||
====
|
||||
|
||||
==== Clear
|
||||
The `clear` command does what you would expect and clears the screen, resetting the prompt
|
||||
in the top left corner.
|
||||
|
||||
==== 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 (see <<interacting-with-the-shell>>) on the next launch.
|
||||
|
||||
==== 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).
|
||||
|
||||
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.
|
||||
|
||||
[[script-command]]
|
||||
==== Script
|
||||
|
||||
The `script` command accepts a local file as an argument and replays commands found there, one at a time.
|
||||
|
||||
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.
|
||||
|
||||
==== History
|
||||
|
||||
The `history` command shows history of a commands which has been executed.
|
||||
|
||||
==== Completion
|
||||
|
||||
The `completion` command set allows you to create _scripts_ files which can be used
|
||||
with am OS shell implementations to provide completion. This is very usefull when
|
||||
working with non-interactive mode.
|
||||
|
||||
Currently only implementation is for _bash_ which works with `bash` sub-command.
|
||||
|
||||
==== Version
|
||||
|
||||
The `version` command shows existing _build_ and _git_ info by integrating into
|
||||
Boot's `BuildProperties` and `GitProperties` if those exists in a shell app.
|
||||
On default only version info is shown and other can be enabled via configuration
|
||||
options.
|
||||
|
||||
Settings are under `spring.shell.command.version` where you can use `enabled` to
|
||||
disable command and optionally define your own template with `template`. Options
|
||||
`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` can be used to control
|
||||
fields in a default template.
|
||||
|
||||
Template default to `classpath:template/version-default.st` and you can define
|
||||
your own, for example having:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
<buildVersion>
|
||||
----
|
||||
====
|
||||
|
||||
Which would simply output something like:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
X.X.X
|
||||
----
|
||||
====
|
||||
|
||||
Attributes added to default template rendering are `buildVersion`, `buildGroup`,
|
||||
`buildGroup`, `buildName`, `buildTime`, `gitShortCommitId`, `gitCommitId`,
|
||||
`gitBranch` and `gitCommitTime`.
|
||||
228
spring-shell-docs/src/main/asciidoc/using-shell-customize.adoc
Normal file
228
spring-shell-docs/src/main/asciidoc/using-shell-customize.adoc
Normal file
@@ -0,0 +1,228 @@
|
||||
=== Customizing the Shell
|
||||
|
||||
[[overriding-or-disabling-built-in-commands]]
|
||||
==== Overriding or Disabling Built-In Commands
|
||||
|
||||
Spring Shell provides <<built-in-commands,Built-in commands>> to let people achieve everyday tasks that many if not
|
||||
all shell applications need. If you are not happy with the way they behave, though, you can disable or override them,
|
||||
as explained in this section.
|
||||
|
||||
[TIP]
|
||||
.Disabling all Built-in Commands
|
||||
=====
|
||||
If you do not need built-in commands at all, there is an easy way to "`disable`" them: don't include them.
|
||||
Either use a maven exclusion on `spring-shell-standard-commands` or, if you are selectively including Spring Shell
|
||||
dependencies, don't include that one in. The follwoing example shows how to exclude `spring-shell-standard-commands`:
|
||||
|
||||
====
|
||||
[source,xml,subs=attributes+]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.shell</groupId>
|
||||
<artifactId>{starter-artifactId}</artifactId>
|
||||
<version>{project-version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.shell</groupId>
|
||||
<artifactId>spring-shell-standard-commands</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
[[disabling-specific-commands]]
|
||||
===== Disabling Specific Commands
|
||||
|
||||
To disable a single built-in command, set the `spring.shell.command.<command>.enabled` property to `false` in the
|
||||
application `Environment`. One way to do so is to pass extra arguments to the Boot application in your `main()`
|
||||
entry point:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public static void main(String[] args) throws Exception {
|
||||
String[] disabledCommands = {"--spring.shell.command.help.enabled=false"}; // <1>
|
||||
String[] fullArgs = StringUtils.concatenateStringArrays(args, disabledCommands);
|
||||
SpringApplication.run(MyApp.class, fullArgs);
|
||||
}
|
||||
----
|
||||
<1> This disables the integrated `help` command
|
||||
====
|
||||
|
||||
===== Overriding Specific Commands
|
||||
|
||||
If, instead of disabling a command, you would rather provide your own implementation, then you can either:
|
||||
|
||||
* Disable the command as explained <<disabling-specific-commands,earlier>> and have your implementation registered
|
||||
with the same name.
|
||||
* Have your implementing class implement the `<Command>.Command` interface. As an example, here is how
|
||||
to override the `clear` command:
|
||||
+
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
public class MyClear implements Clear.Command {
|
||||
|
||||
@ShellMethod("Clear the screen, only better.")
|
||||
public void clear() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
.Please Consider Contributing your Changes
|
||||
====
|
||||
If you feel like your implementation of a standard command could be valuable to the community,
|
||||
please open a pull-request at https://github.com/spring-projects/spring-shell.
|
||||
|
||||
Alternatively, before making any changes on your own, you can open an issue with the project. Feedback is
|
||||
always welcome!
|
||||
====
|
||||
|
||||
// ==== ResultHandlers
|
||||
|
||||
// TBD
|
||||
|
||||
==== PromptProvider
|
||||
After each command invocation, the shell waits for new input from the user, displaying
|
||||
a prompt in yellow:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>
|
||||
----
|
||||
====
|
||||
|
||||
It is possible to customize this behavior by registering a bean of type `PromptProvider`.
|
||||
Such a bean may use internal state to decide what to display to the user (it may, for example,
|
||||
react to https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events-annotation[application events])
|
||||
and can use JLine's `AttributedCharSequence` to display fancy ANSI text.
|
||||
|
||||
The following example shows how to use a `PromptProvider`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Component
|
||||
public class CustomPromptProvider implements PromptProvider {
|
||||
|
||||
private ConnectionDetails connection;
|
||||
|
||||
@Override
|
||||
public AttributedString getPrompt() {
|
||||
if (connection != null) {
|
||||
return new AttributedString(connection.getHost() + ":>",
|
||||
AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW));
|
||||
}
|
||||
else {
|
||||
return new AttributedString("server-unknown:>",
|
||||
AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void handle(ConnectionUpdatedEvent event) {
|
||||
this.connection = event.getConnectionDetails();
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Customizing Command Line Options Behavior
|
||||
|
||||
There can be exactly one shell spesific `ShellApplicationRunner` which simply extends
|
||||
Boot's `ApplicationRunner`. Default behariour is to have actual runner logic in
|
||||
various `ShellRunner` implementations where candidate will be picked up.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
This is a breaking change in `2.1.x` as previous shell versions had an confusing
|
||||
logic how `ApplicationRunner` instances were used. These changes were made
|
||||
to have a better support for interactive and non-interactive modes in a same
|
||||
shell application as it's convenient to fully work on command-line and still
|
||||
have ability to enter interactive mode.
|
||||
|
||||
You can override bean type of `ShellApplicationRunner` if there's a need to
|
||||
customise shell running logic.
|
||||
====
|
||||
|
||||
==== Customizing Arguments Conversion
|
||||
|
||||
Conversion from text input to actual method arguments uses the standard Spring
|
||||
https://docs.spring.io/spring/docs/4.3.11.RELEASE/spring-framework-reference/htmlsingle/#core-convert[conversion] mechanism.
|
||||
Spring Shell installs a new `DefaultConversionService` (with built-in converters enabled)
|
||||
and registers to it any bean of type `Converter<S, T>`, `GenericConverter`, or
|
||||
`ConverterFactory<S, T>` that it finds in the application context.
|
||||
|
||||
This means that you can customize conversion to your custom objects
|
||||
by installing a `Converter<String, Foo>` bean in the context:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellComponent
|
||||
class ConversionCommands {
|
||||
|
||||
@ShellMethod("Shows conversion using Spring converter")
|
||||
public String conversionExample(DomainObject object) {
|
||||
return object.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DomainObject {
|
||||
private final String value;
|
||||
|
||||
DomainObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
class CustomDomainConverter implements Converter<String, DomainObject> {
|
||||
|
||||
@Override
|
||||
public DomainObject convert(String source) {
|
||||
return new DomainObject(source);
|
||||
}
|
||||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
.Mind your String representation
|
||||
=====
|
||||
As in the preceding example, you should have
|
||||
your `toString()` implementations return the converse of what was used
|
||||
to create the object instance. This is because, when a value fails
|
||||
validation, Spring Shell prints:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
The following constraints were not met:
|
||||
--arg <type> : <message> (You passed '<value.toString()>')
|
||||
----
|
||||
====
|
||||
|
||||
See <<validating-command-arguments>> for more information.
|
||||
=====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you want to customize the `ConversionService` further, you can:
|
||||
|
||||
* Have the default one injected in your code and act upon it in some way.
|
||||
* Override it altogether with your own (custom converters need to be registered by hand).
|
||||
The `ConversionService` used by Spring Shell needs to be https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/htmlsingle/#beans-autowired-annotation-qualifiers[qualified] as `"spring-shell"`.
|
||||
====
|
||||
@@ -0,0 +1,16 @@
|
||||
=== Interaction Mode
|
||||
|
||||
Starting from _2.1.x_ a build-in support has been added to distinguish between interactive
|
||||
and non-interactive modes. This has been added so that it's easier to use shell as a
|
||||
simple command-line tool without requiring customisation to accomplish that.
|
||||
|
||||
Currently interactive mode is entered if any command line options are passed when starting
|
||||
or running a shell from a command-line. This especially works well when shell application
|
||||
is compiled with <<native>>.
|
||||
|
||||
Some commands may not have any usefull meaning if running on interactive mode
|
||||
or vice versa on non-interactive mode. For example a build-in `exit` command
|
||||
have no meaning in non-interactive mode as it's used to exit interactive mode.
|
||||
|
||||
Annotation `@ShellMethod` has a field `interactionMode` which can be used to instruct
|
||||
shell when particular command is available.
|
||||
@@ -0,0 +1,359 @@
|
||||
=== Invoking your Commands
|
||||
|
||||
This section addresses how you can control the way in which your commands are invoked.
|
||||
|
||||
==== By Name Versus Positional Parameters
|
||||
|
||||
As seen <<documenting-the-command,earlier>>, decorating a method with `@ShellMethod` is the sole requirement for creating a command.
|
||||
|
||||
The user can set the value of all the method parameters in either of two ways:
|
||||
|
||||
* By using a parameter key (for example, `--arg value`). This approach is called "`by name parameters.`"
|
||||
* Without a key, by setting parameter values in the order in which they appear in the method signature (called "`positional parameters`").
|
||||
|
||||
These two approaches can be mixed and matched, with named parameters always taking precedence (as they are less
|
||||
prone to ambiguity). Consider the following command definition:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Display stuff.")
|
||||
public String echo(int a, int b, int c) {
|
||||
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Given the preceding command definiton, the following invocations are all equivalent, as shown in the output:
|
||||
|
||||
====
|
||||
[source, bash]
|
||||
----
|
||||
shell:>echo 1 2 3 <1>
|
||||
You said a=1, b=2, c=3
|
||||
|
||||
shell:>echo --a 1 --b 2 --c 3 <2>
|
||||
You said a=1, b=2, c=3
|
||||
|
||||
shell:>echo --b 2 --c 3 --a 1 <3>
|
||||
You said a=1, b=2, c=3
|
||||
|
||||
shell:>echo --a 1 2 3 <4>
|
||||
You said a=1, b=2, c=3
|
||||
|
||||
shell:>echo 1 --c 3 2 <5>
|
||||
You said a=1, b=2, c=3
|
||||
----
|
||||
<1> This uses positional parameters.
|
||||
<2> This is an example of full by-name parameters.
|
||||
<3> By-name parameters can be reordered as desired.
|
||||
<4> You can use a mix of the two approaches.
|
||||
<5> The non by-name parameters are resolved in the order in which they appear.
|
||||
====
|
||||
|
||||
===== Customizing the Named Parameter Keys
|
||||
|
||||
As seen <<your-first-command,earlier>>, the default strategy for deriving the key for a named parameter is to use the Java
|
||||
name of the method signature and prefix it with two dashes (`--`). You can customize this in two ways:
|
||||
|
||||
* Use the `prefix()` attribute of the `@ShellMethod` annotation to change the default prefix for the whole method.
|
||||
* Annotate the parameter with the `@ShellOption` annotation to override the entire key in a per-parameter fashion.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod(value = "Display stuff.", prefix="-")
|
||||
public String echo(int a, int b, @ShellOption("--third") int c) {
|
||||
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For such a setup, the possible parameter keys are `-a`, `-b` and `--third`.
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
You can specify several keys for a single parameter. If you do so, these keys are mutually exclusive (only one of them can be used) ways
|
||||
to specify the same parameter. The following example shows the signature of the
|
||||
built-in <<help-command,`help`>> command:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Describe a command.")
|
||||
public String help(@ShellOption({"-C", "--command"}) String command) {
|
||||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
[[optional-parameters-default-values]]
|
||||
==== Optional Parameters and Default Values
|
||||
|
||||
Spring Shell provides the ability to give parameters default values, which lets users omit
|
||||
those parameters. Consider the following command definition:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Say hello.")
|
||||
public String greet(@ShellOption(defaultValue="World") String who) {
|
||||
return "Hello " + who;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
With the preceding definition, the `greet` command can still be invoked as `greet Mother` (or `greet --who Mother`), but the following
|
||||
is also possible:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>greet
|
||||
Hello World
|
||||
----
|
||||
====
|
||||
|
||||
==== Parameter Arity
|
||||
Up to now, it has always been assumed that each parameter maps to a single word entered by the user.
|
||||
Situations may arise, though, when a parameter value should be multi-valued. This is driven by the `arity()`
|
||||
attribute of the `@ShellOption` annotation. You can use a collection or array for the parameter type and specify how
|
||||
many values are expected:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Add Numbers.")
|
||||
public float add(@ShellOption(arity=3) float[] numbers) {
|
||||
return numbers[0] + numbers[1] + numbers[2];
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The users can then invoke the command by using any of the following syntax:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>add 1 2 3.3
|
||||
6.3
|
||||
shell:>add --numbers 1 2 3.3
|
||||
6.3
|
||||
----
|
||||
====
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
When using the _by-name_ parameter approach, the key should *not* be repeated. The following does *not* work:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>add --numbers 1 --numbers 2 --numbers 3.3
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
===== Varying Amount Arity
|
||||
|
||||
The above example demonstrates requiring a known, constant arity for a parameter, three in this case. Allowing any number of multiple values of a parameter can be achieved by leaving `arity` unspecified and using Spring's built-in comma separated value parsing for collections and/or arrays:
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Add a Varying Amount of Numbers.")
|
||||
public double add(@ShellOption double[] numbers) {
|
||||
return Arrays.stream(numbers).sum();
|
||||
}
|
||||
----
|
||||
|
||||
The command may then be invoked with any amount of `numbers`:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>add 1,2,3.3
|
||||
6.3
|
||||
shell:>add --numbers 42
|
||||
42.0
|
||||
shell:>add --numbers 1,2,3.3,4,5
|
||||
15.3
|
||||
----
|
||||
====
|
||||
|
||||
===== Special Handling of Boolean Parameters
|
||||
|
||||
When it comes to parameter arity, one kind of parameter receives a special treatment by default, as
|
||||
is often the case in command-line utilities.
|
||||
Boolean (that is, `boolean` as well as `java.lang.Boolean`) parameters behave like they have an `arity()` of `0` by default, allowing users to set their values by using a "`flag`" approach.
|
||||
Consider the following command definition:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Terminate the system.")
|
||||
public String shutdown(boolean force) {
|
||||
return "You said " + force;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This preceding command definition allows the following invocations:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>shutdown
|
||||
You said false
|
||||
shell:>shutdown --force
|
||||
You said true
|
||||
----
|
||||
====
|
||||
|
||||
TIP: This special treatment plays well with the <<optional-parameters-default-values,default value>> specification. Although the default
|
||||
for boolean parameters is to have their default value be `false`, you can specify otherwise (that is,
|
||||
`@ShellOption(defaultValue="true")`), and the behavior is inverted (that is, not specifying the parameter
|
||||
results in the value being `true`, and specifying the flag results in the value being `false`)
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Having this behavior of implicit `arity()=0` prevents the user from specifying a value (for example, `shutdown --force true`).
|
||||
If you would like to allow this behavior (and forego the flag approach), then force an arity of `1` by using the annotation as follows:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Terminate the system.")
|
||||
public String shutdown(@ShellOption(arity=1, defaultValue="false") boolean force) {
|
||||
return "You said " + force;
|
||||
}
|
||||
----
|
||||
====
|
||||
=====
|
||||
|
||||
[[quotes-handling]]
|
||||
==== Quotes Handling
|
||||
|
||||
Spring Shell takes user input and tokenizes it into words, splitting on space characters.
|
||||
If the user wants to provide a parameter value that contains spaces, that value needs to be quoted.
|
||||
Both single (`'`) and double (`"`) quotes are supported, and those quotes are not part of the value:
|
||||
Consider the following command definition:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Prints what has been entered.")
|
||||
public String echo(String what) {
|
||||
return "You said " + what;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following commands all invoke the preceding command definition:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
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
|
||||
----
|
||||
====
|
||||
|
||||
Supporting both single and double quotes lets the user embed one type of quotes into
|
||||
a value:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>echo "I'm here!"
|
||||
You said I'm here!
|
||||
shell:>echo 'He said "Hi!"'
|
||||
You said He said "Hi!"
|
||||
----
|
||||
====
|
||||
|
||||
That way, the user can use a single quote as an apostrophe in a message.
|
||||
|
||||
Should the user need to embed the same kind of quote that was used to quote the whole parameter,
|
||||
the escape sequence uses the backslash (`\`) character:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>echo 'I\'m here!'
|
||||
You said I'm here!
|
||||
shell:>echo "He said \"Hi!\""
|
||||
You said He said "Hi!"
|
||||
shell:>echo I\'m here!
|
||||
You said I'm here!
|
||||
----
|
||||
====
|
||||
|
||||
It is also possible to escape space characters when not using enclosing quotes:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>echo This\ is\ a\ single\ value
|
||||
You said This is a single value
|
||||
----
|
||||
====
|
||||
|
||||
[[interacting-with-the-shell]]
|
||||
==== Interacting with the Shell
|
||||
|
||||
The Spring Shell project builds on top of the https://github.com/jline/jline3[JLine] library and, as a result, brings
|
||||
a lot of nice interactive features, some of which are detailed in this section.
|
||||
|
||||
First and foremost, Spring Shell supports tab completion almost everywhere possible. So, if there
|
||||
is an `echo` command and the user types `ec` and presses `TAB`, `echo` appears.
|
||||
Should there be several commands that start with `ec`, then the user is prompted to choose (using `TAB` or
|
||||
`Shift + TAB` to navigate and `ENTER` to select.)
|
||||
|
||||
But completion does not stop at command keys. It also works for parameter keys (`--arg`) and even
|
||||
parameter values, if the application developer registered the appropriate beans (see <<providing-tab-completion>>).
|
||||
|
||||
Another nice feature of Spring Shell applications is support for line continuation. If a command and its parameters
|
||||
is too long and does not fit nicely on the screen, a user can chunk it by ending a line with a backslash
|
||||
(`\`) character, pressing `ENTER`, and continuing on the next line. Upon submission of the whole command, this is
|
||||
parsed as if the user entered a single space on line breaks. The following listing shows an example of this behavior:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>register module --type source --name foo \ <1>
|
||||
> --uri file:///tmp/bar
|
||||
Successfully registered module 'source:foo'
|
||||
----
|
||||
<1> command continues on next line
|
||||
====
|
||||
|
||||
Line continuation also automatically triggers if the user has opened a quote (see <<quotes-handling>>)
|
||||
and presses `ENTER` while still in the quotes:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
shell:>echo "Hello <1>
|
||||
dquote> World"
|
||||
You said Hello World
|
||||
----
|
||||
<1> The user pressed `ENTER` here.
|
||||
====
|
||||
|
||||
Finally, Spring Shell applications benefit from a lot of keyboard shortcuts (borrowed from Emacs) with which you may
|
||||
already be familiar from working with your regular OS Shell. Notable shortcuts include `Ctrl+r` to perform
|
||||
a reverse search, `Ctrl+a`] and `Ctrl+e` to move to the beginning and the end of the current line (respectively),
|
||||
and `Esc f` and `Esc b` to move forward or backward (respectively) one word at a time.
|
||||
|
||||
[[providing-tab-completion]]
|
||||
// ===== Providing TAB Completion Proposals
|
||||
|
||||
// TBD
|
||||
31
spring-shell-docs/src/main/asciidoc/using-shell-native.adoc
Normal file
31
spring-shell-docs/src/main/asciidoc/using-shell-native.adoc
Normal file
@@ -0,0 +1,31 @@
|
||||
[[native]]
|
||||
=== Native Support
|
||||
|
||||
Re-work with _2.1.x_ brings in an experimental support for compiling shell application
|
||||
into _native_ application with _GraalVM_ and _spring-native_. As underlying _jline_
|
||||
library works with _GraalVM_ most of a things should just work.
|
||||
|
||||
Project can be compiled with native profile to get sample compiled as an native
|
||||
application:
|
||||
|
||||
====
|
||||
----
|
||||
$ ./mvnw clean package -Pnative
|
||||
----
|
||||
====
|
||||
|
||||
You can then run sample either with interactive or non-interactive mode:
|
||||
|
||||
====
|
||||
----
|
||||
$ ./spring-shell-samples/target/spring-shell-samples help
|
||||
AVAILABLE COMMANDS
|
||||
|
||||
Built-In Commands
|
||||
completion bash: Generate bash completion script
|
||||
help: Display help about available commands.
|
||||
history: Display or save the history of previously run commands
|
||||
script: Read and execute commands from a file.
|
||||
...
|
||||
----
|
||||
====
|
||||
@@ -0,0 +1,54 @@
|
||||
[[organizing-commands]]
|
||||
=== Organizing Commands
|
||||
|
||||
When your shell starts to provide a lot of functionality, you may end up
|
||||
with a lot of commands, which could be confusing for your users. By typing `help`,
|
||||
they would see a daunting list of commands, organized in alphabetical order,
|
||||
which may not always make sense.
|
||||
|
||||
To alleviate this possible confusion, Spring Shell provides the ability to group commands together,
|
||||
with reasonable defaults. Related commands would then end up in the same group (for example, `User Management Commands`)
|
||||
and be displayed together in the help screen and other places.
|
||||
|
||||
By default, commands are grouped according to the class they are implemented in,
|
||||
turning the camel case class name into separate words (so `URLRelatedCommands` becomes `URL Related Commands`).
|
||||
This is a very sensible default, as related commands are often already in the class anyway,
|
||||
because they need to use the same collaborating objects.
|
||||
|
||||
If, however, this behavior does not suit you, you can override the group for a
|
||||
command in the following ways, in order of priority:
|
||||
|
||||
. Specifying a `group()` in the `@ShellMethod` annotation.
|
||||
. Placing a `@ShellCommandGroup` on the class in which the command is defined. This applies
|
||||
the group for all commands defined in that class (unless overridden, as explained earlier).
|
||||
. Placing a `@ShellCommandGroup` on the package (through `package-info.java`)
|
||||
in which the command is defined. This applies to all the commands defined in the
|
||||
package (unless overridden at the method or class level, as explained earlier)
|
||||
|
||||
The following listing shows an example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class UserCommands {
|
||||
@ShellMethod(value = "This command ends up in the 'User Commands' group")
|
||||
public void foo() {}
|
||||
|
||||
@ShellMethod(value = "This command ends up in the 'Other Commands' group",
|
||||
group = "Other Commands")
|
||||
public void bar() {}
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
@ShellCommandGroup("Other Commands")
|
||||
public class SomeCommands {
|
||||
@ShellMethod(value = "This one is in 'Other Commands'")
|
||||
public void wizz() {}
|
||||
|
||||
@ShellMethod(value = "And this one is 'Yet Another Group'",
|
||||
group = "Yet Another Group")
|
||||
public void last() {}
|
||||
}
|
||||
----
|
||||
====
|
||||
49
spring-shell-docs/src/main/asciidoc/using-shell-styling.adoc
Normal file
49
spring-shell-docs/src/main/asciidoc/using-shell-styling.adoc
Normal file
@@ -0,0 +1,49 @@
|
||||
[[styling]]
|
||||
=== Styling
|
||||
|
||||
Starting with _2.1.x_ there is a support for centrally handling styling and theming.
|
||||
There is a default theme named _default_ which can be changed using property
|
||||
`spring.shell.theme.name`.
|
||||
|
||||
To create a new theme register new `Theme` bean with custom `ThemeSettings` where
|
||||
you can tweak styles.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Configuration
|
||||
static class CustomThemeConfig {
|
||||
|
||||
@Bean
|
||||
public Theme myTheme() {
|
||||
return new Theme() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "mytheme";
|
||||
}
|
||||
@Override
|
||||
public ThemeSettings getSettings() {
|
||||
return new MyThemeSettings();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class MyThemeSettings extends ThemeSettings {
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`ThemeResolver` can be used to resolve styles if you want to create
|
||||
_jline_ styled strings programmatically.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@Autowired
|
||||
private ThemeResolver themeResolver;
|
||||
|
||||
String resolvedStyle = themeResolver.resolveTag(TAG_TITLE);
|
||||
AttributedStyle style = themeResolver.resolveStyle(resolvedStyle);
|
||||
----
|
||||
====
|
||||
@@ -0,0 +1,368 @@
|
||||
[[uicomponents]]
|
||||
=== UI Components
|
||||
|
||||
Starting from _2.1.x_ there is a new component model which provides
|
||||
easier way to create higher level user interaction for usual use cases
|
||||
like asking input in a various forms. These usually are just plain text
|
||||
input or choosing something from a list.
|
||||
|
||||
Templates for build-in components are in classpath under
|
||||
_org/springframework/shell/component_.
|
||||
|
||||
Build-in components generally follow logic:
|
||||
|
||||
* Enter run loop for user input
|
||||
* Generate component related context
|
||||
* Render runtime status of a component state
|
||||
* Exit
|
||||
* Render final status of a component state
|
||||
|
||||
==== Component Render
|
||||
|
||||
There are two ways to implement component rendering, firstly fully
|
||||
programmatically or secondly using a _ANTLR Stringtemplate_. Though
|
||||
strictly speaking there is just a simple `Function` renderer interface
|
||||
which takes `Context` as an input and outputs a list of `AttributedString`
|
||||
but this allows to choose between _templating_ and _code_.
|
||||
|
||||
Templating is a good choice if you don't need to anything complex or
|
||||
you just want to slightly modify existing component layouts. Rendering
|
||||
via code then gives you flexibility to do whatever you need.
|
||||
|
||||
Programmatic way to render is simple as to create a `Function`:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
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());
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
And then hook it with a component:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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.setMaskCharater('*');
|
||||
}
|
||||
StringInputContext context = component.run(StringInputContext.empty());
|
||||
return "Got value " + context.getResultValue();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Component have their own context but usually shares some functionality
|
||||
from a parent component types, those context variables are shown below.
|
||||
|
||||
[[textcomponentcontext-template-variables]]
|
||||
.TextComponentContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|resultValue
|
||||
|Value after component renders its result.
|
||||
|
||||
|name
|
||||
|Name of a component, aka its title.
|
||||
|
||||
|message
|
||||
|Possible message set for component.
|
||||
|
||||
|messageLevel
|
||||
|Level of a message, either INFO, WARN or ERROR
|
||||
|
||||
|hasMessageLevelInfo
|
||||
|Return true if level is INFO, false otherwise.
|
||||
|
||||
|hasMessageLevelWarn
|
||||
|Return true if level is WARN, false otherwise.
|
||||
|
||||
|hasMessageLevelError
|
||||
|Return true if level is ERROR, false otherwise.
|
||||
|
||||
|input
|
||||
|Raw user input.
|
||||
|
||||
|===
|
||||
|
||||
|
||||
[[selectorcomponentcontext-template-variables]]
|
||||
.SelectorComponentContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|name
|
||||
|Name of a component, aka title.
|
||||
|
||||
|input
|
||||
|Raw user input, mostly for filter.
|
||||
|
||||
|itemStates
|
||||
|Full list of item states.
|
||||
|
||||
|itemStateView
|
||||
|Visible list of item states.
|
||||
|
||||
|isResult
|
||||
|Return if context is in a result mode.
|
||||
|
||||
|cursorRow
|
||||
|Current cursor row in a selector
|
||||
|
||||
|===
|
||||
|
||||
|
||||
==== Build-in Components
|
||||
|
||||
===== String Input
|
||||
|
||||
Used to ask a simple text input from a user, optionally masking values
|
||||
if content contains something sensitive.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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.setMaskCharater('*');
|
||||
}
|
||||
StringInputContext context = component.run(StringInputContext.empty());
|
||||
return "Got value " + context.getResultValue();
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
image::images/component-text-input-1.svg[text input]
|
||||
|
||||
Context object is `StringInputContext`.
|
||||
|
||||
[[stringinputcontext-template-variables]]
|
||||
.StringInputContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|defaultValue
|
||||
|Default value if set, null otherwise.
|
||||
|
||||
|maskedInput
|
||||
|Masked input value
|
||||
|
||||
|maskedResultValue
|
||||
|Masked result value
|
||||
|
||||
|maskCharacter
|
||||
|Mask character if set, null otherwise.
|
||||
|
||||
|hasMaskCharacter
|
||||
|Is true if mask character is set, false otherwise.
|
||||
|
||||
|model
|
||||
|Parent context variables <<textcomponentcontext-template-variables>>
|
||||
|===
|
||||
|
||||
===== Path Input
|
||||
|
||||
Used to ask a `Path` from a user and gives additional info about a path itself.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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();
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
image::images/component-path-input-1.svg[text input]
|
||||
|
||||
Context object is `PathInputContext`.
|
||||
|
||||
[[pathinputcontext-template-variables]]
|
||||
.PathInputContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|model
|
||||
|Parent context variables <<textcomponentcontext-template-variables>>
|
||||
|===
|
||||
|
||||
===== Confirmation
|
||||
|
||||
Used to ask a simple confirmation from a user and essentially is
|
||||
yes/no question.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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();
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
image::images/component-confirmation-1.svg[text input]
|
||||
|
||||
Context object is `ConfirmationInputContext`.
|
||||
|
||||
[[confirmationinputcontext-template-variables]]
|
||||
.ConfirmationInputContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|defaultValue
|
||||
|Default value, either true or false.
|
||||
|
||||
|model
|
||||
|Parent context variables <<textcomponentcontext-template-variables>>
|
||||
|===
|
||||
|
||||
===== Single Select
|
||||
|
||||
Used to ask an item from a list and is essentially similar to simple
|
||||
dropbox implementation.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellComponent
|
||||
public class ComponentCommands extends AbstractShellComponent {
|
||||
|
||||
@ShellMethod(key = "component single", value = "Single selector", group = "Components")
|
||||
public String singleSelector() {
|
||||
List<SelectorItem<String>> items = new ArrayList<>();
|
||||
items.add(SelectorItem.of("key1", "value1"));
|
||||
items.add(SelectorItem.of("key2", "value2"));
|
||||
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;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
image::images/component-single-select-1.svg[text input]
|
||||
|
||||
Context object is `SingleItemSelectorContext`.
|
||||
|
||||
[[singleitemselectorcontext-template-variables]]
|
||||
.SingleItemSelectorContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|value
|
||||
|Returned value when component exists.
|
||||
|
||||
|rows
|
||||
|Visible items where rows list contains maps of name and selected items.
|
||||
|
||||
|model
|
||||
|Parent context variables <<selectorcomponentcontext-template-variables>>
|
||||
|===
|
||||
|
||||
===== Multi Select
|
||||
|
||||
Used to ask an items from a list.
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
image::images/component-multi-select-1.svg[text input]
|
||||
|
||||
Context object is `MultiItemSelectorContext`.
|
||||
|
||||
[[multiitemselectorcontext-template-variables]]
|
||||
.MultiItemSelectorContext Template Variables
|
||||
|===
|
||||
|Key |Description
|
||||
|
||||
|values
|
||||
|Returned values when component exists.
|
||||
|
||||
|rows
|
||||
|Visible items where rows list contains maps of name, selected, onrow and enabled items.
|
||||
|
||||
|model
|
||||
|Parent context variables <<selectorcomponentcontext-template-variables>>
|
||||
|===
|
||||
@@ -0,0 +1,33 @@
|
||||
[[validating-command-arguments]]
|
||||
=== Validating Command Arguments
|
||||
|
||||
Spring Shell integrates with the https://beanvalidation.org/[Bean Validation API] to support
|
||||
automatic and self-documenting constraints on command parameters.
|
||||
|
||||
Annotations found on command parameters as well as annotations at the method level are
|
||||
honored and trigger validation prior to the command executing. Consider the following command:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod("Change password.")
|
||||
public String changePassword(@Size(min = 8, max = 40) String password) {
|
||||
return "Password successfully set to " + password;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
From the preceding example, you get the following behavior for free:
|
||||
|
||||
====
|
||||
----
|
||||
shell:>change-password hello
|
||||
The following constraints were not met:
|
||||
--password string : size must be between 8 and 40 (You passed 'hello')
|
||||
----
|
||||
====
|
||||
|
||||
.Applies to All Command Implementations
|
||||
NOTE: It is important to note that bean validation applies to all command implementations, whether
|
||||
they use the "standard" API or any other API, through the use of an adapter
|
||||
(see <<support-for-shell-1-and-jcommander,Supporting Other APIs>>)
|
||||
@@ -0,0 +1,55 @@
|
||||
=== Writing Your Own Commands
|
||||
|
||||
The way Spring Shell decides to turn a method into an actual shell command is entirely pluggable
|
||||
(see <<extending-spring-shell>>). However, as of Spring Shell 2.x, the recommended way to write commands
|
||||
is to use the new API described in this section (the standard API).
|
||||
|
||||
When you use the standard API, methods on beans are turned into executable commands, provided that:
|
||||
|
||||
* The bean class bears the `@ShellComponent` annotation. This is used to restrict the set of beans that
|
||||
are considered.
|
||||
* The method bears the `@ShellMethod` annotation.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The `@ShellComponent` is a stereotype annotation that is itself meta-annotated with `@Component`. As a result,
|
||||
you can used it in addition to the filtering mechanism to declare beans (for example, by using `@ComponentScan`).
|
||||
|
||||
You can customize the name of the created bean by using the `value` attribute of the annotation.
|
||||
====
|
||||
|
||||
[[documenting-the-command]]
|
||||
==== Documenting the Command
|
||||
|
||||
The only required attribute of the `@ShellMethod` annotation is its `value` attribute, which should have
|
||||
a short, one-sentence, description of what the command does. This lets your users
|
||||
get consistent help about your commands without having to leave the shell (see <<help-command>>).
|
||||
|
||||
NOTE: The description of your command should be short -- no more than one or two sentences. For better
|
||||
consistency, it should starts with a capital letter and end with a period.
|
||||
|
||||
==== Customizing the Command Name(s)
|
||||
|
||||
By default, there is no need to specify the key for your command (that is, the word(s) that should be used
|
||||
to invoke it in the shell). The name of the method is used as the command key, turning camelCase names into
|
||||
dashed, gnu-style, names (that is, `sayHello()` becomes `say-hello`).
|
||||
|
||||
You can, however, explicitly set the command key, by using the `key` attribute of the annotation:
|
||||
|
||||
====
|
||||
[source, java]
|
||||
----
|
||||
@ShellMethod(value = "Add numbers.", key = "sum")
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: The `key` attribute accepts multiple values.
|
||||
If you set multiple keys for a single method, the command is registered with those different aliases.
|
||||
|
||||
TIP: The command key can contain pretty much any character, including spaces. When coming up with names though,
|
||||
keep in mind that consistency is often appreciated by users (that is, you should avoid mixing dashed-names with
|
||||
spaced names and other inconsistencies).
|
||||
33
spring-shell-docs/src/main/asciidoc/using-shell.adoc
Normal file
33
spring-shell-docs/src/main/asciidoc/using-shell.adoc
Normal file
@@ -0,0 +1,33 @@
|
||||
== Using Spring Shell
|
||||
This section describes how to use Spring Shell.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
_Spring Shell 2.1.x_ is a major rework to bring codebase up-to-date with
|
||||
existing _Spring Boot_ versions, adding new features and especially
|
||||
making it work with _GraalVM_ which makes command-line applications much
|
||||
more relevant on a java space. Moving to new major version also allows
|
||||
us to clean up codebase and make some needed breaking changes.
|
||||
====
|
||||
|
||||
include::using-shell-write-command.adoc[]
|
||||
|
||||
include::using-shell-invoke-command.adoc[]
|
||||
|
||||
include::using-shell-validate-command.adoc[]
|
||||
|
||||
include::using-shell-availability.adoc[]
|
||||
|
||||
include::using-shell-organize.adoc[]
|
||||
|
||||
include::using-shell-built-in.adoc[]
|
||||
|
||||
include::using-shell-interaction-mode.adoc[]
|
||||
|
||||
include::using-shell-native.adoc[]
|
||||
|
||||
include::using-shell-styling.adoc[]
|
||||
|
||||
include::using-shell-ui-components.adoc[]
|
||||
|
||||
include::using-shell-customize.adoc[]
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user