2

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.

beatngu13
  • 7,201
  • 6
  • 37
  • 66
  • Does it work if you run it on the JVM (`mvn gluonfx:run`)? If that is not the case, it has nothing to do with GluonFX plugin or GraalVM. It that works, I take that you are using a manifest file, so probably you need to include it as resource. Run `mvn gluonfx:runagent` first, and then build again the native image. If that doesn't work then post a complete way to reproduce the issue. – José Pereda Dec 20 '21 at 18:19
  • @JoséPereda no, it doesn't work with the `gluonfx:run` goal. I also don't have a manifest file. For the fat JAR, I was using the shade plugin, which has a [`ManifestResourceTransformer`](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer). It allows to set, e.g., the implementation version to `${project.version}`. Does GluonFX plugin offer something comparable? – beatngu13 Dec 20 '21 at 18:26
  • `gluonfx:run` is the same as `javafx:run`. In any case, and given that you are going to build a native image, the shade plugin is not required if you are building from sources. If I take it right, you just want to pass the `${project.version}` to your app, don't you?. Why don't you use the Maven `resources` plugin and add a properties file that you can read from code? – José Pereda Dec 20 '21 at 18:32
  • @JoséPereda yes, using a properties file would be an alternative solution, which I already considered. I'm just curious if there is a "GluonFX plugin"-way of making `Package#getImplementationVersion()` work? – beatngu13 Dec 20 '21 at 18:57
  • Before answering that, when you build the fat jar and you run `java -jar...`, it works fine. However if you run it via JavaFX plugin it doesn't, because you don't have a manifest in your classpath (`target/classes`), and even if you had it, it is not being accessed. The GluonFX plugin for native-image works in this way too. If you create a jar for your core code, and add it as a dependency to the main project, then that will work. But it will add extra complexity (multi-modular project). Using the resources plugin seems much more simple and convenient in this case. – José Pereda Dec 20 '21 at 20:18
  • @JoséPereda feel free to add your [issue comment](https://github.com/beatngu13/pdf-zoom-wizard/issues/278#issuecomment-998252306) as an answer, so I can accept it and close the question. – beatngu13 Dec 21 '21 at 10:05

1 Answers1

2

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:

  • Move your core code to a module, publish a jar, and add it to your main project as a dependency, that should work.
  • Move your manifest key-values to a properties file, and use the Maven resources plugin. That should work out of the box with the GluonFX plugin (make sure you add the properties extension to the resources List). This should be the easiest one.
  • File an issue on Substrate to ask for a manifest. So far, Substrate creates a jar out of the project classes, so in theory it could also have a manifest, and it could be filed with some key-values coming from the plugin's configuration. Adding the manifest is easy, the problem is adding the changes to the configuration to pass down the key-values.
José Pereda
  • 44,311
  • 7
  • 104
  • 132