2. Spring Shell

The core components of the shell are its plugin model, built-in commands, and converters

2.1 Plugin Model

The plugin model is based 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 issus is to provide a classloader per plugin to provide isolation.

2.1.1 Commands

An easy way to declare the commands is to use Spring's component scanning functionality. Here is an example spring-shell-plugin.xml that 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

}

One 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.

2.1.2 Converters

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.

2.2 Built in commands

There are a few built in commands. Here is a listing of their class name and functionality

  • EssentialCommands - exit and quit - to exit the shell.

  • HelpCommands - help - list all commands and their usage

  • 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.

There are also a few commands that are provided by the AbstractShell class, these are

  • date - Displays the local date and time

  • script - Parses the specified resource file and executes its commands

  • system properties - Shows the shell's properties

  • version - Displays current CLI version

2.3 Customizing the shell

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 "$"

  • 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. Use Spring's @Ordered annotation to set the priority of the provider. This allows your provider implementations to take precidence 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!

2.4 Communicating between plugins

As this is a standard Spring application you can use Spring's ApplicationContext event infrastructure to communicate across plugins.

2.5 Command method interception

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 ExecutionProcess should be implemented instead of CommandMarker to access this functionality. The ExecutionProcess 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);

}

2.6 Command line options

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.

2.7 Scripts and comments

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 command using // or ; characters.