changeset 1643:d92c80b54723

Add a summary attribute for commands Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-December/012319.html
author Omair Majid <omajid@redhat.com>
date Wed, 17 Dec 2014 14:51:09 -0500
parents 0d3f0fb73304
children b16767bbc15f
files dev/schema-info-command/distribution/thermostat-plugin.xml distribution/config/commands/add-mongodb-user.properties distribution/config/commands/agent-info.properties distribution/config/commands/agent.properties distribution/config/commands/clean-data.properties distribution/config/commands/connect.properties distribution/config/commands/disconnect.properties distribution/config/commands/gui.properties distribution/config/commands/help.properties distribution/config/commands/list-agents.properties distribution/config/commands/list-vms.properties distribution/config/commands/ping.properties distribution/config/commands/service.properties distribution/config/commands/shell.properties distribution/config/commands/storage.properties distribution/config/commands/vm-info.properties distribution/config/commands/vm-stat.properties distribution/docs/thermostat-plugin.xsd killvm/distribution/thermostat-plugin.xml launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfo.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/CommandInfo.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/HelpCommand.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginInfoSource.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/HelpCommandTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/TestCommandInfo.java storage-profile/distribution/thermostat-plugin.xml validate-command/distribution/thermostat-plugin.xml vm-gc/distribution/thermostat-plugin.xml vm-heap-analysis/distribution/thermostat-plugin.xml vm-profiler/distribution/thermostat-plugin.xml web/endpoint-plugin/distribution/thermostat-plugin.xml
diffstat 38 files changed, 252 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/dev/schema-info-command/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/dev/schema-info-command/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>list-categories</name>
+      <summary>lists all registered categories</summary>
       <description>lists all registered categories in Storage</description>
       <environments>
         <environment>cli</environment>
--- a/distribution/config/commands/add-mongodb-user.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/add-mongodb-user.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -5,6 +5,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}
 
+summary = adds a new mongodb user to the thermostat DB
+
 description = adds a new mongodb user to the thermostat DB, reading credentials \
  from standard input
 
--- a/distribution/config/commands/agent-info.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/agent-info.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}, \
 
+summary = shows info for specified agent
+
 description = shows info for specified agent
 
 usage = agent-info [-d <url>] [-l <level>] -a <agent-id>
--- a/distribution/config/commands/agent.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/agent.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -19,6 +19,8 @@
           org.apache.commons.logging=${commons-logging.version}, \
           org.jboss.netty=${netty.version}
 
+summary = starts and stops the thermostat agent
+
 description = starts and stops the thermostat agent
 
 usage = agent [-d <url>] [-s] [-l <level>]
--- a/distribution/config/commands/clean-data.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/clean-data.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}, \
 
+summary = drop all data related to all of the specified agents
+
 description = drop all data related to all of the specified agents
 
 usage = clean-data [--alive] [--all] <agent_id>
--- a/distribution/config/commands/connect.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/connect.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -13,6 +13,8 @@
           ${osgi.compendium.bundle.symbolic-name}=${osgi.compendium.osgi-version}, \
           com.google.gson=${gson.version}
 
+summary = persistently connect to storage
+
 description = persistently connect to storage
 
 usage = connect -d <url> [-l <level>]
--- a/distribution/config/commands/disconnect.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/disconnect.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -1,5 +1,7 @@
 bundles = com.redhat.thermostat.client.cli=${project.version}
 
+summary = disconnect from the currently used storage
+
 description = disconnect from the currently used storage
 
 usage = disconnect [-l <level>]
--- a/distribution/config/commands/gui.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/gui.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -22,6 +22,8 @@
           ${osgi.compendium.bundle.symbolic-name}=${osgi.compendium.osgi-version}, \
           org.jboss.netty=${netty.version}
 
+summary = launches the GUI client
+
 description = launches the GUI client
 
 usage = gui [-l <level>]
--- a/distribution/config/commands/help.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/help.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -1,5 +1,6 @@
 # HelpCommand is provided by launcher, and needs no other bundles to be loaded.
 bundles =
+summary = show help for a given command or help overview
 description = show help for a given command or help overview
 usage = help [command-name]
 
--- a/distribution/config/commands/list-agents.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/list-agents.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}, \
 
+summary = lists all agents for the current host
+
 description = lists all agents for the current host
 
 usage = list-agents [-d <url>] [-l <level>]
--- a/distribution/config/commands/list-vms.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/list-vms.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}, \
 
+summary = lists all currently monitored VMs
+
 description = lists all currently monitored VMs
 
 usage = list-vms [-d <url>] [-l <level>]
--- a/distribution/config/commands/ping.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/ping.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -14,6 +14,8 @@
           org.apache.commons.logging=${commons-logging.version}, \
           org.jboss.netty=${netty.version}
 
