What are ways to systematically find all modules of the JDK that will be/have been used during the runtime of a Java application through reflection and the service provider model?
jlinks --suggest-providers option, but this requires knowledge of each provider that is lacking.I've been working with a self-contained jar (for our current example code, the Paper Minecraft server, which has requirements to download a jar, the Minecraft jar, and patch it while running it). When I go to build a minimal Docker container for running the application using a runtime like Eclipse Temurin it is recommended:
JRE images are available for all versions of Eclipse Temurin, but it is recommended that you produce a custom JRE-like runtime using jlink (see usage below).
Their code sample:
# Example of custom Java runtime using jlink in a multi-stage container build
FROM eclipse-temurin:21 as jre-build
# Create a custom Java runtime
RUN $JAVA_HOME/bin/jlink \
--add-modules java.base \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /javaruntime
# Define your base image
FROM debian:buster-slim
ENV JAVA_HOME=/opt/java/openjdk
ENV PATH "${JAVA_HOME}/bin:${PATH}"
COPY --from=jre-build /javaruntime $JAVA_HOME
# Continue with your application deployment
RUN mkdir /opt/app
COPY japp.jar /opt/app
CMD ["java", "-jar", "/opt/app/japp.jar"]
From: https://hub.docker.com/_/eclipse-temurin
This build command in particular does not include any required service providers and runs into the common problem of searching for a laundry list of service providers from other modules such as jdk.crypto.cryptoki and jdk.crypto.ec, which are common requirements to complete even a basic HTTPS handshake for web requests.
There is the option to simply use --bind-services to include every single module that could provide a service for java.base module, but as discussed here, this defeats the purpose of creating a slimmed-down JRE in the first place, as it includes everything.
This introduces a weird and frustrating dance of running and breaking your application endlessly until you've found the exact 8-20 piece --add-modules argument to provide just enough services to run your application.
I feel like there should be some form of profiler command or tool shipped with the jdk or potentially otherwise runnable alongside your application that can answer the question "What jdk modules were used during your applications execution?" not just java.base but "java.base leveraged jdk.crypto.cryptoki during this execution" to speed up the process of tracing all required modules for a slimmed down runtime.
I have searched through Stack Overflow questions, blogs, and JDK 21 documentation about JLinks and jdeps for about 6-7ish hours. I am happy to mark an answer correct if I have simply missed a crucial option referenced in another answer either but I have not managed to turn up an answer as of yet. Happy to add additional clarifications if needed. I may eventually write essentially a profiler to handle this, but I want to see if that already exists.
For Java 21,
java -Xlog:class+load=info -cp your-app.jar com.myapp.Main
to show all modules attempted to be loaded.
Filter from there for Java packages as needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With