Skip to content

jdkProxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image. #6079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wangliang181230 opened this issue Feb 27, 2023 · 14 comments
Assignees
Labels
bug native-image spring spring related issue

Comments

@wangliang181230
Copy link

wangliang181230 commented Feb 27, 2023

Jdk proxy object can't get method by the Method Class.getMethod(methodName) in the native-image.

jdkProxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image.

Example project

https://github.com/wangliang181230/example__oracle_graal_issue-6079

Some code

TestInterface.java

public interface TestInterface {

    void foo();

    void bar();

}

ProxyReflectionExample.java

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyReflectionExample {

    public static void main(String[] args) throws Exception {
        TestInterface proxyObj = (TestInterface)Proxy.newProxyInstance(
            ProxyReflectionExample.class.getClassLoader(),
            new Class[] { TestInterface.class },
            (proxy, method, args1) -> {
                System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
                return null;
            });

        try {
            // It will throw `NoSuchMethodException` in `native-image`.
            Method method = proxyObj.getClass().getMethod("foo");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(proxyObj);

            method = proxyObj.getClass().getMethod("bar");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(proxyObj);
        } catch (Throwable t) {
            t.printStackTrace();
        }

        Thread.sleep(10000);
    }
}

reflect-config.json

[
    {
        "name": "TestInterface",
        "methods": [
            { "name": "foo", "parameterTypes": [] },
            { "name": "bar", "parameterTypes": [] }
        ]
    }
]

Console output in JVM

Method: foo,     Declaring Class: jdk.proxy1.$Proxy0
Method: foo,     Declaring Class: TestInterface
Method: bar,     Declaring Class: jdk.proxy1.$Proxy0
Method: bar,     Declaring Class: TestInterface

Error log in native-image

java.lang.NoSuchMethodException: jdk.proxy4.$Proxy60.foo()
        at java.base@17.0.6/java.lang.Class.getMethod(DynamicHub.java:2227)
        at ProxyReflectionExample.main(ProxyReflectionExample.java:17)

Environment and versions:

  1. OS: Windows 10
  2. GraalVM: graalvm-ce-java17-22.3.1 Windows (amd64)

How can I solve this problem?

@wangliang181230 wangliang181230 changed the title Jdk proxy object can't get method by the methodName in native-image Jdk proxy object can't get method in native-image Feb 27, 2023
@wangliang181230 wangliang181230 changed the title Jdk proxy object can't get method in native-image Jdk proxy object can't get method in the native-image Feb 27, 2023
@SergejIsbrecht
Copy link

Do you expect the Proxy to be registered automatically? If not, you would need to register it manually. Please see https://www.graalvm.org/22.0/reference-manual/native-image/DynamicProxy/ -

Also, please add the GraalVM version, which you used.

@wangliang181230 wangliang181230 changed the title Jdk proxy object can't get method in the native-image Jdk proxy object can't get method by the Method Class.getMethod(methodName) in the native-image. Feb 28, 2023
@wangliang181230
Copy link
Author

wangliang181230 commented Feb 28, 2023

Do you expect the Proxy to be registered automatically? If not, you would need to register it manually. Please see https://www.graalvm.org/22.0/reference-manual/native-image/DynamicProxy/ -

My question is not about dynamic proxy, but about the acquisition of dynamic proxy methods.
For example proxyObj.getClass().getMethod(methodName) will throw NoSuchMethodException in native-image.
Of course, I have added the metadata of the interfaces to reflect-config.json and proxy-config.json and successfully created the proxy object in native-image, but the class of the proxy object is dynamic and cannot be added the reflect metadata.

Also, please add the GraalVM version, which you used.

graalvm-ce-java17-22.3.1 Windows (amd64)

@wangliang181230
Copy link
Author

This will cause the semantic inconsistency of proxyObject.getClass().getMethod(methodName) in JVM and native-image.

@SergejIsbrecht
Copy link

Could you maybe add the stacktrace and the config files to this issue?

@wangliang181230
Copy link
Author

wangliang181230 commented Mar 8, 2023

