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 ShellService
or deployed as a service.
Deployed service
The shell can be started as a service directly either from the command line or as a the Vert.x deployment:
vertx run -conf '{"telnetOptions":{"port":5000}}' maven:io.vertx:vertx-shell:3.1.0
or
# 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"},"shiroAuthOptions":{"config":{"properties_path":"file:auth.properties"}}}}' maven:io.vertx:vertx-shell:3.1.0
You can also deploy this service inside your own verticle:
vertx.deploy_verticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", {
'config' => {
'telnetOptions' => {
'host' => "localhost",
'port' => 4000
}
}
})
or
vertx.deploy_verticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", {
'config' => {
'sshOptions' => {
'host' => "localhost",
'port' => 5000,
'keyPairOptions' => {
'path' => "server-keystore.jks",
'password' => "wibble"
},
'config' => {
'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:io.vertx:vertx-shell:3.1.0
|
Programmatic service
The ShellService takes care of starting an instance of Vert.x Shell.
Starting a shell service available via SSH:
require 'vertx-shell/shell_service'
service = VertxShell::ShellService.create(vertx, {
'sSHOptions' => {
'host' => "localhost",
'port' => 5000,
'keyPairOptions' => {
'path' => "server-keystore.jks",
'password' => "wibble"
},
'shiroAuthOptions' => {
'type' => :PROPERTIES,
'config' => {
'properties_path' => "file:/path/to/my/auth.properties"
}
}
}
})
service.start()
Starting a shell service available via Telnet:
require 'vertx-shell/shell_service'
service = VertxShell::ShellService.create(vertx, {
'telnetOptions' => {
'host' => "localhost",
'port' => 4000
}
})
service.start()
The TelnetOptions 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. |
Telnet configuration
The telnet connector is configured by telnetOptions,
the TelnetOptions extends the NetServerOptions so they
have the exact same configuration.
SSH configuration
The SSH connector is configured by SSHOptions:
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:
-
shiroAuthOptions: configures user authentication
The server key configuration reuses the key pair store configuration scheme provided by Vert.x Core:
-
keyPairOptions: set.jkskey pair store -
pfxKeyPairOptions: set.pfxkey pair store -
pemKeyPairOptions: set.pemkey pair store
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: deployes a verticle
-
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
-
-
Metrics commands (requires Dropwizard metrics setup)
-
metrics-ls: show all available metrics
-
metrics-info: show particular metrics
-
-
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 |
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 CommandBuilder.command method: the command process handler is called
by the shell when the command is executed, this handler can be set with the processHandler
method:
require 'vertx-shell/command_builder'
require 'vertx-shell/command_registry'
builder = VertxShell::CommandBuilder.command("my-command")
builder.process_handler() { |process|
# Write a message to the console
process.write("Hello World")
# End the process
process.end()
}
# Register the command
registry = VertxShell::CommandRegistry.get(vertx)
registry.register_command(builder.build())
After a command is created, it needs to be registed to a CommandRegistry. The
command registry holds all the commands for a Vert.x instance.
|
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 CommandProcess object can be used for interacting with the shell.
Command arguments
The args returns the command arguments:
command.process_handler() { |process|
process.args().each do |arg|
# Print each argument on the console
process.write("Argument #{arg}")
end
process.end()
}
Besides it is also possible to create commands using Vert.x CLI: it makes easier to
write command line argument parsing:
-
option and argument parsing
-
argument validation
-
generation of the command usage
require 'vertx/cli'
require 'vertx-shell/command_builder'
cli = Vertx::CLI.create("my-command").add_argument({
'argName' => "my-arg"
}).add_option({
'shortName' => "m",
'longName' => "my-option"
})
command = VertxShell::CommandBuilder.command(cli)
command.process_handler() { |process|
commandLine = process.command_line()
argValue = commandLine.get_argument_value(0)
optValue = commandLine.get_option_value("my-option")
process.write("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:
require 'vertx/cli'
require 'vertx-shell/command_builder'
cli = Vertx::CLI.create("my-command").add_argument({
'argName' => "my-arg"
}).add_option({
'argName' => "help",
'shortName' => "h",
'longName' => "help"
})
command = VertxShell::CommandBuilder.command(cli)
command.process_handler() { |process|
# ...
}
Terminal size
command.process_handler() { |process|
process.write("Current terminal size: (#{process.width()}, #{process.height()})").end()
}
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 session:
command.process_handler() { |process|
session = process.session()
if (session.get("my_key") == nil)
session.put("my key", "my value")
end
process.end()
}
Process I/O
A command can set a setStdin handler
to be notified when the shell receives data, e.g the user uses his keyboard:
command.process_handler() { |process|
process.set_stdin() { |data|
puts "Received #{data}"
}
}
A command can use the stdout to write to the standard output.
command.process_handler() { |process|
process.stdout().write("Hello World")
process.end()
}
Or it can use the write method:
command.process_handler() { |process|
process.write("Hello World")
process.end()
}
Process termination
Calling end ends the current process. It can be called directly
in the invocation of the command handler or any time later:
command.process_handler() { |process|
vertx = process.vertx()
# Set a timer
vertx.set_timer(1000) { |id|
# End the command when the timer is fired
process.end()
}
}
Process events
A command can subscribe to a few process events, named after the posix signals.
SIGINT event
The SIGINT event is fired 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:
command.process_handler() { |process|
vertx = process.vertx()
# Every second print a message on the console
periodicId = vertx.set_periodic(1000) { |id|
process.write("tick\n")
}
# When user press Ctrl+C: cancel the timer and end the process
process.event_handler(:SIGINT) { |event|
vertx.cancel_timer?(periodicId)
process.end()
}
}
When no SIGINT 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.
SIGTSTP/SIGCONT events
The SIGTSTP event is fired when the process is running and the user press Ctrl+Z: the command
is suspended:
-
the command can receive the
SIGTSTPevent 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 SIGCONT event is fired when the process is resumed, usually when the user types fg:
-
the command can receive the
SIGCONTevent when it has registered an handler for this event -
the command will receive anymore data from the standard input when it has registered an stdin handler
command.process_handler() { |process|
# Command is suspended
process.event_handler(:SIGTSTP) { |event|
puts "Suspended"
}
# Command is resumed
process.event_handler(:SIGCONT) { |event|
puts "Resumed"
}
}
Command completion
A command can provide a completion handler when it wants to provide contextual command line interface completion.
Like the process handler, the completion
handler is non blocking because the implementation may use Vert.x services, e.g the file system.
The lineTokens returns a list of 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 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 complete.