In a Java-based CLI application, I'm using the following libraries
The application has different actions that can be executed. I have implemented these using subcommands. The entire setup is as follows:
The main class is annotated with the subcommand classes. In the main method, I first load and validate the configuration, then create an IFactory instance that in turn creates the Guice injector and configures it with modues for my application as well as the config injection module mentioned above. The factory is used to instantiate the subcommand classes as shown in the picocli docs, because the subcommands already need some stuff injected, including instances that in turn need access to configuration parameters. Back in the main class, I hand over the processing to picocli.
There are two places where command line options are currently defined:
All of the above is working as desired so far, and I'd really like to thank everyone involved in creating this kind of infrastructure.
I'm now faced with the following issue: Typesafe Config allows for setting individual configuration options from the command line by
specifying system properties with -Dmy.config.par=foobar
, which is what I would like to use. This works perfectly as long as I either
start my application from with the IDE or execute it from the command line with
java -classpath $CP -Dmy.config.par=foobar org.x2vc.Checker <otherParams>
- i.e., set the property before the main class is specified.
For a distributable version, this is not an option because the classpath can become very long. I'm currently using the
Maven Application Assembler plug-in to generate startup scripts for different platforms, which works really well - except for the part
that I can't easily add -D
parameters before the class name.
I am trying to implement the approach in the picocli documentation - essentially extend the command line options by a -D
option
and set the system properties within my application. I can't seem to get it working however. As far as I've understood, I have a kind of
logical circular dependecy here:
-D
option by picocli only takes place after all of the above has happened.I'm unsure what approach to take from here. I would like to avoid modifying the shell scripts if possible - testing these in various environments would be a huge additional task. The only option I currently see is to move the Guice initialization into the subcommands - is there a better option that I have overlooked?
It does sound like you need to specify these system properties before invoking picocli, maybe even before creating the IFactory
.
Some ideas:
Idea 1:
Users who run your application on Java 11 can use java argument files when they execute the java
command. (This is similar to picocli's @file feature, but this is built in to the java
tool.)
java @myargs org.x2vc.Checker <otherParams>
# contents of myargs:
-classpath $CP
-Dmy.config.par=foobar
-Dmy.config.par2=foobar2
-Dmy.config.par3=foobar3
This solves the problem of the command line becoming too long.
Idea 2:
Use picocli twice: once to parse out the command line arguments, and once again for the actual application. The documentation has an example of two-phase parsing for another use case.
In the first phase you are only interested in system properties, so you don't need a factory, or any logging or any of that. You just create a simple @Command
class that detects -D
properties and ignores everything else.
After these are successfully parsed from the command line and System.setProperty
has been invoked for each of them, phase 1 is complete.
Phase 2 is basically your application as you described above: create the factory, use Guice, instantiate a picocli CommandLine
with that factory, etc.