javajavafxjava-platform-module-systemjava-17jdeps

jdeps can't print-module-deps due to a MultiReleaseException


We have a JavaFX based application which is not modularized (there are reasons, a legacy library is involved) but we build an custom runtime using jdeps and jlink.

We've recently rewritten the app and added a couple of new dependencies, as well as removing others. Now the script that is building the application suddenly stopped working during the jdeps call.

Note: This is happening on Linux – I've yet to test other OS'ses, but I don't expect another result.

When the script calls

~/path/to/jdk/bin/jdeps -q --multi-release 11 --ignore-missing-deps --print-module-deps --class-path ~/path/to/app/target/package/libs/* target/classes/ch/cnlab/uxtest/MainKt.class

the result is always

Exception in thread "main" java.lang.Error: java.util.concurrent.ExecutionException: com.sun.tools.jdeps.MultiReleaseException
    at jdk.jdeps/com.sun.tools.jdeps.DependencyFinder.waitForTasksCompleted(DependencyFinder.java:271)
    at jdk.jdeps/com.sun.tools.jdeps.DependencyFinder.parse(DependencyFinder.java:133)
    at jdk.jdeps/com.sun.tools.jdeps.DepsAnalyzer.transitiveArchiveDeps(DepsAnalyzer.java:217)
    at jdk.jdeps/com.sun.tools.jdeps.DepsAnalyzer.run(DepsAnalyzer.java:138)
    at jdk.jdeps/com.sun.tools.jdeps.ModuleExportsAnalyzer.run(ModuleExportsAnalyzer.java:74)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask$ListModuleDeps.run(JdepsTask.java:1047)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:574)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:533)
    at jdk.jdeps/com.sun.tools.jdeps.Main.main(Main.java:49)
Caused by: java.util.concurrent.ExecutionException: com.sun.tools.jdeps.MultiReleaseException
    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at jdk.jdeps/com.sun.tools.jdeps.DependencyFinder.waitForTasksCompleted(DependencyFinder.java:267)
    ... 8 more
Caused by: com.sun.tools.jdeps.MultiReleaseException
    at jdk.jdeps/com.sun.tools.jdeps.VersionHelper.add(VersionHelper.java:62)
    at jdk.jdeps/com.sun.tools.jdeps.ClassFileReader$JarFileReader.readClassFile(ClassFileReader.java:360)
    at jdk.jdeps/com.sun.tools.jdeps.ClassFileReader$JarFileIterator.hasNext(ClassFileReader.java:402)
    at jdk.jdeps/com.sun.tools.jdeps.DependencyFinder.lambda$parse$5(DependencyFinder.java:179)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)

I couldn't find much in regards to this specific exception and everything I've found so far was not applicable to our situation.

To not wait as long as the build takes to reach this point, I've let the program print out the command before it gets executed and use it on a terminal. Then it get's a bit weirder:

Exception in thread "main" java.lang.module.FindException: Module org.slf4j not found, required by com.dlsc.gmapsfx
        at java.base/java.lang.module.Resolver.findFail(Resolver.java:893)
        at java.base/java.lang.module.Resolver.resolve(Resolver.java:192)
        at java.base/java.lang.module.Resolver.resolve(Resolver.java:141)
        at java.base/java.lang.module.Configuration.resolve(Configuration.java:421)
        at java.base/java.lang.module.Configuration.resolve(Configuration.java:255)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsConfiguration$Builder.build(JdepsConfiguration.java:564)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.buildConfig(JdepsTask.java:603)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:557)
        at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:533)
        at jdk.jdeps/com.sun.tools.jdeps.Main.main(Main.java:49)

Why of all sudden to I get a different exception? Which one is correct now? I have no clue.
Fact is, the jar containing org.slf4j only has the automatic module name.

I really have have no idea, what I should do with little information... If someone else can point to something, I'd be glad.

Thanks, Daniel

PS: The following code prints the command and executes it:

echo "detecting required modules"
CMD="$JDK/bin/jdeps -q --multi-release ${JAVA_VERSION} --ignore-missing-deps --print-module-deps --class-path ${OUT_LIBS}/* ${MAIN_CLASS_FILE}"; echo "$CMD"
detected_modules=$("$JDK"/bin/jdeps -q \
  --multi-release ${JAVA_VERSION} \
  --ignore-missing-deps \
  --print-module-deps \
  --class-path "${OUT_LIBS}/*" \
    "${MAIN_CLASS_FILE}") || exit
echo "detected modules: ${detected_modules}"

They really seem to create different result... 🤷

PPS: If I remove the --multi-release part, I get a different error, that jackson is multi-release, but I need to specify what I want...

Error: jackson-core-2.13.0.jar is a multi-release jar file but --multi-release option is not set

Edit #1

In the pom file, we have the following deps

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-base</artifactId>
    <version>${use.javafx.version}</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>${use.javafx.version}</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-fxml</artifactId>
    <version>${use.javafx.version}</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-graphics</artifactId>
    <version>${use.javafx.version}</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-web</artifactId>
    <version>${use.javafx.version}</version>
</dependency>

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib-jdk8</artifactId>
    <version>${kotlin.version}</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
    <version>${kotlin.version}</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-test-junit</artifactId>
    <version>${kotlin.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-core</artifactId>
    <version>${kotlinx.coroutines.version}</version>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-jdk8</artifactId>
    <version>${kotlinx.coroutines.version}</version>
</dependency>

<dependency>
    <groupId>io.insert-koin</groupId>
    <artifactId>koin-core-jvm</artifactId>
    <version>${koin.version}</version>
</dependency>
<dependency>
    <groupId>io.insert-koin</groupId>
    <artifactId>koin-test-jvm</artifactId>
    <version>${koin.version}</version>
</dependency>

<dependency>
    <groupId>org.simpleframework</groupId>
    <artifactId>simple-xml</artifactId>
    <version>${simplexml.version}</version>
</dependency>

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>${okhttp.version}</version>
</dependency>
<dependency>
    <groupId>net.sf.proguard</groupId>
    <artifactId>proguard-base</artifactId>
    <version>${proguard.version}</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.kordamp.ikonli</groupId>
    <artifactId>ikonli-materialdesign2-pack</artifactId>
    <version>${ikonli.mdi2.version}</version>
</dependency>
<dependency>
    <groupId>org.kordamp.ikonli</groupId>
    <artifactId>ikonli-javafx</artifactId>
    <version>${ikonli.version}</version>
</dependency>
<dependency>
    <groupId>org.controlsfx</groupId>
    <artifactId>controlsfx</artifactId>
    <version>${controlsfx.version}</version>
</dependency>
<dependency>
    <groupId>com.dlsc</groupId>
    <artifactId>GMapsFX</artifactId>
    <version>${gmapfx.version}</version>
</dependency>

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>${sqlite.jdbc.version}</version>
</dependency>

<!-- some custom deps from our company -->

<dependency>
    <groupId>org.conscrypt</groupId>
    <artifactId>conscrypt-openjdk</artifactId>
    <version>${conscrypt.version}</version>
    <classifier>${os.detected.classifier}</classifier>
</dependency>

Resulting in the following JAR files (minus our own):

annotations-13.0.jar
apiguardian-api-1.1.2.jar
conscrypt-openjdk-2.5.2-linux-x86_64.jar
controlsfx-11.1.0.jar
GMapsFX-11.0.2.jar
hamcrest-core-1.3.jar
ikonli-core-12.2.0.jar
ikonli-javafx-12.2.0.jar
ikonli-materialdesign2-pack-12.2.0.jar
jackson-annotations-2.13.0.jar
jackson-core-2.13.0.jar
jackson-databind-2.13.0.jar
jackson-module-kotlin-2.13.0.jar
jakarta.activation-1.2.2.jar
jakarta.activation-api-1.2.2.jar
jakarta.xml.bind-api-2.3.3.jar
jaxb-impl-2.3.3.jar
junit-4.12.jar
junit-jupiter-api-5.8.1.jar
junit-jupiter-engine-5.8.1.jar
junit-platform-commons-1.8.1.jar
junit-platform-engine-1.8.1.jar
koin-core-jvm-3.1.3.jar
koin-test-jvm-3.1.3.jar
kotlin-reflect-1.5.31.jar
kotlin-stdlib-1.5.31.jar
kotlin-stdlib-common-1.5.30.jar
kotlin-stdlib-jdk7-1.5.31.jar
kotlin-stdlib-jdk8-1.5.31.jar
kotlin-test-1.5.31.jar
kotlin-test-annotations-common-1.5.30.jar
kotlin-test-common-1.5.30.jar
kotlin-test-junit-1.5.31.jar
kotlinx-coroutines-core-1.5.2.jar
kotlinx-coroutines-core-jvm-1.5.2.jar
kotlinx-coroutines-jdk8-1.5.2.jar
log4j-api-2.14.1.jar
log4j-core-2.14.1.jar
log4j-web-2.14.1.jar
logback-classic-1.2.3.jar
logback-core-1.2.3.jar
okhttp-4.9.2.jar
okio-2.8.0.jar
opentest4j-1.2.0.jar
proguard-base-6.2.2.jar
simple-xml-2.7.1.jar
slf4j-api-1.7.29.jar
sqlite-jdbc-3.36.0.3.jar
stax-1.2.0.jar
stax-api-1.0.1.jar
xpp3-1.1.3.3.jar

The Java version in use is Bell Soft's Full Liberica JDK 17, we used all recent versions since 14 or 15 where it worked well prior to the rebuild of the UI.


edit #2:

The result of

mvn compile org.apache.maven.plugins:maven-dependency-plugin:3.1.1:resolve -DexcludeTransitive

is

[INFO] The following files have been resolved:
[INFO]    com.fasterxml.jackson.core:jackson-databind:jar:2.13.0:compile -- module com.fasterxml.jackson.databind
[INFO]    com.squareup.okhttp3:okhttp:jar:4.9.2:compile -- module okhttp3 [auto]
[INFO]    com.fasterxml.jackson.core:jackson-annotations:jar:2.13.0:compile -- module com.fasterxml.jackson.annotation
[INFO]    org.openjfx:javafx-fxml:jar:17.0.1:compile -- module javafx.fxmlEmpty [auto]
[INFO]    org.openjfx:javafx-web:jar:17.0.1:compile -- module javafx.webEmpty [auto]
[INFO]    org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.5.31:compile -- module kotlin.stdlib.jdk8
[INFO]    io.insert-koin:koin-core-jvm:jar:3.1.3:compile -- module koin.core.jvm (auto)
[INFO]    org.jetbrains.kotlin:kotlin-test-junit:jar:1.5.31:test -- module kotlin.test.junit
[INFO]    org.simpleframework:simple-xml:jar:2.7.1:compile -- module simple.xml [auto]
[INFO]    org.apache.logging.log4j:log4j-core:jar:2.14.1:compile -- module org.apache.logging.log4j.core [auto]
[INFO]    io.insert-koin:koin-test-jvm:jar:3.1.3:compile -- module koin.test.jvm (auto)
[INFO]    org.junit.jupiter:junit-jupiter-engine:jar:5.8.1:test -- module org.junit.jupiter.engine
[INFO]    org.xerial:sqlite-jdbc:jar:3.36.0.3:compile -- module org.xerial.sqlitejdbc [auto]
[INFO]    net.sf.proguard:proguard-base:jar:6.2.2:runtime -- module proguard.base (auto)
[INFO]    org.openjfx:javafx-graphics:jar:17.0.1:compile -- module javafx.graphicsEmpty [auto]
[INFO]    org.jetbrains.kotlinx:kotlinx-coroutines-core:jar:1.5.2:compile -- module kotlinx.coroutines.core (auto)
[INFO]    org.apache.logging.log4j:log4j-web:jar:2.14.1:compile -- module org.apache.logging.log4j.web [auto]
[INFO]    ch.<ourgroup>:<our-artifact>:1.8.8:compile -- module <our-lib> (auto)
[INFO]    org.openjfx:javafx-controls:jar:17.0.1:compile -- module javafx.controlsEmpty [auto]
[INFO]    org.controlsfx:controlsfx:jar:11.1.0:compile -- module org.controlsfx.controls
[INFO]    com.fasterxml.jackson.core:jackson-core:jar:2.13.0:compile -- module com.fasterxml.jackson.core
[INFO]    com.fasterxml.jackson.module:jackson-module-kotlin:jar:2.13.0:compile -- module com.fasterxml.jackson.kotlin
[INFO]    org.apache.logging.log4j:log4j-api:jar:2.14.1:compile -- module org.apache.logging.log4j
[INFO]    org.openjfx:javafx-base:jar:17.0.1:compile -- module javafx.baseEmpty [auto]
[INFO]    org.junit.jupiter:junit-jupiter-api:jar:5.8.1:test -- module org.junit.jupiter.api
[INFO]    org.kordamp.ikonli:ikonli-javafx:jar:12.2.0:compile -- module org.kordamp.ikonli.javafx
[INFO]    com.dlsc:GMapsFX:jar:11.0.2:compile -- module com.dlsc.gmapsfx
[INFO]    org.jetbrains.kotlin:kotlin-reflect:jar:1.5.31:compile -- module kotlin.reflect
[INFO]    org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:jar:1.5.2:compile -- module kotlinx.coroutines.jdk8 (auto)
[INFO]    org.kordamp.ikonli:ikonli-materialdesign2-pack:jar:12.2.0:compile -- module org.kordamp.ikonli.materialdesign2
[INFO]    org.conscrypt:conscrypt-openjdk:jar:linux-x86_64:2.5.2:compile -- module org.conscrypt [auto]

Solution

  • Update: These issues have been fixed, and a patched version of jdeps is available as part of the early access build for JDK 18 at: http://jdk.java.net/18/ (starting from build 26)


    Turning my comments into an answer. There seem to be 3 bugs going on here:

    1. The MultiReleaseException seems to be because jdeps can not handle classes in different jars that have the same name, such as module-info.class, but are stored in a different META-INF/versions/xxx directory. (JDK-8277165)
    2. The fact that this exception is sometimes suddenly not occuring seems to be the result of a race condition in the code that checks for the above; classes of the same name having multiple versions. (JDK-8277166)
    3. The MultiReleaseException is missing it's exception message since it's thrown as part of an asynchronous task, which wraps it in an ExecutionException, which then leads to jdeps not reporting the exception correctly. (JDK-8277123)

    As for a workaround, I don't think there's a good one at this point, except maybe for editing all the jars on the class path so that they put the module-info.class in the same META-INF/versions/xxx directory (but, this might have other consequences as well, so you probably don't want to run with the edited jars, and only use them for jdeps).