+summary = using the Command Channel, send a ping to a running agent
+
 description = using the Command Channel, send a ping to a running agent
 
 usage = ping <agentId> [-d <url>] [-l <level>]
--- a/distribution/config/commands/service.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/service.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           com.redhat.thermostat.agent.cli=${project.version}, \
           org.jboss.netty=${netty.version}
 
+summary = starts and stops the thermostat storage and agent
+
 description = starts and stops the thermostat storage and agent
 
 usage = service [-l <level>]
--- a/distribution/config/commands/shell.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/shell.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -1,7 +1,11 @@
 # ShellCommand is provided by the tools bundle, which is a bootstrap bundle, and requires no other bundles.
 bundles = com.redhat.thermostat.client.cli=${project.version}
 
-description = launches the Thermostat interactive shell. The prompt displays a "-" to indicate that it is disconnected or a "+" to indicate that it is connected to storage
+summary = launches the Thermostat interactive shell
+
+description = launches the Thermostat interactive shell. \
+The prompt displays a "-" to indicate that it is disconnected or a "+" to indicate that it is connected to storage
+
 usage = shell
 
 # This command does not have any options
--- a/distribution/config/commands/storage.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/storage.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -4,6 +4,8 @@
           org.apache.commons.codec=${commons-codec.osgi-version}, \
           org.jboss.netty=${netty.version}
 
+summary = starts and stops the thermostat storage
+
 description = starts and stops the thermostat storage
 
 usage = storage <-start|--stop|--status> [--dryRun] [-q] [--permitLocalhostException] [-l <level>]
--- a/distribution/config/commands/vm-info.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/vm-info.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -12,6 +12,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}
 
+summary = shows basic information about a VM
+
 description = shows basic information about a VM
 
 usage = vm-info [-vmId <vm>] [--hostId <host>] [-d <url>] [-l <level>]
--- a/distribution/config/commands/vm-stat.properties	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/config/commands/vm-stat.properties	Wed Dec 17 14:51:09 2014 -0500
@@ -13,6 +13,8 @@
           org.apache.commons.collections=${commons-collections.version}, \
           org.apache.commons.logging=${commons-logging.version}
 
+summary = show various statistics about a VM
+
 description = show various statistics about a VM
 
 usage = vm-stat -hostId <host> --vmId <vm> [-c] [-s <time:timeunit>] [-d <url>] [-l <level>]
--- a/distribution/docs/thermostat-plugin.xsd	Tue Dec 16 16:20:05 2014 -0500
+++ b/distribution/docs/thermostat-plugin.xsd	Wed Dec 17 14:51:09 2014 -0500
@@ -7,15 +7,37 @@
 
 <!-- definition of simple elements -->
 <xs:element name="name" type="xs:string"/>
+
 <xs:element name="symbolic-name" type="xs:string"/>
+
 <xs:element name="version" type="xs:string"/>
+
 <xs:element name="usage" type="xs:string"/>
-<xs:element name="description" type="xs:string"/>
+
+<xs:element name="summary" type="xs:string">
+  <xs:annotation>
+    <xs:documentation>A very short summary of what this command does.
+    Ideally around 50 characters.</xs:documentation>
+  </xs:annotation>
+</xs:element>
+
+<xs:element name="description" type="xs:string">
+  <xs:annotation>
+    <xs:documentation>A complete description of what this command
+    does. Can be long.</xs:documentation>
+  </xs:annotation>
+</xs:element>
+
 <xs:element name="short" type="xs:string"/>
+
 <xs:element name="long" type="xs:string"/>
+
 <xs:element name="argument" type="xs:string"/>
+
 <xs:element name="required" type="xs:boolean"/>
+
 <xs:element name="id" type="xs:string"/>
+
 <xs:element name="configuration" type="xs:string"/>
 
 
@@ -69,6 +91,7 @@
     <xs:sequence>
       <xs:element ref="name"/>
       <xs:element ref="usage" minOccurs="0" maxOccurs="1"/>
+      <xs:element ref="summary" minOccurs="0" maxOccurs="1"/>
       <xs:element ref="description"/>
       <xs:element ref="arguments" minOccurs="0" maxOccurs="1"/>
       <xs:element ref="options" minOccurs="0" maxOccurs="1"/>
--- a/killvm/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/killvm/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>kill-vm</name>
+      <summary>kill the specified vm</summary>
       <description>kill the specified vm</description>
       <options>
         <option>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java	Wed Dec 17 14:51:09 2014 -0500
