= Vert.x Shell

Vert.x Shell is a command line interface for the Vert.x runtime available from regular
terminals using different protocols.

Vert.x Shell provides a variety of commands for interacting live with Vert.x services.

Vert.x Shell can be extended with custom commands in any language supported by Vert.x

== Using Vert.x Shell

Vert.x Shell is a Vert.x Service and can be started programmatically via the `link:../../scaladocs/io/vertx/scala/ext/shell/ShellService.html[ShellService]`
or deployed as a service.

=== Shell service

The shell can be started as a service directly either from the command line or as a the Vert.x deployment:

.Starting a shell service available via Telnet
[source,subs="+attributes"]
----
vertx run -conf '{"telnetOptions":{"port":5000}}' maven:${maven.groupId}:${maven.artifactId}:${maven.version}
----

or

.Starting a shell service available via SSH
[source,subs="+attributes"]
----
# create a key pair for the SSH server
keytool -genkey -keyalg RSA -keystore ssh.jks -keysize 2048 -validity 1095 -dname CN=localhost -keypass secret -storepass secret
# create the auth config
echo user.admin=password > auth.properties
# start the shell
vertx run -conf '{"sshOptions":{"port":4000,"keyPairOptions":{"path":"ssh.jks","password":"secret"},"authOptions":{"provider":"shiro","config":{"properties_path":"file:auth.properties"}}}}' maven:${maven.groupId}:${maven.artifactId}:${maven.version}
----

or

.Starting a shell service available via HTTP
[source,subs="+attributes"]
----
# create a certificate for the HTTP server
keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -validity 1095 -dname CN=localhost -keypass secret -storepass secret
# create the auth config
echo user.admin=password > auth.properties
vertx run -conf '{"httpOptions":{"port":8080,"ssl":true,"keyStoreOptions":{"path":"keystore.jks","password":"secret"},"authOptions":{"provider":""shiro,"config":{"properties_path":"file:auth.properties"}}}}' maven:${maven.groupId}:${maven.artifactId}:${maven.version}
----

You can also deploy this service inside your own verticle:

[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("telnetOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 4000)))
)

----

or

[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("sshOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 5000).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/ssh.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "shiro").put("config", new io.vertx.core.json.JsonObject().put("properties_path", "file:/path/to/my/auth.properties")))))
)

----

or

[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("httpOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 8080).put("ssl", true).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/server-keystore.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "shiro").put("config", new io.vertx.core.json.JsonObject().put("properties_path", "file:/path/to/my/auth.properties")))))
)

----

NOTE: when Vert.x Shell is already on your classpath you can use `service:io.vertx.ext.shell` instead
or `maven:${maven.groupId}:${maven.artifactId}:${maven.version}`

=== Programmatic service

The `link:../../scaladocs/io/vertx/scala/ext/shell/ShellService.html[ShellService]` takes care of starting an instance of Vert.x Shell.

Starting a shell service available via SSH:

[source,scala]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setSSHOptions(SSHTermOptions()
    .setHost("localhost")
    .setPort(5000)
    .setKeyPairOptions(JksOptions()
      .setPath("server-keystore.jks")
      .setPassword("wibble")
    )
    .setAuthOptions(ShiroAuthOptions()
      .setType(ShiroAuthRealmType.PROPERTIES)
      .setConfig(new io.vertx.core.json.JsonObject().put("properties_path", "file:/path/to/my/auth.properties"))
    )
  )
)
service.start()

----

Starting a shell service available via Telnet:

[source,scala]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setTelnetOptions(TelnetTermOptions()
    .setHost("localhost")
    .setPort(4000)
  )
)
service.start()

----

The `link:../dataobjects.html#TelnetTermOptions[TelnetTermOptions]` extends the Vert.x Core `NetServerOptions` as the Telnet server
implementation is based on a `NetServer`.

CAUTION: Telnet does not provide any authentication nor encryption at all.

Starting a shell service available via HTTP:

[source,scala]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setHttpOptions(HttpTermOptions()
    .setHost("localhost")
    .setPort(8080)
  )
)
service.start()

----

== Authentication

The SSH and HTTP connectors provide both authentication built on top of _vertx-auth_ with the following supported
providers:

- _shiro_ : provides `.properties` and _LDAP_ backend as seen in the ShellService presentation
- _jdbc_ : JDBC backend
- _mongo_ : MongoDB backend

