I'm trying to build a Java application which will be built into a Linux command line application using native-image provided by GraalVM. I have the below code:
@CommandLine.Command(
name = "my-application",
mixinStandardHelpOptions = true,
helpCommand = true,
version = "my-application 1.0",
description = "My file manipulation application."
)
public class MyApplication implements Callable<Integer> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(new YAMLFactory()
.enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
);
@CommandLine.Parameters(index = "0", description = "The source file.")
private File sourceFile;
@CommandLine.Parameters(index = "1", description = "The target files.")
private List<File> targetFiles;
@CommandLine.Option(names = {"-env", "--environment"}, defaultValue = "TEST")
private Environment environment; // TEST or PROD
@CommandLine.Spec CommandLine.Model.CommandSpec spec;
public static void main(String[] args) {
int exitCode = new CommandLine(new MyApplication()).execute(args);
System.exit(exitCode);
}
@Override
public Integer call() throws Exception {
spec.commandLine()
.getOut()
.println("Starting my-application tool execution...");
// Some file manipulation using Jackson.
spec.commandLine().getOut().println("Execution finished successfully.");
return 0;
}
I build the JAR with maven package and this jar has all needed dependencies. Then I build the native application with:
native-image -jar my-application-1.0-SNAPSHOT-jar-with-dependencies.jar -H:+ReportExceptionStackTraces --no-fallback
And when running the application with:
./my-application-1.0-SNAPSHOT-jar-with-dependencies inputfile.txt outputfile1.txt outputfile2.txt
I get the following error:
Exception in thread "main" picocli.CommandLine$InitializationException: picocli.CommandLine$AutoHelpMixin@62037019 is not a command: it has no @Command, @Option, @Parameters or @Unmatched annotations
at picocli.CommandLine$Model$CommandReflection.validateCommandSpec(CommandLine.java:11680)
at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11510)
at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6236)
at picocli.CommandLine$Model$CommandSpec.mixinStandardHelpOptions(CommandLine.java:7220)
at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11505)
at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6236)
at picocli.CommandLine.<init>(CommandLine.java:227)
at picocli.CommandLine.<init>(CommandLine.java:221)
at picocli.CommandLine.<init>(CommandLine.java:196)
at com.test.MyApplication.main(MyApplication.java:42)
The application works fine when I run it via IntelliJ or a simple java -jar command.
I tried a few things, as removing mixinStandardHelpOptions and helpCommand, but I start getting errors like "Unmatched arguments from index 0" or NullPointer with the spec (for printing outputs).
Before introducing picocli I was successfully able to run the native application and manipulating the files with ObjectMapper, so I believe that the packaging and image building procedure is correct.
Tried to follow the tip here:
https://github.com/remkop/picocli/issues/631
But I'm not sure how to proceed, tried adding something like
@Reflections({
@Reflection(scanClass = CommandLine.Mixin.class),
@Reflection(scanClass = CommandLine.HelpCommand.class)
})
But nothing changed.
Any help would be appreciated :)
Just found out that I was missing a dependency:
<!-- https://mvnrepository.com/artifact/info.picocli/picocli-codegen -->
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>4.6.3</version>
</dependency>
For more information:
https://www.infoq.com/articles/java-native-cli-graalvm-picocli/