@@ -47,14 +47,16 @@
 public class BasicCommandInfo implements CommandInfo {
 
     private final String name;
+    private final String summary;
     private final String description;
     private final String usage;
     private final Options options;
     private final Set<Environment> environments;
     private final List<BundleInformation> bundles;
 
-    public BasicCommandInfo(String name, String description, String usage, Options options, Set<Environment> environments, List<BundleInformation> bundles) {
+    public BasicCommandInfo(String name, String summary, String description, String usage, Options options, Set<Environment> environments, List<BundleInformation> bundles) {
         this.name = name;
+        this.summary = summary;
         this.description = description;
         this.usage = usage;
         this.options = options;
@@ -68,6 +70,11 @@
     }
 
     @Override
+    public String getSummary() {
+        return summary;
+    }
+
+    @Override
     public String getDescription() {
         return description;
     }
@@ -94,7 +101,7 @@
 
     @Override
     public String toString() {
-        return String.format("%s (description='%s', dependencies='%s')", name, description, bundles.toString());
+        return String.format("%s (summary='%s', description='%s', dependencies='%s')", name, summary, description, bundles.toString());
     }
 }
 
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfo.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BuiltInCommandInfo.java	Wed Dec 17 14:51:09 2014 -0500
@@ -59,6 +59,7 @@
 
     private static final Logger logger = LoggingUtils.getLogger(BuiltInCommandInfo.class);
     private static final String PROPERTY_BUNDLES = "bundles";
+    private static final String PROPERTY_SUMMARY = "summary";
     private static final String PROPERTY_DESC = "description";
     private static final String PROPERTY_USAGE = "usage";
     private static final String PROPERTY_OPTIONS = "options";
@@ -70,7 +71,7 @@
     private static final String PROP_OPTREQUIRED = ".required";
     private static final String PROP_OPTDESC = ".description";
     
-    private String name, description, usage;
+    private String name, summary, description, usage;
     private Options options;
     private EnumSet<Environment> environment;
     private List<BundleInformation> dependencies;
@@ -82,6 +83,8 @@
             String key = (String) entry.getKey();
             if (key.equals(PROPERTY_BUNDLES)) {
                 learnDependencies((String) entry.getValue());
+            } else if (key.equals(PROPERTY_SUMMARY)) {
+                summary = properties.getProperty(key);
             } else if (key.equals(PROPERTY_DESC)) {
                 description = properties.getProperty(key);
             } else if (key.equals(PROPERTY_USAGE)) {
@@ -359,18 +362,27 @@
         return result;
     }
 
+    @Override
     public String getName() {
         return name;
     }
 
+    @Override
+    public String getSummary() {
+        return summary;
+    }
+
+    @Override
     public String getDescription() {
         return description;
     }
 
+    @Override
     public String getUsage() {
         return usage;
     }
 
+    @Override
     public Options getOptions() {
         return options;
     }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CommandInfo.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CommandInfo.java	Wed Dec 17 14:51:09 2014 -0500
@@ -52,7 +52,14 @@
     public String getName();
 
     /**
-     * A short description for the command indicating what it does.
+     * A very short description of the command indicating what it does. Ideally
+     * a small sentence.
+     */
+    public String getSummary();
+
+    /**
+     * A description of the command indicating what it does. Unlike
+     * {@link #getSummary()}, this can be as detailed as needed.
      */
     public String getDescription();
 
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java	Wed Dec 17 14:51:09 2014 -0500
@@ -130,6 +130,7 @@
         }
         String name = info1.getName();
 
+        String summary = selectBest(info1.getSummary(), info2.getSummary());
         String description = selectBest(info1.getDescription(), info2.getDescription());
         String usage = selectBest(info1.getUsage(), info2.getUsage());
         Options options = selectBest(info1.getOptions(), info2.getOptions());
@@ -139,7 +140,7 @@
         bundles.addAll(info2.getBundles());
 
 