These options can be created directly using directly `link:../../vertx-auth-common/dataobjects.html#AuthOptions[AuthOptions]`:

- `link:../../vertx-auth-shiro/dataobjects.html#ShiroAuthOptions[ShiroAuthOptions]` for Shiro
- `link:../../vertx-auth-jdbc/dataobjects.html#JDBCAuthOptions[JDBCAuthOptions]` for JDBC
- `link:../../vertx-auth-mongo/dataobjects.html#MongoAuthOptions[MongoAuthOptions]` for Mongo

As for external service configuration in Json, the `authOptions` uses the `provider` property to distinguish:

----
{
  ...
  "authOptions": {
    "provider":"shiro",
    "config": {
      "properties_path":"file:auth.properties"
    }
  }
  ...
}
----

== Telnet term configuration

Telnet terms are configured by `link:../dataobjects.html#ShellServiceOptions#setTelnetOptions(io.vertx.ext.shell.term.TelnetTermOptions)[telnetOptions]`,
the `link:../dataobjects.html#TelnetTermOptions[TelnetTermOptions]` extends the `link:../../vertx-core/dataobjects.html#NetServerOptions[NetServerOptions]` so they
have the exact same configuration.

== SSH term configuration

SSH terms are configured by `link:../dataobjects.html#ShellServiceOptions#setSSHOptions(io.vertx.ext.shell.term.SSHTermOptions)[SSHOptions]`:

- `link:../dataobjects.html#SSHTermOptions#setPort(int)[port]`: port
- `link:../dataobjects.html#SSHTermOptions#setHost(java.lang.String)[host]`: host

Only username/password authentication is supported at the moment, it can be configured with property file
or LDAP, see Vert.x Auth for more info:

- `link:../dataobjects.html#SSHTermOptions#setAuthOptions(io.vertx.ext.auth.AuthOptions)[authOptions]`: configures user authentication

The server key configuration reuses the key pair store configuration scheme provided by _Vert.x Core_:

- `link:../dataobjects.html#SSHTermOptions#setKeyPairOptions(io.vertx.core.net.JksOptions)[keyPairOptions]`: set `.jks` key pair store
- `link:../dataobjects.html#SSHTermOptions#setPfxKeyPairOptions(io.vertx.core.net.PfxOptions)[pfxKeyPairOptions]`: set `.pfx` key pair store
- `link:../dataobjects.html#SSHTermOptions#setPemKeyPairOptions(io.vertx.core.net.PemKeyCertOptions)[pemKeyPairOptions]`: set `.pem` key pair store


.Deploying the Shell Service on SSH with Mongo authentication
[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("sshOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 5000).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/ssh.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "mongo").put("config", new io.vertx.core.json.JsonObject().put("connection_string", "mongodb://localhost:27018")))))
)

----

.Running the Shell Service on SSH with Mongo authentication
[source,scala,subs="+attributes"]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setSSHOptions(SSHTermOptions()
    .setHost("localhost")
    .setPort(5000)
    .setKeyPairOptions(JksOptions()
      .setPath("server-keystore.jks")
      .setPassword("wibble")
    )
    .setAuthOptions(MongoAuthOptions()
      .setConfig(new io.vertx.core.json.JsonObject().put("connection_string", "mongodb://localhost:27018"))
    )
  )
)
service.start()

----

.Deploying the Shell Service on SSH with JDBC authentication
[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("sshOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 5000).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/ssh.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "jdbc").put("config", new io.vertx.core.json.JsonObject().put("url", "jdbc:hsqldb:mem:test?shutdown=true").put("driver_class", "org.hsqldb.jdbcDriver")))))
)

----

.Running the Shell Service on SSH with JDBC authentication
[source,scala,subs="+attributes"]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setSSHOptions(SSHTermOptions()
    .setHost("localhost")
    .setPort(5000)
    .setKeyPairOptions(JksOptions()
      .setPath("server-keystore.jks")
      .setPassword("wibble")
    )
    .setAuthOptions(JDBCAuthOptions()
      .setConfig(new io.vertx.core.json.JsonObject().put("url", "jdbc:hsqldb:mem:test?shutdown=true").put("driver_class", "org.hsqldb.jdbcDriver"))
    )
  )
)
service.start()

----

== HTTP term configuration

