0

I'm working on an application on JavaFX using IntelliJ and Gradle.

Everything seems to work fine while I'm running the app with Gradle's build. I'm planning to actually export it as a fat jar, in order for it to be used without the actual gradle build.

I also don't want to create an artifact using IntelliJ's wizard, because, as far as I know, it just creates it without any dependencies (didn't inform myself too well for that - but that's what I understood from their answers on the forums).

Getting back to building the fat jar with Gradle, this is how my project structure looks like: enter image description here

My module-info.java looks like this:

module com.example.yuber {
    requires javafx.controls;
    requires javafx.fxml;
    requires javafx.web;

    requires org.controlsfx.controls;
    requires com.dlsc.formsfx;
    requires validatorfx;
    requires org.kordamp.ikonli.javafx;
    requires org.kordamp.bootstrapfx.core;
    requires eu.hansolo.tilesfx;
    requires org.json;
    requires com.fasterxml.jackson.core;
    requires com.fasterxml.jackson.databind;
    requires com.fasterxml.jackson.annotation;

    exports com.example.yuber;
    exports com.example.yuber.controllers;
    exports com.example.yuber.models;
    opens com.example.yuber to javafx.fxml;
    opens com.example.yuber.controllers to javafx.fxml;
}

(I needed to include all of these. Couldn't even use Jackson Databind after adding it as a Gradle dependency, before doing this step).

Also, the jar task from build.gradle looks like this:

jar {
    manifest {
        attributes 'Main-Class': 'com.example.yuber.Main'
    }
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

But everytime I run the jar task from Gradle, I receive the following error:

Entry module-info.class is a duplicate but no duplicate handling strategy has been set.

As found in this question, a solution would have been to just include duplicatesStrategy = DuplicatesStrategy.EXCLUDE in my jar task. Doing so indeed builds my jar file, although I can't running, and trying to gradle run my app doesn't do anything but display the error:

Process 'command 'C:\Users\matea\.jdks\corretto-11.0.15\bin\java.exe'' finished with non-zero exit value 1

Also, running the created jar file with java -jar ... results in another error: enter image description here

What I also found on this question was to actually mention the name of the modules inside the Add VM options setting in my Project Structure, like that:

--module-path "C:\Users\matea\.jdks\corretto-11.0.15\lib" --add-modules=javafx.controls,javafx.xml

However, this also didn't seem to work. This is my project info: enter image description here

Does anyone have any idea on why I can't create such a fat jar? Also, am I right by telling that a you can run such a fat jar using java -jar, without having JavaFX and the other modules separately installed?

Mario Mateaș
  • 638
  • 7
  • 25
  • 3
    Modules and fat JARs do not mix. Java only supports one module per JAR file out-of-the-box. This also means JavaFX will be put on the class-path, which is not a supported configuration. Take a look at packaging options by looking at the "Packaging" section of [the JavaFX tag info](https://stackoverflow.com/tags/javafx/info). As an aside, if you're using Gradle to create a fat JAR then I recommend using the [Shadow Plugin](https://github.com/johnrengelman/shadow). – Slaw May 23 '22 at 09:11
  • There is absolutely no point in defining a module info if you have a fat jar as fat jars are not modular. – jewelsea May 23 '22 at 10:10
  • @jewelsea you mean the `module-info.java`? – Mario Mateaș May 23 '22 at 10:14
  • @Slaw Took a look on the link you sent me, but unfortunately all of the things shown there are making me do what I've already described above – Mario Mateaș May 23 '22 at 10:15
  • They are definitely not _all_ telling you to do what you're already doing, as some of the links deal with `jlink` / `jpackage`, others deal with Gradle plugins for the same, another is for JPackageScriptFX, while another links to jdeploy (don't know that one myself). Yet other links point to Stack Overflow Q&As dealing with alternatives to fat JARs. – Slaw May 23 '22 at 10:22
  • 1
    As for creating the fat JAR, I would filter out any `module-info.class` files. The Shadow Plugin should make that easy. And then your main class _cannot be a subclass of `Application`_. You'll have to make a _separate_ main class that simply launches JavaFX. – Slaw May 23 '22 at 10:26
  • @Slaw Shadow seems to have done the job. It didn't work at first, but I've created another class called `Launcher` which actually calls my `Main.main()`. Also mentioned inside `build.gradle`: `application { ... mainClass = 'com.example.yuber.Launcher' }` – Mario Mateaș May 23 '22 at 10:52
  • 2
    Found an old answer of mine that basically goes over what I said in these comments, but it's easier to read: https://stackoverflow.com/questions/70175403/how-to-generate-javafx-jar-from-gradle-including-all-dependencies/70175935#70175935 – Slaw May 23 '22 at 10:55
  • @Slaw Thanks for your advice. You actually saved my life! Not literally ;) – Mario Mateaș May 23 '22 at 11:10
  • "you mean the module-info.java?" -> yes. With the current version of the Java module system, there can be only one module-info created per jar file and packages cannot be split across modules. Placing statements in a module-info that require modules containing packages that are inside your fat jar file makes no sense as you cannot place those modules in your fat jar file. The only solution is to make the application non-modular and run the code in the fat jar from the classpath (which is what you did). For JavaFX modules this is an unsupported runtime configuration (though it works). – jewelsea May 23 '22 at 21:44

0 Answers0