spring-attic / spring-native

Spring Native is now superseded by Spring Boot 3 official native support
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html
Apache License 2.0
2.74k stars 356 forks source link

WebMvcAutoConfigurationAdapter has null ServletContext, due to org.springframework.cloud:spring-cloud-starter-bootstrap #1511

Closed hdeadman closed 2 years ago

hdeadman commented 2 years ago

I am getting the following error where the servlet context isn't set on a bean implementing ServletContextAware when running in java AOT mode:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping': Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No ServletContext set
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[cas-aot.jar!/:2.6.3]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:414) ~[cas-aot.jar!/:2.6.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[cas-aot.jar!/:2.6.3]
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:164) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:195) ~[spring-cloud-context-3.1.0.jar!/:3.1.0]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:114) ~[spring-cloud-context-3.1.0.jar!/:3.1.0]
    at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:77) ~[spring-cloud-context-3.1.0.jar!/:3.1.0]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131) ~[spring-context-5.3.15.jar!/:5.3.15]
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) ~[na:na]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:338) ~[cas-aot.jar!/:2.6.3]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:296) ~[cas-aot.jar!/:2.6.3]
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:164) ~[spring-boot-2.6.3.jar!/:2.6.3]
    at org.apereo.cas.web.CasWebApplication.main(CasWebApplication.java:52) ~[cas-server-webapp-init-6.6.0-SNAPSHOT.jar!/:6.6.0-SNAPSHOT]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[cas.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[cas.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[cas.jar:na]
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[cas.jar:na]
Caused by: java.lang.IllegalStateException: No ServletContext set
    at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.3.15.jar!/:5.3.15]
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:591) ~[spring-webmvc-5.3.15.jar!/:5.3.15]
    at org.springframework.aot.ContextBootstrapInitializer.lambda$initialize$503(ContextBootstrapInitializer.java:1178) ~[cas-aot.jar!/:na]
    at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$ThrowableFunction.apply(BeanDefinitionRegistrar.java:294) ~[spring-native-0.11.3-SNAPSHOT.jar!/:0.11.3-SNAPSHOT]
    at org.springframework.aot.beans.factory.InjectedElementResolver.create(InjectedElementResolver.java:67) ~[spring-native-0.11.3-SNAPSHOT.jar!/:0.11.3-SNAPSHOT]
    at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$BeanInstanceContext.create(BeanDefinitionRegistrar.java:211) ~[spring-native-0.11.3-SNAPSHOT.jar!/:0.11.3-SNAPSHOT]
    at org.springframework.aot.ContextBootstrapInitializer.lambda$initialize$504(ContextBootstrapInitializer.java:1178) ~[cas-aot.jar!/:na]
    at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$ThrowableFunction.apply(BeanDefinitionRegistrar.java:294) ~[spring-native-0.11.3-SNAPSHOT.jar!/:0.11.3-SNAPSHOT]
    at org.springframework.aot.beans.factory.BeanDefinitionRegistrar.lambda$instanceSupplier$0(BeanDefinitionRegistrar.java:115) ~[spring-native-0.11.3-SNAPSHOT.jar!/:0.11.3-SNAPSHOT]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.15.jar!/:5.3.15]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.15.jar!/:5.3.15]
    ... 36 common frames omitted

https://github.com/hdeadman/cas-overlay-template/runs/5342311945?check_suite_focus=true#step:8:5368

Could that be due to eager initialization of beans so that might result from these messages?

2022-02-27 12:57:38.532  INFO 24156 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'CasCoreTicketTransactionConfiguration' of type [org.apereo.cas.config.CasCoreTicketsConfiguration$CasCoreTicketTransactionConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-02-27 12:57:38.574  INFO 24156 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'ticketTransactionManager' of type [org.apereo.cas.authentication.PseudoPlatformTransactionManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-02-27 12:57:38.623  INFO 24156 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'transactionManagementConfigurer' of type [org.apereo.cas.config.CasCoreTicketsConfiguration$CasCoreTicketTransactionConfiguration$$Lambda$1976/0x000000084055e840] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-02-27 12:57:39.569  INFO 24156 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-02-27 12:57:39.763  INFO 24156 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Some of those transaction related beans are internal to the CAS application but integrationChannelResolver is coming from spring integration.

The javadocs for TransactionManagementConfigurer say:

Note that in by-type lookup disambiguation cases, an alternative approach to implementing this interface is to simply mark one of the offending PlatformTransactionManager @Bean methods (or ReactiveTransactionManager @Bean methods) as @Primary. This is even generally preferred since it doesn't lead to early initialization of the TransactionManager bean.

Is using TransactionManagementConfigurer incompatible with spring native?

sdeleuze commented 2 years ago

Please share a minimal repro project where we can see the error on JVM + AOT.

hdeadman commented 2 years ago

In looking closer at the stacktrace I noticed it was likely due to the spring cloud BootstrapApplicationListener class which seems to re-run the SpringApplication. I re-purposed an old reproducer, and the error happens if the implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap' dependency is added.

https://github.com/hdeadman/spring-native-reproducer

You can run ./test-maven.sh --server.port=8080 or ./test-gradle.sh --server.port=8080 to see the error with JVM+AOT. If you remove the spring-cloud-starter-bootstrap dependency from build.gradle or pom.xml then there will not be an error.

If you don't pass in server.port then you will see another error caused by the ServerProperties config class not being available as a bean constructor parameter, the way it is in most spring boot applications (but that is an unrelated issue and the bean is available if you have server.port defined).

You can see the errors here: https://github.com/hdeadman/spring-native-reproducer/actions/runs/1931438614

sdeleuze commented 2 years ago

As documented in https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#support, Spring Cloud Bootstrap is no longer supported with Spring Native 0.11+. We will work on a proper solution in Spring Boot 3 timeframe. if you really need to use it, maybe try with Spring Native 0.10.6.

hdeadman commented 2 years ago

Just to confirm, does "Spring Cloud Bootstrap" refer to all of the code under the org.springframework.cloud.bootstrap package? e.g. PropertySourceLocator etc? Not just the use of the spring-cloud-starter-bootstrap dependency? Is the alternative to use the spring.config.import property?

sdeleuze commented 2 years ago

@OlgaMaciaszek Could you please provide a feedback to @hdeadman ?

OlgaMaciaszek commented 2 years ago

Sure, @sdeleuze; @hdeadman, yes, only the Spring Boot Config Data Import is supported for native, so you'll need to use the spring.config.import property. You can take a look at this native sample.