-        return new BasicCommandInfo(name, description, usage, options, environment, bundles);
+        return new BasicCommandInfo(name, summary, description, usage, options, environment, bundles);
     }
 
     private <T> T selectBest(T first, T second) {
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/HelpCommand.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/HelpCommand.java	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Option;
@@ -113,29 +114,7 @@
     }
 
     private void printCommandSummary(TableRenderer renderer, CommandInfo info) {
-        renderer.printLine(" " + info.getName(), createShortDescription(info));
-    }
-
-    private String createShortDescription(CommandInfo info) {
-        String fullDescription = info.getDescription();
-
-        int firstDot = fullDescription.indexOf('.');
-        if (firstDot == -1) {
-            return fullDescription;
-        }
-
-        String firstSentence = fullDescription.substring(0, firstDot);
-        String shortDescription = firstSentence.trim();
-        shortDescription = lowerCaseFirstLetter(shortDescription);
-        return shortDescription;
-    }
-
-    private String lowerCaseFirstLetter(String shortDescription) {
-        if (Character.isUpperCase(shortDescription.charAt(0))) {
-            shortDescription = shortDescription.substring(0, 1).toLowerCase()
-                    + shortDescription.substring(1);
-        }
-        return shortDescription;
+        renderer.printLine(" " + info.getName(), info.getSummary());
     }
 
     private void printCommandUsage(CommandContext ctx, String cmdName) {
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java	Wed Dec 17 14:51:09 2014 -0500
@@ -114,18 +114,20 @@
 
         private final String commandName;
         private final String usage;
+        private final String summary;
         private final String description;
         private final List<String> positionalArguments;
         private final Options options;
         private final Set<Environment> environment;
         private final List<BundleInformation> bundles;
 
-        public NewCommand(String name, String usage, String description,
+        public NewCommand(String name, String usage, String summary, String description,
                 List<String> positionalArguments, Options options,
                 Set<Environment> environment,
                 List<BundleInformation> bundles) {
             this.commandName = name;
             this.usage = usage;
+            this.summary = summary;
             this.description = description;
             this.positionalArguments = positionalArguments;
             this.options = options;
@@ -146,6 +148,10 @@
             return usage;
         }
 
+        public String getSummary() {
+            return summary;
+        }
+
         public String getDescription() {
             return description;
         }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Wed Dec 17 14:51:09 2014 -0500
@@ -91,7 +91,8 @@
   &lt;commands&gt;
     &lt;command&gt;
       &lt;name&gt;platform&lt;/name&gt;
-      &lt;description&gt;launches a bare bone Platform Client&lt;/description&gt;
+      &lt;summary&gt;launches a bare bone Platform Client&lt;/summary&gt;
+      &lt;description&gt;launches a bare bone Platform Client. Can also do bar if optA is used.&lt;/description&gt;
       &lt;arguments&gt;
         &lt;argument&gt;argument1&lt;/argument&gt;
       &lt;/arguments&gt;
@@ -155,7 +156,8 @@
     &lt;/command&gt;
     &lt;command&gt;
       &lt;name&gt;platform2&lt;/name&gt;
-      &lt;description&gt;launches a bare bone Platform Client&lt;/description&gt;
+      &lt;summary&gt;launches a bare bone Platform Client&lt;/summary&gt;
+      &lt;description&gt;launches a bare bone Platform Client. Can also do bar if foo is used.&lt;/description&gt;
       &lt;arguments&gt;
         &lt;argument&gt;argument2&lt;/argument&gt;
       &lt;/arguments&gt;
@@ -213,7 +215,6 @@
         PluginConfiguration config = null;
         try (FileInputStream fis = new FileInputStream(configurationFile)) {
             config = parse(configurationFile.getParentFile().getName(), fis);
-
         } catch (IOException ioFisClosed) {
             // ignore if fis closing fails
         }
@@ -352,6 +353,7 @@
     private NewCommand parseNewCommand(String pluginName, Node commandNode) {
         String name = null;
         String usage = null;
+        String summary = null;
         String description = null;
         List<String> arguments = new ArrayList<>();
         Options options = new Options();
@@ -365,6 +367,8 @@
                 name = node.getTextContent().trim();
             } else if (node.getNodeName().equals("usage")) {
                 usage = node.getTextContent().trim();
+            } else if (node.getNodeName().equals("summary")) {
+                summary = node.getTextContent().trim();
             } else if (node.getNodeName().equals("description")) {
                 description = parseDescription(node);
             } else if (node.getNodeName().equals("arguments")) {
@@ -378,6 +382,12 @@
             }
         }
 
+        if (summary == null) {
+            logger.warning("plugin " + pluginName + " does not provide a summary for " + name +
+                    ". A summary will be auto-generated.");
+            summary = createSummary(description);
+        }
+
         if (bundles.isEmpty()) {
             logger.warning("plugin " + pluginName  + " provides a new command " + name + " but supplies no bundles");
         }
@@ -387,7 +397,7 @@
                     "name='" + name + "', description='" + description + "', options='" + options + "'");
             return null;
         } else {
-            return new NewCommand(name, usage, description, arguments, options, availableInEnvironments, bundles);
+            return new NewCommand(name, usage, summary, description, arguments, options, availableInEnvironments, bundles);
         }
     }
 
@@ -596,6 +606,26 @@
         return result;
     }
 
