Jdeps Tool
28 Feb 2024Introduction
One of the key aspects of this migration process is updating the codebase to be compatible with the new JDK features and changes in the Java language and platform. Tools like jdeps play a crucial role in this process by helping developers identify and resolve dependencies and compatibility issues.
Jdeps Tool is a command-line tool provided as part of the JDK (Java Development Kit) starting from JDK 8. It stands for “Java Dependency Analysis Tool.” The primary purpose of jdeps is to analyze Java class files or JAR files and determine their dependencies on other classes and JAR files.
We have used this tool to determine if any of our classes, test classes or third party dependencies are accessing any of the JDK internal APIs, because in that case an InaccessibleObjectException or UnsupportedOperationException would be thrown.
Why using the JDK internal API considered not safe
- Unstable Changes: The internal API is not part of the public API, which means it is subject to change at any time without notice. This can lead to unpredictable behavior and compatibility issues when upgrading to new versions of the JDK. Your code may work perfectly fine with one version of the JDK but break with the next minor release.
- Security Vulnerabilities: Since internal APIs are not designed for external use, they often lack the necessary security checks and validations that are present in the public API. This can leave your application exposed to security risks if an attacker manages to exploit these APIs.
- Lack of Support: When you use internal APIs, you are essentially going off the beaten path. There is no official support for these APIs, so if you encounter problems, you will be on your own to find solutions or workarounds.
- Future Compatibility: As the JDK evolves, internal APIs may be deprecated or removed entirely. Relying on them means that future updates to the JDK could break your application without warning.
- Performance Considerations: Internal APIs are not always optimized for performance, especially when compared to the public API. This could lead to suboptimal performance in your application.
- Documentation and Community Knowledge: There is often little to no documentation available for internal APIs, making it difficult to understand how they work and what their limitations are. Additionally, since these APIs are not part of the public API, there is limited community knowledge and experience to draw upon for troubleshooting and best practices.
How to use jdeps
There is 2 approaches to use this plugin:
- By using the plugin directly in the mvn command.
- By defining the maven-jdeps-plugin in the parent pom.
1st approach: using mvn command directly
This could be done by executing this command:
mvn -f ./pom.xml \
-P logisticInternal \
clean compile
org.apache.maven.plugins:maven-jdeps-plugin:3.1.2:jdkinternals \
org.apache.maven.plugins:maven-jdeps-plugin:3.1.2:test-jdkinternals \
-Djdeps.failOnWarning=false \
-Djdeps.verbose=class \
-Djdeps.multiRelease=21 \
-Djdeps.recursive=true > results-jdeps.txt2nd approach: define plugin in parent pom
In order to use it, define this plugin in the parent pom file:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jdeps-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<goals>
<goal>jdkinternals</goal> <!-- verify main classes -->
<goal>test-jdkinternals</goal> <!-- verify test classes -->
</goals>
</execution>
</executions>
<configuration>
<failOnWarning>false</failOnWarning>
<verbose>class</verbose>
<multiRelease>21</multiRelease>
<recursive>true</recursive>
</configuration>
</plugin>
</plugins>
</build>and then we run the maven command:
mvn clean compile \
jdeps:jdkinternals jdeps:test-jdkinternals \
-f ./pom.xml \
-P logisticInternalPlugin Explanation
The two maven goals jdeps:jdkinternals and jdeps:test-jdkinternals are for scanning the source code classes and the test classes.
The configurations in the plugin:
verbose: Print extra info, the valueclassprints class-level dependencies excluding dependencies within the same archive, other values could bepackage.multiRelease: Specifies the version when processing multi-release JAR files version should be an integer >=9 or base.recursive: Recursively traverse all dependencies.failOnWarning: Indicates whether the build will continue even if there arejdepswarnings, it could betrueorfalse.
A suggested way to use the tool is like this (after compiling the code):
mvn -f ./pom.xml \
-P logisticInternal \
clean compile
org.apache.maven.plugins:maven-jdeps-plugin:3.1.2:jdkinternals \
org.apache.maven.plugins:maven-jdeps-plugin:3.1.2:test-jdkinternals \
-Djdeps.failOnWarning=false \
-Djdeps.verbose=class \
-Djdeps.multiRelease=21 \
-Djdeps.recursive=true | grep "JDK internal API" | sort | uniqThis with the previous plugin configuration will result in a list of classes that are using the JDK internal API.
Output Example:
com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.CleanerJava9 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.CleanerJava9$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.CleanerJava9$2 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent$Mpsc$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0$2 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0$3 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0$4 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0$6 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueConsumerNodeRef -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueProducerNodeRef -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.LinkedQueueNode -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess -> sun.misc.Unsafe JDK internal API (jdk.unsupported)