0

I'm playing around with modules and am trying to use reflection to invoke a method on a proxy. I get the following error:

java.lang.IllegalAccessException: class ca.example.MyStore.ProxyExample (in module ca.exampe.MyStore) cannot access class com.sun.proxy.jdk.proxy2.$Proxy10 (in module jdk.proxy2) because module jdk.proxy2 does not export com.sun.proxy.jdk.proxy2 to module ca.example.MyStore

Here is the method invocation on the proxy instance that causes the above exception when the default method is attempted to be invoked:

    public Object executeDefaultMethod() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        // can not use Proxy when the app is modularized unless you specify that the module is open
        // open module notes: https://stackoverflow.com/questions/53927375/how-do-java-module-directives-impact-reflection-access-into-a-module
        Object proxy = Proxy.newProxyInstance(getSystemClassLoader(), new Class<?>[] { Coupon.class },
                (prox, method, args) -> {
                    if (method.isDefault()) {
                        // invokeDefault is new with Java 16
                        return InvocationHandler.invokeDefault(prox, method, args);
                    }
                    return null;
                }
        );
        Method method = proxy.getClass().getMethod("itemDiscount");
        return method.invoke(proxy);
    }

Interestingly, if I open the module, the code works. Why is this? The reflective call is happening within code that exists in the module. What is this error telling me?

timmy
  • 1,752
  • 6
  • 23
  • 35
  • 2
    Use `Coupon.class` instead of `proxy.getClass()`. – Johannes Kuhn Jan 09 '23 at 23:25
  • 3
    This isn’t even a module specific issue. Using `getClass().getMethod(…)` instead of `{baseclass or interface}.class.getMethod(…)` has been wrong since Reflection was introduced, for exactly that reason. You may end up at a `Method` object with an inaccessible declaring class. On the other hand, when you can use `Coupon.class` in this code, you can also use `((Coupon)proxy).itemDiscount()` – Holger Jan 12 '23 at 09:51
  • Thanks @Holger. Yes I can use `Coupon.class` and it works with a closed module. But it is interesting that with the `open` module, the code sample in the question works. – timmy Jan 22 '23 at 02:50
  • I wonder if this is related to the change in Java16 to strongly encapsulate JDK internals? https://www.happycoders.eu/java/java-16-features/#Strongly_Encapsulate_JDK_Internals_by_Default – timmy Jan 22 '23 at 02:53
  • 2
    As said, it is not related to modules at all. `getClass().getMethod(…)` may return an inaccessible method. That’s the fundamental problem of using `getClass().getMethod(…)`. The reasons why the method is inaccessible or the circumstances under which the method happens to be accessible, may vary and are not important. – Holger Jan 23 '23 at 07:45
  • But @Holger, `getClass().getMethod(…)` works if the module is open. JDK 16 made the default mode for modules strongly encapsulated. That's why changing the module encapsulation allows `getClass().getMethod(…)` to work – timmy Jan 23 '23 at 13:58
  • 2
    [Simple example](https://ideone.com/2uFBjH) which can reproduce the problem in all Java versions from 5 to 17. No module system required. `getClass().getMethod(…)` is not the right approach. The reason why the access fails in a particular use case is irrelevant. – Holger Jan 23 '23 at 17:46
  • @Holger you're not reading the full error message in the original question. It clearly references the module system. `(in module jdk.proxy2) because module jdk.proxy2 does not export com.sun.proxy.jdk.proxy2 to module ca.example.MyStore` There error goes away when you either export the package the class is in or define the module as open. Sounds like the following JEP: https://openjdk.org/jeps/396 – timmy Jan 24 '23 at 01:50
  • Also, the example for the `ExecutorService` fails, since the `execute` method is package protected. If you invoke a `public` method via `getClass().getMethod()` it works. The example in the question is invoking a public method. Another counter example: https://ideone.com/MiyBIU – timmy Jan 24 '23 at 02:08
  • 2
    As said, *the reasons why the method is inaccessible or the circumstances under which the method happens to be accessible, may vary and are not important.* `getClass().getMethod(…)` is not the right approach. Point. Stop bothering me with your irrelevant details. – Holger Jan 24 '23 at 07:46
  • It's not an irrelevant detail @Holger. Your answer is wrong. Where does the Java documentation ever say " the circumstances under which the method happens to be accessible, may vary and are not important". You have several examples where `getClass().getMethod(...)` works. You also are not understanding the original example and are not understanding the cause of the `IllegalAccessException` which make it clear that the strong encapsulation that is the default mode of the module system starting with JDK16 is the cause. Please stop bothering people with your irrelevant code snippets. – timmy Jan 25 '23 at 03:43

1 Answers1

0

The reason the example in the question fails is due to changes in JDK 16 that make strong encapsulation of JDK internals the default setting as described in https://openjdk.org/jeps/396.

JDK 9 introduced the module system and to aid in migration, weak encapsulation was the default setting.

Starting with JDK 16, strong encapsulation is the default setting. See the relevant detail in the JEP here as it relates to the example in the question:

In Java 9, we improved both the security and the maintainability of the JDK by leveraging modules to limit access to its internal elements. Modules provide strong encapsulation, which means that 

Code outside of a module can only access the public and protected elements of the packages exported by that module

This is why exporting the package the Coupon class in the module-info.java file allows the above example to work:

exports ca.MyStore.domain.coupons

Alternatively, one can define the module itself as an open module. This works, but is probably not recommended.

timmy
  • 1,752
  • 6
  • 23
  • 35