Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beginner question: How to call top command and sub command #687

Closed
duracotton opened this issue May 9, 2019 · 8 comments
Closed

Beginner question: How to call top command and sub command #687

duracotton opened this issue May 9, 2019 · 8 comments

Comments

@duracotton
Copy link

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?

@remkop
Copy link
Owner

remkop commented May 9, 2019

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

@remkop
Copy link
Owner

remkop commented May 9, 2019

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".

@duracotton
Copy link
Author

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 :)

@remkop
Copy link
Owner

remkop commented May 10, 2019

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: #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.

@duracotton
Copy link
Author

duracotton commented May 10, 2019

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 #687 (comment) 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"

@remkop
Copy link
Owner

remkop commented May 10, 2019

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.

@duracotton
Copy link
Author

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

Thank you :)

@remkop
Copy link
Owner

remkop commented May 10, 2019

You’re welcome!
Please star the project on GitHub if you like it! ;-)

@remkop remkop closed this as completed May 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants