This is number seven of the Java 9 series: articles looking at some of the JDK Enhancement Proposals (JEPS) hoping to make their way into Java 9. Last week we looked at Variable Handles, and touched upon JEP 260.

This week will take a more in-depth look at JEP 260: Encapsulate Most Internal APIs. This proposes to make most of the JDK internal APIs inaccessible by default, in particular the non-critical APIs that were never intended for external use. The critical internal APIs not intended for external use will gradually have supported replacements. Critical internal APIs that have replacements in JDK 9 will be deprecated and encapsulated or removed in JDK 10.

Platform Security

The motivation for encapsulating internal APIs is to improve the integrity and security of the Java platform. Leveraging the module system will support this. In order to determine which APIs to encapsulate, two broad categories were recognised:

  1. APIs that don’t appear to be used outside the JDK, so are non-critical. Also in this category are APIs that are used, but replacement functionality is provided by other libraries that are either part of Java SE 8 standard (in java.* or java.*) or JDK-specific and annotated with @jdk.
  2. APIs that provide critical functionality that would be difficult to implement outside of the JDK.

Category 1 APIs will be in a module that does not export their package, so they won’t be available for other modules. The critical internal APIs of category 2 will remain accessible, which will be:

These will be moved to a JDK-specific module, jdk.unsupported, which will export the packages they are placed in. The APIs will be accessible to code on the class path, and modules that import the jdk.unsupported module.

Critical APIs

sun.misc.Signal provides ANSI/ISO C signal support. A signal can report exceptional behaviour or report an asynchronous event outside the program. The JVM internally registers a special C signal handler which creates a new thread. An example would be signals to terminate the application. sun.misc.SignalHandler is the signal handler interface exposed.

sun.reflect.Reflection::getCallerClass(int x) tells you which classes are in the call stack. It ignores frames associated with java.lang.reflect.Method.invoke() and returns the class x frames up the stack.

sun.reflect.ReflectionFactory is “the master factory for all reflective objects, both those in java.lang.reflect (Fields, Methods, Constructors) as well as their delegates (FieldAccessors, MethodAccessors, ConstructorAccessors).” The methods, including newConstructorForSerialization(), can cause “subversion of the Java language”, so access to the constructor of the class has a security check, similar to sun.misc.Unsafe.

Why is Unsafe, unsafe?

We mentioned last week that sun.misc.Unsafe is an unsupported API that is only intended for use by core Java classes, but that is commonly used for high performance fenced loads/stores and atomic updates, compromising safety standards for speed. It allows low level programming without using the JNI, for which you need to know some C code.

Without proper care, some of the methods in Unsafe are dangerous: for example:
allocateMemory(long bytesAllocated). This allocated a new block of native memory of size bytesAllocated. Memory has to be explicitly unallocated (with the freeMemory(long address) method) or reallocated (reallocateMemory(long address, long bytes).

Several of the other methods are not recommended due to use of native memory. Native memory is not part of the JVM heap, and therefore not subject to garbage collection. Without careful management of the native memory (which won’t be a problem if you are familiar with C++) you leave yourself open to memory leaks. Other memory manipulation functionality, such as putByte(long address, byte x) used incorrectly can lead to inconsistency on the JVM.

Will my code break?

If an internal API is used critically by an application, and is encapsulated in Java 9, then migrating to Java 9 will cause the application to fail.

You can use the dependency analysis tool in JDK 8: jeps, to see if your application depends on any internal APIs:

> jdeps -dotoutput <dot-file-dir> -jdkinternals <one-or-more-jar-files....>

The JEP recommends:

  • Testing code against the early access builds of JDK 9.
  • As a last resort, exposing the API via a command line flag at compile and run time.

To expose the API, the command is:

--add-exports <source-module>/<package>=<target-module>(,<target-module>)*

<source-module> and <target-module> are module names. <package> is the name of a package.

How projects will change

The jeps wiki lists some of the internal APIs and the recommended way to replace them. For example, functionality in sun.misc.ClassLoaderUtil can be replaced with java.net.URLClassLoader.close().

The module declaration, module-info.java file, for jdk.unsupported looks like this:

module jdk.unsupported {
    exports sun.misc;
    exports sun.reflect;
    exports com.sun.nio.file;
}

So in order to import the jdk.unsupported module,

module foo.bar {
    requires jdk.unsupported;
}

Which will get access to the exported packages. For more information on how to use the module system, see The State of The Module System.

 

Next up: Multi-Release JAR Files

Java 9 series: Encapsulate Most Internal APIs

| Java Language| 1,148 views | 0 Comments
Profile photo of Katharine
About The Author
- Katharine is a Java developer by trade, turned Community & Content Manager for Voxxed. Helping developers learn and share knowledge. Contact me at kbe@voxxed.com with any news, articles or tutorials.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>