HTTP terms are configured by `link:../dataobjects.html#ShellServiceOptions#setHttpOptions(io.vertx.ext.shell.term.HttpTermOptions)[httpOptions]`, the http options
extends the `link:../../vertx-core/dataobjects.html#HttpServerOptions[HttpServerOptions]` so they expose the exact same configuration.

In addition there are extra options for configuring an HTTP term:

- `link:../dataobjects.html#HttpTermOptions#setAuthOptions(io.vertx.ext.auth.AuthOptions)[authOptions]`: configures user authentication
- `link:../dataobjects.html#HttpTermOptions#setSockJSHandlerOptions(io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions)[sockJSHandlerOptions]`: configures SockJS
- `link:../dataobjects.html#HttpTermOptions#setSockJSPath(java.lang.String)[sockJSPath]`: the SockJS path in the router

.Deploying the Shell Service on HTTP with Mongo authentication
[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("httpOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 8080).put("ssl", true).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/server-keystore.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "mongo").put("config", new io.vertx.core.json.JsonObject().put("connection_string", "mongodb://localhost:27018")))))
)

----

.Running the Shell Service on HTTP with Mongo authentication
[source,scala,subs="+attributes"]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setHttpOptions(HttpTermOptions()
    .setHost("localhost")
    .setPort(8080)
    .setAuthOptions(MongoAuthOptions()
      .setConfig(new io.vertx.core.json.JsonObject().put("connection_string", "mongodb://localhost:27018"))
    )
  )
)
service.start()

----

.Deploying the Shell Service on HTTP with JDBC authentication
[source,scala,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", DeploymentOptions()
  .setConfig(new io.vertx.core.json.JsonObject().put("httpOptions", new io.vertx.core.json.JsonObject().put("host", "localhost").put("port", 8080).put("ssl", true).put("keyPairOptions", new io.vertx.core.json.JsonObject().put("path", "src/test/resources/server-keystore.jks").put("password", "wibble")).put("authOptions", new io.vertx.core.json.JsonObject().put("provider", "jdbc").put("config", new io.vertx.core.json.JsonObject().put("url", "jdbc:hsqldb:mem:test?shutdown=true").put("driver_class", "org.hsqldb.jdbcDriver")))))
)

----

.Running the Shell Service on HTTP with JDBC authentication
[source,scala,subs="+attributes"]
----
var service = ShellService.create(vertx, ShellServiceOptions()
  .setHttpOptions(HttpTermOptions()
    .setHost("localhost")
    .setPort(8080)
    .setAuthOptions(JDBCAuthOptions()
      .setConfig(new io.vertx.core.json.JsonObject().put("url", "jdbc:hsqldb:mem:test?shutdown=true").put("driver_class", "org.hsqldb.jdbcDriver"))
    )
  )
)
service.start()

----

== Keymap configuration

The shell uses a default keymap configuration that can be overriden using the `inputrc` property of the various
term configuration object:

- `link:../dataobjects.html#TelnetTermOptions#setIntputrc(java.lang.String)[intputrc]`
- `link:../dataobjects.html#SSHTermOptions#setIntputrc(java.lang.String)[intputrc]`
- `link:../dataobjects.html#HttpTermOptions#setIntputrc(java.lang.String)[intputrc]`

The `inputrc` must point to a file available via the classloader or the filesystem.

The `inputrc` only function bindings and the available functions are:

- _backward-char_
- _forward-char_
- _next-history_
- _previous-history_
- _backward-delete-char_
- _backward-delete-char_
- _backward-word_
- _end-of-line_
- _beginning-of-line_
- _delete-char_
- _delete-char_
- _complete_
- _accept-line_
- _accept-line_
- _kill-line_
- _backward-word_
- _forward-word_
- _backward-kill-word_

NOTE: Extra functions can be added, however this is done by implementing functions of the `Term.d` project on which
Vert.x Shell is based, for instance the https://github.com/termd/termd/blob/c1629623c8a3add4bde7778640bf8cc233a7c98f/src/examples/java/examples/readlinefunction/ReverseFunction.java[reverse function]
can be implemented and then declared in a `META-INF/services/io.termd.core.readline.Function` to be loaded by the shell.

== Base commands

To find out the available commands you can use the _help_ builtin command:

. Verticle commands
.. verticle-ls: list all deployed verticles
.. verticle-undeploy: undeploy a verticle
.. verticle-deploy: deploys a verticle with deployment options as JSON string
.. verticle-factories: list all known verticle factories
. File system commands
.. ls
.. cd
.. pwd
. Bus commands
.. bus-tail: display all incoming messages on an event bus address
.. bus-send: send a message on the event bus
. Net commands
.. net-ls: list all available net servers, including HTTP servers
. Shared data commands
.. local-map-put
.. local-map-get
.. local-map-rm
. Various commands
.. echo
.. sleep
.. help
.. exit
.. logout
. Job control
.. fg
.. bg
.. jobs

NOTE: this command list should evolve in next releases of Vert.x Shell. Other Vert.x project may provide commands to extend
Vert.x Shell, for instance Dropwizard Metrics.

== Extending Vert.x Shell

Vert.x Shell can be extended with custom commands in any of the languages supporting code generation.

A command is created by the `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandBuilder.html#command(java.lang.String)[CommandBuilder.command]` method: the command process handler is called
by the shell when the command is executed, this handler can be set with the `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandBuilder.html#processHandler(io.vertx.core.Handler)[processHandler]`
method:

[source,scala]
----

var builder = CommandBuilder.command("my-command")
builder.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  // Write a message to the console
  process.write("Hello World")

  // End the process
  process.end()
})

// Register the command
var registry = CommandRegistry.getShared(vertx)
registry.registerCommand(builder.build(vertx))

----

After a command is created, it needs to be registed to a `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandRegistry.html[CommandRegistry]`. The
command registry holds all the commands for a Vert.x instance.

A command is registered until it is unregistered with the `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandRegistry.html#unregisterCommand(java.lang.String)[unregisterCommand]`.
When a command is registered from a Verticle, this command is unregistered when this verticle is undeployed.

NOTE: Command callbacks are invoked in the `io.vertx.core.Context` when the command is registered in the
registry. Keep this in mind if you maintain state in a command.

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html[CommandProcess]` object can be used for interacting with the shell.

=== Command arguments

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#args()[args]` returns the command arguments:

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  process.args().foreach(arg => {
    // Print each argument on the console
    process.write(s"Argument ${arg}")
  })


  process.end()
})

----

Besides it is also possible to create commands using `link:../../scaladocs/io/vertx/scala/core/cli/CLI.html[Vert.x CLI]`: it makes easier to
write command line argument parsing:

- _option_ and _argument_ parsing
- argument _validation_
- generation of the command _usage_

[source,scala]
----
var cli = CLI.create("my-command").addArgument(Argument()
  .setArgName("my-arg")
).addOption(Option()
  .setShortName("m")
  .setLongName("my-option")
)
var command = CommandBuilder.command(cli)
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  var commandLine = process.commandLine()

  var argValue = commandLine.getArgumentValue(0)
  var optValue = commandLine.getOptionValue("my-option")
  process.write(s"The argument is ${argValue} and the option is ${optValue}")

  process.end()
})

----

When an option named _help_ is added to the CLI object, the shell will take care of generating the command usage
when the option is activated:

[source,scala]
----
var cli = CLI.create("my-command").addArgument(Argument()
  .setArgName("my-arg")
).addOption(Option()
  .setArgName("help")
  .setShortName("h")
  .setLongName("help")
)
var command = CommandBuilder.command(cli)
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {
  // ...
})

----

When the command executes the `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html[process]` is provided for interacting
with the shell. A `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html[CommandProcess]` extends `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html[Tty]`
which is used for interacting with the terminal.

=== Terminal usage

==== terminal I/O

The `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#stdinHandler(io.vertx.core.Handler)[stdinHandler]` handler is used to be notified when the terminal
receives data, e.g the user uses his keyboard:

[source,scala]
----
tty.stdinHandler((data: java.lang.String) => {
  println(s"Received ${data}")
})

----

A command can use the `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#write(java.lang.String)[write]` to write to the standard output.

[source,scala]
----
tty.write("Hello World")

----

==== Terminal size

The current terminal size can be obtained using `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#width()[width]` and
`link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#height()[height]`.

[source,scala]
----
tty.write(s"Current terminal size: (${tty.width()}, ${tty.height()})")

----

==== Resize event

When the size of the terminal changes the `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#resizehandler(io.vertx.core.Handler)[resizehandler]`
is called, the new terminal size can be obtained with `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#width()[width]` and
`link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#height()[height]`.

[source,scala]
----
tty.resizehandler((v: java.lang.Void) => {
  println(s"terminal resized : ${tty.width()} ${tty.height()}")
})

