diff --git a/dump.rdb b/dump.rdb deleted file mode 100644 index e0b2599b..00000000 Binary files a/dump.rdb and /dev/null differ diff --git a/spring-shell-docs/pom.xml b/spring-shell-docs/pom.xml index bc66561d..c6e08cf9 100644 --- a/spring-shell-docs/pom.xml +++ b/spring-shell-docs/pom.xml @@ -66,7 +66,7 @@ shared-head - prettify + coderay @@ -78,7 +78,7 @@ pdf - rouge + coderay diff --git a/spring-shell-docs/src/main/asciidoc/extending-spring-shell.adoc b/spring-shell-docs/src/main/asciidoc/extending-spring-shell.adoc index 5be857e2..cf962432 100644 --- a/spring-shell-docs/src/main/asciidoc/extending-spring-shell.adoc +++ b/spring-shell-docs/src/main/asciidoc/extending-spring-shell.adoc @@ -1,3 +1,4 @@ +[[extending-spring-shell]] == Extending Spring Shell === Support for Spring Shell 1 and JCommander 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 630f3634..04349069 100644 --- a/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc +++ b/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc @@ -112,28 +112,539 @@ Try to play with the shell (hint: there is a `help` command) and when you're don The rest of this document delves deeper into the whole Spring Shell programming model. === Writing your own Commands -`@ShellComponent`, `@ShellMethod`, etc. + +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). + +Using the _standard_ API, methods on beans will be 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 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 name of the created bean can be customized using the `value` attribute of the annotation. +==== + +==== It's all about Documentation! + +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[]). + +[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. +==== + +==== 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`). + +It is possible, however, to explicitly set the command key, using the `key` attribute of the annotation, like so: +[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, 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._) +==== -=== Invoking your commands +=== Invoking your Commands ==== By Name _vs._ Positional Parameters -==== Quotes Handling -==== Interacting with the Shell -Line Continuation, kbd:[TAB] Completion, Search, etc. +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). + +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 +[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); + } +---- +then the following invocations are all equivalent, as witnessed by 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 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: + +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. + +Have a look at 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 will be `-a`, `-b` and `--third`. + +[TIP] +==== +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). Here is an example: +[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 will allow the user to omit +those parameters: +[source, java] +---- + @ShellMethod("Say hello.") + public String greet(@ShellOption(defaultValue="World"} String who) { + return "Hello " + who; + } +---- + +Now, 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 +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 command may then be invoked 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 +---- +==== + +===== 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 +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: +[source, java] +---- + @ShellMethod("Terminate the system.") + public String shutdown(boolean force) { + return "You said " + force; + } +---- + +This allows the following invocations: +[source] +---- +shell:>shutdown +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`) +==== + +[WARNING] +==== +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.") + 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 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: + +[source, java] +---- + @ShellMethod("Prints what has been entered.") + public String echo(String what) { + return "You said " + what; + } +---- + +[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 allows the user to easily 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!" +---- + +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, as such: +[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 +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.) + +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[]). + +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. + +[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 xref:quotes-handling[]) +and hits kbd:[ENTER] while still in the quotes: +[source] +---- +shell:>echo "Hello <1> +dquote> World" +You said Hello World +---- +<1> user presses kbd:[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. + +[[providing-tab-completion]] +===== Providing TAB Completion Proposals + +TBD + + +[[dynamic-command-availability]] === Dynamic Command Availability -=== Built-In Commands -* clear -* help -* exit -* stacktrace -* script +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 +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 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: + +[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"); + } +} +---- + +Here you see the `connect` method is used to connect to the server (details omitted), altering 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 +of a method named exactly as the command method with the `Availability` prefix 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. +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[]. + +[TIP] +==== +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. +==== + +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: +[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 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` +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"); + } +---- + +[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. +==== + + + +[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 +can benefit from that. +==== + +=== Built-In Commands +Any application built 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. + +[[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 +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: +[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 ` 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._ + +Here is 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 = ] +---- + + +==== 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. + +==== 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. + +==== 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. === Customizing the Shell +[[overriding-or-disabling-built-in-commands]] ==== Overriding or Disabling Built-In Commands ==== ResultHandlers @@ -142,6 +653,8 @@ Line Continuation, kbd:[TAB] Completion, Search, etc. ==== Customizing Command Line Options Behavior +==== ConversionService + //==== Overriding the JLine Parser //=== Using Without Spring Boot