The core components of the shell are its plugin model, built-in commands, and converters.
The plugin model is based on Spring. Each plugin jar is required to
contain the file
META-INF/spring/spring-shell-plugin.xml
. These files
will be loaded to bootstrap a Spring
ApplicationContext
when the shell is
started. The essential boostrapping code that looks for your contributions
looks like this:
new ClassPathXmlApplicationContext("classpath*:/META-INF/spring/spring-shell-plugin.xml");
In the spring-shell-plugin.xml
file you should
define the command classes and any other collaborating objects that
support the command's actions. The plugin model is depicted in the
following diagram
Note that the current plugin model loads all plugins under the same class loader. An open JIRA issue suggests providing a classloader per plugin to provide isolation.
An easy way to declare the commands is to use Spring's component
scanning functionality. Here is an example
spring-shell-plugin.xml
from the sample
application:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <context:component-scan base-package="org.springframework.shell.samples.helloworld.commands" /> </beans>
The commands are Spring components, demarcated as such using the
@Component
annotation. For example, the shell of the
HelloWorldCommands
class from the sample
application looks like this
@Component public class HelloWorldCommands implements CommandMarker { // use any Spring annotations for Dependency Injection or other Spring // interfaces as required. // methods with @Cli annotations go here }
Once the commands are registered and instantiated by the Spring
container, they are registered with the core shell parser so that the
@Cli
annotations can be processed. The way the
commands are identified is through the use of the
CommandMarker
interface.
The
org.springframework.shell.core.Converter
interface provides the contract to convert the strings that are entered
on the command line to rich Java types passed into the arguments of
@Cli
-annotated methods.
By default converters for common types are registered. These cover primitive types (boolean, int, float...) as well as Date, Character, and File.
If you need to register any additional
Converter
instances, register them with
the Spring container in the
spring-shell-plugin.xml
file and they will be
picked up automatically.
There are a few built in commands. Here is a listing of their class name and functionality
ConsoleCommands
-
clr
and clear
- to clear the console.
DateCommands
-
date
- show the current date and time.
ExitCommands
-
exit
and quit
- to exit the
shell.
HelpCommands
- help
-
list all commands and their usage
InlineCommentCommands
- //
and ;
shows the valid characters to use for inline comments
OsCommands
- the keyword for this command
is the exclamation point, !
. After the exclamation
point you can pass in a unix/windows command string to be
executed.
SystemPropertyCommands
- system properties
- shows the shell's system properties
VersionCommands
- version
- shows the shell's version
There are two commands in provided by the
AbstractShell
class related to useage of block comments
/*
and */
- The begin and end characters for block comments
There are a few extension points that allow you to customize the shell. The extension points are the interfaces
BannerProvider
- Specifies the
banner text, welcome message, and version number that will be
displayed when the shell is started
PromptProvider
- Specifies the
command prompt text, eg. "shell>
" or
"#
" or "$
". This will be called
after every command execution so it does not need to be a static
string.
HistoryFileNameProvider
-
Specifies the name of the command history file
There is a default implementation for these interfaces but you
should create your own implementations for your own shell application.
All of these interfaces extend from
NamedProvider
. Use
Spring's @Order
annotation to set the priority of the
provider. This allows your provider implementations to take precedence
over any other implementations that maybe present on the classpath from
other plugins.
To make cool "ASCII art" banners the website http://patorjk.com/software/taag is quite neat!
As this is a standard Spring application you can use Spring's
ApplicationContext
event infrastructure to
communicate across plugins.
It has shown to be useful to provide a simple form of interception
around the invocation of a command method. This enables the command class
to check for updates to state, such as configuration information modified
by other plugins, before the command method is executed. The interface
ExecutionProcessor
should be implemented
instead of CommandMarker
to access this
functionality. The ExecutionProcessor
interface is shown below
public interface ExecutionProcessor extends CommandMarker { /** * Method called before invoking the target command (described by {@link ParseResult}). * Additionally, for advanced cases, the parse result itself effectively changing the * invocation calling site. * * @param invocationContext target command context * @return the invocation target */ ParseResult beforeInvocation(ParseResult invocationContext); /** * Method called after successfully invoking the target command (described by * {@link ParseResult}). * * @param invocationContext target command context * @param result the invocation result */ void afterReturningInvocation(ParseResult invocationContext, Object result); /** * Method called after invoking the target command (described by {@link ParseResult}) * had thrown an exception . * * @param invocationContext target command context * @param thrown the thrown object */ void afterThrowingInvocation(ParseResult invocationContext, Throwable thrown); }
There are a few command line options that can be specified when starting the shell. They are
--profiles
- Specifies values for the system
property spring.profiles.active so that Spring 3.1 and greater profile
support is enabled.
--cmdfile
- Specifies a file to read that
contains shell commands
--histsize
- Specifies the maximum number of
lines to store in the command history file. Default value is
3000.
--disableInternalCommands
- Flag that disables all commands that would
be pre-registered with the shell. There is no argument to this option. You can selectively add
back any internal commands by referencing them in your shell plugin file. Look at the
Spring Shell javadocs for specific commands located in the
org.springframework.shell.commands
package as well as the section in this documentation
of Built in commands.
Scripts can be executed either by passing in the
--cmdfile
argument at startup or by executing the
script
command inside the shell. When using scripts it
helps to add comments and this can be done using block comments that start
and end with /*
and */
or an inline
one line comment using the //
or ;
characters.