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

AutoComplete: System.exit() kills maven build #596

Closed
markusheiden opened this issue Jan 7, 2019 · 19 comments
Closed

AutoComplete: System.exit() kills maven build #596

markusheiden opened this issue Jan 7, 2019 · 19 comments
Labels
theme: auto-completion An issue or change related to auto-completion type: enhancement ✨
Milestone

Comments

@markusheiden
Copy link

When using AutoComplete with the exec-maven-plugin to generate the script, the maven build process gets aborted hard, because AutoComplete uses System.exit(). So subsequent plugins (e.g. in the deploy phase) won't get executed. May you please add an option to suppress the System.exit() call?

@remkop remkop added this to the 3.9.1 milestone Jan 7, 2019
@remkop
Copy link
Owner

remkop commented Jan 8, 2019

Thanks for raising this issue.
This was introduced by the changes made for #582 .

I'm thinking to address this by adding two command line flags:

--exit-on-error   When this option is specified, the `AutoComplete` application exits with a non-zero
                  exit code when an error is encountered during execution.
--exit-on-success When this option is specified, the `AutoComplete` application exits with a zero
                  exit code when the application completes successfully.

The default would be to not call System.exit.

@bobtiernay-okta, since you raised #582, can you confirm that the above changes would still meet your requirements?

@remkop remkop added theme: auto-completion An issue or change related to auto-completion status: in-progress 👷‍♂️ labels Jan 8, 2019
@bobtiernay-okta
Copy link
Contributor

I guess I’m not understanding why System.exit is causing an issue. If it is, can you not specify the fork option?

Also, I’m partial to gnu style arguments using hyphens :)

@remkop
Copy link
Owner

remkop commented Jan 8, 2019

I updated the hyphens in the option names above. But I’m not sure if program options are the way to go. Perhaps a system property is better, to handle the case where user input was invalid: picocli may throw an error before all command line arguments are parsed.

I can see @bobtiernay-okta’s use case for exit with non-zero error code on failure, and I can also see the use case for not calling System.exit() on success.

It makes sense to provide some mechanism to let users control this behavior.

What should this mechanism be? I can think of command line options or system properties, any other ideas? Also, what should be the default behaviour?

@markusheiden
Copy link
Author

@bobtiernay-okta I may use the exec goal instead of the java goal of the exec-maven-plugin to be able to fork, but this results much more configuration, because I have to create the classpath myself.

@remkop I would prefer a command line option. An alternative could be to throw an exception on error, e.g. --throw-on-error.

@remkop
Copy link
Owner

remkop commented Jan 8, 2019

@markusheiden, can you clarify why you prefer a command line option?
If you are using the java goal, you can specify system properties, according to the Usage docs:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
          <execution>
            ...
            <goals>
              <goal>java</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <mainClass>com.example.Main</mainClass>
          <arguments>
            <argument>argument1</argument>
            ...
          </arguments>
          <systemProperties>
            <systemProperty>
              <key>myproperty</key>
              <value>myvalue</value>
            </systemProperty>
            ...
          </systemProperties>
        </configuration>
      </plugin>
    </plugins>
  </build>
   ...
</project>

@markusheiden
Copy link
Author

@remkop As you indirectly documented above: The maven configuration would be shorter with a command line option, because there are already command line options. I can live with system properties or environment variables too, if you prefer that way.

@remkop
Copy link
Owner

remkop commented Jan 8, 2019

@markusheiden, thanks for the clarification.

The problem I see with a command line option is that it is difficult to handle invalid input. For example:

java -cp x.jar picocli.AutoComplete --invalid-option --exit-on-error

Picocli will internally throw an exception when the first invalid option is seen, so before it sees the option related to exit behaviour. I'm leaning towards using a system property, since this does not have that drawback.

