SHL-23 - Create docbook based reference guide

This commit is contained in:
Mark Pollack
2012-07-18 00:33:17 -04:00
parent fb2ba23587
commit 509eb6fd45
13 changed files with 387 additions and 67 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@@ -16,16 +16,16 @@
<surname>Pollack</surname>
<affiliation>SpringSource</affiliation>
</author>
<author>
<firstname>Jarred</firstname>
<surname>Li</surname>
<affiliation>VMware</affiliation>
</author>
<author>
<firstname>Costin</firstname>
<surname>Leau</surname>
<affiliation>SpringSource</affiliation>
</author>
<author>
<firstname>Jarred</firstname>
<surname>Li</surname>
<affiliation>VMware</affiliation>
</author>
</authorgroup>
<legalnotice>
@@ -63,7 +63,7 @@
<part id="samples">
<title>Spring Shell Sample application</title>
<xi:include href="samples/introduction.xml"/>
<xi:include href="samples/sample-application.xml"/>
<xi:include href="samples/simple-application.xml"/>
</part>
</book>

View File

@@ -6,7 +6,7 @@
xmlns:ns2="http://www.w3.org/1999/xlink"
xmlns:ns="http://docbook.org/ns/docbook">
<para>The Spring Shell provides an interactive shell that lets you
contribute commands using a simple POJO based programming model.</para>
contribute commands using a simple Spring based programming model.</para>
<para>This document is the reference guide for the Spring Shell and covers
the key classes that are part of the Shell infrastructure, the plugin model,

View File

@@ -8,11 +8,7 @@
xmlns:ns="http://docbook.org/ns/docbook">
<title>Requirements</title>
<para>The Spring Shell requires JDK level 6.0 (just like Hadoop) and above
and the Spring <ulink
url="http://www.springsource.org/about">Framework</ulink> 3.0 (3.1
recommended) and above.<ulink
url="http://www.gemstone.com/products/gemfire"/><ulink
url="http://hbase.apache.org/"/><ulink url="http://hive.apache.org/"/><ulink
url="http://pig.apache.org/"/></para>
<para>The Spring Shell requires JDK level 6.0 and above as well as the
Spring <ulink url="http://www.springsource.org/about">Framework</ulink> 3.0
(3.1 recommended) and above.</para>
</chapter>

View File

@@ -8,45 +8,40 @@
<title>Preface</title>
<para>The Spring Shell provides an interactive shell that allows you to
plugin your own custom commands using a Spring based POJO programming
plugin your own custom commands using a Spring based programming
model.</para>
<para>The shell has been extracted from the Spring Roo project, giving it a
strong foundation and rich feature set. One significant change from Spring
Roo is that the plugin model is no longer based on OSGi but instead uses
Spring IoC container to discover commands through classpath scanning. There
is currently no classloader isolation between plugins, however that maybe
added in future versions.</para>
<para>The shell has been extracted from the <link
ns2:href="http://www.springsource.org/spring-roo/">Spring Roo
project</link>, giving it a strong foundation and rich feature set. One
significant change from Spring Roo is that the plugin model is no longer
based on OSGi but instead uses Spring IoC container to discover commands
through classpath scanning. There is currently no classloader isolation
between plugins, however that maybe added in future versions.</para>
<para>Spring Shell's features include</para>
<itemizedlist>
<listitem>
<para>A POJO based programming model to contribute custom
commands</para>
<para>A simple, annotation driven, programming model to contribute
custom commands</para>
</listitem>
<listitem>
<para>Use Spring's classpath scanning functionality as a basis for a
command plugin strategy</para>
<para>Use of Spring's classpath scanning functionality asthe basis for a
command plugin strategy and command develoment</para>
</listitem>
<listitem>
<para>Inheritance of the <link
ns2:href="http://static.springsource.org/spring-roo/reference/html-single/index.html#usage-shell">Roo
Shell features</link></para>
Shell features</link>, most notably tab completion, colorization, and
script execution.</para>
</listitem>
<listitem>
<para>Tab completion</para>
</listitem>
<listitem>
<para>Scripting and Script recording</para>
</listitem>
<listitem>
<para>Customize command prompt, banner, shell history file name.</para>
<para>Customizatin of command prompt, banner, shell history file
name.</para>
</listitem>
</itemizedlist>

