Search code examples
javajavafxnio

JavaFX - ProviderNotFoundException: Provider not found


I am building my JavaFX application together with the required JVM (openjdk-18.0.2.1) components using in jlink. The build script is almost no different from being created using IDEA by default.

import org.gradle.internal.os.OperatingSystem

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.13'
    id 'org.beryx.jlink' version '2.25.0'
}

group 'com.prototype'
version '1.0'

repositories {
    mavenCentral()
}

ext {
    junitVersion = '5.9.0'
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(18))
    }
    //withSourcesJar()
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

application {
    mainModule = 'com.prototype.catassistant'
    mainClass = 'com.prototype.catassistant.CatAssistantApplication'
}

javafx {
    version = '18.0.1'
    modules = ['javafx.controls', 'javafx.fxml', 'javafx.web']
}

dependencies {

    // https://mvnrepository.com/artifact/org.jetbrains/annotations
    implementation 'org.jetbrains:annotations:23.0.0'

    implementation('org.controlsfx:controlsfx:11.1.1')

    // https://www.benf.org/other/cfr/api/index.html
    // https://mvnrepository.com/artifact/org.benf/cfr
    implementation 'org.benf:cfr:0.152'

    // https://mvnrepository.com/artifact/com.google.code.gson/gson
    implementation 'com.google.code.gson:gson:2.9.1'

    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}

test {
    useJUnitPlatform()
}

jlink {
    imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    jpackage {
        def currentOs = OperatingSystem.current()
        def imgType = currentOs.windows ? 'ico' : currentOs.macOsX ? 'icns' : 'png'
        icon = "src/main/resources/java.$imgType"
        if (currentOs.windows) {
            installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut', '--vendor', 'Prototype']
            imageOptions += ['--win-console',/* '--icon', 'src/main/resources/icon.ico'*/]
        }
    }
    launcher {
        name = 'CatAssistant'
    }
}

jlinkZip {
    group = 'distribution'
}

After building the image, I successfully run it and it works, but when it comes to working with jar files, I get an incomprehensible error: java.nio.file.ProviderNotFoundException: Provider not found Everything works fine from IDEA. I am also confused by the fact that after the build, "(Unknown Source)" is written in the entire stacktrace instead of lines of code, if there is such a possibility, I would like to fix it.

Caused by: java.nio.file.ProviderNotFoundException: Provider not found
        at java.base/java.nio.file.FileSystems.newFileSystem(Unknown Source)
        at java.base/java.nio.file.FileSystems.newFileSystem(Unknown Source)
        at [email protected]/com.prototype.catassistant.helper.FileHelper.readJarFile(Unknown Source)

FileHelper#readJarFile

    // https://stackoverflow.com/a/37413531/10663941
    public static void readJarFile(JarFile jar) {
        try (FileSystem system = FileSystems.newFileSystem(jar.getPath())) { // <- exception
            for (Path directory : system.getRootDirectories()) {
                try (Stream<Path> stream = Files.walk(directory)) {
                    stream.filter(Predicate.not(Files::isDirectory))
                            .forEach(path -> jar.addEntry(path, read(system.getPath(path.toString()))));
                }
            }
        } catch (IOException ex) {
            AlertUtil.showExceptionDialog(ex);
            throw new RuntimeException(ex);
        }
    }

I need help in solving this problem


Solution

  • ProviderNotFoundException

    Most likely the java.nio.file.ProviderNotFoundException error is caused by the jdk.zipfs module not being included in the run-time image created by jlink / jpackage1. That module is a "provider module" for the java.nio.file.FileSystemProvider SPI. As such, I suspect none of the other modules that are part of your application directly or indirectly require the jdk.zipfs module. Typically, modules do not requires provider modules, as that defeats the purpose of the uses / provides mechanism of the Java Platform Module System (see the links in the second solution below for an explanation).

    There are three ways you can fix this:

    1. Use --bind-services when invoking jlink. This will cause jlink to look at all the required modules to see if any of them uses an SPI. If they do, then it will look for all available modules which provides implementations of the SPI and include them. Unfortunately, this will cause a lot of other modules to be included in your custom run-time image that you don't necessarily need.

    2. Modify your module-info descriptor to add a requires jdk.zipfs directive. However, as noted in the documentation, having an "application module" require a "provider module" is not recommended.

    3. Include an --add-modules jdk.zipfs argument when invoking jlink. This will force jlink to include the jdk.zipfs module without the unfortunate side-effects of the previous two solutions. Personally, I recommend using this solution in your case.

    Unkown Source

    The "Unknown Source" bits in the stack trace are caused by the --strip-debug argument which, unsurprisingly, removes debugging information from the byte-code. That includes the source file names and line numbers. Having --strip-debug reduces the size of the compiled code, which is good for the deployment version of your application.


    1. You've confirmed this.