javaeclipsemavenjavafxjava-module

Can I exclude maven resource folder from a Java module?


I created two Maven projects 'javafx' and 'commonfx' to reproduce an error. Two Maven JavaFX Projects

module commonfx {
   exports commonfx.calculator;
}

module javafx {
       requires javafx.controls;
       requires javafx.graphics;
       requires javafx.fxml;
       requires commonfx;
       opens org.example to javafx.graphics, javafx.fxml;
}

Both projects have an resource folder. They do not export those folders but anyway I am getting constantly error messages:

Error occurred during initialization of boot layer

java.lang.LayerInstantiationException: 
Package bundle in both module javafx and module commonfx

Whenever I rerun the application, I am getting a new error message. But each time a resource folder is same named in both modules.

Error occurred during initialization of boot layer

java.lang.LayerInstantiationException: 
Package images in both module commonfx and module javafx

Error occurred during initialization of boot layer

java.lang.LayerInstantiationException: 
Package main.resources.images in both module commonfx and module javafx.

Used technologies:

Is it possible that the maven resource folder are getting ignored?

Code to reproduce the exception:

Project commonfx

package commonfx.calculator;

public class FxCalculator {

    public FxCalculator() {
        super();
    }

    public Integer calculate() {
        return 2;
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>common.fx</groupId>
   <artifactId>commonfx</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <properties>
      <maven.compiler.source>21</maven.compiler.source>
      <maven.compiler.target>21</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <javafx.version>21</javafx.version>
      <javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-controls</artifactId>
         <version>21.0.2</version>
      </dependency>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-fxml</artifactId>
         <version>${javafx.version}</version>
      </dependency>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-web</artifactId>
         <version>${javafx.version}</version>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-maven-plugin</artifactId>
            <version>${javafx.maven.plugin.version}</version>
         </plugin>
      </plugins>
   </build>
</project>

Project javafx

package org.example;

import commonfx.calculator.FxCalculator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Javafx extends Application {
    private FxCalculator calculator;

    @Override
    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");
        calculator = new FxCalculator();
        Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "." + " Number FX: " + calculator.calculate());
        Scene scene = new Scene(new StackPane(l), 640, 480);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.example</groupId>
   <artifactId>javafx</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <properties>
      <maven.compiler.source>21</maven.compiler.source>
      <maven.compiler.target>21</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <javafx.version>21</javafx.version>
      <javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
         <version>3.2.4</version>
      </dependency>
      <dependency>
         <groupId>net.rgielen</groupId>
         <artifactId>javafx-weaver-spring-boot-starter</artifactId>
         <version>1.3.0</version>
      </dependency>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-controls</artifactId>
         <version>21.0.2</version>
      </dependency>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-fxml</artifactId>
         <version>${javafx.version}</version>
      </dependency>
      <dependency>
         <groupId>org.openjfx</groupId>
         <artifactId>javafx-web</artifactId>
         <version>${javafx.version}</version>
      </dependency>
      <dependency>
         <groupId>common.fx</groupId>
         <artifactId>commonfx</artifactId>
         <version>0.0.1-SNAPSHOT</version>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-maven-plugin</artifactId>
            <version>${javafx.maven.plugin.version}</version>
            <configuration>
               <mainClass>org.example.Javafx</mainClass>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

Solution

  • TL;DR: This is a split package problem. The solution is to either:

    1. Rename your packages so that they are unique.

    2. Rename your packages to something that is not a valid package name (only an option if the packages do not contain class files).

    3. Make your code non-modular and place your code and its dependencies (except JavaFX) on the class-path.

    See jewelsea's answer for a working example of the first solution, as well as reasons why the third solution is likely the best for your (the questioner's) specific scenario.


    The Problem

    The error you are seeing is due to two modules containing the same package. This is not something that should be solved by excluding resource folders via Maven.

    Resource-only Packages

    First, it is not a requirement for a package to contain class files to be considered a package. Really the only important thing is that the package has a valid name and contains something, whether that be a class file or some other resource. Plus, class files are basically just a special type of resource. From the perspective of a (typical) class loader, resources and class files are found and opened in the exact same way.

    In other words, bundle, config, and images are all packages in your case. Those are valid package names and they contain resources. Also, since those are packages contained in the module, the resources within them are encapsulated (a feature based on packages).

    Split Packages

    When two (or more) modules contain a package with the same name, that package is said to be "split". The Java Platform Module System ensures that when a split package exists, the two (or more) modules do not interfere with each other. It ensures this at two different steps in the process of loading modules.

    During module resolution, the resolution can fail if:

    Two or more modules in the configuration export the same package to a module that reads both. This includes the case where a module M containing package p reads another module that exports p to M.

    Then a module layer is defined, which can fail if:

    Two or more modules with the same package are mapped to the same class loader.

    Neither bundle nor images is exported, so your two modules are being resolved just fine. The error you see occurs when the layer is being defined, hence LayerInstantiationException as the exception type. Your two modules are being mapped to the same class loader (the system/application class loader).

    Basically, split packages are completely forbidden for modules loaded via the --module-path command line option. Both the module readability graph and whether or not a split package is exported are irrelevant.

    Also see Java 9 overlapping non-exported packages.


    Solutions

    There are at least three solutions: use unique package names, use invalid package names, or make your code non-modular.

    Unique Package Names

    You should use unique package names, even when the package is not going to be exported. A conventional way to come up with a unique package name is by combining a reversed domain name with a name that identifies the artifact. These two things are typically pulled from the Maven coordinates of said artifact. For example, if you have:

    <groupId>com.example</groupId>
    <artifactId>commonsfx</artifactId>
    

    In your POM, then your package name would be com.example.commonsfx. That would also be a good name for your module.

    See jewelsea's answer for a full example.

    Use Invalid Package Names

    If you rename bundle and images to something that is not a valid package name, then they will no longer be considered packages. This will let you have the same "package" in multiple modules, but it will also mean the resources within those "packages" are no longer encapsulated.

    Warning: This is only an option if the packages do not contain class files.

    Make Your Code Non-Modular

    If you make your code non-modular and place it on the class-path then your split packages will no longer be a problem. For your specific case, this is likely the best solution. See the end of jewelsea's answer for reasons why.