View File

@@ -10,35 +10,151 @@
<title>Developing Spring Shell Applications</title>
<para>Contributing commands to the shell is very easy. There are only a few
annotations you need to learn. The implementation style of the command is in
the style of developing an application that uses dependency injection as you
can leverage all the features of the Spring container.</para>
annotations you need to learn. The implementation style of the command is
the same as developing classes in for application that uses dependency
injection. You can leverage all the features of the Spring container to
implement your command classes.</para>
<section>
<title>Marker Interface</title>
<para>The first step to creating a command is to implement the market
interface CommandMarker and to annotate your class with Spring's
@Component annotation. (Note there is an open JIRA issue to provide a
@CliCommand meta-annotation to avoid having to use a market interface)
Taking the </para>
</section>
<para>The first step to creating a command is to implement the marker
interface <interfacename>CommandMarker</interfacename> and to annotate
your class with Spring's <classname>@Component </classname>annotation.
(Note there is an open JIRA issue to provide a
<classname>@CliCommand</classname> meta-annotation to avoid having to use
a marker interface). Using the code from the helloworld sample
application, the shell of a <classname>HelloWorldCommands</classname>
class is shown below </para>
<section>
<title>CLI Annotations</title>
<programlisting>@Component
public class HelloWorldCommands implements CommandMarker {
// use any Spring annotations for Dependency Injection or other Spring interfaces as required.
<para>annotations</para>
// methods with @Cli annotations go here
}</programlisting>
</section>
<section>
<title>Logging</title>
<para/>
<para>Logging is done using JDK logging. Simply add a LOG declaration as
shown below to use a logger.</para>
<programlisting>@Component
public class HelloWorldCommands implements CommandMarker {
protected final Logger LOG = Logger.getLogger(getClass().getName());
// methods with @Cli annotations go here
}</programlisting>
</section>
<section>
<title>CLI Annotations</title>
<para>There are three annotations used on methods and method arguments
that define main contract for interacting with the shell. These are</para>
<itemizedlist>
<listitem>
<para><classname>CliAvailabilityIndicator</classname> - Placed on a
method that returns a boolean value and indicates if a particular
command can be presented in the shell. This decision is usually based
on the history of commands that have been executed previously. It
prevents extraneous commands being presented until some preconditions
are met, for example the execution of a 'configuration'
command.</para>
</listitem>
<listitem>
<para><classname>CliCommand</classname> - Placed on a method that
provides a command to the shell. Its value provides one or more
strings that serve as the start of a particular command name. These
must be unique within the entire application, across all
plugins.</para>
</listitem>
<listitem>
<para><classname>CliOptions</classname> - Placed on the arguments of a
command methods, allowing it to declare the argument value as
mandatory or optional with a default value.</para>
</listitem>
</itemizedlist>
<para>Here is a simple use of these annotations in a command class </para>
<programlisting language="java">@Component
public class HelloWorldCommands implements CommandMarker {
protected final Logger LOG = Logger.getLogger(getClass().getName());
@CliAvailabilityIndicator({"hw simple"})
public boolean isCommandAvailable() {
return true;
}
@CliCommand(value = "hw simple", help = "Print a simple hello world message")
public void simple(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
@CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello", specifiedDefaultValue="At work") final String location) {
LOG.info("Message = [" + message + "] Location = [" + location + "]");
}
}</programlisting>
<para>The method annotated with <classname>@CliAvailabilityIndicator
</classname>is returning true so that the one and only command in this
class is exposed to the shell to be invoked. If there were more commands
in the class, you would list them as comma separated value.</para>
<para>The <classname>@CliCommand</classname> annotation is creating the
command '<literal>hw simple</literal>' in the shell. The help message is
what will be printed if you use the build in <literal>help</literal>
command. The method name is '<methodname>simple</methodname>' but it could
just have well been any other name.</para>
<para>The <classname>@CliOption</classname> annotation on each of the
command arguments is where you will spend most of your time authoring
commands. You need to decide which arguments are required, which are
optional, and if they are optional is there a default value. In this case
there are two arguments or keys to the command, message and location. The
key message is required and a help message is provided to give guidance to
the user when tabbing to get completion for the command. </para>
<para>The implementation of the 'simple' method is trivial, just a log
statement, but this is where you would typically call other collaborating
objects that were injected into the class via Spring.</para>
<para>The method argument types in this example are
<classname>String</classname>, which doesn't present any issue with type
conversion. You can specify methods with any rich object type including
basic primitive types such as int, float etc. For all types other than
those handled by the shell by default (basic types,
<classname>Date</classname>, <classname>File</classname>) you will need to
register your own implementation of the
<interfacename>org.springframework.shell.core.Converter</interfacename>
interface with the container in your plugin.</para>
</section>
<section>
<title>Building and running the shell</title>
<para/>
<para>In our opinion, the easiest way to build an execute the shell is to
cut-n-paste the gradle script in the example application. This uses the
application plugin from gradle to create a bin directory with a startup
script for windows and Unix and places all dependent jars in a lib
directory. Maven has a similar plugin - the <link
xlink:href="http://mojo.codehaus.org/appassembler/appassembler-maven-plugin/">AppAssembler</link>
plugin.</para>
<para>The main class of the shell is
<classname>org.springframework.shell.Bootstrap</classname>. As long as you
place other plugins, perhaps developed independently, on the classpath,
the Bootstrap class will incorporate them into the shell.</para>
</section>
</chapter>