----

==== Terminal type

The terminal type is useful for sending escape codes to the remote terminal: `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html#type()[type]`
returns the current terminal type, it can be null if the terminal has not advertised the value.

[source,scala]
----
println(s"terminal type : ${tty.type()}")

----

=== Shell session

The shell is a connected service that naturally maintains a session with the client, this session can be
used in commands to scope data. A command can get the session with `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#session()[session]`:

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  var session = process.session()

  if (session.get("my_key") == null) {
    session.put("my key", "my value")
  }

  process.end()
})

----

=== Process termination

Calling `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#end()[end]` ends the current process. It can be called directly
in the invocation of the command handler or any time later:

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {
  var vertx = process.vertx()

  // Set a timer
  vertx.setTimer(1000, (id: java.lang.Long) => {

    // End the command when the timer is fired
    process.end()
  })
})

----

=== Process events

A command can subscribe to a few process events.

==== Interrupt event

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#interruptHandler(io.vertx.core.Handler)[interruptHandler]` is called when the process
is interrupted, this event is fired when the user press _Ctrl+C_ during the execution of a command. This handler can
be used for interrupting commands _blocking_ the CLI and gracefully ending the command process:

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {
  var vertx = process.vertx()

  // Every second print a message on the console
  var periodicId = vertx.setPeriodic(1000, (id: java.lang.Long) => {
    process.write("tick\n")
  })

  // When user press Ctrl+C: cancel the timer and end the process
  process.interruptHandler((v: java.lang.Void) => {
    vertx.cancelTimer(periodicId)
    process.end()
  })
})

----

When no interrupt handler is registered, pressing _Ctrl+C_ will have no effect on the current process and the event
will be delayed and will likely be handled by the shell, like printing a new line on the console.

==== Suspend/resume events

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#suspendHandler(io.vertx.core.Handler)[suspendHandler]` is called when the process
is running and the user press _Ctrl+Z_, the command is _suspended_:

- the command can receive the suspend event when it has registered an handler for this event
- the command will not receive anymore data from the standard input
- the shell prompt the user for input
- the command can receive interrupts event or end events

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#resumeHandler(io.vertx.core.Handler)[resumeHandler]` is called when the process
is resumed, usually when the user types _fg_:

- the command can receive the resume event when it has registered an handler for this event
- the command will receive again data from the standard input when it has registered an stdin handler

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  // Command is suspended
  process.suspendHandler((v: java.lang.Void) => {
    println("Suspended")
  })

  // Command is resumed
  process.resumeHandler((v: java.lang.Void) => {
    println("Resumed")
  })
})

----

==== End events

The `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#endHandler(io.vertx.core.Handler)[endHandler]` (io.vertx.core.Handler)} is
called when the process is running or suspended and the command terminates, for instance the shell session is closed,
the command is _terminated_.

[source,scala]
----
command.processHandler((process: io.vertx.scala.ext.shell.command.CommandProcess) => {

  // Command terminates
  process.endHandler((v: java.lang.Void) => {
    println("Terminated")
  })
})

----

The end handler is called even when the command invokes `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandProcess.html#end()[end]`.

This handler is useful for cleaning up resources upon command termination, for instance closing a client or a timer.

=== Command completion

A command can provide a completion handler when it wants to provide contextual command line interface completion.

Like the process handler, the `link:../../scaladocs/io/vertx/scala/ext/shell/command/CommandBuilder.html#completionHandler(io.vertx.core.Handler)[completion
handler]` is non blocking because the implementation may use Vert.x services, e.g the file system.

The `link:../../scaladocs/io/vertx/scala/ext/shell/cli/Completion.html#lineTokens()[lineTokens]` returns a list of `link:../../scaladocs/io/vertx/scala/ext/shell/cli/CliToken.html[tokens]`
from the beginning of the line to the cursor position. The list can be empty if the cursor when the cursor is at the
beginning of the line.

The `link:../../scaladocs/io/vertx/scala/ext/shell/cli/Completion.html#rawLine()[rawLine]` returns the current completed from the beginning
of the line to the cursor position, in raw format, i.e without any char escape performed.

Completion ends with a call to `link:../../scaladocs/io/vertx/scala/ext/shell/cli/Completion.html#complete(java.util.List)[complete]`.

== Shell server

