Update docs

This commit is contained in:
Janne Valkealahti
2022-05-14 16:36:53 +01:00
parent 3599182536
commit ab476345fa
11 changed files with 294 additions and 232 deletions

View File

@@ -12,3 +12,17 @@ Essentially few things needs to happen before you have a working _spring shell_
You will get a full working _spring shell_ application without defining any user level commands
as some basic build-in commands are provided out of a box like `help` and `history`.
[NOTE]
====
Throughout this documentation we make a references to configuring something using
annotations which mostly relates to use of `@ShellMethod` and `@ShellOption` and
programmatic way which relates to use of `CommandRegistration`.
Programmatic model is how things are actually registered even if you use annotations.
Annotations `@ShellMethod` and `@ShellOption` are considered as legacy feature
which we don't yet want to remove. `CommandRegistration` is a new development
model where new features are added. We are most likely going to replace existing
annotations with something better order to support new features in a
`CommandRegistration` model.
====

View File

@@ -1,228 +0,0 @@
==== 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"`.
====

View File

@@ -0,0 +1,44 @@
[[using-shell-options-arity]]
==== Arity
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Sometimes you want to have more fine control of how many parameters with an option
are processed when parsing operation happens. Arity is defined as min and max
values where min must be positive integer and max has to be more or equal to min.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-arityints]
----
====
Arity can also be defined as an `OptionArity` enum which are shortcuts
with table shown below.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-arityenum]
----
====
.OptionArity
|===
|Value |min/max
|ZERO
|0 / 0
|ZERO_OR_ONE
|0 / 1
|EXACTLY_ONE
|1 / 1
|ZERO_OR_MORE
| 0 / Integer MAX
|ONE_OR_MORE
|1 / Integer MAX
|===

View File

@@ -0,0 +1,15 @@
[[using-shell-options-default]]
==== Default Value
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Having a default value for an option is somewhat related to
<<using-shell-options-optional>> as there are cases where you
may want to know if user defined an option and make a difference
based on a default value.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-default]
----
====

View File

@@ -0,0 +1,35 @@
[[using-shell-options-definition]]
==== Definition
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Options can be defined within a target method as annotations in a method arguments
or with programmatically with `CommandRegistration`.
Having a target method with argument is automatically registered with a matching
argument name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-without-annotation]
----
====
`@ShellOption` annotation can be used to define an option name if you
don't want it to be same as argument name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-with-annotation]
----
====
Programmatic way with `CommandRegistration` is to use method adding a long name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-longarg]
----
====

View File

@@ -0,0 +1,13 @@
[[using-shell-options-optional]]
==== Optional Value
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Option is either required or not and it generally speaking depends on
a command target how this behaves.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-optional]
----
====

View File

@@ -0,0 +1,12 @@
[[using-shell-options-positional]]
==== Positional
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Positional information is mostly related to a command target method.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-positional]
----
====

View File

@@ -0,0 +1,27 @@
[[using-shell-options-short]]
==== Short Format
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Short style _POSIX_ option in most is just a synonym to long format but
adds additional feature to combine those options together. Having short
options _a_, _b_, _c_ can be used as `-abc`.
Programmatically short option is defined by using short name function.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-shortarg]
----
====
Short option with combined format is powerful if type is defined as a flag
which means type is a _boolean_. That way you can define a presense of a flags
as `-abc`, `-abc true` or `-abc false`.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-shortargbooleans]
----
====

View File

@@ -1,5 +1,5 @@
[[validating-command-arguments]]
==== Validating Command Arguments
==== Validation
Spring Shell integrates with the https://beanvalidation.org/[Bean Validation API] to support
automatic and self-documenting constraints on command parameters.
@@ -27,7 +27,10 @@ The following constraints were not met:
----
====
[NOTE]
.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
====
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>>)
====

View File

@@ -1,6 +1,20 @@
[[using-shell-options]]
=== Options
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
include::using-shell-options-generic.adoc[]
Command line arguments can be separated into options and positional parameters.
Following sections describes features how options are defined and used.
include::using-shell-options-definition.adoc[]
include::using-shell-options-short.adoc[]
include::using-shell-options-arity.adoc[]
include::using-shell-options-positional.adoc[]
include::using-shell-options-optional.adoc[]
include::using-shell-options-default.adoc[]
include::using-shell-options-validation.adoc[]

View File

@@ -0,0 +1,113 @@
package org.springframework.shell.docs;
import org.springframework.shell.command.CommandRegistration;
import org.springframework.shell.command.CommandRegistration.OptionArity;
import org.springframework.shell.standard.ShellOption;
public class OptionSnippets {
class Dump1 {
// tag::option-with-annotation[]
public String example(@ShellOption(value = { "argx" }) String arg1) {
return "Hello " + arg1;
}
// end::option-with-annotation[]
}
class Dump2 {
// tag::option-without-annotation[]
public String example(String arg1) {
return "Hello " + arg1;
}
// end::option-without-annotation[]
}
public void dump1() {
// tag::option-registration-longarg[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.and()
.build();
// end::option-registration-longarg[]
// tag::option-registration-shortarg[]
CommandRegistration.builder()
.withOption()
.shortNames('a')
.and()
.withOption()
.shortNames('b')
.and()
.withOption()
.shortNames('c')
.and()
.build();
// end::option-registration-shortarg[]
// tag::option-registration-shortargbooleans[]
CommandRegistration.builder()
.withOption()
.shortNames('a')
.type(boolean.class)
.and()
.withOption()
.shortNames('b')
.type(boolean.class)
.and()
.withOption()
.shortNames('c')
.type(boolean.class)
.and()
.build();
// end::option-registration-shortargbooleans[]
// tag::option-registration-arityenum[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.arity(OptionArity.EXACTLY_ONE)
.and()
.build();
// end::option-registration-arityenum[]
// tag::option-registration-arityints[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.arity(0, 1)
.and()
.build();
// end::option-registration-arityints[]
// tag::option-registration-optional[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.required()
.and()
.build();
// end::option-registration-optional[]
// tag::option-registration-positional[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.position(0)
.and()
.build();
// end::option-registration-positional[]
// tag::option-registration-default[]
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.defaultValue("defaultValue")
.and()
.build();
// end::option-registration-default[]
}
}