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

API to know all the commands #156

Closed
nagkumar opened this issue Jul 28, 2017 · 36 comments
Closed

API to know all the commands #156

nagkumar opened this issue Jul 28, 2017 · 36 comments
Labels
status: declined ❌ A suggestion or change that we don't feel we should currently apply type: API 🔌 type: doc 📘 type: question ❔
Milestone

Comments

@nagkumar
Copy link

Looking for an API to know all the commands defined. This can be helpful to print help message of all the commands in case customer asks for global help of all the commands

@remkop
Copy link
Owner

remkop commented Jul 28, 2017

Please take a look at the CommandLine::getSubcommands method: https://github.com/remkop/picocli/blob/master/src/main/java/picocli/CommandLine.java#L185

@remkop
Copy link
Owner

remkop commented Jul 28, 2017

Also, by default CommandLine::usage will print the list of subcommands available for the current command.

@nagkumar
Copy link
Author

nagkumar commented Jul 28, 2017

Is there similar api to to know all the commands i.e which are annotated with @command

@remkop
Copy link
Owner

remkop commented Jul 29, 2017

You mean in the classpath? You could iterate over all classes and query them with cls.isAnnotationPresent(Command.class) but there are so many classes in the classpath that this would be very slow...

@remkop
Copy link
Owner

remkop commented Jul 29, 2017

It may be easier to think of it this way: a Java program is started with a number of command line arguments. Think of the Java program as the top level command. In picocli this is the object that the CommandLine was constructed with.

Your program can understand some subcommands (and each of these can have nested sub-subcommands to any depth). When you create your program you know what subcommands it can handle. For each of these you call commandLine.addSubcommand(subcommandName, subcommandObject). Since you created the program you know what subcommands exist.

@nagkumar
Copy link
Author

Instead of parsing every class, would it be possible for an api, which can do this during the annotation processing. This way, extra overheads at run time can be reduced

@remkop
Copy link
Owner

remkop commented Jul 31, 2017

Thanks for the interesting feedback and discussion on this ticket and #155.

Currently, picocli is designed to have a hierarchy of subcommands in a tree structure, with a single CommandLine object at the top of the hierarchy. In this design, applications can always query the top-level CommandLine object to get a map of the registered subcommands. Each of these subcommands can be further queried for the more deeply nested sub-sub commands, etc.

I am aware of your proposal in #152 to register subcommands via annotations. The question raised in this ticket (API to know all commands) is one of the questions that need to be answered if we were to register subcommands via annotations. Perhaps we should make a note of that in the #152 ticket.

Given the above explanation of the current picocli design, are you ok to close this question?

@nagkumar
Copy link
Author

nagkumar commented Aug 1, 2017

This ticket API need not be linked to sub commands tickets #152, as we any way need to know all the main commands in a given application.

#152 would impact this API, by support of query of main commands, which are not sub commands

@remkop
Copy link
Owner

remkop commented Aug 1, 2017

