javagradlepackagejava-module

What is causing this error? "Module main contains package javafx.fxml, module javafx.fxml exports package javafx.fxml to main"


From what I understand and read already the problem is that the module is imported twice somewhere, but I don't understand where. This is my module-info.java file:

module main {
    requires org.apache.logging.log4j;
    requires javafx.controls;
    requires javafx.fxml;
    requires java.sql;
}

And this is my build.gradle

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

group 'org'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

ext {
    junitVersion = '5.10.0'
}

sourceCompatibility = '21'
targetCompatibility = '21'

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

application {
    mainModule = 'main'
    mainClass = 'main.HelloApplication'
}

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

dependencies {
    implementation('org.controlsfx:controlsfx:11.1.2')

    implementation('com.dlsc.formsfx:formsfx-core:11.6.0') {
        exclude(group: 'org.openjfx')
    }
    implementation('net.synedra:validatorfx:0.4.0') {
        exclude(group: 'org.openjfx')
    }
    implementation('org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0')

    // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
    implementation 'org.apache.logging.log4j:log4j-core:2.22.1'

    // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api
    implementation 'org.apache.logging.log4j:log4j-api:2.22.0'

    testImplementation platform('org.junit:junit-bom:5.9.1')
    testImplementation 'org.junit.jupiter:junit-jupiter'


    // https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
    runtimeOnly 'com.mysql:mysql-connector-j:8.3.0'
}

test {
    useJUnitPlatform()
}

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    manifest {
        attributes('Main-Class': 'main.HelloApplication', 'Multi-Release': 'true')
    }
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}


jlink {
    imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'app'
    }
}

jlinkZip {
    group = 'distribution'
}

I tried changing my module-info file a bunch and if this error is solved then I have a different one each time for example:

module main {
    requires org.apache.logging.log4j;
    requires javafx.controls;
    requires javafx.fxml;
    requires java.sql;
    requires org.controlsfx.controls;
    requires com.dlsc.formsfx;
    requires org.kordamp.bootstrapfx.core;

    exports main;
    exports main.model;
    exports main.repository;
    exports main.service;
    exports main.controller;

    opens main;
    opens main.model;
    opens main.repository;
    opens main.service;
    opens main.controller;
}

This will now generate "Module main contains package javafx.beans.value, module javafx.base exports package javafx.beans.value to main" or in another run "Module main contains package javafx.event, module javafx.base exports package javafx.event to main".


Solution

  • TL;DR: Do not create a fat JAR file when developing modular applications.


    That error means that two modules contain the same package. The Java Platform Module System (JPMS) does not allow split packages. And in this case, the error is saying that your own module contains packages from a JavaFX module.

    You have configured the jar task to create a so-called fat JAR file (i.e., a JAR with dependencies). Those dependencies include the JavaFX modules. But this will cause issues with the run task at the least. When your code is modular, the run task depends on the jar task and executes the JAR file as a module1. The run task will also place dependencies on the module-path2. Those dependencies include the JavaFX modules3. Thus, you now have JavaFX packages split between your module and the JavaFX modules and you see your error.

    I suspect the same problem will occur when executing the jlink task, and probably all tasks that deal with JPMS modules, but I am not as familiar with The Badass JLink Plugin.

    The solution is to remove the following from the jar task configuration:

    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    

    As an aside, if you prefer to deploy your application as a fat JAR file instead of as a self-contained application, then I recommend you make your project non-modular. You should also exclude any and all module-info.class files from the fat JAR file. And critically, your main class must not be a subclass of Application. You must create a separate "launcher" main class that, at a minimum, simply launches the JavaFX application.

    For more information about packaging JavaFX applications, see the "Packaging" section of the JavaFX tag info page.


    1. When your code is non-modular then the run task only depends on the compileJava and processResources tasks (indirectly via the classes task). The difference in behavior between modular and non-modular projects was part of a solution to a problem regarding resources and modules.

    2. The org.openjfx.javafxplugin plugin will modify the run task to place JavaFX on the module-path for non-modular projects. In other words, JavaFX will end up on the module-path regardless.

    3. The dependencies include not only JavaFX but also all the other non-test dependencies you declared. You are getting an error specifically relating to JavaFX packages, but the problem exists for all dependencies.