Some possibilities:

  • picocli.autocomplete.enableExitCodes
  • picocli.autocomplete.disableExitCodes
  • picocli.autocomplete.enable/disableSystemExit
  • picocli.autocomplete.exit (don't call System.exit unless this property is defined)
  • picocli.autocomplete.noexit (always call System.exit unless this property is defined)

Thoughts?

@markusheiden
Copy link
Author

If think it is better to let the exit codes enabled by default, because then AutoComplete can easier be used as standalone tool. So for me picocli.autocomplete.disableExitCodes, picocli.autocomplete.disableSystemExit and picocli.autocomplete.noexit are all fine. Maybe picocli.autocomplete.throwOnError would be a good option name too? Because if there is no exit code, there has to be an exception to to let the caller know about an error.

@bobtiernay-okta
Copy link
Contributor

I actually just hit this, but I didn't know it was an issue even. I just happened to look at my CI script and it hasn't been publishing since I upgraded. The reason is that it terminated before the deploy phase could complete. I think this will be an edge case that catches people off guard. Probably better to at least not call exit with a zero return code as mentioned.

@remkop
Copy link
Owner

remkop commented Jan 9, 2019

@markusheiden, good point. I'll ensure that exceptions are properly propagated when System.exit is not enabled.

@bobtiernay-okta, ok, so it looks like the default should be to not call System.exit and only call System.exit when explicitly requested.

One remaining question is whether there should be a single property to enable all System.exit calls or separate system properties for calling System.exit when there was a problems (non-zero exit code) and when the application ended successfully (zero exit code).

Something like the below:

  • picocli.autocomplete.systemExitOnError (non-zero exit code)
  • picocli.autocomplete.systemExitOnSuccess (zero exit code)
  • picocli.autocomplete.enableSystemExit (regardless of exit code)

Thoughts?

@RobertZenz
Copy link
Contributor

Guarding it behind a property which is by default off is okay, in my opinion. Especially if you provide the means to handle all error cases correctly outside of picocli. I'd drop the third option, it doesn't add a lot of value but has the potential to confuse the user "I removed the exit on error option, why is it still exiting?".

Theoretically one should never call System.exit(int), especially not in libraries, but I see that this is sometimes desirable behavior.

@bobtiernay-okta
Copy link
Contributor

Just as an FYI, you can workaround this issue in Maven by using the following exec goal configuration:

          <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${exec-maven-plugin.version}</version>
                <executions>
                    <execution>
                        <id>generate-autocompletion-script</id>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <!-- See https://picocli.info/autocomplete.html#_generate_completion_script -->
                        <configuration>
                            <executable>java</executable>
                            <arguments>
                                <argument>-cp</argument>
                                <classpath/>
                                <argument>picocli.AutoComplete</argument>
                                <argument>-f</argument>
                                <argument>-o</argument>
                                <argument>${project.build.directory}/my-completion.sh</argument>
                                <argument>my.MyCommand</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

@remkop Perhaps we should update the docs?

@markusheiden Is this really any more configuration than the java goal? I think it may be only one more line:

<classpath/>

@bobtiernay-okta
Copy link
Contributor

bobtiernay-okta commented Jan 9, 2019

Theoretically one should never call System.exit(int), especially not in libraries, but I see that this is sometimes desirable behavior.

@RobertZenz I agree that this should not be general Java design. But I do believe it is acceptable for CLI applications at the highest level of control for example, I always try to do something like:

public final class Main {

    @SuppressWarnings("PMD.DoNotCallSystemExit")
    public static void main(String[] args) {
        System.exit(Runner.run(args).code());
    }

}

If you think about this jar as a binary then this is acceptable. The only reason why this is coming up here is an implementation detail of the maven-exec-plugin which is the fact that it runs in process and not forked. The solution is simple as discussed above.

remkop added a commit that referenced this issue Jan 9, 2019
@remkop
Copy link
Owner

remkop commented Jan 9, 2019

Thanks everyone for thinking together to help solve this issue! I pushed a fix to master, please verify if it matches your expectations.

As part of the fix, the usage help message now has the following footer:

Exit Code
Set the following system properties to control the exit code of this program:
 "picocli.autocomplete.systemExitOnSuccess" - call `System.exit(0)` when
                                              execution completes normally
 "picocli.autocomplete.systemExitOnError"   - call `System.exit(ERROR_CODE)`
                                              when an error occurs
If these system properties are not defined or have value "false", this program
completes without terminating the JVM.

The only thing remaining is updating the documentation (autocomplete.adoc). It makes sense to me that we suggest to use the exec-maven-plugin with the exec goal instead of the java goal. Can anyone provide a snippet with the above system properties that has been tested and can be included in the autocomplete.doc docs?

@bobtiernay-okta
Copy link
Contributor

bobtiernay-okta commented Jan 10, 2019

The only thing remaining is updating the documentation (autocomplete.adoc). It makes sense to me that we suggest to use the exec-maven-plugin with the exec goal instead of the java goal. Can anyone provide a snippet with the above system properties that has been tested and can be included in the autocomplete.doc docs?

I don't think that's required for the exec goal since it will always spawn a new process (meaning it's impervious to System#exit). It is only needed for the java goal because it shares the same JVM as the Maven plugin.

@remkop
Copy link
Owner

remkop commented Jan 10, 2019

@bobtiernay-okta The purpose of letting AutoComplete exit with non-zero exit codes (#582) was to be able to fail the build if there is a problem generating the completion script. So our example in autocomplete.doc should be updated to show users how to make the build fail when there's an issue.

Using the java goal, we don't want to call System.exit because it would kill the process instead of failing the build. Therefore our example should show how to use the exec goal with the necessary system properties to make AutoComplete produce non-zero exit codes on error.

I believe the exec goal will fail the build if the exit code is non-zero (well, I'm guessing from the fact that there is a successCodes property...)

So we should update the example, no?

@remkop
Copy link
Owner

remkop commented Jan 10, 2019

FYI: I have a working example. Will commit shortly.

@remkop remkop closed this as completed in 7b901e9 Jan 10, 2019
@remkop
Copy link
Owner

remkop commented Jan 10, 2019

Docs updated. Done, closing this ticket. Thanks everyone for your help!

@bobtiernay-okta
Copy link
Contributor

So we should update the example, no?

Ah yes, apologies. I forgot we were changing the default behavior. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: auto-completion An issue or change related to auto-completion type: enhancement ✨
Projects
None yet
Development

No branches or pull requests

4 participants