Search code examples
javagradlerx-java2javafx-11openfx

java.lang.NoClassDefFoundError: io/reactivex/subjects/Subject when building jar in Intellij Idea (gradle + JavaFX 11)


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.


Solution

  • 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'
    }