javajavafxgraalvmgraalvm-native-imagegluonfx

Package#getImplementationVersion() returns null with native image (GraalVM/GluonFX)


I have a JavaFX-based desktop app, for which I create native images via GraalVM/GluonFX. To retrieve the version of the app during runtime, I previously – when there was only a fat JAR of the app – used Package#getImplementationVersion(). However, this returns null.

I assume this is because I don't set the manifest entries properly? My configuration of the gluonfx-maven-plugin:

<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>gluonfx-maven-plugin</artifactId>
    <version>1.0.10</version>
    <configuration>
        <mainClass>${mainClass}</mainClass>
        <nativeImageArgs>
            <arg>--allow-incomplete-classpath</arg>
            <arg>--initialize-at-build-time=org.pdfclown.Version</arg>
            <arg>--no-fallback</arg>
        </nativeImageArgs>
    </configuration>
</plugin>

Is there a way to configure the plugin such that Package#getImplementationVersion() returns the app's version? I wasn't able to find something in the corresponding documentation. Also, a related issue in GraalVM has been resolved in 2020.


Solution

  • Let say you have a Maven project and you add to your main Application class:

    @Override
    public void start(Stage primaryStage) throws Exception {
        ...
        System.out.println("Main class version: " + getClass().getPackage().getImplementationVersion());
    }
    

    If you do a fat jar with the shade plugin and take care of adding the required manifest entries like Implementation-Version (let's say 1.0), when you run:

    java --module-path=PATH_TO_FX --add-modules javafx.controls,javafx.fxml -jar my-fat-jar.jar
    

    it should print:

    Main class version: 1.0
    

    However if you run

    mvn javafx:run
    

    it prints:

    Main class version: null
    

    as you have reported.

    The reason for this: there is no manifest in the classpath, and the classes (target/classes) are not packed in a jar.

    If your project has a third party dependency that includes a manifest with an Implementation-Version entry (let's say 2.0), doing:

    @Override
    public void start(Stage primaryStage) throws Exception {
        ...
        System.out.println("3rd party Main class version: " + com.my.third.party.MainClass.class.getPackage().getImplementationVersion());
    }
    

    will work:

    mvn javafx:run
    [INFO] --- javafx-maven-plugin:0.0.8:run (default-cli) @ MyProject ---
    3rd party Main class version: 2.0.
    

    because the jar is a dependency of packed classes with a manifest.

    And doing the native image mvn gluonfx:build gluonfx:nativerun also works:

    [INFO] --- gluonfx-maven-plugin:1.0.10:nativerun (default-cli) @ MyProject ---
    [Mon Dec 20 21:29:16 CET 2021][INFO] ==================== RUN TASK ====================
    [Mon Dec 20 21:29:16 CET 2021][INFO] [SUB] 2021-12-20 21:29:16.810 MyProject[23068:414510] Starting Gluon VM...
    [Mon Dec 20 21:29:16 CET 2021][INFO] [SUB] Dec 20, 2021 9:29:16 PM com.sun.javafx.application.PlatformImpl startup
    [Mon Dec 20 21:29:16 CET 2021][INFO] [SUB] WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @21c815e4'
    [Mon Dec 20 21:29:16 CET 2021][INFO] [SUB] 3rd party Main class version: 2.0
    

    So at this point I see three options: