title: Java 11 with JavaFX building JAR with gradle
body: <p>We are developing an JavaFX 11 application with the Adopt JDK 11 and IntelliJ using gradle. At the end we need wanted an EXE file for windows (the application is only designed for windows). In the first try we also used launch4J within gradle, due the following problem we would also be fine using a BAT file which we could migrate to an exe file instead. </p>
  
  <p>So our main goal and question is, how can we create a executable jar file.</p>
  
  <p>We made several approaches with different results and we are completly lost.</p>
  
  <h2>FAT JAR</h2>
  
  <p>We made a standalone application for testing purpose:</p>
  
  <pre><code>package de.test;
  
  import javafx.application.Application;
  import javafx.scene.Scene;
  import javafx.scene.control.Button;
  import javafx.scene.control.TextArea;
  import javafx.scene.control.TextField;
  import javafx.scene.layout.VBox;
  import javafx.stage.Stage;
  
  public class Main extends Application {
  
      public static void main(String[] args) {
          launch();
      }
  
      @Override
      public void start(Stage primaryStage) throws Exception {
          VBox vBox = new VBox();
  
          Button button = new Button("Klick");
  
          TextField textfield = new TextField();
          TextArea area = new TextArea();
          area.setMinHeight(300);
  
          button.setOnAction(event -&gt; area.setText(area.getText() + " --  Klick Version 1.0.8"));
          vBox.getChildren().addAll(button, textfield,area);
  
          primaryStage.setScene(new Scene(vBox));
          primaryStage.setTitle("Test");
          primaryStage.show();
      }
  }
  </code></pre>
  
  <p><strong>FAT JAR</strong></p>
  
  <p>And our first try was a <strong>FAT-JAR</strong> with or without a modules-info.java file using the javafx plugin from gradle. Here is our gradle file</p>
  
  <pre><code>plugins {
      id 'java'
      id 'application'
      id 'org.openjfx.javafxplugin' version '0.0.8'
  }
  
  group 'de.test'
  version '1.0.8'
  
  sourceCompatibility = 11
  
  javafx {
      modules = ['javafx.controls']
  }
  
  mainClassName = 'de.test.Main'
  
  jar {
      manifest {
          attributes 'Main-Class': mainClassName
      }
      from {
          configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
      }
  }
  
  repositories {
      mavenLocal()
      mavenCentral()
  }
  </code></pre>
  
  <p>After running <strong>gradle clean jar</strong> we tried to execute the jar with the following command <strong>java -jar ApplicationTest-1.0.8.jar</strong> the result:</p>
  
  <blockquote>
    <p>Error: Could not find or load main class de.test.Main Caused by:
    java.lang.NoClassDefFoundError: javafx/application/Application</p>
  </blockquote>
  
  <hr>
  
  <h2>FAT JAR with dependencies and module-info.java</h2>
  
  <p>Then we tried to add the dependencies for JavaFX like this inside the gradle file</p>
  
  <pre><code>compile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '4.3.0.Final'
  compile "org.openjfx:javafx-graphics:11.0.2:win"
  compile "org.openjfx:javafx-base:11.0.2:win"
  compile "org.openjfx:javafx-controls:11.0.2:win"
  compile "org.openjfx:javafx-fxml:11.0.2:win"
  compile "org.openjfx:javafx-graphics:11.0.2:win"
  </code></pre>
  
  <p>And this is the module-info.java discriptor we added to the package de.test:</p>
  
  <pre><code>module ApplicationTest.main {
  
      requires javafx.controls;
  
      exports de.test;
  
  }
  </code></pre>
  
  <p>When calling <strong>gradle installDist</strong> we used this command</p>
  
  <blockquote>
    <p>cd build\install\ApplicationTest\lib java --add-modules
    "javafx.controls" --module-path . -jar ApplicationTest-1.0.8.jar</p>
  </blockquote>
  
  <p>At least the application started at this point, but the layout was destroyed with the following info messages:</p>
  
  <blockquote>
    <p>Sep. 20, 2019 8:32:55 VORM. com.sun.javafx.css.StyleManager
    loadStylesheetUnPrivileged INFO: Could not load stylesheet:
    com/sun/javafx/scene/control/skin/modena/modena.css Sep. 20, 2019
    8:32:55 VORM. com.sun.javafx.css.StyleManager
    loadStylesheetUnPrivileged INFO: Could not load stylesheet:
    com/sun/javafx/scene/control/skin/modena/modena.css</p>
  </blockquote>
  
  <p><a href="https://i.stack.imgur.com/ebG6V.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ebG6V.png" alt="No layout when using the JAR file"></a></p>
  
  <p>By the way: IntelliJ is always working when using the command <strong>gradle clean run</strong></p>
  
  <p><a href="https://i.stack.imgur.com/17rt4.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/17rt4.png" alt="Using IntelliJ the layout is fine"></a></p>
  
  <hr>
  
  <h2>JLink with module-info.java</h2>
  
  <p>The next approach was using jlink and the following modules-info.java </p>
  
  <pre><code>module ApplicationTest.main {
      requires javafx.controls;
      requires javafx.graphics;
  
      exports de.test;
  
  }
  </code></pre>
  
  <p>We needed to extend the gradle file a bit as the following</p>
  
  <pre><code>plugins {
      id 'java'
      id 'application'
      id 'org.openjfx.javafxplugin' version '0.0.8'
      id 'org.beryx.jlink' version '2.10.4'
  }
  
  group 'de.test'
  version '1.0.8'
  
  sourceCompatibility = 11
  
  mainClassName = 'de.test.Main'
  
  javafx {
      version = 11
      modules = [ 'javafx.controls']
  }
  
  jlink {
      options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
  }
  
  
  repositories {
      mavenLocal()
      mavenCentral()
  }
  </code></pre>
  
  <p>When using <strong>gradle jlink</strong> and executing the <strong>cd build\image\bin\ApplicationTest.bat</strong> file, the application starts <strong>WITH</strong> layout. </p>
  
  <p>At this point we where kind of happy. Now we needed to add some dependencies for our application.</p>
  
  <p>Using the following dependencies worked perfectly</p>
  
  <pre><code>dependencies {
      compile "commons-io:commons-io:2.6"
      compile "org.apache.logging.log4j:log4j-api:2.11.2"
      compile "dom4j:dom4j:1.6.1"
      compile "commons-lang:commons-lang:2.6"
      compile "axis:axis:1.4"
      compile "jaxen:jaxen:1.1.6"
      compile "net.java.dev.jna:platform:3.5.2"
      compile "org.apache.poi:poi:4.1.0"
      compile "org.apache.poi:poi-scratchpad:4.1.0"
      compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"
  }
  </code></pre>
  
  <p>But as soon as I add the other dependencies I get different kind of error messages. I tried each dependency alone to isolate the error message.</p>
  
  <h2>org.jboss.resteasy - resteasy-client - 3.7.0.Final</h2>
  
  <pre><code>Cannot derive uses clause from service loader invocation in: javax/ws/rs/client/FactoryFinder.find().
  Cannot derive uses clause from service loader invocation in: javax/ws/rs/ext/FactoryFinder.find().
  Cannot derive uses clause from service loader invocation in: javax/ws/rs/sse/FactoryFinder.find().
  Cannot derive uses clause from service loader invocation in: javax/xml/bind/ServiceLoaderUtil.firstByServiceLoader().
  
  ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:154: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
      provides javax.ws.rs.ext.Providers with org.jboss.resteasy.plugins.interceptors.CacheControlFeature,
                                                                                     ^
  ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:155: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
                  org.jboss.resteasy.plugins.interceptors.encoding.ClientContentEncodingAnnotationFeature,
                                                                  ^
  ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:156: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
                  org.jboss.resteasy.plugins.interceptors.encoding.MessageSanitizerContainerResponseFilter,
                                                                  ^
  ...     
                                              ^
  25 errors
  </code></pre>
  
  <h2>Dependency: org.apache.logging.log4j:log4j-core:2.11.2</h2>
  
  <pre><code>package org.apache.logging.log4j.spi is not visible
  (package org.apache.logging.log4j.spi is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)
  package org.apache.logging.log4j.message is not visible
  (package org.apache.logging.log4j.message is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)
  </code></pre>
  
  <h2>Dependency: org.jboss.resteasy:resteasy-multipart-provider:3.7.0.Final</h2>
  
  <pre><code>module not found: java.activation
  </code></pre>
  
  <h2>Dependecyy: org.jboss.resteasy:resteasy-jackson2-provider:3.7.0.Final</h2>
  
  <pre><code>package javax.ws.rs.ext does not exist
  package javax.ws.rs.ext does not exist
  package javax.ws.rs.ext does not exist
  </code></pre>
  
  <h2>Conclusion</h2>
  
  <p>We're not getting anywhere right now. It probably has something to do with modules-info.java, but the instructions and documentation on this topic are quite complex and often very detailed. We just wanted an application that could be started outside the IntelliJ.</p>
  
  <p>We are open for all solutions as long as they work. We don't need a state of the art solution either, but something from which we can somehow get an EXE in the end (also with the help of an external tool). Our application doesn't have to be modular, but we neither get it nor solve it.</p>
  
  <h2>EDIT 1) FAT Jar with 2nd main class</h2>
  
  <p>Like in <a href="https://stackoverflow.com/questions/52569724/javafx-11-create-a-jar-file-with-gradle/52571719#52571719">this aricle</a> we added a second main java class without extending <strong>Application</strong> and put that to the gradle file</p>
  
  <pre><code>package de.test;
  
  public class Main {
      public static void main(String[] args) {
          MainFX.main(args);
      }
  
  }
  </code></pre>
  
  <p>and the other class looks like this:</p>
  
  <pre><code>package de.test;
  
  import ...;
  
  public class MainFX extends Application {
  
      public static void main(String[] args) {
          launch();
      }
  
      @Override
      public void start(Stage primaryStage) throws Exception {
      ...
      }
  }
  </code></pre>
  
  <p>and the gradle file looks like this:</p>
  
  <pre><code>plugins {
      id 'java'
      id 'application'
  }
  
  group 'de.test'
  version '1.0.8'
  
  sourceCompatibility = 11
  
  mainClassName = 'de.test.MainFX'
  
  jar {
      manifest {
          attributes 'Main-Class': mainClassName
      }
      from {
          configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
      }
  }
  
  repositories {
      mavenLocal()
      mavenCentral()
  }
  
  dependencies {
      compile "commons-io:commons-io:2.6"
      compile "org.apache.logging.log4j:log4j-api:2.11.2"
      compile "dom4j:dom4j:1.6.1"
      compile "commons-lang:commons-lang:2.6"
      compile "axis:axis:1.4"
      compile "jaxen:jaxen:1.1.6"
      compile "net.java.dev.jna:platform:3.5.2"
      compile "org.apache.poi:poi:4.1.0"
      compile "org.apache.poi:poi-scratchpad:4.1.0"
      compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"
  
      compile "org.jboss.resteasy:resteasy-multipart-provider:3.7.0.Final"
      compile "org.jboss.resteasy:resteasy-jackson2-provider:3.7.0.Final"
      compile "org.apache.logging.log4j:log4j-core:2.11.2"
      compile "org.jboss.resteasy:resteasy-client:3.7.0.Final"
      compile "org.openjfx:javafx-graphics:11.0.2:win"
      compile "org.openjfx:javafx-base:11.0.2:win"
      compile "org.openjfx:javafx-controls:11.0.2:win"
      compile "org.openjfx:javafx-fxml:11.0.2:win"
      compile "org.openjfx:javafx-graphics:11.0.2:win"
  }
  </code></pre>
  
  <p>The result is really strange <strong>java -jar ApplicationTest.jar</strong> returns</p>
  
  <blockquote>
    <p>Error: Could not find or load main class de.test.MainFX Caused by:
    java.lang.ClassNotFoundException: de.test.MainFX</p>
  </blockquote>
  
  <p>We double checked the jar file and the class files are in the correct location (de\test\Main.class and de\test\MainFX.class)</p>
  
  <h2>EDIT 2) FAT Jar with 2nd main class</h2>
  
  <p>Based on <a href="https://stackoverflow.com/questions/54063041/package-a-non-modular-javafx-application?noredirect=1&amp;lq=1">this article</a> we added the javafx plugin again, removed the javafx dependencies and defined the Launcher-Main inside the manifest and the regular FXML Mail as mainClassName:</p>
  
  <pre><code>...
  mainClassName = 'de.test.MainFX'
  
  javafx {
      version = 11
      modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web']
  }
  
  jar {
      manifest {
          attributes 'Main-Class': 'de.test.MainLauncher'
      }
      from {
          configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
      }
  }
  </code></pre>
  
  <p>The result is</p>
  
  <pre><code>&gt; Exception in thread "main" java.lang.NoClassDefFoundError:
  &gt; javafx/application/Application
  &gt;         at java.base/java.lang.ClassLoader.defineClass1(Native Method)
  &gt;         at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
  &gt;         at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
  &gt;         at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
  &gt;         at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
  &gt;         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
  &gt;         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
  &gt;         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
  &gt;         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
  &gt;         at de.test.MainLauncher.main(MainLauncher.java:7) Caused by: java.lang.ClassNotFoundException: javafx.application.Application
  &gt;         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
  &gt;         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
  &gt;         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
  &gt;         ... 10 more
  </code></pre>
  
  <p>So we <strong>added</strong> the dependencies for javafx again with the following result:</p>
  
  <pre><code>Graphics Device initialization failed for :  d3d, sw
  Error initializing QuantumRenderer: no suitable pipeline found
  java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
          at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
          at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:222)
          at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
          at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
          at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
          at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
          at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
          at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
          at java.base/java.lang.Thread.run(Thread.java:834)
  Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
          at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
          at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
          ... 1 more
  Exception in thread "main" java.lang.RuntimeException: No toolkit found
          at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
          at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
          at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
          at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
          at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
          at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
          at java.base/java.lang.Thread.run(Thread.java:834)
  </code></pre>