+    private String createSummary(String fullDescription) {
+        int firstDot = fullDescription.indexOf('.');
+        if (firstDot == -1) {
+            return fullDescription;
+        }
+
+        String firstSentence = fullDescription.substring(0, firstDot);
+        String shortDescription = firstSentence.trim();
+        shortDescription = lowerCaseFirstLetter(shortDescription);
+        return shortDescription;
+    }
+
+    private String lowerCaseFirstLetter(String shortDescription) {
+        if (Character.isUpperCase(shortDescription.charAt(0))) {
+            shortDescription = shortDescription.substring(0, 1).toLowerCase()
+                    + shortDescription.substring(1);
+        }
+        return shortDescription;
+    }
+
 
     private static class ConfigurationParserErrorHandler implements ErrorHandler {
 
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginInfoSource.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginInfoSource.java	Wed Dec 17 14:51:09 2014 -0500
@@ -173,6 +173,7 @@
                 usage = usageBuilder.getUsage(commandName, command.getOptions(), command.getPositionalArguments().toArray(new String[0]));
             }
             BasicCommandInfo info = new BasicCommandInfo(commandName,
+                    command.getSummary(),
                     command.getDescription(),
                     usage,
                     command.getOptions(),
@@ -194,6 +195,7 @@
                 updatedBundles.addAll(old.getBundles());
                 updatedBundles.addAll(entry.getValue());
                 BasicCommandInfo updated = new BasicCommandInfo(old.getName(),
+                        old.getSummary(),
                         old.getDescription(),
                         old.getUsage(),
                         old.getOptions(),
@@ -212,7 +214,7 @@
         }
         List<BundleInformation> bundles = additionalBundlesForExistingCommands.get(name);
         if (bundles != null) {
-            return new BasicCommandInfo(name, null, null, null, null, bundles);
+            return createCommandInfo(name, bundles);
         }
         throw new CommandInfoNotFoundException(name);
     }
@@ -222,11 +224,15 @@
         List<CommandInfo> result = new ArrayList<>();
         result.addAll(allNewCommands.values());
         for (Entry<String, List<BundleInformation>> entry : additionalBundlesForExistingCommands.entrySet()) {
-            result.add(new BasicCommandInfo(entry.getKey(), null, null, null, null, entry.getValue()));
+            result.add(createCommandInfo(entry.getKey(), entry.getValue()));
         }
         return result;
     }
 
