Search code examples
javajavafxjlinkjavafx-11jdeps

jdeps fails with Java 11 and JavaFX in fat jar


I am trying to use jdeps (and jlink) for deployment of my Java 11 app which is using JavaFX 11. All works fine running inside my Java IDE. But when I use jdep I get the following error, which indicates that some module or package cannot be found. I am stuck. Thanks for your help.

jdeps --list-deps --module-path /Users/…/target:/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home/jmods:/Users/…/javafx-sdk-11.0.2/lib --add-modules chaincoder,javafx.fxml,javafx.base,javafx.controls,javafx.graphics,javafx.web /Users/…/target/chaincoder4-1.0.jar

Error: Module javafx.media contains package javafx.collections, module javafx.base exports package javafx.collections to javafx.media

module-info.java is

module chaincoder {

requires   javafx.web;
requires   javafx.graphics;
requires   javafx.controls;
requires   javafx.fxml;
requires   javafx.base;
requires   javafx.media;

requires java.desktop;
requires java.base;
requires java.xml;
requires java.logging;

requires jdk.jsobject;

exports   core;

}

pom.xml is

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>11.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>11.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-base</artifactId>
            <version>11.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>11.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>11.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>11.0.2</version>
        </dependency>


<build>
    <plugins>

        <plugin>
            <!-- Build an executable JAR -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>core.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <id>unpack-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>unpack-dependencies</goal>
                    </goals>
                    <configuration>
                        <excludeScope>system</excludeScope>
                        <excludeGroupIds>junit,org.mockito,org.hamcrest</excludeGroupIds>
                        <outputDirectory>${project.build.directory}/classes</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>11</source>
                <target>11</target>
                <compilerArguments>
                    <bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
                </compilerArguments>
            </configuration>
        </plugin> 
    </plugins>

</build>

Solution

  • Any reason why you are using the maven-dependency-plugin plugin?

    If you see what the unpack-dependencies goal is doing, you will notice that the target/classes folders contains more than 90 MB of classes, after the plugin extracts all the project dependencies, including the JavaFX ones, and combines them all.

    And guess what happens with the different module-info.class files for each of the different JavaFX modules? Only one prevails, in this case the one from javafx.media.

    So you have created a new module in target/classes!

    It is named javafx.media, contains your project classes and all the JavaFX classes, and that includes all from javafx.base or javafx.graphics.

    If you check this target/classes/module-info.class file:

    module javafx.media {
        requires transitive javafx.base;
        requires transitive javafx.graphics;
        ...
    }
    

    this "module", that already contains all the JavaFX classes, will also require again those classes from javafx.base and javafx.graphics, duplicating them.

    This explains your error message:

    Error: Module javafx.media contains package javafx.collections, 
           module javafx.base exports package javafx.collections to javafx.media
    

    Now you can see that your new module javafx.media contains many packages that are also exported from javafx.base to it, and that violates the split package not allowed condition:

    The same Java package can only be exported by a single Java module at runtime. In other words, you cannot have two (or more) modules that export the same package in use at the same time. The Java VM will complain at startup if you do.

    Possible Solutions

    • Maybe you don't need the maven-dependency-plugin plugin at all? Remove it if you don't need it.

    • You are adding the target folder with all the JavaFX classes to the module-path:

      jdeps --list-deps --module-path /Users/…/target:...
      

    Obviously this is wrong, as the classes folder contains all the JavaFX classes and a wrong module-info descriptor. It is not a valid module, so just remove it from the module-path.

    • Then you don't need to add the JDK path to the module-path, that is done by default.

    So you can just have:

    jdeps --list-deps --module-path /Users/…/javafx-sdk-11.0.2/lib
    
    • Then, when you add the modules, you don't need to add javafx.base or javafx.graphics, as those are included from transitive dependencies (this also applies to the dependencies listed in your pom). See for instance javafx.web.

    So you will have:

    jdeps --list-deps --module-path /Users/…/javafx-sdk-11.0.2/lib
        -add-modules chaincoder,javafx.fxml,javafx.web
    
    • But note that you have a module named chaincoder, that already includes all the modules that are required, so you can have just this:

      jdeps --list-deps --module-path /Users/…/javafx-sdk-11.0.2/lib
          -add-modules chaincoder /Users/…/target/chaincoder4-1.0.jar
      

    That should work.

    On a final note, you can get rid of the jfxrt.jar from the compiler plugin:

    <configuration>
        <source>11</source>
        <target>11</target>
        <compilerArguments>
                <bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
        </compilerArguments>
    </configuration>
    

    As it doesn't exist anymore. You have all the JavaFX classes included in the dependencies:

    <configuration>
        <source>11</source>
        <target>11</target>
    </configuration>