Me trying to explain the problem behind #ReflectiveAccessToNonExportedTypes

Remi Forax forax at univ-mlv.fr
Thu Jul 14 15:06:36 UTC 2016


Let me try to explain again the problem and the possible solution.

Let say i have two modules, module1 and module2.
In module1, I have two packages module1.exported and module1.nonexported,
so module1 has this module-info.java

  module module1 {
    exports module1.exported;
  }

in module1.nonexported, I have a class A that's implement an interface I defined like this:
  package module1.nonexported;

  import module1.exported.I;

  public class A implements I {
    @Override
    public void foo() {
      System.out.println("foo");
    }
  }

I is defined in module1.exported like this:
  package module1.exported;

  import module1.nonexported.A;

  public interface I {
    public void foo();
  
    static I instance() {
      return new A();
    }
  }

Now, in module2, i want to use I so module2 requires module1, like this:
  module module2 {
    requires module1;
  }

and I have a main defined in the package modul2.main, like that:
  package module2.main;

import java.lang.reflect.Method;

import module1.exported.I;

public class Main {
  public static void main(String[] args) throws ReflectiveOperationException {
    System.out.println(Class.forName("module1.nonexported.A"));
    
    I i = I.instance();
    Method m = i.getClass().getMethod("foo");
    m.invoke(i);
  }
}

If i run this code in with the two modules in the classpath,
everything work and the code prints
  class module1.nonexported.A
  foo

Now, if if the two modules are in the modulepath,
Class.forName works but m.invoke() throws an IllegalAccessException because A is in a non exported package
  java.lang.IllegalAccessException: class module2.main.Main (in module module2) cannot access class module1.nonexported.A (in module module1) because module module1 does not export module1.nonexported to module module2

If i try with m.setAccessible(true), before m.invoke(), setAccessible throws an exception InaccessibleObjectException
  java.lang.reflect.InaccessibleObjectException: Unable to make member of class module1.nonexported.A accessible:  module module1 does not export module1.nonexported to module module2

A way to solve this issue, is to use the interface I to call foo instead of using the class A,
  Method m = I.class.getMethod("foo");
  m.invoke(i);

in that case, everything is Ok, because i don't try to use the method foo of A anymore.

What Mark as proposed is to introduce a new "export dynamic" semantics which doesn't allow the package to be visible at compile time but to be visible at runtime.
This solve the issue by supposing that everyone will write export dynamic on every non exported package to be backward compatible with the Java 8 behavior.

While i agree that "export dynamic" is a semantics that jigsaw should provide, i disagree with the syntax "export dynamic" because for me, the a non exported package should have this semantics in order to be backward compatible with existing code.
So we should have 3 way to export or not a package,
  1. don't export at compile time, don't export at runtime
  2. don't export at compile time, export at runtime
  3. export at compile time, export at runtime.

2 should be the default, 1 should be use by the package of the JDK (or any other libs) that want strong security, 3 is for the package that defines API (that will be maintained forever).

Rémi

 



More information about the jpms-spec-observers mailing list