I am running some app with JavaFX 11 (OpenFX). The app is written in IntellijIdea and built with gradle. When running in IDE everything's ok. When building a jar it builds successfully but when I try to execute it I get an error:
Exception in thread "main" java.lang.NoClassDefFoundError: io/reactivex/subjects/Subject
To build a jar I use onslip.gradle-one-jar plugin. My gradle is the following:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.5'
id 'com.github.onslip.gradle-one-jar' version '1.0.5'
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.openjfx:javafx-base:11:win"
implementation "org.openjfx:javafx-graphics:11:win"
implementation "org.openjfx:javafx-controls:11:win"
implementation "org.openjfx:javafx-fxml:11:win"
implementation 'com.jfoenix:jfoenix:9.0.8'
implementation group: 'commons-validator', name: 'commons-validator', version: '1.6'
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
implementation group: 'com.sun.mail', name: 'javax.mail', version: '1.6.2'
implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.5'
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
javafx {
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = 'jetliner.Main'
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes 'Main-Class': 'jetliner.Main'
}
}
task awesomeFunJar(type: OneJar) {
mainClass = 'jetliner.Main'
}
For jar building I run awesomeFunJar task.
The main problem you have in your build is related to how you define the dependencies with implementation
(not compile
anymore), while on the other hand, you build your jar based on configurations.compile
.
Since implementation
and compile
are not the same, configurations.compile
contains only the classes of your project, not the third party dependencies (including JavaFX).
Running the build task will generate a very small fat jar of 3 KB. Obviously this jar misses all the classes from the dependencies.
Solution
Replace in your jar task configurations.compile
with configurations.compileClasspath
:
jar {
from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes 'Main-Class': 'jetliner.Main'
}
}
Run ./gradlew build
to generate a fat jar (around 23 MB with the given dependencies), that can be run with:
java -jar build/libs/myproject.jar
Or run ./gradlew awesomeFunJar
to produce a similar fat jar (21 MB), that runs with:
java -jar build/libs/myproject-standalone.jar
In both cases, the io.reactivex.rxjava2
dependencies are included.
Note 1: I haven't used this gradle-one-jar plugin before, and I don't see any major advantage in having the custom loader, all I see is that for a helloFX sample takes really too long to load it compare with the regular fat jar.
If you use it because the fat jar doesn't run, maybe you need a Launcher
class, as explained here:
public class Launcher {
public static void main(String[] args) {
Main.main(args);
}
}
and then replace the mainClass with jetliner.Launcher
.
Note 2: The JavaFX plugin (latest version 0.0.7) uses implementation
, and it takes care of adding the JavaFX dependencies, so you can simplify the build file:
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.7'
id 'com.github.onslip.gradle-one-jar' version '1.0.5'
}
repositories {
jcenter()
}
dependencies {
implementation 'com.jfoenix:jfoenix:9.0.8'
implementation group: 'commons-validator', name: 'commons-validator', version: '1.6'
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
implementation group: 'com.sun.mail', name: 'javax.mail', version: '1.6.2'
implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.5'
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
javafx {
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = 'jetliner.Launcher'
jar {
manifest {
attributes 'Main-Class': 'jetliner.Launcher'
}
from {
configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
task awesomeFunJar(type: OneJar) {
mainClass = 'jetliner.Main'
}