The Shell service is a convenient facade for starting a preconfigured shell either programmatically or as a Vert.x service.
When more flexibility is needed, a `link:../../scaladocs/io/vertx/scala/ext/shell/ShellServer.html[ShellServer]` can be used instead of the service.

For instance the shell http term can be configured to use an existing router instead of starting its own http server.

Using a shell server requires explicit configuration but provides full flexiblity, a shell server is setup in a few
steps:

[source,scala]
----

var server = ShellServer.create(vertx)

var shellRouter = Router.router(vertx)
router.mountSubRouter("/shell", shellRouter)
var httpTermServer = TermServer.createHttpTermServer(vertx, router)

var sshTermServer = TermServer.createSSHTermServer(vertx)

server.registerTermServer(httpTermServer)
server.registerTermServer(sshTermServer)

server.registerCommandResolver(CommandResolver.baseCommands(vertx))

server.listen()

----
<1> create a the shell server
<2> create an HTTP term server mounted on an existing router
<3> create an SSH term server
<4> register term servers
<5> register all base commands
<6> finally start the shell server

Besides, the shell server can also be used for creating in process shell session: it provides a programmatic interactive shell.

In process shell session can be created with `link:../../scaladocs/io/vertx/scala/ext/shell/ShellServer.html#createShell(io.vertx.ext.shell.term.Term)[createShell]`:

[source,scala]
----

// Create a shell ession
var shell = shellServer.createShell()


----

The main use case is running or testing a command:

[source,scala]
----

// Create a shell
var shell = shellServer.createShell()

// Create a job fo the command
var job = shell.createJob("my-command 1234")

// Create a pseudo terminal
var pty = Pty.create()
pty.stdoutHandler((data: java.lang.String) => {
  println(s"Command wrote ${data}")
})

// Run the command
job.setTty(pty.slave())
job.statusUpdateHandler((status: io.vertx.scala.ext.shell.system.ExecStatus) => {
  println(s"Command terminated with status ${status}")
})

----

The `link:../../scaladocs/io/vertx/scala/ext/shell/term/Pty.html[Pty]` pseudo terminal is the main interface for interacting with the command
when it's running:

- uses standard input/output for writing or reading strings
- resize the terminal

The `link:../../scaladocs/io/vertx/scala/ext/shell/system/JobController.html#close(io.vertx.core.Handler)[close]` closes the shell, it will terminate all jobs in the current shell
session.

== Terminal servers

Vert.x Shell also provides bare terminal servers for those who need to write pure terminal applications.

A `link:../../scaladocs/io/vertx/scala/ext/shell/term/Term.html[Term]` handler must be set on a term server before starting it. This handler will
handle each term when the user connects.

An `link:../../vertx-auth-common/dataobjects.html#AuthOptions[AuthOptions]` can be set on `link:../dataobjects.html#SSHTermOptions[SSHTermOptions]` and `link:../dataobjects.html#HttpTermOptions[HttpTermOptions]`.
Alternatively, an `link:../../scaladocs/io/vertx/scala/ext/auth/AuthProvider.html[AuthProvider]` can be `link:../../scaladocs/io/vertx/scala/ext/shell/term/TermServer.html#authProvider(io.vertx.ext.auth.AuthProvider)[set]`
directly on the term server before starting it.

=== SSH term

The terminal server `link:../../scaladocs/io/vertx/scala/ext/shell/term/Term.html[Term]` handler accepts incoming terminal connections.
When a remote terminal connects, the `link:../../scaladocs/io/vertx/scala/ext/shell/term/Term.html[Term]` can be used to interact with connected
terminal.

[source,scala]
----
var server = TermServer.createSSHTermServer(vertx, SSHTermOptions()
  .setPort(5000)
  .setHost("localhost")
)
server.termHandler((term: io.vertx.scala.ext.shell.term.Term) => {
  term.stdinHandler((line: java.lang.String) => {
    term.write(line)
  })
})
server.listen()

----

The `link:../../scaladocs/io/vertx/scala/ext/shell/term/Term.html[Term]` is also a `link:../../scaladocs/io/vertx/scala/ext/shell/term/Tty.html[Tty]`, this section explains
how to use the tty.

=== Telnet term

[source,scala]
----
var server = TermServer.createTelnetTermServer(vertx, TelnetTermOptions()
  .setPort(5000)
  .setHost("localhost")
)
server.termHandler((term: io.vertx.scala.ext.shell.term.Term) => {
  term.stdinHandler((line: java.lang.String) => {
    term.write(line)
  })
})
server.listen()

