Building FatJar for JavaFX with gradle and intellij, getting NoClassDefFOundError
Asked Answered
W

1

6

I have set up an OpenJDK 12 project in IntelliJ (2019.2) using the built-in Gradle support. To design the GUI I'm using JavaFX 12. I have followed and read the setup guide several times, I have no issues running the program in my IDE, the issue is when I try to build a .jar file for distribution that I run into problems. I have not been able to find a solution that works so far and I've searched QUITE a lot, nearly tearing my hair out over this. Currently when i try to run my jar file with java -jar "jarName".jar I get the following error:

Exception in thread "main" java.lang.NoClassDefFoundError: javafx/application/Application
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
        at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
        at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at com.CAM.Starter.main(Starter.java:6)
Caused by: java.lang.ClassNotFoundException: javafx.application.Application
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 10 more

I have tried moving my main class to a separate one that doesn't extend Application, which is what gives the above error. Without moving my Main class I get a different error.

My build.gradle looks like this currently:

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
}

group 'ClassicAddonManager'
version '0.2'
sourceCompatibility = 11

repositories {
    mavenCentral()
}

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

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile 'net.sourceforge.htmlunit:htmlunit:2.13'
    compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
    compile group: 'net.lingala.zip4j', name: 'zip4j', version: '1.2.4'
}

jar {
    manifest {
        attributes 'Main-Class': 'com.CAM.Starter'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

If I have my main method extending Application then I get an error stating that my main class could not be found even though I can see it is present in the generated .jar file.

All I'm trying to do, is to generate a file that a friend with no knowledge of programming could run. Ideally, a file that they could run without having to install Java first. I know it should be possible to do this, but Gradle is new to me so I'm not sure if that is what is causing all this headache. Is there potentially an easier way to deploy? Especially given that this is a solo-project?

EDIT

I have tried the modular part of the guide. Doing that I have over 100 error when attempting to build. They are all something in the vein of:

javafx.graphicsEmpty reads package org.xml.sax from both xml.apis and java.xml
Windowpane answered 11/8, 2019 at 18:2 Comment(5)
See openjfx.io/openjfx-docs/#modular, section non-modular project with Gradle.Puryear
@JoséPereda I've already read that through and tried it. Doing so provided me with another issue in that I couldn't generate the jar to begin with. Received a bunch of errors such as: javafx.graphicsEmpty reads package org.xml.sax from both xml.apis and java.xmlWindowpane
When you run java -jar yourfatjar.jar on command line, does it work?Puryear
@JoséPereda yes, that works just fine. It's only when I double click. I've tried messing around with registry and default programs but nothing. I also am stuck with the issue then, that if I close CMD it will close the GUI.Windowpane
About double-click, this post might help.Puryear
S
16

If you want to do a fat jar using Gradle but not a shadow plugin, usually you will do:

jar {
    manifest {
        attributes 'Main-Class': 'com.CAM.Starter'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

However, there is an important fact: compile is deprecated, and the JavaFX plugin uses implementation by default.

As a consequence, configuration.compile might be empty, or at least, it won't contain the JavaFX classes.

The solution is to check the runtimeClasspath configuration, as we will have all the classes there, and we can make the fat jar:

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

This is explained in the OpenJFX docs, https://openjfx.io/openjfx-docs/#modular, section non-modular projects, subsection gradle.

Once you have your fat jar, you can run it with:

java -jar yourFatJar.jar

Note that double-clicking on it won't work, as explained in detail here, so it can be convenient to create a small batch instead.

A better solution is to do a modular project and use jlink to create a runtime image (it also includes a launcher script). You can distribute this image to other users that don't have even JDK installed. With gradle, you can just include the 'org.beryx.jlink' plugin, like in this sample.

And you can also use the early version of jpackage to create and distribute an installer.

Selvage answered 11/8, 2019 at 19:19 Comment(1)
This may be needed (definitely so for me): duplicatesStrategy(DuplicatesStrategy.EXCLUDE)Song

© 2022 - 2024 — McMap. All rights reserved.