(Sorry, I pressed the wrong button. Didn't mean to close this issue. )

Let's clarify our terminology to make sure we are talking about the same thing.

In my view, a Java application has an entry point (the main method) which corresponds 1-on-1 with the main command.

The name of the main command is not in the String[] array passed to the main(String[] args) method. What appears in the argument array are subcommands, options and nested sub-subcommands.

In this terminology, each Java application (a class with a main method) has only one main command.

Does that match what you call main commands and subcommands?

@remkop remkop closed this as completed Aug 1, 2017
@remkop remkop reopened this Aug 1, 2017
@nagkumar
Copy link
Author

nagkumar commented Aug 2, 2017

Remkop, the API needed for the situation as said in #155

i.e for usage of CLI in the following way

java -cp myApp.jar com.teja.Application create_parking 213
java -cp myApp.jar com.teja.Application park car white ka05ma2456
java -cp myApp.jar com.teja.Application leave 4
java -cp myApp.jar com.teja.Application status

My observation in cli applications is that, Java Class (only one main method for all the CLI commands) and jar would be same. The subsequent parameter would be the main command. In above example, i see create_parking
park
leave
status

as main commands.

i.e. there is only one main method in the application, which knows what the main cli command is based on args[0] i.e first argument.

@remkop
Copy link
Owner

remkop commented Aug 2, 2017

In picocli, create_parking, park, leave etc are subcommands, not main commands. This is documented in the picocli user manual, please see 10.2. Parsing Subcommands.

I should update the documentation to make this more clear.

@remkop
Copy link
Owner

remkop commented Aug 2, 2017

I took a look at your project.

I now understand your use case a bit better. Basically, when the user requests help, you want to print the usage() of the subcommand that was invoked, and also print a list of all available commands. To accomplish the latter, you want to call usage() on the main command. The problem is that the subcommand does not have a reference to the main command.

You solved this by taking the first CommandLine main command object from the list returned by parse, and passing this to the subcommand before calling execute on the subcommand.

I think that looks pretty good. If I can make a suggestion: why not pass the first CommandLine main command object as a parameter to the execute method of the subcommand that was invoked? That way you no longer need to call lSubCommand.setRootCMDLine(lRootCmdLine) and you can even remove the AbstCLICmd.rootCMDLine field altogether.

Something like this:

// class CMDUtils
public static final void invokeCLICmd(final String[] aArgs) throws Exception {
    List<CommandLine> list = createCLISpec().parse(aArgs);
    assert list.size() == 2;
    AbstCLICmd lSubCommand = ((AbstCLICmd) list.get(1).getCommand());

    // pass the main command so we can show list of available commands
    lSubCommand.execute(list.get(0)); 
}
...

The execute method signature would look like this:

public interface CLICmd {
    public CLICmd execute(CommandLine allCommands) throws Exception;
    ...
}
// class AbstCLICmd 
@Override
public final CLICmd execute(CommandLine allCommands) throws Exception {
    if (isHelpRequested()) {
        CommandLine.usage(this, System.err);
        allCommands.usage(System.err); // show list of available commands
    }
    ...

Alternatively, you could pass the full List<CommandLine> to the execute method, but perhaps that is overkill.

@nagkumar
Copy link
Author

nagkumar commented Aug 2, 2017

Glad that you looked at my code.. Thank you so much.
b.t.w, this repository was supposed to private rep, which I have made it now else where.

I can privately share you the code.. in case you already don't have

I can follow your suggestion.. however, passing the reference in all the methods (I am assuming that, one of the called method also needs reference of allCommands in some context) called by execute() also demands change of signature to take allCommands reference as parameter. which is not advised..

@nagkumar
Copy link
Author

nagkumar commented Aug 2, 2017

In my code, I see the following as issues, w.r.t CLI, which i wish picoCLI to consider to enhance its api

  1. Every Sub command can have reference to its parent (subcommand which is added through addSubcommand() )
  2. addSubcommand() can have equivalent annotation
    e.g @Command(name = Leave.LEAVE_CMD, description = "remove the vehicle from parking lot", subcommandof=RootCmd.class) This can potentially eliminate long api calls and easy to maintain relationships with annotations than with code
  3. API to know all commands which are annotated with @command
  4. API to know all commands which are annotated with @command and does not belong to any other parent (reference to point 2)

@remkop
Copy link
Owner

remkop commented Aug 2, 2017

  1. Sounds reasonable. I can add a method like CommandLine::getParentCommand() returning a CommandLine.
  2. Annotation equivalent of addSubcommand() is covered by Annotation way to represent subcommands #152. Need to think about this more.
  3. Two questions: What is the use case for this? Also, what is the scope (inspect within one class where subcommands are inner classes, or in same package, or in the same jar file, or scan the whole classpath)?
  4. Same questions as (3)

@nagkumar
Copy link
Author

nagkumar commented Aug 2, 2017

3 & 4 use case I have discussed above

java -cp myApp.jar com.teja.Application create_parking 213
java -cp myApp.jar com.teja.Application park car white ka05ma2456
java -cp myApp.jar com.teja.Application leave 4
java -cp myApp.jar com.teja.Application status

In such cli application where main method is only one, how do we print all commands help message (don't wish to create a virtual command)

@remkop
Copy link
Owner

remkop commented Aug 2, 2017

Thanks for the clarification.

Can we rename this issue to "Add getParentCommand API", and rename issue #155 to "API to know all commands"?

@nagkumar
Copy link
Author

nagkumar commented Aug 2, 2017

Sure. Thank U.

@remkop remkop added this to the 0.9.8 milestone Aug 3, 2017
@remkop
Copy link
Owner

remkop commented Aug 4, 2017

  1. I added method CommandLine::getParent (see Add getParent API #157). Please see if this works for you.
  2. Annotation equivalent of addSubcommand() is implemented in Annotation way to represent subcommands #152. Please see if this works for you.

@nagkumar
Copy link
Author

nagkumar commented Aug 4, 2017

sure Rem, is the latest jar pushed to maven
I only see June update here (https://mvnrepository.com/artifact/info.picocli/picocli)

@remkop
Copy link
Owner

remkop commented Aug 4, 2017

I didn't make a release yet. Can you check out and build master to confirm?

@nagkumar
Copy link
Author

nagkumar commented Aug 5, 2017

Tested with 0.9.8-SNAPSHOT.jar local jar, every thing works.
Thank you. it would be nice if you could push *-SNAPSHOT.jar to maven rep.

@remkop
Copy link
Owner

remkop commented Aug 5, 2017

Thanks for the confirmation.
I'm thinking to release 0.9.8 soon, hopefully this weekend.

@nagkumar
Copy link
Author

nagkumar commented Aug 5, 2017

I am sure, most may not like it, However I do wish in similar lines to getParent(), if getCommand() method CommandLine returns @command object, which would also have reference to CommandLine reference.

My thoughts on this is @command object is domain Object, it is nice to deal with this reference everywhere in application/solution rather than referring/passing CommadLine everywhere.

@remkop
Copy link
Owner

remkop commented Aug 5, 2017

I see your point. It would be nice to deal with the domain objects directly everywhere.

We added getParent so that CommandLine objects can traverse the subcommand hierarchy in both directions.

For domain objects to be able to do this, they would need to implement some interface, but I don't want picocli to impose any restrictions on the domain objects.

@remkop
Copy link
Owner

remkop commented Aug 6, 2017

Ok then, on the the last point, the title of this issue:

  • API to know all commands annotated with @Command

You mention you need this in order to print a help message with all commands.

Picocli gives you that ability if you create a main command and register your commands as subcommands. What I don't understand is why you don't want to do this.

Furthermore, what if you have a JAR with two command line applications, each with a different set of subcommands? Automatically picking up all @Command classes would incorrectly mix all these classes.

I really don't see any benefit in doing this...

@remkop remkop removed this from the 0.9.8 milestone Aug 6, 2017
@nagkumar
Copy link
Author

nagkumar commented Aug 6, 2017

Creating main command and adding every others as sub command is a hack , also code is saying wrong relationships which are not real, but created to print messages of all commands.

Presence of query API may avoid one main command class all together in every CLI application.

@remkop
Copy link
Owner

remkop commented Aug 6, 2017

No, this is not a hack.

Every picocli application has a main command, even CLIs without subcommands.

For example, this application has a main command without subcommands:

java -cp myjar.jar com.example.Zip -file /path/to/file

Note that the user never specifies the name of the main @command on the command line. The main command is always implicit on the command line.

In your examples:

java -cp myApp.jar com.teja.Application status

The user needs to specify status (the name of the Status @command) on the command line. That means status is a subcommand, not a main command. It must be registered as a subcommand if you want to use picocli to parse the command line.

@remkop
Copy link
Owner

remkop commented Aug 7, 2017

Note that this is not just about printing a list of all commands.

Your Application is a command line application, and as such it should probably have a usage help message by itself, so users can do the following and get general information about what the application does:

java -cp myApp.jar com.teja.Application --help

Your Application may support other global options, like --verbose, etcetera, which apply to all subcommands.

java -cp myApp.jar com.teja.Application --verbose park  car white ka05ma2456
                            ^               ^      ^     ^
                            |               |      |     |
                        main command    global    sub-   parameter to subcommand
                                        option  command

By making Application (or some other internal class) the main command, and status and park subcommands of that main command, you get all the above behaviour out of the box. This is the natural way to model a CLI with subcommands like your application. It is not a hack.

@remkop
Copy link
Owner

remkop commented Aug 7, 2017

(By the way, picocli-0.9.8 is now available for download from Maven Central. The user manual is also updated.)

@nagkumar
Copy link
Author

nagkumar commented Aug 7, 2017

It is the time, we start re-defining main command differently in the context of picocli as it specific to java applications.

I see com.teja.Application as argument passed to java. It has no relevance to picocli framework main command.

Assume the other scenario too, what if I have a way to convert com.teja.Application into exe file say app.exe. Then I shall invoke the same command

app.exe --verbose park car white ka05ma2456

My understanding is app.exe as application not as main command unless com.teja.Application is annotated with @command. CLI framework, should not enforce that com.teja.Application has to be annotated with @command.

The way I wish PICOCLI to view main command is, any class which is annotated with @command annotation and potentially, we can take subcommands as the ones which are also annotated with @command and also added to another @command class through::addSubcommand()or with respective annotation.

@remkop
Copy link
Owner

remkop commented Aug 7, 2017

Doing what you're asking for would re-introduce a host of problems that are already solved with the current main command-subcommand hierarchy.

What you describe as the benefit (ability to avoid one main command class all together in every CLI application) does not seem like a big problem. In fact, I am convinced that as soon as your application starts to have global options and global help, you will find that omitting the central main command is not even desirable and changing the design to support this would be a mistake.

I'm going to mark this feature request as "won't fix" for now, until someone can show me a use case that cannot be accomplished with the current main command-subcommand hierarchy. (I mean, "I just don't want to create a central main command" is not enough reason for me to do this.)

@remkop remkop added the status: declined ❌ A suggestion or change that we don't feel we should currently apply label Aug 7, 2017
@remkop
Copy link
Owner

remkop commented Aug 7, 2017

But again, thank you for the interesting discussions and good suggestions! Thanks to your input picocli now has a getParent method and the ability to declaratively add subcommands via annotations, and I credited these to you in the release notes. Also, later I'll work to make subcommands case-insensitive.

Thanks for your contributions, I greatly appreciate them!

@nagkumar
Copy link
Author

nagkumar commented Aug 7, 2017

(I mean, "I just don't want to create a central main command" is not enough reason for me to do this.)

Ok, if PICOCli wishes to enforce this, hope it could be documented, specially on the definition of main command which is always the first argument to java app, it needs to have @command and it also needs to have subcommands attribute taking all the commands in that application and has main() method.

@remkop
Copy link
Owner

remkop commented Aug 7, 2017

Sure, let me know what section in the user manual specifically needs to be clarified.
I am open to pull requests for documentation improvements.

@nagkumar
Copy link
Author

nagkumar commented Aug 7, 2017

This section can be refined http://picocli.info/#_introduction
public class Example {

can be made as

@Command(subcommands= {})
public class Example {

  public static void main(String[] args){
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined ❌ A suggestion or change that we don't feel we should currently apply type: API 🔌 type: doc 📘 type: question ❔
Projects
None yet
Development

No branches or pull requests

2 participants