diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-basics.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-basics.adoc index 4a40e656..e1971405 100644 --- a/spring-shell-docs/src/main/asciidoc/using-shell-basics.adoc +++ b/spring-shell-docs/src/main/asciidoc/using-shell-basics.adoc @@ -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. +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-customization-generic.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-customization-generic.adoc deleted file mode 100644 index 92529884..00000000 --- a/spring-shell-docs/src/main/asciidoc/using-shell-customization-generic.adoc +++ /dev/null @@ -1,228 +0,0 @@ -==== Customizing the Shell - -[[overriding-or-disabling-built-in-commands]] -===== Overriding or Disabling Built-In Commands - -Spring Shell provides <> 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+] ----- - - org.springframework.shell - {starter-artifactId} - {project-version} - - - org.springframework.shell - spring-shell-standard-commands - - - ----- -==== -===== - -[[disabling-specific-commands]] -====== Disabling Specific Commands - -To disable a single built-in command, set the `spring.shell.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 <> and have your implementation registered -with the same name. -* Have your implementing class implement the `.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`, `GenericConverter`, or -`ConverterFactory` that it finds in the application context. - -This means that you can customize conversion to your custom objects -by installing a `Converter` 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 { - - @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 : (You passed '') ----- -==== - -See <> 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"`. -==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-arity.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-arity.adoc new file mode 100644 index 00000000..a5f0012b --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-arity.adoc @@ -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 +|=== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-default.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-default.adoc new file mode 100644 index 00000000..8ae745cb --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-default.adoc @@ -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 +<> 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] +---- +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-definition.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-definition.adoc new file mode 100644 index 00000000..6e38a23d --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-definition.adoc @@ -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] +---- +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-optional.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-optional.adoc new file mode 100644 index 00000000..32bbf79a --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-optional.adoc @@ -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] +---- +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-positional.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-positional.adoc new file mode 100644 index 00000000..4a612192 --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-positional.adoc @@ -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] +---- +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-short.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-short.adoc new file mode 100644 index 00000000..b6c24f67 --- /dev/null +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-short.adoc @@ -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] +---- +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options-validation.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options-validation.adoc index 08a25707..a535736f 100644 --- a/spring-shell-docs/src/main/asciidoc/using-shell-options-validation.adoc +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options-validation.adoc @@ -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 <>) +==== diff --git a/spring-shell-docs/src/main/asciidoc/using-shell-options.adoc b/spring-shell-docs/src/main/asciidoc/using-shell-options.adoc index f6008783..bf079c54 100644 --- a/spring-shell-docs/src/main/asciidoc/using-shell-options.adoc +++ b/spring-shell-docs/src/main/asciidoc/using-shell-options.adoc @@ -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[] diff --git a/spring-shell-docs/src/test/java/org/springframework/shell/docs/OptionSnippets.java b/spring-shell-docs/src/test/java/org/springframework/shell/docs/OptionSnippets.java new file mode 100644 index 00000000..1d4c516a --- /dev/null +++ b/spring-shell-docs/src/test/java/org/springframework/shell/docs/OptionSnippets.java @@ -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[] + + } + +}