+    private BasicCommandInfo createCommandInfo(String name, List<BundleInformation> bundles) {
+        return new BasicCommandInfo(name, null, null, null, null, null, bundles);
+    }
+
     public Map<String, String> getConfiguration(String pluginID, String fileName) throws IOException {
         Configurations config = this.allConfigs.get(new PluginID(pluginID));
         if (config != null && config.containsFile(fileName)) {
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java	Wed Dec 17 14:51:09 2014 -0500
@@ -48,27 +48,28 @@
 
 import com.redhat.thermostat.launcher.BundleInformation;
 
-
 public class BasicCommandInfoTest {
 
     @Test
     public void testBasics() {
         final String NAME = "the_name";
+        final String SUMMARY = "some-summary";
         final String DESCRIPTION = "some-description";
         final String USAGE = "some-usage";
         final Options OPTIONS = new Options();
         final Set<Environment> ENVIRONMENT = EnumSet.noneOf(Environment.class);
         final List<BundleInformation> BUNDLES = Collections.emptyList();
 
-        BasicCommandInfo info = new BasicCommandInfo(NAME, DESCRIPTION, USAGE, OPTIONS, ENVIRONMENT, BUNDLES);
+        BasicCommandInfo info = new BasicCommandInfo(NAME, SUMMARY, DESCRIPTION, USAGE, OPTIONS, ENVIRONMENT, BUNDLES);
 
         assertEquals(NAME, info.getName());
+        assertEquals(SUMMARY, info.getSummary());
         assertEquals(DESCRIPTION, info.getDescription());
         assertEquals(USAGE, info.getUsage());
         assertEquals(OPTIONS, info.getOptions());
         assertEquals(BUNDLES, info.getBundles());
 
-        assertEquals(String.format("%s (description='%s', dependencies='%s')", NAME, DESCRIPTION, BUNDLES.toString()),
+        assertEquals(String.format("%s (summary='%s', description='%s', dependencies='%s')", NAME, SUMMARY, DESCRIPTION, BUNDLES.toString()),
                 info.toString());
     }
 }
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/HelpCommandTest.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/HelpCommandTest.java	Wed Dec 17 14:51:09 2014 -0500
@@ -101,14 +101,14 @@
         
         CommandInfo info1 = mock(CommandInfo.class);
         when(info1.getName()).thenReturn("test1");
-        when(info1.getDescription()).thenReturn("test command 1");
+        when(info1.getSummary()).thenReturn("test command 1");
         when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         infoList.add(info1);
 
         CommandInfo info2 = mock(CommandInfo.class);
         when(info2.getName()).thenReturn("test2longname");
-        when(info2.getDescription()).thenReturn("test command 2");
+        when(info2.getSummary()).thenReturn("test command 2");
         when(info2.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
         infoList.add(info2);
 
@@ -127,31 +127,6 @@
         assertEquals(expected, actual);
     }
 
-    @Test
-    public void verifyHelpShortensCommandDescriptions() {
-        Collection<CommandInfo> infoList = new ArrayList<CommandInfo>();
-
-        CommandInfo info1 = mock(CommandInfo.class);
-        when(info1.getName()).thenReturn("test1");
-        when(info1.getDescription()).thenReturn("A test command. This command does some test stuff."
-                + "This is a very, very long description that provides too much information for the summary");
-        when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
-
-        infoList.add(info1);
-
-        when(infos.getCommandInfos()).thenReturn(infoList);
-
-        HelpCommand cmd = new HelpCommand();
-        cmd.setEnvironment(Environment.CLI);
-        cmd.setCommandInfoSource(infos);
-
-        Arguments args = mock(Arguments.class);
-        cmd.run(ctxFactory.createContext(args));
-        String expected = "list of commands:\n\n"
-                        + " test1         a test command\n";
-        String actual = ctxFactory.getOutput();
-        assertEquals(expected, actual);
-    }
 
     @Test
     public void verifyHelpKnownCmdPrintsCommandUsage() {
@@ -182,27 +157,27 @@
     public void verifyHelpKnownCmdPrintsCommandUsageSorted() {
         CommandInfo helpInfo = mock(CommandInfo.class);
         when(helpInfo.getName()).thenReturn("help");
-        when(helpInfo.getDescription()).thenReturn("show help");
+        when(helpInfo.getSummary()).thenReturn("show help");
         when(helpInfo.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         CommandInfo info1 = mock(CommandInfo.class);
         when(info1.getName()).thenReturn("test1");
-        when(info1.getDescription()).thenReturn("test command 1");
+        when(info1.getSummary()).thenReturn("test command 1");
         when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         CommandInfo info2 = mock(CommandInfo.class);
         when(info2.getName()).thenReturn("test2");
-        when(info2.getDescription()).thenReturn("test command 2");
+        when(info2.getSummary()).thenReturn("test command 2");
         when(info2.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         CommandInfo info3 = mock(CommandInfo.class);
         when(info3.getName()).thenReturn("test3");
-        when(info3.getDescription()).thenReturn("test command 3");
+        when(info3.getSummary()).thenReturn("test command 3");
         when(info3.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         CommandInfo info4 = mock(CommandInfo.class);
         when(info4.getName()).thenReturn("test4");
-        when(info4.getDescription()).thenReturn("test command 4");
+        when(info4.getSummary()).thenReturn("test command 4");
         when(info4.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         when(infos.getCommandInfos()).thenReturn(Arrays.asList(info2, helpInfo, info4, info3, info1));
@@ -228,27 +203,27 @@
     public void verifyHelpFiltersCommands() {
         CommandInfo helpInfo = mock(CommandInfo.class);
         when(helpInfo.getName()).thenReturn("help");
-        when(helpInfo.getDescription()).thenReturn("show help");
+        when(helpInfo.getSummary()).thenReturn("show help");
         when(helpInfo.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI));
 
         CommandInfo info1 = mock(CommandInfo.class);
         when(info1.getName()).thenReturn("test1");
-        when(info1.getDescription()).thenReturn("test command 1");
+        when(info1.getSummary()).thenReturn("test command 1");
         when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI, Environment.SHELL));
 
         CommandInfo info2 = mock(CommandInfo.class);
         when(info2.getName()).thenReturn("test2");
-        when(info2.getDescription()).thenReturn("test command 2");
+        when(info2.getSummary()).thenReturn("test command 2");
         when(info2.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI));
 
         CommandInfo info3 = mock(CommandInfo.class);
         when(info3.getName()).thenReturn("test3");
-        when(info3.getDescription()).thenReturn("test command 3");
+        when(info3.getSummary()).thenReturn("test command 3");
         when(info3.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL));
 
         CommandInfo info4 = mock(CommandInfo.class);
         when(info4.getName()).thenReturn("test4");
-        when(info4.getDescription()).thenReturn("test command 4");
+        when(info4.getSummary()).thenReturn("test command 4");
         when(info4.getEnvironments()).thenReturn(EnumSet.of(Environment.CLI));
 
         when(infos.getCommandInfos()).thenReturn(Arrays.asList(info2, helpInfo, info4, info3, info1));
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java	Wed Dec 17 14:51:09 2014 -0500
@@ -163,6 +163,7 @@
         // option is properly set up
         Option logLevel = new Option("l", "logLevel", true, null);
         options1.addOption(logLevel);
+        when(info1.getSummary()).thenReturn("description 1");
         when(info1.getDescription()).thenReturn("description 1");
         when(info1.getOptions()).thenReturn(options1);
         when(info1.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL, Environment.CLI));
@@ -175,7 +176,7 @@
         options2.addOption(opt3);
         Option opt4 = new Option(null, "arg4", true, null);
         options2.addOption(opt4);
-        when(info2.getDescription()).thenReturn("description 2");
+        when(info2.getSummary()).thenReturn("description 2");
         when(info2.getOptions()).thenReturn(options2);
         when(info2.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL, Environment.CLI));
 
@@ -183,7 +184,7 @@
         CommandInfo info3 = mock(CommandInfo.class);
         when(info3.getName()).thenReturn(name3);
         cmd3.setStorageRequired(true);
-        when(info3.getDescription()).thenReturn("description 3");
+        when(info3.getSummary()).thenReturn("description 3");
         when(info3.getOptions()).thenReturn(new Options());
         when(info3.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL, Environment.CLI));
 
@@ -193,14 +194,14 @@
         CommandInfo info4 = mock(CommandInfo.class);
         when(info4.getName()).thenReturn(name4);
         cmd4.setStorageRequired(false);
-        when(info4.getDescription()).thenReturn("description 4");
+        when(info4.getSummary()).thenReturn("description 4");
         when(info4.getOptions()).thenReturn(new Options());
         when(info4.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL, Environment.CLI));
         
         AbstractStateNotifyingCommand basicCmd = mock(AbstractStateNotifyingCommand.class);
         CommandInfo basicInfo = mock(CommandInfo.class);
         when(basicInfo.getName()).thenReturn("basic");
-        when(basicInfo.getDescription()).thenReturn("nothing that means anything");
+        when(basicInfo.getSummary()).thenReturn("nothing that means anything");
         when(basicInfo.getEnvironments()).thenReturn(EnumSet.of(Environment.SHELL, Environment.CLI));
         when(basicCmd.isStorageRequired()).thenReturn(false);
         Options options = new Options();
@@ -210,7 +211,7 @@
 
         CommandInfo helpCommandInfo = mock(CommandInfo.class);
         when(helpCommandInfo.getName()).thenReturn("help");
-        when(helpCommandInfo.getDescription()).thenReturn("print help information");
+        when(helpCommandInfo.getSummary()).thenReturn("print help information");
         when(helpCommandInfo.getBundles()).thenReturn(new ArrayList<BundleInformation>());
         when(helpCommandInfo.getOptions()).thenReturn(new Options());
         when(helpCommandInfo.getUsage()).thenReturn("thermostat help");
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java	Wed Dec 17 14:51:09 2014 -0500
@@ -49,6 +49,7 @@
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
 import java.util.List;
+
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.OptionGroup;
 import org.apache.commons.cli.Options;
@@ -297,6 +298,65 @@
     }
 
     @Test