----

=== HTTP term

The `link:../../scaladocs/io/vertx/scala/ext/shell/term/TermServer.html#createHttpTermServer(io.vertx.core.Vertx)[TermServer.createHttpTermServer]` method creates an HTTP term server, built
on top of Vert.x Web using the SockJS protocol.

[source,scala]
----
var server = TermServer.createHttpTermServer(vertx, HttpTermOptions()
  .setPort(5000)
  .setHost("localhost")
)
server.termHandler((term: io.vertx.scala.ext.shell.term.Term) => {
  term.stdinHandler((line: java.lang.String) => {
    term.write(line)
  })
})
server.listen()

----

An HTTP term can start its own HTTP server, or it can reuse an existing Vert.x Web `link:../../scaladocs/io/vertx/scala/ext/web/Router.html[Router]`.

The shell can be found at `/shell.html`.

[source,scala]
----
var server = TermServer.createHttpTermServer(vertx, router, HttpTermOptions()
  .setPort(5000)
  .setHost("localhost")
)
server.termHandler((term: io.vertx.scala.ext.shell.term.Term) => {
  term.stdinHandler((line: java.lang.String) => {
    term.write(line)
  })
})
server.listen()

----

The later option is convenient when the HTTP shell is integrated in an existing HTTP server.

The HTTP term server by default is configured for serving:

- the `shell.html` page
- the `https://github.com/chjj/term.js/[term.js]` client library
- the `vertxshell.js` client library

The `vertxshell.js` integrates `term.js` is the client side part of the HTTP term.

It integrates `term.js` with SockJS and needs the URL of the HTTP term server endpoint:

[source,javascript]
----
window.addEventListener('load', function () {
  var url = 'http://localhost/shell';
  new VertxTerm(url, {
    cols: 80,
    rows: 24
   });
 });
----

Straight websockets can also be used, if so, the remote term URL should be suffixed with `/websocket`:

[source,javascript]
----
window.addEventListener('load', function () {
  var url = 'ws://localhost/shell/websocket';
  new VertxTerm(url, {
    cols: 80,
    rows: 24
   });
 });
----

For customization purpose these resources can be copied and customized, they are available in the Vert.x Shell
jar under the `io.vertx.ext.shell` packages.

== Command discovery

The command discovery can be used when new commands need to be added to Vert.x without an explicit registration.

For example, the _Dropwizard_ metrics service, adds specific metrics command to the shell service on the fly.

It can be achieved via the `java.util.ServiceLoader` of a `CommandResolverFactory`.

[source,java]
----
public class CustomCommands implements CommandResolverFactory {

  public void resolver(Vertx vertx, Handler<AsyncResult<CommandResolver>> resolverHandler) {
    resolverHandler.handler(() -> Arrays.asList(myCommand1, myCommand2));
  }
}
----

The `resolver` method is async, because the resolver may need to wait some condition before commands
are resolved.

The shell service discovery using the service loader mechanism:

.The service provider file `META-INF/services/io.vertx.ext.shell.spi.CommandResolverFactory`
[source]
----
my.CustomCommands
----

This is only valid for the `link:../../scaladocs/io/vertx/scala/ext/shell/ShellService.html[ShellService]`. `link:../../scaladocs/io/vertx/scala/ext/shell/ShellServer.html[ShellServer]`
don't use this mechanism.

== Command pack

A command pack is a jar that provides new Vert.x Shell commands.

Such jar just need to be present on the classpath and it is discovered by Vertx. Shell.

[source,java]
----
public class CommandPackExample implements CommandResolverFactory {

  @Override
  public void resolver(Vertx vertx, Handler<AsyncResult<CommandResolver>> resolveHandler) {
    List<Command> commands = new ArrayList<>();

    // Add commands
    commands.add(Command.create(vertx, JavaCommandExample.class));

    // Add another command
    commands.add(CommandBuilder.command("another-command").processHandler(process -> {
      // Handle process
    }).build(vertx));

    // Resolve with the commands
    resolveHandler.handle(Future.succeededFuture(() -> commands));
  }
}
----

The command pack uses command discovery mechanism, so it needs the descriptor:

.`META-INF/services/io.vertx.ext.shell.spi.CommandResolverFactory` descriptor
[source]
----
examples.pack.CommandPackExample
----