beawolf - Fotolia

Use the Java JDeps tool to root out internal API calls

If you have internal Java API calls in your code, it will soon stop working. Here's a way to identify those calls and replace the problematic code with options on how to fix it.

The Java class loader is a fairly simple beast. When a Java class is invoked, the class loader checks if the file exists on the file system. If it does, the class gets loaded. The class loader is by no means discerning.

One problem with the class loader's lack of discernment is the fact that there is no way to restrict an application's ability to access certain Java components, despite the fact that there are certain packages and JAR files the purveyors of the Java language would rather developers left alone. The internal Java APIs are a prime example.

Developers aren't supposed to call any of the internal code that makes the Java Development Kit (JDK) work. In fact, Oracle even provides a Java 8 JDeps tool to help identify and warn developers if such calls exist. Unfortunately, there are a number of extremely helpful classes in there that can make developers' lives easier or enable them to perform some bytecode-based tricks that would otherwise be impossible to turn. References to classes such as Unsafe or Base64Encoder should never occur in a well-built Java application, but despite that fact, references to internal Java APIs abound.

Java dependencies and new Java releases

Here's the thing: Since Java 9 introduced modules, class loader rules have completely changed. With modules, developers can restrict the ability of applications that run on the Java virtual machine to call on certain libraries, and this is exactly what Oracle has done with internal Java APIs. Since modularizing the platform, Oracle can now flip a switch and cause any internal Java API reference to fail. The switch hasn't been flipped yet, but Oracle has warned the community that it will do so in a future release. As a result, developers will need to weed out all of those internal Java API calls from their code.

The Java 8 JDeps tool -- which is now packaged as part of every new Java release -- can be run against a JAR file or a given code base. The Java JDeps tool not only identifies pieces of offending code; it suggests alternatives. It's a fairly easy tool to use and one that should be made part of every Maven build or continuous integration pipeline.

Java JDeps tool example with Unsafe and BASE64Encoder

Java JDeps example

The Java 8 JDeps tool is a command-line utility found in the \bin directory of the JDK. It has a number of switches that provide reports on the various dependencies that exist within a Java application, but the –jdkinternals option is the one that will report on Java API misuse.

Here's an example of how the Java 8 JDeps tool works. Let's say offending code calls internal Java APIs. Here's a class that calls on both Unsafe and BASE64Encoder:

package com.mcnz.jdeps;
 
import java.lang.reflect.Field;
import sun.misc.BASE64Encoder;
import sun.misc.Unsafe;
 
/* Java 8 JDeps example */
public class UnsafeAtAnySpeed {
 
  public static void main(String[] args) throws Exception {
 
    /* The Java JDeps tool will flag calls to Base64Encoder */
    BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    String s = encoder.encode("JDeps Example".getBytes());
 
    /* The Java JDeps tool will flag calls to Unsafe */
    Field f = Unsafe.class.getDeclaredField("More-JDeps");
    System.out.println(s + f);
  }
}

A Maven build of this code, which can be found on GitHub, produces a JAR file named jdeps-example.jar. The command to have the Java JDeps tool identify calls to internal APIs is:

$ jdeps –verbose –jdkinternals jdeps-example.jar

When the JDeps command is run, it flags the reference to Unsafe and BASE64Encoder and provides the following warning: JDK internal APIs are unsupported and private to the JDK implementation. They are subject to be removed or changed incompatibly and could break your application.

java JDeps utility
The Java JDeps tool can be used to weed out troublesome internal API calls.

Furthermore, the Java JDeps tool can even suggest an alternative to a BASE64Encoder call. Instead, the developer should use the java.util.Base64 class, which has been part of the JDK since the 1.5 release.

The internal Java APIs do have their appeal, as developers can accomplish things otherwise not possible in their standard libraries. But it's time to fight off that temptation for good, because this capability will soon be taken away. Instead, incorporate the Java JDeps utility into your Maven build and CI routines, and remove all of those references to internal APIs.

Dig Deeper on Java Development Tools