'Jdeps Module java.annotation not found

I'm trying to create a minimal jre for Spring Boot microservices using jdeps and jlink, but I'm getting the following error when I get to the using jdeps part

Exception in thread "main" java.lang.module.FindException: Module java.annotation not found, required by org.apache.tomcat.embed.core
    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)

I already tried the following commands with no effect

jdeps --ignore-missing-deps --multi-release 17 --module-path target/lib/* target/errorrr-*.jar
jdeps --multi-release 16 --module-path target/lib/* target/errorrr-*.jar
jdeps --ignore-missing-deps --multi-release 17 --class-path target/lib/* target/errorrr-*.jar

I already tried it with java versions 11, 16 and 17 and different versions of Spring Boot.

All dependencies needed for build are copied to target/lib folder by maven-dependency-plugin plugin when I run mvn install

After identifying the responsible dependency I created a new project from scratch with only it to isolate the error, but it remained.

I tried to use gradle at first but as the error remained I changed it to mavem but also no change.

When I add the specified dependency that is being requested the error changes to

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

My pom.xml

   <?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>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.6.0</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.example</groupId>
   <artifactId>errorrr</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>errorrr</name>
   <description>Demo project for Spring Boot</description>
   <properties>
       <java.version>17</java.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>

   </dependencies>

   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-dependency-plugin</artifactId>
               <executions>
                   <execution>
                       <id>copy-dependencies</id>
                       <phase>package</phase>
                       <goals>
                           <goal>copy-dependencies</goal>
                       </goals>
                       <configuration>
                           <outputDirectory>${project.build.directory}/lib</outputDirectory>
                       </configuration>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>

</project>

If I don't need to use this dependency I can do all the build processes and at the end I have a 76mb jre



Solution 1:[1]

I arrived at a solution that is valid, not perfect, but it works correctly. In my case the lib "jackson" was the cause of the problem. It uses java multi release and somehow during jdeps it was causing some error. After some tests I understood that I could remove this lib from the evaluation, and that no module would be missing from deps.info. I needed to add this task for removal:

task myDeleteTask(type: Delete) {
    delete files("${buildDir}/temp-lib/jackson-databind-{your_version}.jar")
    delete files("${buildDir}/temp-lib/jackson-datatype-jdk8-{your_version}.jar")
    delete files("${buildDir}/temp-lib/jackson-datatype-jsr310-{your_version}.jar")
    delete files("${buildDir}/temp-lib/jackson-module-parameter-names-{your_version}.jar")
    delete files("${buildDir}/temp-lib/jackson-core-{your_version}.jar")
    delete files("${buildDir}/temp-lib/jackson-dataformat-cbor-{your_version}.jar")
}
task tempCopyDependencies(type: Copy) {
    from configurations.runtimeClasspath
    into "$buildDir/temp-lib"
}

tasks.named("build"){
    finalizedBy("tempCopyDependencies")
    finalizedBy("myDeleteTask")
}

OBS: in the question I used maven because it is easier and I have a wider knowledge, but my real project uses gradle, which ends up making the delete task easier to do

Solution 2:[2]

I have been struggling with a similar issue In my gradle spring boot project I am using the output of the following for adding modules in jlink in my dockerfile with (openjdk:17-alpine):

RUN jdeps \
    --ignore-missing-deps \
    -q \
    --multi-release 17 \
    --print-module-deps \
    --class-path build/lib/* \
    app.jar > deps.info

RUN jlink --verbose \
    --compress 2 \
    --strip-java-debug-attributes \
    --no-header-files \
    --no-man-pages \
    --output jre \
    --add-modules $(cat deps.info)

I think your mvn build is fine as long as you have all the required dependencies. But just in case I modified my gradle jar task to include the dependencies as follow:

jar {
     manifest {
          attributes "Main-Class": "com.demo.Application"
     }
     duplicatesStrategy = DuplicatesStrategy.INCLUDE
     from {
          configurations.default.collect { it.isDirectory() ? it : zipTree(it) 
     }
   }
}

Solution 3:[3]

I was facing a similar issue, what helped in my case - is specifying both --class-path and --module-path pointing to the same directory with libs. According to your example, I think it should be jdeps --ignore-missing-deps --print-module-deps --multi-release 17 --module-path="target/lib/*" --class-path="target/lib/*" target/errorrr-*.jar.

Also, jdeps from JDK 17 (and maybe earlier versions) seems to have a bug where it can throw com.sun.tools.jdeps.MultiReleaseException. It seems to have been fixed in JDK 18, at least it works without any issues for me.

With Docker you can do a staged build that will identify the dependencies first using JDK 18, and then build a new JRE image out of JDK 17. Like this:

FROM amazoncorretto:18-alpine as deps

COPY ./app.jar /app/app.jar
RUN mkdir /app/unpacked && \
    cd /app/unpacked && \
    unzip ../app.jar && \
    cd .. && \
    $JAVA_HOME/bin/jdeps \
    --ignore-missing-deps \
    --print-module-deps \
    -q \
    --recursive \
    --multi-release 17 \
    --class-path="./unpacked/BOOT-INF/lib/*" \
    --module-path="./unpacked/BOOT-INF/lib/*" \
    ./app.jar > /deps.info

FROM amazoncorretto:17.0.3-alpine as corretto-jdk

RUN apk add --no-cache binutils

COPY --from=deps /deps.info /deps.info

RUN $JAVA_HOME/bin/jlink \
    --verbose \
    --add-modules $(cat /deps.info) \
    --strip-debug \
    --no-man-pages \
    --no-header-files \
    --compress=2 \
    --output /customjre

You can check the full example here: https://github.com/monosoul/jvm-in-docker/blob/main/jre-slim-auto.dockerfile

For anyone interested, here's a blog article about using jlink and jdeps: https://blog.monosoul.dev/2022/04/25/reduce-java-docker-image-size/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bryan Motta
Solution 2 SHK
Solution 3