Skip to content
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

RuntimeHintsUtils#registerAnnotation fails to register SynthesizedAnnotation proxy #28767

Closed
mhalbritter opened this issue Jul 6, 2022 · 5 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing type: bug A general bug
Milestone

Comments

@mhalbritter
Copy link
Contributor

mhalbritter commented Jul 6, 2022

I've encountered a use-case in which RuntimeHintsUtils.registerAnnotation isn't enough to work with the annotation in a native setting. I'm using this code:

RuntimeHintsUtils.registerAnnotation(hints, Endpoint.class);

where Endpoint is org.springframework.boot.actuate.endpoint.annotation.Endpoint:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective
public @interface Endpoint {

	String id() default "";

	boolean enableByDefault() default true;

}

There are no @AliasFor annotations used, so RuntimeHintsUtils.registerAnnotation won't register a proxy for Endpoint and SynthesizedAnnotation.

But if Endpoint is now used in this code:

String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.applicationContext, Endpoint.class);

it fails with

Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface org.springframework.boot.actuate.endpoint.annotation.Endpoint, interface org.springframework.core.annotation.SynthesizedAnnotation] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
	at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89) ~[na:na]
	at com.oracle.svm.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:158) ~[na:na]
	at java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:48) ~[actuator-aot:na]
	at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1037) ~[actuator-aot:na]
	at org.springframework.core.annotation.SynthesizedMergedAnnotationInvocationHandler.createProxy(SynthesizedMergedAnnotationInvocationHandler.java:305) ~[na:na]
	at org.springframework.core.annotation.TypeMappedAnnotation.createSynthesizedAnnotation(TypeMappedAnnotation.java:333) ~[na:na]
	at org.springframework.core.annotation.AbstractMergedAnnotation.synthesize(AbstractMergedAnnotation.java:210) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.core.annotation.AbstractMergedAnnotation.synthesize(AbstractMergedAnnotation.java:200) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(DefaultListableBeanFactory.java:733) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(DefaultListableBeanFactory.java:723) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForAnnotation(DefaultListableBeanFactory.java:693) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.context.support.AbstractApplicationContext.getBeanNamesForAnnotation(AbstractApplicationContext.java:1301) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(BeanFactoryUtils.java:285) ~[na:na]
	at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.createEndpointBeans(EndpointDiscoverer.java:130) ~[actuator-aot:3.0.0-SNAPSHOT]
	at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.discoverEndpoints(EndpointDiscoverer.java:123) ~[actuator-aot:3.0.0-SNAPSHOT]
	at org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer.getEndpoints(EndpointDiscoverer.java:117) ~[actuator-aot:3.0.0-SNAPSHOT]
	at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.servletEndpointRegistrar(ServletEndpointManagementContextConfiguration.java:61) ~[actuator-aot:0.0.1-SNAPSHOT]
	at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration_WebMvcServletEndpointManagementContextConfiguration__BeanDefinitions.lambda$getServletEndpointRegistrarInstance$0(ServletEndpointManagementContextConfiguration_WebMvcServletEndpointManagementContextConfiguration__BeanDefinitions.java:45) ~[na:na]
	at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51) ~[actuator-aot:6.0.0-SNAPSHOT]
	at org.springframework.beans.factory.aot.AutowiredInstantiationArgumentsResolver.resolve(AutowiredInstantiationArgumentsResolver.java:156) ~[na:na]
	at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration_WebMvcServletEndpointManagementContextConfiguration__BeanDefinitions.getServletEndpointRegistrarInstance(ServletEndpointManagementContextConfiguration_WebMvcServletEndpointManagementContextConfiguration__BeanDefinitions.java:45) ~[na:na]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1224) ~[actuator-aot:6.0.0-SNAPSHOT]
	... 50 common frames omitted

It seems like @AliasFor isn't the only thing which should trigger proxy creation.

At the moment I'm working around this with:

hints.proxies().registerJdkProxy(Endpoint.class, SynthesizedAnnotation.class);
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 6, 2022
@sbrannen sbrannen added theme: aot An issue related to Ahead-of-time processing in: core Issues in core modules (aop, beans, core, context, expression) labels Jul 6, 2022
@sbrannen
Copy link
Member

sbrannen commented Jul 6, 2022

I've encountered a use-case in which RuntimeHintsUtils.registerAnnotation isn't enough to work with the annotation in a native setting.

That's to be expected. RuntimeHintsUtils.registerAnnotation can only reason about the annotation itself and meta-annotations in the meta-annotation hierarchy above the supplied annotation type.

If @Endpoint is used as a meta-annotation with attribute overrides in a composed annotation, you should actually invoke RuntimeHintsUtils.registerAnnotation with the composed annotation instead of Endpoint.class. Then RuntimeHintsUtils.registerAnnotation should automatically register the correct proxy class configuration for @Endpoint.

@sbrannen
Copy link
Member

sbrannen commented Jul 6, 2022

One concrete example is @WebEndpoint whose attributes are annotated with @AliasFor(annotation = Endpoint.class), which in turn requires that @Endpoint by synthesizable (i.e., will need proxy configuration).


In summary, this effectively "works as designed", but I'll leave it open in order for the team to put more thought into it and potentially to improve the documentation to point out what I explained above.

@sbrannen sbrannen added this to the Triage Queue milestone Jul 6, 2022
@mhalbritter
Copy link
Contributor Author

Ah, I see. Thanks for the explanation.

@mhalbritter
Copy link
Contributor Author

mhalbritter commented Jul 6, 2022

If @Endpoint is used as a meta-annotation with attribute overrides in a composed annotation, you should actually invoke RuntimeHintsUtils.registerAnnotation with the composed annotation instead of Endpoint.class. Then RuntimeHintsUtils.registerAnnotation should automatically register the correct proxy class configuration for @Endpoint

When I register WebEndpoint, there will be no proxy hints generated for Endpoint, only for WebEndpoint.

@sbrannen
Copy link
Member

sbrannen commented Jul 6, 2022

When I register WebEndpoint, there will be no proxy hints generated for Endpoint, only for WebEndpoint.

Ahhh... then that's likely a bug.

We'll look into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) theme: aot An issue related to Ahead-of-time processing type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants