gradlejava-11jvm-argumentsjavafx-11jfoenix

Create a runnable jar with Gradle and JavaFX that uses custom JVM arguments


Context: I'm building a modular (Java 11 - JavaFX 11) project on Intellij that uses Gradle 5.4.1, with some external libraries added to it (JFoenix, ControlsFX, ShadowJar...).

Problem: The library JFoenix(a JavaFX library) doesn't really support Java11, it needs some custom 'applicationDefaultJvmArgs' to run (see gradle.build file below). When I compile and run the project it works fine, but when trying to create a FatJar(using ShadowJar) or a runtime image (see https://openjfx.io/openjfx-docs/) it shows an error (because the JFoenix component JFXDatePicker needs those 'applicationDefaultJvmArgs to work')

Code:

build.gradle:

plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
id 'com.github.johnrengelman.shadow' version "5.0.0"
id "org.beryx.jlink" version "2.9.4"
}

group 'yayarh'
version '1.0-SNAPSHOT'

sourceCompatibility = '11'
targetCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    implementation group: 'com.jfoenix', name: 'jfoenix', version: '9.0.9'
    implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.19'
    implementation group: 'org.controlsfx', name: 'controlsfx', version: '11.0.0'
}

mainClassName = "yayarh.Launcher"

application {
    'yayarh.Launcher'
    applicationDefaultJvmArgs = ["--add-exports=javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
                                 "--add-exports=javafx.controls/com.sun.javafx.scene.control=com.jfoenix",
                                 "--add-exports=javafx.base/com.sun.javafx.binding=com.jfoenix",
                                 "--add-exports=javafx.graphics/com.sun.javafx.stage=com.jfoenix",
                                 "--add-exports=javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
                                 "--add-exports=javafx.base/com.sun.javafx.event=com.jfoenix"]
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

jlink {
    launcher {
        name = 'launcher'
    }
}


jar {
    manifest {
        attributes "Main-Class": 'yayarh.Launcher'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}


javafx {
    version = '11'
    modules = ['javafx.controls', 'javafx.fxml']
}

module-info.java:

module sifana {
requires javafx.fxml;
requires javafx.controls;
requires java.desktop;
requires com.jfoenix;
requires mysql.connector.java;
requires java.sql;
requires org.controlsfx.controls;

opens yayarh to javafx.fxml;

exports yayarh;

}

Errors shown when running the FatJar:

file:/E:/IntellijProjects/SifanaManager/build/libs/sifana-1.0-SNAPSHOT-all.jar!/yayarh/OmraN1.fxml:19

        at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3237)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3194)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3163)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3136)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3113)
        at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3106)
        at yayarh.HomeController.goToOmraScene(HomeController.java:20)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
        at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
        at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1784)
        at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.Node.fireEvent(Node.java:8879)
        at javafx.scene.control.Button.fire(Button.java:200)
        at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
        at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
        at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.Scene$MouseHandler.process(Scene.java:3851)
        at javafx.scene.Scene$MouseHandler.access$1200(Scene.java:3579)
        at javafx.scene.Scene.processMouseEvent(Scene.java:1849)
        at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2588)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
        at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
        at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
        at com.sun.glass.ui.View.notifyMouse(View.java:942)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
        at java.base/java.lang.Thread.run(Thread.java:835)
Caused by: java.lang.NullPointerException
        at com.jfoenix.controls.JFXDatePicker.initialize(JFXDatePicker.java:84)
        at com.jfoenix.controls.JFXDatePicker.<init>(JFXDatePicker.java:62)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
        at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:166)
        at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:404)
        at java.base/java.lang.Class.newInstance(Class.java:590)
        at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:1019)
        at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:754)
        at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
        at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
        ... 67 more

The needed solution: I want to package my project as a jar or executable (it will run only on windows) that includes the 'applicationDefaultJvmArgs' so that the JFoenix components can work without errors.

Note: this is my first ever question here + I don't have a lot of experience with JavaFX and exporting projects to jar/exe.


Solution

  • For anyone having the same problem, please check the official Openjfx documentation on how to create runtime images with JavaFX and Gradle, you can then add your custom jvm arguments inside the jlink block:

     application {
        'yayarh.Launcher'
        applicationDefaultJvmArgs = ["--add-exports=javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
                                     "--add-exports=javafx.controls/com.sun.javafx.scene.control=com.jfoenix",
                                     "--add-exports=javafx.base/com.sun.javafx.binding=com.jfoenix",
                                     "--add-exports=javafx.graphics/com.sun.javafx.stage=com.jfoenix",
                                     "--add-exports=javafx.controls/com.sun.javafx.scene.control.behavior=com.jfoenix",
                                     "--add-exports=javafx.base/com.sun.javafx.event=com.jfoenix"]
    }
    
       jlink {
        ...
        launcher {
            name = 'Your app name'
            jvmArgs = applicationDefaultJvmArgs as List<String>
         }
        }