Hi,
I have a Spring Boot 2 application (Spring Boot v2.0.1.RELEASE, Spring v5.0.5.RELEASE) that uses WebSockets + JQuery + Bootstrap. Basically I had both org.springframework.boot:spring-boot-starter-webflux and org.springframework.boot:spring-boot-starter-websocket in my dependencies.
My complete dependencies were:
compile("org.springframework.boot:spring-boot-starter-webflux") {
exclude(module: "spring-boot-starter-tomcat")
}
compile("org.springframework.boot:spring-boot-starter-undertow")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.apache.commons:commons-lang3")
compile("io.micrometer:micrometer-registry-graphite")
compile("org.springframework.boot:spring-boot-starter-amqp")
compile("org.webjars:bootstrap:3.0.3")
compile("org.webjars:jquery:2.0.3-1")
compile("org.springframework.boot:spring-boot-starter-websocket") {
exclude(module: "spring-boot-starter-tomcat")
}
Today I decided to get rid of usual controllers and WebSockets, and turn to WebFlux routers and Reactive WebSockets. As for libraries, generally I just removed org.springframework.boot:spring-boot-starter-websocket from dependencies.
Surprize! It appeared that static resource serving stopped working at once. OK, I added "org.webjars:webjars-locator:0.34" to classpath, as recommended by docs ("WebJars is also supported via WebJarsResourceResolver and automatically registered when "org.webjars:webjars-locator" is present on the classpath. "), which didn't have any effect (no Webjar mappings are registered on app startup), and then updated my router config to be as follows:
@Configuration
@EnableWebFlux
class MyRouteConfiguration implements WebFluxConfigurer {
......................
@Bean
RouterFunction<ServerResponse> staticResourceRouter() {
return resources("/**", new ClassPathResource("static/"));
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/")
.resourceChain(true)
.addResolver(new WebJarsResourceResolver());
}
}
This has restored the HTMLs loading from static/, but requests to Webjar resources like JQuery JS and Bootstrap CSS end with
28-05-2018 13:23:10.926+00:00 [XNIO-1 I/O-1] ERROR io.undertow.request - UT005071: Undertow request failed HttpServerExchange{ GET /webjars/bootstrap/3.0.3/css/bootstrap.min.css request {Accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8], Accept-Language=[en-US,en;q=0.9], Cache-Control=[max-age=0], Accept-Encoding=[gzip, deflate, br], User-Agent=[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.139 Chrome/66.0.3359.139 Safari/537.36], If-Modified-Since=[Wed, 20 Sep 2017 07:36:54 GMT], Connection=[keep-alive], Cookie=[JSESSIONID=p8bHubML-JHZgEy2311GTl15Uu694Wv1CB_7PHyl], Upgrade-Insecure-Requests=[1], Host=[localhost:12228]} response HttpServerExchange{ GET /webjars/bootstrap/3.0.3/css/bootstrap.min.css request {Accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8], Accept-Language=[en-US,en;q=0.9], Cache-Control=[max-age=0], Accept-Encoding=[gzip, deflate, br], User-Agent=[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.139 Chrome/66.0.3359.139 Safari/537.36], If-Modified-Since=[Wed, 20 Sep 2017 07:36:54 GMT], Connection=[keep-alive], Cookie=[JSESSIONID=p8bHubML-JHZgEy2311GTl15Uu694Wv1CB_7PHyl], Upgrade-Insecure-Requests=[1], Host=[localhost:12228]} response {}}}
java.lang.StackOverflowError: null
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.actual(Operators.java:1274)
at reactor.core.publisher.InnerOperator.currentContext(InnerOperator.java:32)
at reactor.core.publisher.FluxPeek$PeekSubscriber.currentContext(FluxPeek.java:112)
at reactor.core.publisher.InnerOperator.currentContext(InnerOperator.java:32)
at reactor.core.publisher.FluxPeek$PeekSubscriber.currentContext(FluxPeek.java:112)
at reactor.core.publisher.InnerOperator.currentContext(InnerOperator.java:32)
at reactor.core.publisher.FluxPeek$PeekSubscriber.currentContext(FluxPeek.java:112)
........................
That's for existing Webjar resources, I checked this: requests for missing ones end properly with 404.
Crazy! And what fixes this is adding back compile("org.springframework.boot:spring-boot-starter-websocket"). But this breaks the routers, which do not work then :(
Am I missing something, or doing something wrong? I would be very thankful for an advice.
Looks like I'm missing a vital dependency, or it's a bug.
It's hard to understand what's wrong here, since you're not providing a sample application that I can run to reproduce the issue. We've also got tests covering that particular feature.
In the first step, you mentioned that you had spring-boot-starter-websocket as a dependency; this depends on spring-boot-starter-web, so at that point your application was a Spring MVC application. Any WebFluxConfigurer configuration class at that point would not be considered, nor any WebFlux router.
You've later removed that dependency and then you ran into this issue. I'm guessing something is/was turning off the Spring WebFlux auto-configuration.
Note that the Spring Boot reference documentation now recommends adding the following dependency for improved WebJar support (this is a managed dependency, so no need to specify its version):
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator-core</artifactId>
</dependency>
Now, your WebFluxConfigurer class is annotated with @EnableWebFlux, which totally disables the WebFlux auto-configuration in Spring Boot. So static resources support is not configured for you.
Re-adding the websocket starter turns this back into an MVC application, as explained above.
I'm closing this issue since it looks like a StackOverflow question (the Spring Boot team and the wider community are answering questions there).
If you think this is a genuine bug, please reopen this issue with a sample application that reproduces the issue so I can narrow down the problem.
Thanks!
Hi Brian,
Thanks for the comment!
I guess there's no need to write a post at SO, because your suggestion to remove @EnableWebFlux worked like a charm.
However, I must admit that this confuses me. IMO, this is not the behavior one intuitively expects. May I ask the follow-up question here?
Documentation (https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) states:
The WebFlux Java config declares components required to process requests with annotated controllers or functional endpoints, and it offers an API to customize the configuration.
1.10.1. Enable WebFlux config
Use the @EnableWebFlux annotation in your Java config:
@Configuration
@EnableWebFlux
public class WebConfig {
}
The above registers a number of Spring WebFlux infrastructure beans also adapting to dependencies available on the classpath — for JSON, XML, etc.
1.10.2. WebFlux config API
In your Java config implement the WebFluxConfigurer interface:
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
// Implement configuration methods...
}
So, first of all, there's no mention that @EnableWebFlux for a config class that optionally implements WebFluxConfigurer disables the auto-configuration, and you have to configure the static resource loading manually.
Secondly - I see that router-powered endpoints also work without @EnableWebFlux. What's the purpose of this annotation then?
Sorry, if I'm missing something obvious.
You're quoting the Spring Framework reference documentation. The Spring Boot reference documentation states otherwise. Please ask further questions on StackOverflow - we're not just asking that to annoy you, but rather because Q&A works better on StackOverflow and it's more likely to help other community members.
Most helpful comment
You're quoting the Spring Framework reference documentation. The Spring Boot reference documentation states otherwise. Please ask further questions on StackOverflow - we're not just asking that to annoy you, but rather because Q&A works better on StackOverflow and it's more likely to help other community members.