dschulten
dschulten

Reputation: 3122

spring-integration: Webflux inboundGateway not mapped to request

I have set up a sample application with an /sse webflux resource which produces text/event-stream.

When I try to call the resource using curl, I get a 404 when I request without Accept header and a 406 when I request with Accept header as follows:

curl -v http://localhost:8080/events
curl -H "Accept: text/event-stream" -v http://localhost:8080/events

I cannot make it work so far.

I use the following example code for the Webflux.inboundGateway (alongside other Http.inboundGateway flows which work fine):

@SpringBootApplication
@EnableIntegration
@EnableWebFlux
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean
  public IntegrationFlow sseFlow() {
      return IntegrationFlows
        .from(WebFlux.inboundGateway("/sse")
                .requestMapping(m -> m.produces(MediaType.TEXT_EVENT_STREAM_VALUE)))
        .handle((p, h) -> Flux.just("foo", "bar", "baz"))
        .get();
  }

  // ... more integration flows, but with Http.inboundGateway

In my pom.xml I have these webflux related dependencies:

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-webflux</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

At startup I get a message from the WebfluxIntegrationRequestMappingHandlerMapping that it has mapped /sse (it only says xIntegrationRequestMappingHandlerMapping because the class name is so long, fortunately we see the x):

2018-05-05 08:08:27.269  INFO 45160 --- [  restartedMain] xIntegrationRequestMappingHandlerMapping : Mapped "{[/sse],methods=[GET || POST],produces=[text/event-stream]}" onto public abstract reactor.core.publisher.Mono<java.lang.Void> org.springframework.web.server.WebHandler.handle(org.springframework.web.server.ServerWebExchange)

Now for the interesting part. In the following debug log you can see that the application apparently does not consider the WebfluxIntegrationRequestMappingHandlerMapping, but only other HandlerMappings. As a result it chooses the ResourceHttpRequestHandler instead.

This happens when calling without Accept header:

2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/sse]
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-04 15:59:12.281 DEBUG 36196 --- [nio-8080-exec-3] .IntegrationRequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] .IntegrationRequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/sse] are [/**]
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/sse] are {}
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/sse] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@2b3f154f]]] and 1 interceptor
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/sse] is: -1
2018-05-04 15:59:12.282 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2018-05-04 15:59:12.283 DEBUG 36196 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2018-05-04 15:59:12.284 DEBUG 36196 --- [nio-8080-exec-3] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'basicErrorController'
2018-05-04 15:59:12.284 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/error] is: -1
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{timestamp=Fri May 04 15:59:12 CEST 2018, status=404, error=Not Found, message=No message available, path=/sse}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@7d49dc3e]
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-04 15:59:12.286 DEBUG 36196 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Successfully completed request

When called with Accept header, I see an additional warning Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation in the log while the /error resource is handled:

2018-05-05 07:47:19.356 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Bound request context to thread: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] .IntegrationRequestMappingHandlerMapping : Looking up handler method for path /sse
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] .IntegrationRequestMappingHandlerMapping : Did not find handler method for [/sse]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/sse] are [/**]
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/sse] are {}
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/sse] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@2b3f154f]]] and 1 interceptor
2018-05-05 07:47:19.357 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/sse] is: -1
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2018-05-05 07:47:19.358 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter  : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@6b1c36b4
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'basicErrorController'
2018-05-05 07:47:19.359 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/error] is: -1
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .w.s.m.a.ResponseStatusExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolving exception from handler [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360  WARN 36196 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2018-05-05 07:47:19.360 DEBUG 36196 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Successfully completed request

Why does Spring not consider the WebfluxIntegrationRequestMappingHandlerMapping? Or what else goes wrong here?

Upvotes: 1

Views: 1521

Answers (1)

dschulten
dschulten

Reputation: 3122

You have to decide whether you run a reactive application or an mvc application. When both spring-boot-starter-web and spring-boot-starter-webflux are defined as dependencies on the classpath, Spring Boot configures a Spring MVC application by default. That was my problem.

I had to remove spring-boot-starter-web and spring-integration-http so that Spring Boot starts Netty, not Tomcat. An alternative is to tell Spring explicitly to do that:

public static void main(String[] args) {
    SpringApplication application = new SpringApplication(WebfluxApplication.class);
    application.setWebApplicationType(WebApplicationType.REACTIVE);
    application.run(WebfluxApplication.class, args);
}

Mixing spring-integration-http and spring-integration-webflux Webflux.inboundGateway in one application seems not possible, you have to create two separate applications, one which starts Tomcat for servlet requests and another one which starts Netty.

Possible Workaround if you do not want to switch to a reactive application: How to create a Spring Reactor Flux from Http integration flow? shows how to route incoming http POSTS to a reactive publisher and provide them as events via an @GetMapping MVC resource using SSE.

Also see https://github.com/spring-projects/spring-boot/issues/11025.

Upvotes: 1

Related Questions