Close

Java 9 Modules - The Unnamed Module and Accessing JDK Internal Code via Reflection

[Last Updated: Mar 16, 2018]

In the last example we saw that a Java 9 module cannot use reflection to access non-public field/method/constructor unless the target module 'opens' itself for reflection. In this tutorial we will learn how the code that was written in Java 8 or before can still continue to work without explicitly opening for reflection.

The old code which runs in Java 9 is automatically promoted to unnamed modules. Java 9 introduced a new JVM option --illegal-access which can be assigned one of the following values:

  • permit (default):

    This mode automatically 'opens' packages (written in Java 8 or older code) when run on the classpath. This allows code that relies on the use of setAccessible to break into JDK internals, to continue working as per previous releases.

    In this mode, the first reflective-access operation to any JDK package causes a warning to be issued. However, no warnings are issued after the first occurrence. This mode is the default for JDK 9 but will change in a future release.

  • warn:

    This mode is identical to 'permit' except that a warning message is issued for each illegal reflective-access operation.

  • debug:

    This mode is identical to 'warn' except that both a warning message and a stack trace are issued for each illegal reflective-access operation.

  • deny:

    This mode disables all illegal-access operations except for those enabled by other command-line options, such as--add-opens. This mode will become the default in a future release.

Note that this option may not be available in future JDK versions at all. From Java tool doc:

The default mode, --illegal-access=permit, is intended to make you aware of code on the class path that reflectively accesses any JDK-internal APIs at least once. To learn about all such accesses, you can use the warn or the debug modes. For each library or framework on the class path that requires illegal access, you have two options:

  • If the component's maintainers have already released a fixed version that no longer uses JDK-internal APIs then you can consider upgrading to that version.

  • If the component still needs to be fixed, then you can contact its maintainers and ask them to replace their use of JDK-internal APIs with the proper exported APIs.

Example

Following is a class which uses JDK internal API via reflection:

package com.logicbig.example;

import java.lang.reflect.Field;
import java.math.BigDecimal;

public class ExampleMain {
  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
      BigDecimal bd = new BigDecimal(10.245);

      Field f = bd.getClass().getDeclaredField("scale");
      f.setAccessible(true);
      int scale = f.getInt(bd);
      System.out.println(scale);

      f = bd.getClass().getDeclaredField("precision");
      f.setAccessible(true);
      int precision = f.getInt(bd);
      System.out.println(precision);
  }
}

Let's compile and run it on classpath (as unnamed module):

D:\java9-reflection-illegal-access>javac -d out src/com/logicbig/example/ExampleMain.java

Running with default --illegal-access=permit option:

D:\java9-reflection-illegal-access>java -cp out com.logicbig.example.ExampleMain
49
0
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.logicbig.example.ExampleMain (file:/D:/java9-reflection-illegal-access/out/) to field java.math.BigDecimal.scale
WARNING: Please consider reporting this to the maintainers of com.logicbig.example.ExampleMain
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Running with --illegal-access=warn option:

D:\java9-reflection-illegal-access>java --illegal-access=warn -cp out com.logicbig.example.ExampleMain
49
0
WARNING: Illegal reflective access by com.logicbig.example.ExampleMain (file:/D:/java9-reflection-illegal-access/out/) to field java.math.BigDecimal.scale
WARNING: Illegal reflective access by com.logicbig.example.ExampleMain (file:/D:/java9-reflection-illegal-access/out/) to field java.math.BigDecimal.precision

Running with --illegal-access=debug option:

D:\java9-reflection-illegal-access>java --illegal-access=debug -cp out com.logicbig.example.ExampleMain
49
0
WARNING: Illegal reflective access by com.logicbig.example.ExampleMain (file:/D:/java9-reflection-illegal-access/out/) to field java.math.BigDecimal.scale
at com.logicbig.example.ExampleMain.main(ExampleMain.java:11)
WARNING: Illegal reflective access by com.logicbig.example.ExampleMain (file:/D:/java9-reflection-illegal-access/out/) to field java.math.BigDecimal.precision
at com.logicbig.example.ExampleMain.main(ExampleMain.java:16)

Running with --illegal-access=deny option:

D:\java9-reflection-illegal-access>java --illegal-access=deny -cp out com.logicbig.example.ExampleMain
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final int java.math.BigDecimal.scale accessible: module java.base does not "opens java.math" to unnamed module @43556938
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.setAccessible(Unknown Source)
at com.logicbig.example.ExampleMain.main(ExampleMain.java:11)

Example Project

Dependencies and Technologies Used:

  • JDK 9.0.1
Unnamed Modules and Accessing JDK Internals via Reflection Select All Download
  • java9-reflection-illegal-access
    • src
      • com
        • logicbig
          • example
            • ExampleMain.java

    See Also