Given the way that @Conditional(NotReactiveWebApplicationCondition.class) is designed in RestTemplateAutoConfiguration,
if we have both spring-web and spring-webflux dependencies, the condition evaluates to false, and rest template is not autconfigured. This happened to me simply because I am importing prometheus-rsocket-spring which contains webflux as dependency
This did not happen in previous versions, I just updated to 2.6. I tried to disabled it by means of spring.main.web-application-type: servlet but it still does not configure it
Comment From: mhalbritter
Hello, I can't reproduce this. I've added spring-boot-starter-web and prometheus-rsocket-spring to my Spring Boot 2.6.6 application and RestTemplateBuilder is still auto-configured.
If you'd like us to spend some time investigating, please take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.
Comment From: nightswimmings
Thanks a lot @mhalbritter, sorry for stealing your time I created a project from zero with my description and indeed, I could not reproduce. But:
My app is a more complex big app, and it is SERVLET based as opposed to REACTIVE (uses resttemplate instead of webclient, for instance), but it is not a web app because it does not bring up a web context (so I define it as spring.main.web-application-type:NONE)
I had to remove the spring-web-starter from the sample and set spring.main.web-application-type=NONE/SERVLET, and then I can reproduce it. The problem is not on the condition itself. The issue happens because spring.main.web-application-type is not being correctly parsed in SpringBootTestContextBootstrapper.getWebApplicationType, so if you don't have certain web dependencies but you have flux ones, it fallbacks to REACTIVE identitfication. If you did the test and replace the spring-web-starter for a bare spring-boot-starter and then set up spring.main.web-application-type to NONE or Servlet, you will see that resttemplatebuilder fails context if required, becasue following line is not managing to bind the property value but null (its in application.properties)
binder.bind("spring.main.web-application-type", Bindable.of(WebApplicationType.class)) -> null
Would you like me to attach a sample or provided you already did it prefer just change the spring-web-starter by the spring-boot-starter and retry?
I am using 2.6.4 and JDK17 BTW
Comment From: wilkinsona
Sounds like there are some similarities with #29170 and #29695.
Comment From: nightswimmings
@wilkinsona I was looking for an issue with this binder.bind("spring.main.web-application-type", Bindable.of(WebApplicationType.class)) -> null but I am surprised I could not find anything. From my ignorance, but being such a low-level line, it totally looks like if Bindable stopped being able to parse the enum. I get 2 DataObjectBinders in the binding loop, JavaBeanBinder and ValueObjectBinder and both return null when trying to bind the app
Comment From: mhalbritter
When including spring-boot-starter-web, then RestTemplateBuilder is available as a bean and the auto-configuration report looks like this:
RestTemplateAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.client.RestTemplate' (OnClassCondition)
- NoneNestedConditions 0 matched 1 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication not a reactive web application (RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition)
When not including spring-boot-starter-web, then RestTemplateBuilder isn't available as a bean and the auto-configuration report looks like this:
RestTemplateAutoConfiguration:
Did not match:
- NoneNestedConditions 1 matched 0 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication found ConfigurableReactiveWebEnvironment (RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition)
Matched:
- @ConditionalOnClass found required class 'org.springframework.web.client.RestTemplate' (OnClassCondition)
RestTemplate is on the classpath in both cases, as spring-boot-starter-webflux (which is pulled in by prometheus-rsocket-spring) has a dependency on org.springframework:spring-web.
When starter-web is on the classpath, org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplication returns match = false, while it returns true if starter-web is not on the classpath.
Maybe the OnWebApplicationCondition should respect the setting of spring.main.web-application-type?
Comment From: mhalbritter
Huh, it already does. When not including spring-boot-starter-web, i can set spring.main.web-application-type=NONE and I get the RestTemplateBuilder bean. If I set it to REACTIVE, I won't get a bean.
@nightswimmings You should be able to fix your problem by setting spring.main.web-application-type=NONE, even with spring-boot-starter-webflux on the classpath, this will auto-configure a RestTemplateBuilder for you (at least in non-test code).
Comment From: nightswimmings
Thanks for confirming the expected behavior, @mhalbritter. I have the feeling that this is related to Java17. Are you using it for your tests?
Comment From: nightswimmings
Ok, it is not JDK-related (tested in 8,11,15,17). I can make it work if I set the property as a @SpringBootTest(property) but not on src/main/resources or src/test/resources both through .properties or .yml. I am making sure it is not an IDE thing and property files end up in classes / test-classes, and a mvn verify fails directly through the CLI. It is failing both when injecting it in a @SpringBootTest or a main code class
Comment From: nightswimmings
@mhalbritter Here is the sample I am using resttemplatebug.zip
Comment From: mhalbritter
Thanks for the reproducer. There's indeed a bug.
spring:
main:
web-application-type: none
This YAML content is ignored when running tests via @SpringBootTest. It doesn't matter if the YAML is in src/main/resources or src/test/resources. That's why the main method runs without problems, but the tests do not.
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplication returns true in tests if this is in the YAML, because context.getResourceLoader() is a org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext.
But when using @SpringBootTest(properties = "spring.main.web-application-type=NONE"), then org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition#isReactiveWebApplication returns false in tests, as context.getResourceLoader() is a org.springframework.context.annotation.AnnotationConfigApplicationContext. And then the RestTemplateBuilder bean is available.
This could be related to https://github.com/spring-projects/spring-boot/issues/29170, but I'm not sure if it's the same bug. @wilkinsona what do you think?
Comment From: wilkinsona
I'm not 100% sure that it's the same bug, but there are some strong similarities. It's also very similar to https://github.com/spring-projects/spring-boot/issues/29169 which we thought we'd fixed.
Comment From: mhalbritter
Looks like #29169 isn't fixed :/ The reproducer is using the newest Spring Boot 2.6.6. I can reproduce it in 2.5.12, too.
Comment From: mhalbritter
Hey @nightswimmings, thanks for the report. I've edited the title of your issue and marked it as a bug.
Comment From: wilkinsona
Looks like https://github.com/spring-projects/spring-boot/issues/29169 isn't fixed
It appears to be a bit more subtle than that. The reproducer for 29169 fails with 2.5.8 and earlier and passes with 2.5.9 and later. It looks like we successfully fixed the specific problem reported in #29169 but there's a broader problem. I guess that's not surprising given that we've also got https://github.com/spring-projects/spring-boot/issues/29170 and https://github.com/spring-projects/spring-boot/issues/29695 to consider.
Comment From: wilkinsona
This is the same problem as #29170. The problem is that we configure the SpringApplication with an ApplicationContextFactory that always returns a particular type of context based on the context type that's been deduced at that time. If the web application type is then changed by configuration property binding, the factory produces a context of the wrong type. We see the problem here where the context ends up being a reactive web context which prevents creation of the RestTemplateBuilder and in #29170 where the context ends up being a servlet web context when it should have been a reactive web context.