Search code examples
javajavafxjavafx-11openjfx

OpenJFX Custom runtime image using Maven and jlink - Module exports or command line arguments?


I'm trying to create a custom runtime image that will not require installed JRE/JDK on the computer. I have followed the tutorial presented on OpenJFX Documentation (JavaFX and IntelliJ - Modular with Maven) and I am able to run the created image, but I want to include for my application class com.sun.glass.ui.Window (in module javafx.graphics).

Before custom images, I was parsing as command line arguments the following: --add-opens javafx.graphics/com.sun.glass.ui=ALL-UNNAMED

I want to include this in runtime, so should I modify the pom from Maven to include for javafx-maven-plugin a (not successful) or should I edit the project module.info to export the requested package from javafx.graphics.

Thanks, Andrei

Pom.xml module.info.java

module com.andrei {
    requires javafx.controls;
    requires javafx.graphics;
    exports com.andrei;
    exports com.sun.glass.ui to com.andrei;
}

"Package "com.sun.glass.ui" is declared in module "javafx.graphics", which does not export to module "com.andrei" "


Solution

  • The javafx-maven-plugin should be able to do what you are trying to do. However, it doesn't do it so far, so I've just filed these two issues: Options for javafx:run are incompatible with javafx:jlink and Missing link vm options parameter.

    While this gets resolved and a new version is published, there is an easy (but manual) fix:

    Compilation time

    Before modifying the javafx-maven-plugin, you need to allow your IDE to work with the private package. You can't do it from the module-info, but you can easily do that from the maven-compiler-plugin using compilerArgs:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
            <compilerArgs>
                <arg>--add-exports</arg>
                <arg>javafx.graphics/com.sun.glass.ui=com.andrei</arg>
            </compilerArgs>
        </configuration>
    </plugin>
    

    Now in your code you can make use of that private package, and IntelliJ won't complain.

    After running from the Maven window Lifecycle -> clean, and Lifecycle -> compile, something like this is allowed in the editor:

    @Override
    public void start(Stage stage) throws Exception {
        ...
        stage.setScene(scene);
        stage.show();
    
        com.sun.glass.ui.Window.getWindows().forEach(System.out::println);
    }
    

    Runtime

    However, if you do mvn clean compile javafx:run, the code above will fail:

    Caused by: java.lang.IllegalAccessError: class com.andrei.Main (in module com.andrei) cannot access class com.sun.glass.ui.Window (in module javafx.graphics) because module javafx.graphics does not export com.sun.glass.ui to module com.andrei.

    As explained in the plugin readme, you cad add VM options that will be passed to the java tool:

    <plugin>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-maven-plugin</artifactId>
        <version>0.0.2</version>
        <configuration>
            <options>
                <option>--add-opens</option>
                <option>javafx.graphics/com.sun.glass.ui=com.andrei</option>
            </options>
            ...
    </configuration>
    </plugin>
    

    Now you can run: mvn clean compile javafx:run, and that will work, and you will get information for the current stage printed out.

    Runtime image

    Finally, if you run: mvn clean compile javafx:jlink, this will fail, because the content in <options> is not recognized by jlink (first issue filed), so you have to comment it out:

    <plugin>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-maven-plugin</artifactId>
        <version>0.0.2</version>
        <configuration>
            <!--<options>-->
              <!--<option>--add-opens</option>-->
              <!--<option>javafx.graphics/com.sun.glass.ui=com.andrei</option>-->
            <!--</options>-->
            <launcher>launcher</launcher>
            <mainClass>com.andrei/com.andrei.Main</mainClass>
        ...
    </configuration>
    </plugin>
    

    Now mvn clean compile javafx:jlink will work, but when running you will get the same error as above because the private package is not exported.

    However, you can edit the launcher file under target/image/bin/launcher:

    #!/bin/sh
    JLINK_VM_OPTIONS=
    DIR=`dirname $0`
    $DIR/java $JLINK_VM_OPTIONS -m com.andrei/com.andrei.Main $@
    

    As you can see, there is an empty JLINK_VM_OPTIONS variable that can be filled with your vm options.

    Until the second issue filed is solved, just modify that line:

    #!/bin/sh
    JLINK_VM_OPTIONS="--add-opens javafx.graphics/com.sun.glass.ui=com.andrei"
    DIR=`dirname $0`
    $DIR/java $JLINK_VM_OPTIONS -m fx/org.openjfx.MainApp $@
    

    save, and run: target/image/bin/launcher, and it will work.