Ch- ch- ch- changes!

Java 9 was, in a number of ways, possibly the most disruptive change to the language, largely due to the introduction of the Java Platform Module System (JPMS), which for the first time enforces the Java team’s insistence that programs not use JDK internals. Not only are many classes and methods suddenly inaccessible, a number have actually been removed, and even reflective access to those internals is suddenly deprecated. I can illustrate the impact with my library, SimpleStub. This library generates stub classes at run time using ASM, a capability that was originally implemented by using reflection on JDK internals, such as:

defineClassMethod = AccessController.doPrivileged(
      new PrivilegedExceptionAction() {
           public Method run() throws Exception {
             Class<?> cl = Class.forName("java.lang.ClassLoader");
             return cl.getDeclaredMethod("defineClass", String.class, 
                                 byte[].class, int.class, int.class);
           }
       });
 defineClassMethod.setAccessible(true);
 return defineClassMethod.invoke(anchorClass.getClassLoader(), 
                         className, classBytes, 0, classBytes.length);

As of Java 9, we’re not supposed to use reflection on the java.lang package, and at runtime, the JDK will print an illegal access warning, which is supposed to be turned into an error in the future. So in SimpleStub I changed the code to use Unsafe.defineClass:

private final Unsafe unsafe = AccessController.doPrivileged(
     new PrivilegedAction() {
          public Unsafe run() {
            try {
               Field field =
                      Unsafe.class.getDeclaredField("theUnsafe");
               field.setAccessible(true);
               return (Unsafe) field.get(null);
            } catch (NoSuchFieldException | IllegalAccessException e) {
               throw new Error("Could not access Unsafe", exc);
            }
          }
     }
);

return unsafe.defineClass( className, classBytes, 0, 
 classBytes.length, classLoader, null );

So, problem solved? Not exactly, as the plans for the in-development JDK 11 include removing the Unsafe.defineClass method. It has been replaced by code allowing this:

MethodHandles.Lookup lookup = 
     MethodHandles.privateLookupIn(anchorClass, MethodHandles.lookup())
                  .dropLookupMode(MethodHandles.Lookup.PRIVATE);
return lookup.defineClass(classBytes);

in which anchorClass is a class in the package in which the new class will be defined. That will require a bit of rework, but the main problem is that this relies on methods that were only added in Java 9! Switching to that implementation would break the library for earlier implementations.

The language designers, of course, were fully aware of the problems they were creating, and added a new feature: the multi-release (MR) JAR, as part of JEP-238. A problem, though, is that more than two years after this feature was announced, the tools still don’t actually provide much in the way of support. My next post will explore the current options.