Could you maybe add the stacktrace and the config files to this issue?

I modified the content of this issue. PTAL

@wangliang181230 wangliang181230 changed the title Jdk proxy object can't get method by the Method Class.getMethod(methodName) in the native-image. proxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image. Mar 8, 2023
@wangliang181230 wangliang181230 changed the title proxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image. jdkProxyObj.getClass().getMethod("xxx") will throw java.lang.NoSuchMethodException: jdk.proxy4.$Proxy61.xxx() in the native-image. Mar 8, 2023
@oubidar-Abderrahim oubidar-Abderrahim self-assigned this Mar 8, 2023
@oubidar-Abderrahim
Copy link
Member

Thank you for sharing this, we'll take a look into it shortly

@vjovanov
Copy link
Member

vjovanov commented Mar 12, 2023

This is currently not supported in the metadata. We should allow for proxies to be marked with an interface list:

  {
        "proxy": ["TestInterface"],
        "methods": [
            { "name": "foo", "parameterTypes": [] },
            { "name": "bar", "parameterTypes": [] }
        ]
    }

Until we introduce that feature, a possible workaround is to use initialization at build time. Rewrite the program in the following way:

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface TestInterface {

    void foo();

    void bar();

}

public class ProxyReflectionExample {

    static class ProxyReflectionStatics {
        static final TestInterface proxyObj = (TestInterface) Proxy.newProxyInstance(
                        ProxyReflectionExample.class.getClassLoader(),
                        new Class[]{TestInterface.class},
                        (proxy, method, args1) -> {
                            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
                            return null;
                        });

    }

    public static void main(String[] args) throws Exception {
        try {
            // It will throw `NoSuchMethodException` in `native-image`.
            Method method = ProxyReflectionStatics.proxyObj.getClass().getMethod("foo");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(ProxyReflectionStatics.proxyObj);

            method = ProxyReflectionStatics.proxyObj.getClass().getMethod("bar");
            System.out.println("Method: " + method.getName() + ",     Declaring Class: " + method.getDeclaringClass().getName());
            method.invoke(ProxyReflectionStatics.proxyObj);
        } catch (Throwable t) {
            t.printStackTrace();
        }

        Thread.sleep(10000);
    }
}

and build it with:

native-image ProxyReflectionExample --initialize-at-build-time=ProxyReflectionExample\$ProxyReflectionStatics

@wangliang181230
Copy link
Author

wangliang181230 commented Mar 23, 2023

@vjovanov
However, in many applications, it is not known which interface to proxy.
The above code is only an example I provided.

@sdeleuze
Copy link
Collaborator

Looks like Spring Framework AOP support is impacted by this bug, see this comment for more details. Could this use case be supported out of the box?

@sdeleuze sdeleuze added the spring spring related issue label Jun 19, 2023
@vjovanov
Copy link
Member

We definitely need to make this a priority. @sdeleuze I am not sure what out of the box means here?

@sdeleuze
Copy link
Collaborator

I mean without additonal hints specified via JSON files if possible.

@vjovanov
Copy link
Member

That would mean that we have to register all methods of a proxy for reflection. This would lead to bloating of the image which we try to avoid in all places.

Is there a difference between this JSON entry and other entries? Why would it be hard to specify the entry in JSON?

@sdeleuze
Copy link
Collaborator

Indeed, we should be careful about the footprint.

If we specify metadata explicitly, don't we need all the usual variants (invoke versus introspect, declared versus public versus individual methods)?

Should we make introspect the default with the upcoming new reflection mode?

If we need all the variants, should they be in proxy-config.json, or in reflect-config.json with a list of proxy interfaces instead of a classname (which in this case is not something known before native-image invocation?

Will those new entries be backward compatible with GraalVM 22.3.0 and Jube 2023 releases?

@oubidar-Abderrahim oubidar-Abderrahim removed their assignment Feb 5, 2024
@loicottet
Copy link
Member

Fixed by #8822

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug native-image spring spring related issue
Projects
None yet
Development

No branches or pull requests

6 participants