From 7473c9ec5c88fbe29fe88f0916ae8d3483684d36 Mon Sep 17 00:00:00 2001 From: Jay Bryant Date: Sat, 18 Dec 2021 08:12:51 +0000 Subject: [PATCH] Editing pass Edited for spelling, puncuation, grammar, usage, and corporate voice. --- .../src/main/asciidoc/index.adoc | 2 +- .../src/main/asciidoc/using-spring-shell.adoc | 577 +++++++++++------- .../main/asciidoc/what-is-spring-shell.adoc | 15 +- 3 files changed, 364 insertions(+), 230 deletions(-) diff --git a/spring-shell-docs/src/main/asciidoc/index.adoc b/spring-shell-docs/src/main/asciidoc/index.adoc index 41de753b..38cb45bc 100644 --- a/spring-shell-docs/src/main/asciidoc/index.adoc +++ b/spring-shell-docs/src/main/asciidoc/index.adoc @@ -15,7 +15,7 @@ ifdef::backend-pdf[] NOTE: This documentation is also available as https://docs.spring.io/spring-shell/docs/{projectVersion}/reference/htmlsingle/index.html[HTML]. endif::[] -(C) 2017 Pivotal Software, Inc. +(C) 2017 - 2020 Pivotal Software, Inc. _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further diff --git a/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc b/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc index b3ddd1f4..d4de557c 100644 --- a/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc +++ b/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc @@ -2,18 +2,23 @@ == Using Spring Shell -=== Getting Started -To see what Spring Shell has to offer, let's write a trivial shell application that -has a simple command to add two numbers together. +This section describes how to use Spring Shell. + +=== 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 -==== Let's Write a Simple Boot App 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, let's create a simple Boot application, for example -using https://start.spring.io. This minimal application only depends on `spring-boot-starter` -and configures the `spring-boot-maven-plugin`, generating an executable über-jar: +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] ---- ... @@ -23,13 +28,17 @@ and configures the `spring-boot-maven-plugin`, generating an executable über-ja spring-boot-starter ... + ---- +==== ==== 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+] ---- ... @@ -40,24 +49,25 @@ configuring only the necessary beans as needed: ... ---- - -[CAUTION] -==== -Given that Spring Shell will kick in and start the REPL by virtue of this dependency being present, -you'll need to either build skipping tests (`-DskipTests`) throughout this tutorial or remove the sample integration test -that was generated by https://start.spring.io. If you don't do so, the integration test will create -the Spring `ApplicationContext` and, depending on your build tool, will stay stuck in the eval loop or crash with a NPE. ==== -==== Your first command -It's time to add our first command. Create a new class (name it however you want) and +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. + +[[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 +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; @@ -74,9 +84,13 @@ public class MyCommands { } } ---- +==== -==== Let's Give It a Ride! -Build the application and run the generated jar, like so; +==== Trying the Application + +To build the application and run the generated jar, run the following command: + +==== [source, bash] ---- ./mvnw clean install -DskipTests @@ -84,10 +98,12 @@ Build the application and run the generated jar, like so; java -jar target/demo-0.0.1-SNAPSHOT.jar ---- +==== -You'll be greeted by the following screen (the banner comes from Spring Boot, and can be customized -https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-banner[as usual]): +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] ---- @@ -101,58 +117,61 @@ https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-featu shell:> ---- +==== -Below is a yellow `shell:>` prompt that invites you to type commands. Type `add 1 2` then kbd:[ENTER] and admire the magic! +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) and when you're done, type `exit` kbd:[ENTER]. +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. -=== Writing your own Commands +=== Writing Your Own Commands The way Spring Shell decides to turn a method into an actual shell command is entirely pluggable -(see xref:extending-spring-shell[]), but as of Spring Shell 2.x, the recommended way to write commands -is to use the new API described in this section (the so-called _standard_ API). +(see <>). 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). -Using the _standard_ API, methods on beans will be turned into executable commands provided that +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 +* 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. +* The method bears the `@ShellMethod` annotation. [TIP] ==== -The `@ShellComponent` is a stereotype annotation itself meta-annotated with `@Component`. As such, it -can be used in addition to the filtering mechanism to also _declare_ beans (_e.g._ using `@ComponentScan`). +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`). -The name of the created bean can be customized using the `value` attribute of the annotation. +You can customize the name of the created bean by using the `value` attribute of the annotation. ==== -==== It's all about Documentation! +[[documenting-the-command]] +==== Documenting the Command -The only required attribute of the `@ShellMethod` annotation is its `value` attribute, which should be used -to write a short, one-sentence, description of what the command does. This is important so that your users can -get consistent help about your commands without having to leave the shell (see xref:help-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 <>). -[NOTE] -==== -The description of your command should be short, one or two sentences only. For better consistency, it is -recommended that it starts with a capital letter and ends with a dot. -==== +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 (_i.e._ the word(s) that should be used -to invoke it in the shell). The name of the method will be used as the command key, turning camelCase names into -dashed, gnu-style, names (that is, `sayHello()` will become `say-hello`). +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`). -It is possible, however, to explicitly set the command key, using the `key` attribute of the annotation, like so: +You can, however, explicitly set the command key, by using the `key` attribute of the annotation: + +==== [source, java] ---- @ShellMethod(value = "Add numbers.", key = "sum") @@ -161,30 +180,32 @@ It is possible, however, to explicitly set the command key, using the `key` attr } ---- - -[NOTE] -==== -The `key` attribute accepts multiple values. -If you set multiple keys for a single method, then the command will be registered using 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 (_i.e._ avoid mixing dashed-names with spaced names, _etc._) -==== +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). === Invoking your Commands -==== By Name _vs._ Positional Parameters -As seen above, decorating a method with `@ShellMethod` is the sole requirement for creating a command. -When doing so, the user can set the value of all method parameters in two possible ways: -* using a parameter key (_e.g._ `--arg value`). This approach is called "by name" parameters -* or without a key, simply setting parameter values in the same order they appear in the method signature ("positional" parameters). +This section addresses how you can control the way in which your commands are invoked. + +==== By Name Versus Positional Parameters + +As seen <>, 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). As such, given the following command +prone to ambiguity). Consider the following command definition: + +==== [source, java] ---- @ShellMethod("Display stuff.") @@ -192,7 +213,11 @@ prone to ambiguity). As such, given the following command return String.format("You said a=%d, b=%d, c=%d", a, b, c); } ---- -then the following invocations are all equivalent, as witnessed by the output: +==== + +Given the preceding command definiton, the following invocations are all equivalent, as shown in the output: + +==== [source, bash] ---- shell:>echo 1 2 3 <1> @@ -210,21 +235,24 @@ 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 they appear +<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 Key(s) -As seen above, the default strategy for deriving the key for a named parameter is to use the java -name of the method signature and prefixing it with two dashes (`--`). This can be customized in two ways: +===== Customizing the Named Parameter Keys -1. to change the default prefix for the whole method, use the `prefix()` attribute of the -`@ShellMethod` annotation -2. to override the _whole_ key on a per-parameter fashion, annotate the parameter with the `@ShellOption` annotation. +As seen <>, 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: -Have a look at the following example: +* 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="-") @@ -232,14 +260,17 @@ Have a look at the following example: return String.format("You said a=%d, b=%d, c=%d", a, b, c); } ---- +==== -For such a setup, the possible parameter keys will be `-a`, `-b` and `--third`. +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 <> command: + ==== -It is possible to specify several keys for a single parameter. If so, these will be mutually exclusive ways -to specify the same parameter (so only one of them can be used). As an example, here is the signature of the -built-in xref:help-command[`help`] command: [source, java] ---- @ShellMethod("Describe a command.") @@ -248,11 +279,15 @@ built-in xref:help-command[`help`] command: } ---- ==== +===== [[optional-parameters-default-values]] ==== Optional Parameters and Default Values -Spring Shell provides the ability to give parameters default values, which will allow the user to omit -those parameters: + +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.") @@ -260,20 +295,26 @@ those parameters: return "Hello " + who; } ---- +==== -Now, the `greet` command can still be invoked as `greet Mother` (or `greet --who Mother`), but the following +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 mapped 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. Simply use a collection or array for the parameter type, and specify how +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.") @@ -281,8 +322,11 @@ many values are expected: return numbers[0] + numbers[1] + numbers[2]; } ---- +==== -The command may then be invoked using any of the following syntax: +The users can then invoke the command by using any of the following syntax: + +==== [source] ---- shell:>add 1 2 3.3 @@ -290,24 +334,32 @@ shell:>add 1 2 3.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 ---- ==== +===== ===== Infinite Arity + TO BE IMPLEMENTED ===== Special Handling of Boolean Parameters -When it comes to parameter arity, there is a kind of parameters that receives a special treatment by default, as + +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 using a "flag" approach. -Take a look at the following: +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.") @@ -315,8 +367,11 @@ Take a look at the following: return "You said " + force; } ---- +==== -This allows the following invocations: +This preceding command definition allows the following invocations: + +==== [source] ---- shell:>shutdown @@ -324,19 +379,19 @@ You said false shell:>shutdown --force You said true ---- +==== -[TIP] -==== -This special treatment plays well with the xref: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 (_i.e._ -`@ShellOption(defaultValue="true")`) and the behavior will be inverted (that is, not specifying the parameter -will result in the value being `true`, and specifying the flag will result in the value being `false`) -==== +TIP: This special treatment plays well with the <> 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: + ==== -Having this behavior of implicit `arity()=0` prevents the user from specifying a value (_e.g._ `shutdown --force true`). -If you would like to allow this behavior (and forego the flag approach), then force an arity of `1` using the annotation: [source, java] ---- @ShellMethod("Terminate the system.") @@ -345,13 +400,17 @@ If you would like to allow this behavior (and forego the flag approach), then fo } ---- ==== +===== [[quotes-handling]] ==== Quotes Handling -Spring Shell takes user input and tokenizes it in _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 will not be part of the value: +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.") @@ -359,7 +418,11 @@ Both single (`'`) and double (`"`) quotes are supported, and those quotes will n return "You said " + what; } ---- +==== +The following commands all invoke the preceding command definition: + +==== [source] ---- shell:>echo Hello @@ -371,9 +434,12 @@ You said Hello World shell:>echo "Hello World" You said Hello World ---- +==== -Supporting both single and double quotes allows the user to easily embed one type of quotes into +Supporting both single and double quotes lets the user embed one type of quotes into a value: + +==== [source] ---- shell:>echo "I'm here!" @@ -381,9 +447,14 @@ 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!' @@ -393,32 +464,38 @@ 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, as such: +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 such brings + +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 kbd:[TAB] completion almost everywhere possible. So if there -is an `echo` command and the user presses kbd:[e], kbd:[c], kbd:[TAB] then `echo` will appear. -Should there be several commands that start with `ec`, then the user will be prompted to choose (using kbd:[TAB] or -kbd:[Shift + TAB] to navigate, and kbd:[ENTER] for selection.) +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 xref:providing-tab-completion[]). +parameter values, if the application developer registered the appropriate beans (see <>). -Another nice feature of Spring Shell apps is support for line continuation. If a command and its parameters -is too long and does not fit nicely on screen, a user may chunk it and terminate a line with a backslash (`\`) character -then hit kbd:[ENTER] and continue on the next line. Uppon submission of the whole command, this will -be parsed as if the user entered a single space on line breaks. +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> @@ -426,22 +503,25 @@ shell:>register module --type source --name foo \ <1> Successfully registered module 'source:foo' ---- <1> command continues on next line +==== -Line continuation also automatically triggers if the user has opened a quote (see xref:quotes-handling[]) -and hits kbd:[ENTER] while still in the quotes: +Line continuation also automatically triggers if the user has opened a quote (see <>) +and presses `ENTER` while still in the quotes: + +==== [source] ---- shell:>echo "Hello <1> dquote> World" You said Hello World ---- -<1> user presses kbd:[ENTER] here +<1> The user pressed `ENTER` here. +==== - -Lastly, Spring Shell apps benefit from a lot of keyboard shortcuts you may already be familiar with when -working with your regular OS Shell, borrowed from Emacs. Notable shortcuts include kbd:[Ctrl+r] to perform -a reverse search, kbd:[Ctrl+a] and kbd:[Ctrl+e] to move to beginning and end of line respectively or kbd:[Esc f] and -kbd:[Esc b] to move forward (_resp._ backward) one word at a time. +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 @@ -452,11 +532,12 @@ TBD === Validating Command Arguments Spring Shell integrates with the https://beanvalidation.org/[Bean Validation API] to support -automatic and self documenting constraints on command parameters. +automatic and self-documenting constraints on command parameters. -Annotations found on command parameters as well as annotations at the method level will be -honored and trigger validation prior to the command executing. Given the following command: +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.") @@ -464,35 +545,37 @@ honored and trigger validation prior to the command executing. Given the followi return "Password successfully set to " + password; } ---- +==== -You'll get this behavior, for free: +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') ---- +==== -[NOTE] .Applies to All Command Implementations -==== -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 xref:support-for-shell-1-and-jcommander[Supporting Other APIs]) -==== +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 <>) [[dynamic-command-availability]] === Dynamic Command Availability -There may be times when registered commands don't make sense, due to internal state of the application. -For example, maybe there is a `download` command, but it only works once the user has used `connect` on a remote +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 _does_ exist, but that it is not available at the time. -Spring Shell lets the developer do that, even providing a short explanation of the reason for +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`. -Let's start with a simple example: +Consider the following example: +==== [source, java] ---- @ShellComponent @@ -518,32 +601,38 @@ public class MyCommands { } } ---- +==== -Here you see the `connect` method is used to connect to the server (details omitted), altering state +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 will be marked as _unavailable_ till the user has connected, thanks to the presence +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. -In case of the command not being available, an explanation has to be provided. +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 leveraged in the integrated help. See xref:help-command[]. +Information about currently unavailable commands is also used in the integrated help. See <>. [TIP] ==== -The reason provided when the command is not available should read nicely if appended after "Because ..." +The reason provided when the command is not available should read nicely if appended after "`Because`". -It's best not to start the sentence with a capital and not add a final dot. +You should not start the sentence with a capital or add a final period ==== -If for some reason naming the availability method after the name of the command method does not suit you, you -can provide an explicit name using the `@ShellMethodAvailability`, like so: +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.") @@ -559,12 +648,14 @@ can provide an explicit name using the `@ShellMethodAvailability`, like so: } ---- <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 all at one. Instead of having to stick the `@ShellMethodAvailability` -on all command methods, Spring Shell allows the user to flip things around and put the `@ShellMethodAvailabilty` +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.") @@ -584,12 +675,15 @@ annotation on the availability method, specifying the names of the commands that : Availability.unavailable("you are not connected"); } ---- - -[NOTE] ==== -The default value for the `@ShellMethodAvailability.value()` attribute is `"*"` and this serves as a special -wildcard that matches all command names. It's thus easy to turn all commands of a single class on or off -with a single availability method. Here is an example below: + +[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 @@ -609,41 +703,42 @@ public class Toggles { } ---- ==== +===== -[TIP] -==== -Spring Shell does not impose much constraints on how to write commands and how to organize classes. -But it's often good practice to put related commands in the same class, and the availability indicators +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. -==== [[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. Typing `help` -they would see a daunting list of commands, organized by alphabetical order, +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, Spring Shell provides the ability to group commands together, -with reasonable defaults. Related commands would then end up in the same _group_ (_e.g._ `User Management Commands`) +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 will be grouped according to the class they are implemented in, +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, -for they need to use the same collaborating objects. +because they need to use the same collaborating objects. -If however, this behavior does not suit you, you can override the group for a +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 the command is defined in. This will apply -the group for all commands defined in that class (unless overridden as above) -* placing a `@ShellCommandGroup` on the package (_via_ `package-info.java`) -the command is defined in. This will apply to all commands defined in the -package (unless overridden at the method or class level as explained above) +. 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) -Here is a short example: +The following listing shows an example: + +==== [source,java] ---- public class UserCommands { @@ -667,23 +762,29 @@ public class SomeCommands { public void last() {} } ---- +==== [[built-in-commands]] === Built-In Commands -Any application built using the `{starter-artifactId}` artifact + +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. -These commands can be overridden or disabled individually (see xref:overriding-or-disabling-built-in-commands[]), but if they're -not, this section describes their behavior. +You can override or disable these commands individually (see <>). +However, if they are not overridden or disabled, this section describes their behavior. [[help-command]] ==== Integrated Documentation with the `help` Command -Running a shell application often implies that the user is in a graphically limited environment. And although, in the era of mobile -phones we're always connected, accessing a web browser or any other rich UI application such as a pdf viewer may not always + +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` + kbd:[ENTER] will list all the known commands to the shell (including xref:dynamic-command-availability[unavailable] commands) -and a short description of what they do: +Typing `help` + `ENTER` lists all the commands known to the shell (including <> commands) +and a short description of what they do, similar to the following: + +==== [source] ---- shell:>help @@ -703,11 +804,14 @@ AVAILABLE COMMANDS Commands marked with (*) are currently unavailable. Type `help ` to learn more. ---- +==== -Typing `help ` will display more detailed information about a command, including the available parameters, their -type and whether they are mandatory or not, _etc._ +Typing `help ` shows more detailed information about a command, including the available parameters, their +type, whether they are mandatory or not, and other details. -Here is the `help` command applied to itself: +The follwoing listing shows the `help` command applied to itself: + +==== ---- shell:>help help @@ -722,46 +826,53 @@ OPTIONS -C or --command string The command to obtain help for. [Optional, default = ] ---- - +==== ==== Clearing the Screen The `clear` command does what you would expect and clears the screen, resetting the prompt in the top left corner. -==== Exitting the Shell -The `quit` command (also aliased as `exit`) simply requests the shell to quit, gracefully -closing the Spring application context. If not overridden, a JLine `History` bean will write a history of all -commands executed to disk, so that they are available again (see xref:interacting-with-the-shell[]) on next launch. +==== Exiting the Shell + +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 <>) on the next launch. ==== Displaying Details about an Error + 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 purpose, Spring Shell remembers the last exception that occurred and the user can later use the `stacktrace` -command to print all the gory details on the console. +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]] ==== Running a Batch of Commands -The `script` command accepts a local file as an argument and will replay commands found there, one at a time. -Reading from the file behaves exactly like inside the interactive shell, so lines starting with `//` will be considered -as comments and ignored, while lines ending with `\` will trigger line continuation. +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. === Customizing the Shell [[overriding-or-disabling-built-in-commands]] ==== Overriding or Disabling Built-In Commands -xref:built-in-commands[Built-in commands] are provided with Spring Shell to achieve everyday tasks that many if not -all shell applications need. If you're not happy with the way they behave though, you can disable or override them, as explained in this section. + +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`: + ==== -If you don't need built-in commands at all, then there is an easy way to "disable" them: just don't include them! -Either use a maven exclusion on `spring-shell-standard-commands` or, if you're selectively including Spring Shell dependencies, -don't bring that one in! [source,xml,subs=attributes+] ---- @@ -777,11 +888,15 @@ don't bring that one in! ---- ==== +===== +[[disabling-specific-commands]] ===== Disabling Specific Commands -To disable a single built-in command, simply set the `spring.shell.command..enabled` property to `false` in the app -`Environment`. One easy way to do this is to pass extra args to the Boot application in your `main()` entry point: +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 { @@ -791,14 +906,17 @@ To disable a single built-in command, simply set the `spring.shell.command. This disables the integrated `help` command +==== ===== Overriding Specific Commands -If, instead of disabling a command you'd rather provide your own implementation, then you can either -* disable the command like explained above and have your implementation registered with the same name -* have your implementing class implement the `.Command` interface. As an example, here is how +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 { @@ -809,12 +927,13 @@ public class MyClear implements Clear.Command { } } ---- +==== [NOTE] .Please Consider Contributing your Changes ==== If you feel like your implementation of a standard command could be valuable to the community, -please consider opening a pull-request at https://github.com/spring-projects/spring-shell. +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! @@ -822,21 +941,27 @@ always welcome! ==== ResultHandlers +TBD + ==== PromptProvider After each command invocation, the shell waits for new input from the user, displaying -a _prompt_ in yellow: +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 +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. -Here is a fictional example: +The following example shows how to use a `PromptProvider`: + +==== [source, java] ---- @Component @@ -862,6 +987,7 @@ public class CustomPromptProvider implements PromptProvider { } } ---- +==== ==== Customizing Command Line Options Behavior Spring Shell comes with two default Spring Boot `ApplicationRunners`: @@ -869,29 +995,33 @@ Spring Shell comes with two default Spring Boot `ApplicationRunners`: * `InteractiveShellApplicationRunner` bootstraps the Shell REPL. It sets up the JLine infrastructure and eventually calls `Shell.run()` * `ScriptShellApplicationRunner` looks for program arguments that start with `@`, assumes those are local file names and -tries to run commands contained in those files (with the same semantics as the xref:script-command[script command]) and -then exits the process (by effectively disabling the `InteractiveShellApplicationRunner`, see below). +tries to run commands contained in those files (with the same semantics as the <>) and +then exits the process (by effectively disabling the `InteractiveShellApplicationRunner` -- see below). -If this behavior does not suit you, simply provide one (or more) bean of type `ApplicationRunner` -and optionally disable the standard ones. You'll want to take inspiration from the `ScriptShellApplicationRunner`: +If this behavior does not suit you, provide at least one bean of type `ApplicationRunner` +and optionally disable the standard ones. You can take inspiration from the `ScriptShellApplicationRunner`: +==== [source,java] ---- include::../../../../spring-shell-core/src/main/java/org/springframework/shell/jline/ScriptShellApplicationRunner.java[tag=documentation] ... ---- +==== ==== 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 +and registers to it any bean of type `Converter`, `GenericConverter`, or `ConverterFactory` that it finds in the application context. -This means that it's really easy to customize conversion to your custom objects of type `Foo`: -just install a `Converter` bean in the context. +This means that you can customize conversion to your custom objects +by installing a `Converter` bean in the context: +==== [source, java] ---- @ShellComponent @@ -926,29 +1056,34 @@ class CustomDomainConverter implements Converter { } ---- +==== [TIP] .Mind your String representation -==== -As in the example above, it's probably a good idea if you can to have +===== +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 +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 xref:validating-command-arguments[] for more information. ==== +See <> for more information. +===== + [NOTE] ==== -If you want to customize the `ConversionService` further, you can either +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 will 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"`. +* 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"`. ==== //==== Overriding the JLine Parser diff --git a/spring-shell-docs/src/main/asciidoc/what-is-spring-shell.adoc b/spring-shell-docs/src/main/asciidoc/what-is-spring-shell.adoc index fe78d52b..2a9d60c5 100644 --- a/spring-shell-docs/src/main/asciidoc/what-is-spring-shell.adoc +++ b/spring-shell-docs/src/main/asciidoc/what-is-spring-shell.adoc @@ -1,16 +1,15 @@ == What is Spring Shell? Not all applications need a fancy web user interface! -Sometimes, interacting with an application using an interactive terminal is +Sometimes, interacting with an application through an interactive terminal is the most appropriate way to get things done. -Spring Shell allows one to easily create such a runnable application, where the -user will enter textual commands that will get executed until the program terminates. +Spring Shell lets you create such a runnable application, where the +user enters textual commands that are run until the program terminates. The Spring Shell project provides the infrastructure to create such a REPL (Read, Eval, -Print Loop), allowing the developer to concentrate on the commands implementation, using +Print Loop), letting you concentrate on the commands implementation by using the familiar Spring programming model. -Advanced features such as parsing, kbd:[TAB] completion, colorization of output, fancy ascii-art -table display, input conversion and validation all come for free, with the developer only -having to focus on core command logic. - +Advanced features such as parsing, tab completion, colorization of output, fancy ascii-art +table display, input conversion, and validation are all include, freeing you +to focus on core command logic.