+    public void testSummaryIsReadCorrectly() throws UnsupportedEncodingException {
+        String config = "<?xml version=\"1.0\"?>\n" +
+                "<plugin>\n" +
+                "  <commands>\n" +
+                "    <command type='provides'>\n" +
+                "      <name>test</name>\n" +
+                "      <summary>some summary</summary>\n" +
+                "      <description>some description</description>\n" +
+                "      <environments>" +
+                "        <environment>shell</environment>" +
+                "        <environment>cli</environment>" +
+                "      </environments>" +
+                "    </command>\n" +
+                "  </commands>\n" +
+                "</plugin>";
+
+        PluginConfiguration result = new PluginConfigurationParser()
+                .parse("test", new ByteArrayInputStream(config.getBytes("UTF-8")));
+
+        assertEquals(0, result.getExtendedCommands().size());
+
+        List<NewCommand> newCommands = result.getNewCommands();
+        assertEquals(1, newCommands.size());
+
+        NewCommand command = newCommands.get(0);
+        assertEquals("test", command.getCommandName());
+        assertEquals("some summary", command.getSummary());
+    }
+
+    @Test
+    public void testFirstSentenceOfDescriptionIsUsedAsTheSummaryIfSummaryIsMissing() throws UnsupportedEncodingException {
+        String config = "<?xml version=\"1.0\"?>\n" +
+                "<plugin>\n" +
+                "  <commands>\n" +
+                "    <command type='provides'>\n" +
+                "      <name>test</name>\n" +
+                "      <description>Some description. Some other long stuff.</description>\n" +
+                "      <environments>" +
+                "        <environment>shell</environment>" +
+                "        <environment>cli</environment>" +
+                "      </environments>" +
+                "    </command>\n" +
+                "  </commands>\n" +
+                "</plugin>";
+
+        PluginConfiguration result = new PluginConfigurationParser()
+                .parse("test", new ByteArrayInputStream(config.getBytes("UTF-8")));
+
+        assertEquals(0, result.getExtendedCommands().size());
+
+        List<NewCommand> newCommands = result.getNewCommands();
+        assertEquals(1, newCommands.size());
+
+        NewCommand command = newCommands.get(0);
+        assertEquals("test", command.getCommandName());
+        assertEquals("some description", command.getSummary());
+    }
+
+    @Test
     public void testNewLinesAreRemovedFromDescription() throws UnsupportedEncodingException {
         String newLine = System.lineSeparator();
         String config = "<?xml version=\"1.0\"?>\n" +
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TestCommandInfo.java	Tue Dec 16 16:20:05 2014 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/TestCommandInfo.java	Wed Dec 17 14:51:09 2014 -0500
@@ -48,6 +48,7 @@
 public class TestCommandInfo implements CommandInfo {
 
     private String name;
+    private String summary;
     private String description;
     private String usage;
 
@@ -64,6 +65,15 @@
     }
 
     @Override
+    public String getSummary() {
+        return summary;
+    }
+
+    public void setSummary(String summary) {
+        this.summary = summary;
+    }
+
+    @Override
     public String getDescription() {
         return description;
     }
--- a/storage-profile/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/storage-profile/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,7 +42,12 @@
   <commands>
     <command>
       <name>storage-profile</name>
-      <description>profile how fast storage operations are</description>
+      <summary>profile how fast storage operations are</summary>
+      <description>
+      Profile how fast storage operations are. Runs a set of tests that
+      exercies various aspects of storage to see how well it performs for that
+      given workload.
+      </description>
       <options>
         <option common="true">
           <long>dbUrl</long>
--- a/validate-command/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/validate-command/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>validate</name>
+      <summary>validates a thermostat plug-in XML file</summary>
       <description>validates a thermostat plug-in XML file against the schema</description>
       <arguments>
         <argument>file</argument>
--- a/vm-gc/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/vm-gc/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>gc</name>
+      <summary>perform Garbage Collection on the given vm</summary>
       <description>perform Garbage Collection on the given vm</description>
       <options>
         <option>
--- a/vm-heap-analysis/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/vm-heap-analysis/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>dump-heap</name>
+      <summary>trigger a heap dump on the VM</summary>
       <description>trigger a heap dump on the VM</description>
       <options>
         <option>
@@ -95,6 +96,7 @@
     </command>
     <command>
       <name>find-objects</name>
+      <summary>finds objects in a heapdump</summary>
       <description>finds objects in a heapdump</description>
       <arguments>
         <argument>pattern</argument>
@@ -151,6 +153,7 @@
     </command>
     <command>
       <name>find-root</name>
+      <summary>finds the shortest path from an object to a GC root</summary>
       <description>finds the shortest path from an object to a GC root</description>
       <options>
         <option>
@@ -210,6 +213,7 @@
     </command>
     <command>
       <name>list-heap-dumps</name>
+      <summary>list all heap dumps</summary>
       <description>list all heap dumps</description>
       <options>
         <option>
@@ -263,6 +267,7 @@
     </command>
     <command>
       <name>object-info</name>
+      <summary>prints information about an object in a heap dump</summary>
       <description>prints information about an object in a heap dump</description>
       <options>
         <option>
@@ -316,6 +321,7 @@
     </command>
     <command>
       <name>save-heap-dump-to-file</name>
+      <summary>saves a heap dump to a local file</summary>
       <description>saves a heap dump to a local file</description>
       <options>
         <option>
@@ -369,6 +375,7 @@
     </command>
     <command>
       <name>show-heap-histogram</name>
+      <summary>show the heap histogram</summary>
       <description>show the heap histogram</description>
       <options>
         <option>
--- a/vm-profiler/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/vm-profiler/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -40,6 +40,7 @@
     <commands>
         <command>
             <name>profile-vm</name>
+            <summary>instrument and profile a target vm</summary>
             <description>
             Profile a target vm. This is an instrumenting profiler that
             modifies the bytecode of the target application to record method
--- a/web/endpoint-plugin/distribution/thermostat-plugin.xml	Tue Dec 16 16:20:05 2014 -0500
+++ b/web/endpoint-plugin/distribution/thermostat-plugin.xml	Wed Dec 17 14:51:09 2014 -0500
@@ -42,6 +42,7 @@
   <commands>
     <command>
       <name>web-storage-service</name>
+      <summary>starts mongodb storage, an embedded servlet container and an agent</summary>
       <description>starts mongodb storage, an embedded servlet container with the web archive deployed and then connects an agent to this web endpoint</description>
       <environments>
         <environment>cli</environment>