Improve command line
authorMathieu Baudier <mbaudier@argeo.org>
Fri, 28 Oct 2022 06:48:47 +0000 (08:48 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Fri, 28 Oct 2022 06:48:47 +0000 (08:48 +0200)
org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java
org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java
org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java
org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java
org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java
org.argeo.api.cli/src/org/argeo/api/cli/PrintHelpRequestException.java [new file with mode: 0644]
org.argeo.cms.cli/bnd.bnd
org.argeo.cms.cli/src/org/argeo/cms/cli/ArgeoCli.java
org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/cli/SshCli.java
sdk/deploy/argeo-cms/usr/.gitignore [new file with mode: 0644]
sdk/deploy/argeo-cms/usr/bin/argeo [new file with mode: 0755]

index b2a12a603dc416ba2d4e26f99086b0dcb5e437fd..935247feee47d8b1fa42617b7442cd48ee92a649 100644 (file)
@@ -1,5 +1,6 @@
 package org.argeo.api.cli;
 
+/** Exception thrown when the provided arguments are not correct. */
 public class CommandArgsException extends IllegalArgumentException {
        private static final long serialVersionUID = -7271050747105253935L;
        private String commandName;
index 52c033433d69343736eec3d6b14ad1f6f558fb41..b3f0517b47bd751c2872f0cb5357d7b148b429f0 100644 (file)
@@ -3,7 +3,7 @@ package org.argeo.api.cli;
 import java.util.List;
 
 /** {@link RuntimeException} referring during a command run. */
-public class CommandRuntimeException extends RuntimeException {
+class CommandRuntimeException extends RuntimeException {
        private static final long serialVersionUID = 5595999301269377128L;
 
        private final DescribedCommand<?> command;
index b82308a2ba39f56ead0a3cc152b5d58926433fa9..5bbfcfa070d7cee0a2e13c722c4561b6812ec8c8 100644 (file)
@@ -18,8 +18,6 @@ import org.apache.commons.cli.ParseException;
 
 /** Base class for a CLI managing sub commands. */
 public abstract class CommandsCli implements DescribedCommand<Object> {
-       public final static String HELP = "help";
-
        private final String commandName;
        private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
 
@@ -33,11 +31,17 @@ public abstract class CommandsCli implements DescribedCommand<Object> {
        public Object apply(List<String> args) {
                String cmd = null;
                List<String> newArgs = new ArrayList<>();
+               boolean isHelpOption = false;
                try {
                        CommandLineParser clParser = new DefaultParser();
                        CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
                        List<String> leftOvers = commonCl.getArgList();
                        for (String arg : leftOvers) {
+                               if (arg.equals("--" + HelpCommand.HELP_OPTION.getLongOpt())) {
+                                       isHelpOption = true;
+                                       // TODO break?
+                               }
+
                                if (!arg.startsWith("-") && cmd == null) {
                                        cmd = arg;
                                } else {
@@ -50,6 +54,18 @@ public abstract class CommandsCli implements DescribedCommand<Object> {
                }
 
                Function<List<String>, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand();
+
+               // --help option
+               if (!(function instanceof CommandsCli))
+                       if (function instanceof DescribedCommand<?> command)
+                               if (isHelpOption) {
+                                       throw new PrintHelpRequestException(cmd, this);
+//                                     StringWriter out = new StringWriter();
+//                                     HelpCommand.printHelp(command, out);
+//                                     System.out.println(out.toString());
+//                                     return null;
+                               }
+
                if (function == null)
                        throw new IllegalArgumentException("Uknown command " + cmd);
                try {
@@ -85,7 +101,7 @@ public abstract class CommandsCli implements DescribedCommand<Object> {
 
        protected void addCommandsCli(CommandsCli commandsCli) {
                addCommand(commandsCli.getCommandName(), commandsCli);
-               commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli));
+               commandsCli.addCommand(HelpCommand.HELP, new HelpCommand(this, commandsCli));
        }
 
        public String getCommandName() {
@@ -101,7 +117,7 @@ public abstract class CommandsCli implements DescribedCommand<Object> {
        }
 
        public HelpCommand getHelpCommand() {
-               return (HelpCommand) getCommand(HELP);
+               return (HelpCommand) getCommand(HelpCommand.HELP);
        }
 
        public Function<List<String>, String> getDefaultCommand() {
@@ -111,10 +127,15 @@ public abstract class CommandsCli implements DescribedCommand<Object> {
        /** In order to implement quickly a main method. */
        public static void mainImpl(CommandsCli cli, String[] args) {
                try {
-                       cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli));
+                       cli.addCommand(HelpCommand.HELP, new HelpCommand(null, cli));
                        Object output = cli.apply(Arrays.asList(args));
-                       System.out.println(output);
+                       if (output != null)
+                               System.out.println(output);
                        System.exit(0);
+               } catch (PrintHelpRequestException e) {
+                       StringWriter out = new StringWriter();
+                       HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
+                       System.out.println(out.toString());
                } catch (CommandArgsException e) {
                        System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage());
                        Throwable cause = e.getCause();
index 7a9d5d90111ece240c66576b51d5f5613776f7d1..51cb2cecaa2558698d63a30d89d7a479fbda71a6 100644 (file)
@@ -41,6 +41,11 @@ public interface DescribedCommand<T> extends Function<List<String>, T> {
                        Object output = command.apply(Arrays.asList(args));
                        System.out.println(output);
                        System.exit(0);
+               } catch (PrintHelpRequestException e) {
+                       StringWriter out = new StringWriter();
+                       HelpCommand.printHelp(command, out);
+                       System.out.println(out.toString());
+                       System.exit(1);
                } catch (IllegalArgumentException e) {
                        StringWriter out = new StringWriter();
                        HelpCommand.printHelp(command, out);
index d5285a60c25f1bf297e5dc9a49e9a63f35edea75..fdcaf0224c8797bffb1665f4e1f3ddaa354cf100 100644 (file)
@@ -6,10 +6,20 @@ import java.util.List;
 import java.util.function.Function;
 
 import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 
 /** A special command that can describe {@link DescribedCommand}. */
 public class HelpCommand implements DescribedCommand<String> {
+       /**
+        * System property forcing the root command to this value (typically the name of
+        * a script).
+        */
+       public final static String ROOT_COMMAND_PROPERTY = "org.argeo.api.cli.rootCommand";
+
+       final static String HELP = "help";
+       final static Option HELP_OPTION = Option.builder().longOpt(HELP).desc("print this help").build();
+
        private CommandsCli commandsCli;
        private CommandsCli parentCommandsCli;
 
@@ -91,6 +101,9 @@ public class HelpCommand implements DescribedCommand<String> {
                if (hc.getParentCommandsCli() != null) {
                        return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName();
                } else {
+                       String rootCommand = System.getProperty(ROOT_COMMAND_PROPERTY);
+                       if (rootCommand != null)
+                               return rootCommand;
                        return commandsCli.getCommandName();
                }
        }
@@ -99,8 +112,10 @@ public class HelpCommand implements DescribedCommand<String> {
                String usage = "java " + command.getClass().getName()
                                + (command.getUsage() != null ? " " + command.getUsage() : "");
                HelpFormatter formatter = new HelpFormatter();
-               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
-                               helpLeftPad, helpDescPad, command.getExamples(), false);
+               Options options = command.getOptions();
+               options.addOption(HelpCommand.HELP_OPTION);
+               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
+                               helpDescPad, command.getExamples(), false);
 
        }
 
@@ -108,8 +123,10 @@ public class HelpCommand implements DescribedCommand<String> {
                DescribedCommand<?> command = (DescribedCommand<?>) commandsCli.getCommand(commandName);
                String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command);
                HelpFormatter formatter = new HelpFormatter();
-               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
-                               helpLeftPad, helpDescPad, command.getExamples(), false);
+               Options options = command.getOptions();
+               options.addOption(HelpCommand.HELP_OPTION);
+               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), options, helpLeftPad,
+                               helpDescPad, command.getExamples(), false);
 
        }
 
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/PrintHelpRequestException.java b/org.argeo.api.cli/src/org/argeo/api/cli/PrintHelpRequestException.java
new file mode 100644 (file)
index 0000000..017386b
--- /dev/null
@@ -0,0 +1,29 @@
+package org.argeo.api.cli;
+
+/** An exception indicating that help should be printed. */
+class PrintHelpRequestException extends RuntimeException {
+
+       private static final long serialVersionUID = -9029122270660656639L;
+
+       private String commandName;
+       private volatile CommandsCli commandsCli;
+
+       public PrintHelpRequestException(String commandName, CommandsCli commandsCli) {
+               super();
+               this.commandName = commandName;
+               this.commandsCli = commandsCli;
+       }
+
+       public static long getSerialversionuid() {
+               return serialVersionUID;
+       }
+
+       public String getCommandName() {
+               return commandName;
+       }
+
+       public CommandsCli getCommandsCli() {
+               return commandsCli;
+       }
+
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3b7027710140f5b449be1fbd863661017f8e339a 100644 (file)
@@ -0,0 +1,71 @@
+Main-Class: org.argeo.cms.cli.ArgeoCli
+
+Class-Path: \
+org.argeo.api.acr.2.3.jar \
+org.argeo.api.cli.2.3.jar \
+org.argeo.api.cms.2.3.jar \
+org.argeo.api.uuid.2.3.jar \
+org.argeo.cms.2.3.jar \
+org.argeo.cms.ee.2.3.jar \
+org.argeo.cms.lib.equinox.2.3.jar \
+org.argeo.cms.lib.jetty.2.3.jar \
+org.argeo.cms.lib.pgsql.2.3.jar \
+org.argeo.cms.lib.sshd.2.3.jar \
+org.argeo.cms.ux.2.3.jar \
+org.argeo.init.2.3.jar \
+org.argeo.util.2.3.jar \
+../org.argeo.tp/bcmail.1.71.jar \
+../org.argeo.tp/bcpg.1.71.jar \
+../org.argeo.tp/bcpkix.1.71.jar \
+../org.argeo.tp/bcprov.1.71.jar \
+../org.argeo.tp/bcutil.1.71.jar \
+../org.argeo.tp/com.fasterxml.jackson.core.jackson-annotations.2.13.jar \
+../org.argeo.tp/com.fasterxml.jackson.core.jackson-core.2.13.jar \
+../org.argeo.tp/com.fasterxml.jackson.core.jackson-databind.2.13.jar \
+../org.argeo.tp/com.googlecode.javaewah.JavaEWAH.1.1.jar \
+../org.argeo.tp/com.sun.mail.mbox.1.6.jar \
+../org.argeo.tp/com.zaxxer.sparsebits.1.2.jar \
+../org.argeo.tp/jakarta.mail.1.6.jar \
+../org.argeo.tp/jakarta.servlet.api.4.0.jar \
+../org.argeo.tp/javax.activation.1.2.jar \
+../org.argeo.tp/javax.websocket.1.1.jar \
+../org.argeo.tp/net.i2p.crypto.eddsa.0.3.jar \
+../org.argeo.tp/org.argeo.ext.slf4j.2.3.jar \
+../org.argeo.tp/org.h2.1.4.jar \
+../org.argeo.tp/org.postgresql.jdbc42.42.3.jar \
+../org.argeo.tp/org.slf4j.api.1.7.jar \
+../org.argeo.tp/org.slf4j.commons.logging.1.7.jar \
+../org.argeo.tp/org.tukaani.xz.1.9.jar \
+../org.argeo.tp/org.w3c.css.sac.1.3.jar \
+../org.argeo.tp/org.w3c.dom.smil.1.0.jar \
+../org.argeo.tp/org.w3c.dom.svg.1.1.jar \
+../org.argeo.tp.apache/org.apache.batik.1.14.jar \
+../org.argeo.tp.apache/org.apache.batik.constants.1.14.jar \
+../org.argeo.tp.apache/org.apache.batik.css.1.14.jar \
+../org.argeo.tp.apache/org.apache.batik.i18n.1.14.jar \
+../org.argeo.tp.apache/org.apache.batik.util.1.14.jar \
+../org.argeo.tp.apache/org.apache.commons.cli.1.5.jar \
+../org.argeo.tp.apache/org.apache.commons.codec.1.15.jar \
+../org.argeo.tp.apache/org.apache.commons.compress.1.21.jar \
+../org.argeo.tp.apache/org.apache.commons.exec.1.3.jar \
+../org.argeo.tp.apache/org.apache.commons.fileupload.1.4.jar \
+../org.argeo.tp.apache/org.apache.commons.httpclient.3.1.jar \
+../org.argeo.tp.apache/org.apache.commons.io.2.11.jar \
+../org.argeo.tp.apache/org.apache.commons.vfs.2.9.jar \
+../org.argeo.tp.apache/org.apache.httpcomponents.httpclient.4.5.jar \
+../org.argeo.tp.apache/org.apache.httpcomponents.httpcore.4.4.jar \
+../org.argeo.tp.apache/org.apache.httpcomponents.httpmime.4.5.jar \
+../org.argeo.tp.apache/org.apache.sshd.2.8.jar \
+../org.argeo.tp.apache/org.apache.sshd.cli.2.8.jar \
+../org.argeo.tp.apache/org.apache.sshd.git.2.8.jar \
+../org.argeo.tp.apache/org.apache.sshd.putty.2.8.jar \
+../org.argeo.tp.apache/org.apache.sshd.scp.2.8.jar \
+../org.argeo.tp.apache/org.apache.sshd.sftp.2.8.jar \
+../org.argeo.tp.apache/org.apache.tika.core.1.28.jar \
+../org.argeo.tp.apache/org.apache.tika.parsers.1.28.jar \
+../org.argeo.tp.apache/org.apache.tomcat.jni.9.0.jar \
+../org.argeo.tp.apache/org.apache.xalan.2.7.jar \
+../org.argeo.tp.apache/org.apache.xerces.2.12.jar \
+../org.argeo.tp.apache/org.apache.xmlgraphics.2.7.jar \
+../org.argeo.tp.apache/org.apache.xml.resolver.1.2.jar \
+
index 1dd57f3df9083ecba86824789d5418c1b6f99b66..346cb3c3a8d860c32cc14b5499e3ea88d9e77c3c 100644 (file)
@@ -22,7 +22,11 @@ public class ArgeoCli extends CommandsCli {
 
        @Override
        public String getDescription() {
-               return "Argeo utilities";
+               return "Argeo CMS utilities";
+       }
+
+       public static void main(String[] args) {
+               mainImpl(new ArgeoCli("argeo"), args);
        }
 
 }
index 4ec456b34d46054ad898728a84b0d21180a2adb4..287727544cb92d56a97f92194d85ee48b3e28a1a 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.ssh.cli;
 
 import org.argeo.api.cli.CommandsCli;
 
+/** SSH command line interface. */
 public class SshCli extends CommandsCli {
        public SshCli(String commandName) {
                super(commandName);
diff --git a/sdk/deploy/argeo-cms/usr/.gitignore b/sdk/deploy/argeo-cms/usr/.gitignore
new file mode 100644 (file)
index 0000000..08eb0a0
--- /dev/null
@@ -0,0 +1 @@
+!bin/
\ No newline at end of file
diff --git a/sdk/deploy/argeo-cms/usr/bin/argeo b/sdk/deploy/argeo-cms/usr/bin/argeo
new file mode 100755 (executable)
index 0000000..636fd47
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+java -Dorg.argeo.api.cli.rootCommand=$0 -jar /usr/share/a2/org.argeo.cms/org.argeo.cms.cli.2.3.jar "$@"
\ No newline at end of file