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

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

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

  • 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

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

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 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);

}

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.

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

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