Mercurial > hg > release > thermostat-0.9
changeset 1074:dead5cab5a9a
Compute usage strings automatically
Reviewed-by: neugens
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-April/006405.html
line wrap: on
line diff
--- a/distribution/docs/plugin.xsd Fri Apr 26 11:08:23 2013 -0600 +++ b/distribution/docs/plugin.xsd Fri Apr 26 16:45:56 2013 -0400 @@ -9,6 +9,7 @@ <xs:element name="name" type="xs:string"/> <xs:element name="bundle" type="xs:string"/> <xs:element name="dependency" type="xs:string"/> +<xs:element name="usage" type="xs:string"/> <xs:element name="description" type="xs:string"/> <xs:element name="short" type="xs:string"/> <xs:element name="long" type="xs:string"/> @@ -62,7 +63,9 @@ <xs:complexType> <xs:sequence> <xs:element ref="name"/> + <xs:element ref="usage" minOccurs="0" maxOccurs="1"/> <xs:element ref="description"/> + <xs:element ref="arguments" minOccurs="0" maxOccurs="1"/> <xs:element ref="options"/> <xs:element ref="bundles"/> <xs:element ref="dependencies"/> @@ -71,6 +74,14 @@ </xs:element> +<xs:element name="arguments"> + <xs:complexType> + <xs:sequence> + <xs:element ref="argument" minOccurs="0" maxOccurs="1"/> + </xs:sequence> + </xs:complexType> +</xs:element> + <xs:element name="options"> <xs:complexType> <xs:sequence>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Apr 26 16:45:56 2013 -0400 @@ -69,14 +69,18 @@ private static final Logger logger = LoggingUtils.getLogger(PluginCommandInfoSource.class); + private final UsageStringBuilder usageBuilder; + private Map<String, BasicCommandInfo> allNewCommands = new HashMap<>(); private Map<String, List<String>> additionalBundlesForExistingCommands = new HashMap<>(); public PluginCommandInfoSource(String internalJarRoot, String pluginRootDir) { - this(new File(internalJarRoot), new File(pluginRootDir), new PluginConfigurationParser()); + this(new File(internalJarRoot), new File(pluginRootDir), new PluginConfigurationParser(), new UsageStringBuilder()); } - PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, PluginConfigurationParser parser) { + PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, PluginConfigurationParser parser, UsageStringBuilder usageBuilder) { + this.usageBuilder = usageBuilder; + File[] pluginDirs = pluginRootDir.listFiles(); if (pluginDirs == null) { logger.log(Level.SEVERE, "plugin root dir " + pluginRootDir + " does not exist"); @@ -132,9 +136,13 @@ addIfValidPath(bundlePaths, coreJarRoot, command.getDepenedencyBundles()); + String usage = command.getUsage(); + if (usage == null) { + usage = usageBuilder.getUsage(commandName, command.getOptions(), command.getPositionalArguments().toArray(new String[0])); + } BasicCommandInfo info = new BasicCommandInfo(commandName, command.getDescription(), - command.getUsage(), + usage, command.getOptions(), bundlePaths);
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Apr 26 16:45:56 2013 -0400 @@ -87,16 +87,20 @@ public static class NewCommand { private final String commandName; + private final String usage; private final String description; - private String usage; + private final List<String> positionalArguments; private final Options options; private final List<String> additionalResources; private final List<String> coreDeps; - public NewCommand(String name, String description, - Options options, List<String> additionalResources, List<String> coreDeps) { + public NewCommand(String name, String usage, String description, + List<String> positionalArguments, Options options, + List<String> additionalResources, List<String> coreDeps) { this.commandName = name; + this.usage = usage; this.description = description; + this.positionalArguments = positionalArguments; this.options = options; this.additionalResources = additionalResources; this.coreDeps = coreDeps; @@ -106,14 +110,25 @@ return commandName; } + /** + * The usage string may be null if no usage string was explicitly + * provided. In that case, usage should be "computed" using options and + * arguments + */ + public String getUsage() { + return usage; + } + public String getDescription() { return description; } - public String getUsage() { - return usage; + /** Returns a list of strings indicating positional arguments */ + public List<String> getPositionalArguments() { + return positionalArguments; } + /** Returns options (both optional and required) */ public Options getOptions() { return options; }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Apr 26 16:45:56 2013 -0400 @@ -285,7 +285,7 @@ } if (bundles.isEmpty()) { - logger.warning("plugin " + pluginName + " extends the command " + name + " but supplies no bundles"); + logger.warning("plugin " + pluginName + " extends the command " + name + " but supplies no bundles"); } if (name == null) { @@ -297,7 +297,9 @@ private NewCommand parseNewCommand(String pluginName, Node commandNode) { String name = null; + String usage = null; String description = null; + List<String> arguments = new ArrayList<>(); Options options = new Options(); List<String> bundles = new ArrayList<>(); List<String> dependencies = new ArrayList<>(); @@ -307,8 +309,12 @@ Node node = nodes.item(i); if (node.getNodeName().equals("name")) { name = node.getTextContent().trim(); + } else if (node.getNodeName().equals("usage")) { + usage = node.getTextContent().trim(); } else if (node.getNodeName().equals("description")) { description = node.getTextContent().trim(); + } else if (node.getNodeName().equals("arguments")) { + arguments = parseArguments(pluginName, name, node); } else if (node.getNodeName().equals("options")) { options = parseOptions(node); } else if (node.getNodeName().equals("bundles")) { @@ -330,44 +336,39 @@ "name='" + name + "', description='" + description + "', options='" + options + "'"); return null; } else { - return new NewCommand(name, description, options, bundles, dependencies); + return new NewCommand(name, usage, description, arguments, options, bundles, dependencies); } } private Collection<String> parseBundles(String pluginName, String commandName, Node bundlesNode) { - List<String> bundles = new ArrayList<>(); - NodeList nodes = bundlesNode.getChildNodes(); + return parseNodeAsList(pluginName, commandName, bundlesNode, "bundle"); + } + + private Collection<String> parseDependencies(String pluginName, String commandName, Node dependenciesNode) { + return parseNodeAsList(pluginName, commandName, dependenciesNode, "dependency"); + } + + private List<String> parseArguments(String pluginName, String commandName, Node argumentsNode) { + return parseNodeAsList(pluginName, commandName, argumentsNode, "argument"); + } + + private List<String> parseNodeAsList(String pluginName, String commandName, Node parentNode, String childElementName) { + List<String> result = new ArrayList<>(); + NodeList nodes = parentNode.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); - if (node.getNodeName().equals("bundle")) { - String bundleName = node.getTextContent().trim(); - bundles.add(bundleName); + if (node.getNodeName().equals(childElementName)) { + String data = node.getTextContent().trim(); + result.add(data); } } - if (bundles.isEmpty()) { - logger.warning("plugin " + pluginName + " has an empty bundles element for command " + commandName); + if (result.isEmpty()) { + logger.warning("plugin " + pluginName + " has an empty " + parentNode.getNodeName() + + " element for command " + commandName); } - return bundles; - } - - private Collection<String> parseDependencies(String pluginName, String commandName, Node dependenciesNode) { - List<String> dependencies = new ArrayList<>(); - NodeList nodes = dependenciesNode.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - if (node.getNodeName().equals("dependency")) { - String bundleName = node.getTextContent().trim(); - dependencies.add(bundleName); - } - } - - if (dependencies.isEmpty()) { - logger.warning("plugin " + pluginName + " has an empty dependencies element for command " + commandName); - } - - return dependencies; + return result; } private Options parseOptions(Node optionsNode) { @@ -479,8 +480,10 @@ } } - Option opt = new Option(shortName, longName, Boolean.parseBoolean(argument), description); - opt.setArgName(longName != null? longName : shortName); + Option opt = new Option(shortName, longName, (argument != null), description); + if (argument != null) { + opt.setArgName(argument); + } opt.setRequired(required); return opt; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/UsageStringBuilder.java Fri Apr 26 16:45:56 2013 -0400 @@ -0,0 +1,106 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.launcher.internal; + +import java.util.Collection; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class UsageStringBuilder { + + /** + * Generates a 'usage' string based on the name (of a program) and the + * options that program will accept on the command line. + * + * @param name the program name + * @param options the options accepted by this program + * @return a String representing the usage. + */ + public String getUsage(String name, Options options, String... positionalArguments) { + StringBuilder result = new StringBuilder(); + result.append(name); + // commons-cli has no support for generics, so suppress this warning. + @SuppressWarnings("unchecked") + Collection<Option> opts = options.getOptions(); + // iterate twice to handle/print required options first, followed by optional ones + for (Option option : opts) { + appendOption(result, option, true); + } + for (Option option : opts) { + appendOption(result, option, false); + } + // print positional arguments last + if (positionalArguments != null) { + for (String positionalArg : positionalArguments) { + result.append(" ").append(positionalArg); + } + } + + return result.toString(); + } + + private void appendOption(StringBuilder result, Option option, boolean requiredOptionsOnly) { + if (option.isRequired() != requiredOptionsOnly) { + return; + } + + result.append(" "); + + if (!option.isRequired()) { + result.append("["); + } + + // prefer to display long form if available + if (option.hasLongOpt()) { + result.append("--").append(option.getLongOpt()); + } else { + result.append("-").append(option.getOpt()); + } + + if (option.hasArg()) { + result.append(" ").append("<").append(option.getArgName()).append(">"); + } else if (option.hasOptionalArg()) { + result.append("[").append(" ").append("<").append(option.getArgName()).append(">").append("]"); + } + + if (!option.isRequired()) { + result.append("]"); + } + } + +}
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java Fri Apr 26 16:45:56 2013 -0400 @@ -71,12 +71,14 @@ private Path pluginRootDir; private PluginConfigurationParser parser; private PluginConfiguration parserResult; + private UsageStringBuilder usageBuilder; @Before public void setUp() throws IOException { parser = mock(PluginConfigurationParser.class); parserResult = mock(PluginConfiguration.class); when(parser.parse(isA(File.class))).thenReturn(parserResult); + usageBuilder = mock(UsageStringBuilder.class); testRoot = Files.createTempDirectory("thermostat"); pluginRootDir = testRoot.resolve("plugins"); @@ -115,7 +117,7 @@ Files.createDirectory(pluginDir); } - new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser); + new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser, usageBuilder); ArgumentCaptor<File> configFilesCaptor = ArgumentCaptor.forClass(File.class); verify(parser, times(pluginDirs.length)).parse(configFilesCaptor.capture()); @@ -131,12 +133,12 @@ public void verifyMissingConfigurationFileIsHandledCorrectly() throws FileNotFoundException { when(parser.parse(isA(File.class))).thenThrow(new FileNotFoundException("test")); - new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser); + new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser, usageBuilder); } @Test(expected = CommandInfoNotFoundException.class) public void verifyMissingCommandInfo() { - PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser); + PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser, usageBuilder); source.getCommandInfo("TEST"); } @@ -161,7 +163,7 @@ when(parserResult.getExtendedCommands()).thenReturn(Arrays.asList(extensions)); - PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser); + PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser, usageBuilder); CommandInfo info = source.getCommandInfo("command-name"); assertEquals("command-name", info.getName()); @@ -193,14 +195,14 @@ NewCommand cmd = mock(NewCommand.class); when(cmd.getCommandName()).thenReturn(NAME); when(cmd.getDescription()).thenReturn(DESCRIPTION); - when(cmd.getUsage()).thenReturn(USAGE); + when(usageBuilder.getUsage(NAME, OPTIONS)).thenReturn(USAGE); when(cmd.getOptions()).thenReturn(OPTIONS); when(cmd.getPluginBundles()).thenReturn(Arrays.asList(PLUGIN_BUNDLE)); when(cmd.getDepenedencyBundles()).thenReturn(Arrays.asList(DEPENDENCY_BUNDLE)); when(parserResult.getNewCommands()).thenReturn(Arrays.asList(cmd)); - PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser); + PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir.toFile(), pluginRootDir.toFile(), parser, usageBuilder); CommandInfo result = source.getCommandInfo(NAME);
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Fri Apr 26 11:08:23 2013 -0600 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Fri Apr 26 16:45:56 2013 -0400 @@ -187,6 +187,41 @@ } @Test + public void testArgumentParsing() throws UnsupportedEncodingException { + String config = "<?xml version=\"1.0\"?>\n" + + "<plugin>\n" + + " <commands>\n" + + " <command type='provides'>\n" + + " <name>test</name>\n" + + " <description>just a test</description>\n" + + " <arguments>\n" + + " <argument>file</argument>\n" + + " </arguments>\n" + + " </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("just a test", command.getDescription()); + assertEquals(null, command.getUsage()); + Options opts = command.getOptions(); + assertTrue(opts.getOptions().isEmpty()); + + List<String> args = command.getPositionalArguments(); + assertEquals(1, args.size()); + assertEquals("file", args.get(0)); + } + + @Test public void testOptionParsing() throws UnsupportedEncodingException { String config = "<?xml version=\"1.0\"?>\n" + "<plugin>\n" + @@ -200,14 +235,12 @@ " <option>\n" + " <long>exclusive-a</long>\n" + " <short>a</short>\n" + - " <argument>false</argument>\n" + " <required>false</required>\n" + " <description>exclusive option a</description>\n" + " </option>\n" + " <option>\n" + " <long>exclusive-b</long>\n" + " <short>b</short>\n" + - " <argument>false</argument>\n" + " <required>false</required>\n" + " <description>exclusive option b</description>\n" + " </option>\n" + @@ -215,7 +248,7 @@ " <option>\n" + " <long>long</long>\n" + " <short>l</short>\n" + - " <argument>true</argument>\n" + + " <argument>name</argument>\n" + " <required>true</required>\n" + " <description>some required and long option</description>\n" + " </option>\n" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/UsageStringBuilderTest.java Fri Apr 26 16:45:56 2013 -0400 @@ -0,0 +1,149 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.launcher.internal; + +import static org.junit.Assert.assertEquals; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.junit.Before; +import org.junit.Test; + +public class UsageStringBuilderTest { + + private UsageStringBuilder builder; + + @Before + public void setUp() { + builder = new UsageStringBuilder(); + } + + @Test + public void verifyUsageWithNoOptions() { + Options options = new Options(); + String usage = builder.getUsage("test", options); + assertEquals("test", usage); + } + + @Test + public void verifyUsageWithSingleShortOption() { + Option a = new Option("a", "something"); + a.setRequired(false); + Options options = new Options(); + options.addOption(a); + + String usage = builder.getUsage("test", options); + assertEquals("test [-a]", usage); + } + + @Test + public void verifyUsageWithShortAndLongOptions() { + Options options = new Options(); + + Option a = new Option("a", "something"); + options.addOption(a); + + Option b = new Option("b", "bee", false, "another thing"); + options.addOption(b); + + String usage = builder.getUsage("test", options); + assertEquals("test [--bee] [-a]", usage); + } + + @Test + public void verifyOptionWithArgument() { + Options options = new Options(); + + Option a = new Option("a", true, "something"); + a.setArgName("aaah"); + options.addOption(a); + + String usage = builder.getUsage("test", options); + assertEquals("test [-a <aaah>]", usage); + } + + @Test + public void verifyRequiredSingleShortOption() { + Option a = new Option("a", "something"); + a.setRequired(true); + Options options = new Options(); + options.addOption(a); + + String usage = builder.getUsage("test", options); + assertEquals("test -a", usage); + } + + @Test + public void verifyRequiredOptionsBeforeOptionalOnes() { + Options options = new Options(); + + Option a = new Option("a", "something"); + a.setRequired(true); + options.addOption(a); + + Option b = new Option("b", "something"); + b.setRequired(false); + options.addOption(b); + + Option c = new Option("c", "something"); + c.setRequired(false); + options.addOption(c); + + String usage = builder.getUsage("test", options); + assertEquals("test -a [-b] [-c]", usage); + } + + @Test + public void verifyPositionArgumentsAreIncluded() { + Options options = new Options(); + + String usage = builder.getUsage("test", options, "agent-id", "vm-id"); + assertEquals("test agent-id vm-id", usage); + } + + @Test + public void verifyPositionArgumentsAreDisplayedLast() { + Options options = new Options(); + + Option a = new Option("a", "something"); + a.setRequired(true); + options.addOption(a); + + String usage = builder.getUsage("test", options, "agent-id", "vm-id"); + assertEquals("test -a agent-id vm-id", usage); + } +}
--- a/vm-heap-analysis/distribution/plugin.xml Fri Apr 26 11:08:23 2013 -0600 +++ b/vm-heap-analysis/distribution/plugin.xml Fri Apr 26 16:45:56 2013 -0400 @@ -47,14 +47,14 @@ <option> <long>hostId</long> <short>a</short> - <argument>true</argument> + <argument>host</argument> <required>true</required> <description>the ID of the host to monitor</description> </option> <option> <long>vmId</long> <short>v</short> - <argument>true</argument> + <argument>vm</argument> <required>true</required> <description>the ID of the VM to monitor</description> </option> @@ -91,18 +91,21 @@ <command> <name>find-objects</name> <description>finds objects in a heapdump</description> + <arguments> + <argument>pattern</argument> + </arguments> <options> <option> <long>heapId</long> <short>h</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the heapdump to analyze</description> </option> <option> <long>limit</long> <short>L</short> - <argument>true</argument> + <argument>limit</argument> <required>false</required> <description>limit search to top N results, defaults to 10</description> </option> @@ -143,21 +146,20 @@ <option> <long>heapId</long> <short>h</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the heapdump to analyze</description> </option> <option> <long>objectId</long> <short>o</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the object to query</description> </option> <option> <long>all</long> <short>a</short> - <argument>false</argument> <required>false</required> <description>finds all paths to GC roots</description> </option> @@ -198,14 +200,14 @@ <option> <long>hostId</long> <short>a</short> - <argument>true</argument> + <argument>host</argument> <required>false</required> <description>the ID of the host to monitor</description> </option> <option> <long>vmId</long> <short>v</short> - <argument>true</argument> + <argument>vm</argument> <required>false</required> <description>the ID of the VM to monitor</description> </option> @@ -246,14 +248,14 @@ <option> <long>heapId</long> <short>h</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the heapdump to analyze</description> </option> <option> <long>objectId</long> <short>o</short> - <argument>true</argument> + <argument>object</argument> <required>true</required> <description>the ID of the object to query</description> </option> @@ -294,14 +296,14 @@ <option> <long>heapId</long> <short>h</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the heapdump to analyze</description> </option> <option> <long>file</long> <short>f</short> - <argument>true</argument> + <argument>filename</argument> <required>true</required> <description>the file name to save to</description> </option> @@ -342,7 +344,7 @@ <option> <long>heapId</long> <short>h</short> - <argument>true</argument> + <argument>heap</argument> <required>true</required> <description>the ID of the heapdump to analyze</description> </option>