View File

@@ -23,8 +23,8 @@
looks like this<programlisting>new ClassPathXmlApplicationContext("classpath*:/META-INF/spring/spring-shell-plugin.xml");</programlisting></para>
<para>In the <literal>spring-shell-plugin.xml</literal> file you should
define the command classes and any other collaboration objects that
support the commands actions. The plugin model is depicted in the
define the command classes and any other collaborating objects that
support the command's actions. The plugin model is depicted in the
following diagram</para>
<mediaobject>
@@ -41,7 +41,7 @@
<title>Commands</title>
<para>An easy way to declare the commands is to use Spring's component
scanning functionality.Here is an example
scanning functionality. Here is an example
<literal>spring-shell-plugin.xml </literal>that from the sample
application.</para>
@@ -56,7 +56,7 @@
&lt;/beans&gt;</programlisting>
<para>The commands are Spring components, demarcated as such using the
<literal>@Component</literal> annotation. For example, the
<literal>@Component</literal> annotation. For example, the shell of the
<classname>HelloWorldCommands</classname> class from the sample
application looks like this</para>
@@ -82,7 +82,7 @@ public class HelloWorldCommands implements CommandMarker {
<para>The
<interfacename>org.springframework.shell.core.Converter</interfacename>
interface provides the contract to convert the strings that are entered
in the command to rich Java types passed into the arguments of
on the command line to rich Java types passed into the arguments of
<classname>@Cli</classname>-annotated methods.</para>
<para>By default converters for common types are registered. These cover
@@ -122,6 +122,30 @@ public class HelloWorldCommands implements CommandMarker {
executed.</para>
</listitem>
</itemizedlist>
<para>There are also a few commands that are provided by the
<classname>AbstractShell</classname> class, these are</para>
<itemizedlist>
<listitem>
<para><literal>date</literal> - Displays the local date and
time</para>
</listitem>
<listitem>
<para><literal>script</literal> - Parses the specified resource file
and executes its commands</para>
</listitem>
<listitem>
<para><literal>system properties</literal> - Shows the shell's
properties</para>
</listitem>
<listitem>
<para><literal>version</literal> - Displays current CLI version</para>
</listitem>
</itemizedlist>
</section>
<section>
@@ -133,13 +157,14 @@ public class HelloWorldCommands implements CommandMarker {
<itemizedlist>
<listitem>
<para><interfacename>BannerProvider</interfacename> - Specifies the
banner text , welcome message, and version number that will be
banner text, welcome message, and version number that will be
displayed when the shell is started</para>
</listitem>
<listitem>
<para><interfacename>PromptProvider</interfacename> - Specifies the
command prompt text</para>
command prompt text, eg. "<literal>shell&gt;</literal>" or
"<literal>#</literal>" or "<literal>$</literal>"</para>
</listitem>
<listitem>
@@ -176,10 +201,10 @@ public class HelloWorldCommands implements CommandMarker {
<para>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 is executed. The interface
by other plugins, before the command method is executed. The interface
<interfacename>ExecutionProcess</interfacename> should be implemented
instead of <interfacename>CommandMarker</interfacename> to access this
functionlatiy. The <interfacename>ExecutionProcess</interfacename>
functionality. The <interfacename>ExecutionProcess</interfacename>
interface is shown below</para>
<programlisting>public interface ExecutionProcessor extends CommandMarker {
@@ -212,4 +237,43 @@ public class HelloWorldCommands implements CommandMarker {
}</programlisting>
</section>
<section>
<title>Command line options</title>
<para>There are a few command line options that can be specified when
starting the shell. They are </para>
<itemizedlist>
<listitem>
<para><literal>--profiles</literal> - Specifies values for the system
property spring.profiles.active so that Spring 3.1 and greater <link
xlink:href="http://blog.springsource.com/2011/02/11/spring-framework-3-1-m1-released/">profile
support </link>is enabled.</para>
</listitem>
<listitem>
<para><literal>--cmdfile</literal> - Specifies a file to read that
contains shell commands</para>
</listitem>
<listitem>
<para><literal>--histsize</literal> - Specifies the maximum number of
lines to store in the command history file. Default value is
3000.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Scripts and comments</title>
<para>Scripts can be executed either by passing in the
<literal>--cmdfile</literal> argument at startup or by executing the
<literal>script</literal> 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 <literal>/*</literal> and <literal>*/</literal> or an inline
one line command using <literal>//</literal> or <literal>;</literal>
characters.</para>
</section>
</chapter>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5.0" xml:id="simple-application" xmlns="http://docbook.org/ns/docbook"
<chapter version="5.0" xml:id="simple-application"
xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
@@ -8,12 +9,127 @@
xmlns:ns="http://docbook.org/ns/docbook">
<title>Simple sample application using the Spring Shell</title>
<section id="shell:simple-application">
<section>
<title>Introduction</title>
This sample demonstrates how to execute a MapReduce application and a script that interacts with HDFS inside a Spring based application. It does not use spring Batch or Spring Integration.
<para>The sample application named 'helloworld' contains three
'<literal>hw</literal>' commands, they are '<literal>hw simple</literal>',
'<literal>hw complex</literal>' and '<literal>hw enum</literal>' and
demonstrate simple to intermediate level usage of the
<literal>@Cli</literal> annotation classes for creating commands.</para>
<para>The example code is located in the distribution directory
<literal>&lt;spring-hadoop-install-dir&gt;/samples/wordcount</literal>.</para>
<literal>&lt;spring-shell-install-dir&gt;/samples/helloworld</literal>.</para>
<para>To build the example cd to the helloworld directory and execute
<literal>..\..\gradlew installApp</literal>. To run the application cd to
<literal>build\install\helloworld\bin</literal> and execute the helloworld
script.</para>
</section>
<section>
<title>HelloWorldCommands</title>
<para>The <classname>HelloWorldCommands</classname> class is show
below</para>
<programlisting language="java">package org.springframework.shell.samples.helloworld.commands;
import java.util.logging.Logger;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldCommands implements CommandMarker {
protected final Logger LOG = Logger.getLogger(getClass().getName());
private boolean simpleCommandExecuted = false;
@CliAvailabilityIndicator({"hw simple"})
public boolean isSimpleAvailable() {
//always available
return true;
}
@CliAvailabilityIndicator({"hw complex", "hw enum"})
public boolean isComplexAvailable() {
if (simpleCommandExecuted) {
return true;
} else {
return false;
}
}
@CliCommand(value = "hw simple", help = "Print a simple hello world message")
public void simple(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
@CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello", specifiedDefaultValue="At work") final String location) {
LOG.info("Message = [" + message + "] Location = [" + location + "]");
simpleCommandExecuted = true;
}
@CliCommand(value = "hw complex", help = "Print a complex hello world message")
public void hello(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
@CliOption(key = { "name1"}, mandatory = true, help = "Say hello to the first name") final String name1,
@CliOption(key = { "name2" }, mandatory = true, help = "Say hello to a second name") final String name2,
@CliOption(key = { "time" }, mandatory = false, specifiedDefaultValue="now", help = "When you are saying hello") final String time,
@CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello") final String location) {
LOG.info("Hello " + name1 + " and " + name2 + ". Your special message is " + message + ". time=[" + time + "] location=[" + location + "]");
}
@CliCommand(value = "hw enum", help = "Print a simple hello world message from an enumerated value")
public void eenum(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final MessageType message){
LOG.info("Hello. You special enumerated message is " + message);
}
enum MessageType {
Type1("type1"),
Type2("type2"),
Type3("type3");
private String type;
private MessageType(String type){
this.type = type;
}
public String getType(){
return type;
}
}
}
</programlisting>
<para>The use of the <classname>@CliAvailabilityIndicator</classname>
annotation on two methods, <methodname>isSimpleAvailable</methodname> and
<methodname>isComplexAvailable</methodname> shows how you can enable the
presence of the '<literal>hw complex</literal>' and '<literal>hw
enum</literal>' commands only if the '<literal>hw simple</literal>'
command was executed.</para>
<para>Here is an example session showing the behavior.</para>
<mediaobject>
<imageobject>
<imagedata fileref="../images/shell-example.jpg"/>
</imageobject>
</mediaobject>
<para>The '<literal>hw enum</literal>' command shows how the shell
supports the use of Enumeration as command method arguments.</para>
<mediaobject>
<imageobject>
<imagedata fileref="../images/shell-example-enum.jpg"/>
</imageobject>
</mediaobject>
</section>
</chapter>

View File

@@ -5,4 +5,4 @@ target/
/log.roo
*.log
/bin/

View File

@@ -11,15 +11,34 @@ import org.springframework.stereotype.Component;
@Component
public class HelloWorldCommands implements CommandMarker {
protected final Logger LOG = Logger.getLogger(getClass().getName());
@CliAvailabilityIndicator({"hw echo"})
public boolean isCommandAvailable() {
private boolean simpleCommandExecuted = false;
@CliAvailabilityIndicator({"hw simple"})
public boolean isSimpleAvailable() {
//always available
return true;
}
@CliAvailabilityIndicator({"hw complex", "hw enum"})
public boolean isComplexCommandAvailable() {
if (simpleCommandExecuted) {
return true;
} else {
return false;
}
}
@CliCommand(value = "hw simple", help = "Print a simple hello world message")
public void simple(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
@CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello", specifiedDefaultValue="At work") final String location) {
LOG.info("Message = [" + message + "] Location = [" + location + "]");
simpleCommandExecuted = true;
}
@CliCommand(value = "hw complex", help = "Print a complex hello world message")
public void hello(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,

View File

@@ -20,6 +20,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* Annotates a method that provides a command to the shell.
*
* @author Ben Alex
* @since 1.0
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CliCommand {

View File

@@ -22,7 +22,13 @@ import java.lang.annotation.Target;
import org.springframework.shell.core.Converter;
/**
* Annotates the arguments of a command methods, allowing it to declare the argument value as mandatory or optional with a default value.
*
* @author Ben Alex
* @since 1.0
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CliOption {