Picocli: Beginner question: How to call top command and sub command

Created on 9 May 2019  路  8Comments  路  Source: remkop/picocli

Hi,

I have an application which requires a base set of input parameters.
I have two use cases which require all of these parameters but each of them also a different input parameter on top.

So I thought it would be the best to call the top-level command with the base set of input parameters plus the sub command method which processes the corresponding use case then with one call to the application.

I read the following chapters: https://picocli.info/#_subcommand_methods and https://picocli.info/#_convenience_methods_for_subcommands

So it should be something like this I guess - similiar to what is shown in the first link of the documentation (chapter 18.2.1):

@Command(
        name = "rla",
        mixinStandardHelpOptions = true
        )
public class App{

    @Option(names = {"-bb"}, required = true)
    private String bb;
    //think of several required variables here

    public static void main(String[] args) {          
        CommandLine cl =  new CommandLine(App.getInstance());
        cl.parseWithHandler(new RunAll(), args);    //convencience method as suggested in documentation chapter 18.4. 
    }

    @Command
    public void usecase1(@Option(names = {"-uc1p"}) String abc) {
        System.out.println("\n\n Usecase 1" + abc + "\n\n");
    }

    @Command
    public void usecase2(@Option(names = {"-uc2p"}) String abc) {
        System.out.println("\n\n Usecase 2" + abc + "\n\n");
    }

}

Results:
_With calling the top command_
java -jar "rla.jar" rla -bb "z" usecase1 -uc1p "a":

Unmatched argument: rla
Usage:

rla [-hV] [-bb=] [COMMAND]

Description:

XXXX

Options:
-bb=
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
Commands:
usecase1
usecase2

_Or only calling the subcommand:_
java -jar "rla.jar" usecase1 -uc1p "a":

Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:61)
Caused by: picocli.CommandLine$ExecutionException: Parsed command (com.XXXXXXXXXX.App@4802796d) is not Method, Runnable or Callable
at picocli.CommandLine.execute(CommandLine.java:1213)
at picocli.CommandLine.access$800(CommandLine.java:141)
at picocli.CommandLine$RunAll.handle(CommandLine.java:1413)
at picocli.CommandLine$RunAll.handle(CommandLine.java:1376)
at picocli.CommandLine$AbstractParseResultHandler.handleParseResult(Comm
andLine.java:1243)
at picocli.CommandLine.parseWithHandlers(CommandLine.java:1526)
at picocli.CommandLine.parseWithHandler(CommandLine.java:1465)
at com.XXXXXXXXXX.App.main(App.java:112)
... 5 more

How do I call the app correctly :D?

question

All 8 comments

I assume that the com.XXXXXXXXXX.App class is the Main-Class of the rla jar. Therefore, by executing java -jar rla.jar, the com.XXXXXXXXXX.App main method is invoked, which runs the "rla" command.

So, java -jar rla.jar invokes the rla command. These two are equivalent: you run the rla command by invoking java -jar rla.jar.

This explains why we see the "Unmatched argument: rla" error when specifying another "rla" command line argument after java -jar rla.jar.

The correct way to invoke the rla command and a subcommand would be to specify the required options and any subcommands after java -jar rla.jar:

java -jar rla.jar -bb z usecase1 -uc1p a

One more thing: you need to let the App class implement Runnable or Callable.
This is necessary in order to use parseWithHandlers and RunAll.

This is the reason for the second error "(com.XXXXXXXXXX.App@4802796d) is not Method, Runnable or Callable".

Thank you very much Remko, so I guess I was on the right path somehow.
My App class was implementing Runnable before and I had my code (which kicks off the business logic) inside my run() method. Now that I got some more use cases I wanted to have a method responsible for each use case and therefore move my code from the run() completely to these new methods. I somehow thought I can remove Runnable/Callable from the class then...

Just another question if you don't mind: So it's working now. But is it ok to have an empty run() method then? What would be the best practice if you only want to use sub commands and don't need the run() method / top level command?

I appreciate your support here which often also covers questions which shouldn't bother you :)

is it ok to have an empty run() method then?

Yes, that is fine.

What would be the best practice if you only want to use sub commands and don't need the run() method / top level command?

There is an outstanding request for this: https://github.com/remkop/picocli/issues/625.
I plan to fix that before the 4.0 GA release.

Meanwhile, the workaround is to print an error message (and maybe show usage help) in the top command's run method. See https://stackoverflow.com/questions/53043937/picocli-how-to-make-subcommands-required for a concrete example.

Thank you!

I have re-added the Runnable and run-Method (including an error message which gets printed if the top level command is called) to the class.
And your example is working.

But I have a slighly differnent main-method setup:

    public static void main(String[] args) { 

        CommandLine cl =  new CommandLine(App.getInstance());
        cl.setToggleBooleanFlags(false);
        List<Object> results = cl.parseWithHandlers(new CommandLine.RunAll(), CommandLine.defaultExceptionHandler().andExit(-1), args);             
    }

So I want to use CommandLine.RunAll() to make it possible that multiple sub commands are executed with one call to the application.

In combination with your explanation in https://github.com/remkop/picocli/issues/687#issuecomment-490930294 when using "java -jar rla.jar usecase1 -uc1p a usecase2 -uc2p b", the top-level command is therefore always executed and thus the error message inside the run() gets always printed.
2019-05-10 12_08_56-mRemoteNG - confCons xml - de-muc-mbcpo148 v1 10 JACOB

(What I read in #625 is a slightly different requirement I think: Making the subcommands required additionally to the top-level command.)

So my question would be: Is it possible to only call the sub methods when using CommandLine.RunAll()? :)

E: Oops, is it even possible to call multiple (independent from each other) sub commands at once?
rla.jar" usecase1 -u1 "ABC" usecase2 -u2 "DEF"

I think what you are looking for is RunLast instead of RunAll.
That will run the last subcommand that the user specified.

This is the default and is what most applications use.

Ja, in this case (multiple non-hierarchical sub commands not possible with one call) it makes sense to use RunLast then.

Thank you :)

You鈥檙e welcome!
Please star the project on GitHub if you like it! ;-)

Was this page helpful?
0 / 5 - 0 ratings