6

I have a problem with Spring Boot configuration. I got exception on some mobile devices which should use sockets:

java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container
    at org.springframework.util.Assert.isTrue(Assert.java:65)
    at org.springframework.http.server.ServletServerHttpAsyncRequestControl.<init>(ServletServerHttpAsyncRequestControl.java:59)
    at org.springframework.http.server.ServletServerHttpRequest.getAsyncRequestControl(ServletServerHttpRequest.java:202)
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.handleInitialRequest(AbstractHttpSockJsSession.java:202)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequestInternal(AbstractHttpSendingTransportHandler.java:66)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequest(AbstractHttpSendingTransportHandler.java:58)
    at org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService.handleTransportRequest(TransportHandlingSockJsService.java:254)
    at org.springframework.web.socket.sockjs.support.AbstractSockJsService.handleRequest(AbstractSockJsService.java:322)
    at org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler.handleRequest(SockJsHttpRequestHandler.java:88)
    at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    ...

As I see from the exception, I have to enable async processing by adding true into the web.xml file. But this is the problem because I don't have it - our project uses Spring Boot.

Is there any way to provide the same functionality in Spring Boot like does?

4 Answers 4

16

You just need to define dispatcherServlet @Bean:

@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServlet() {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
            new DispatcherServlet(), "/");
    registration.setAsyncSupported(true);
    return registration;
}

It overrides that default one from DispatcherServletAutoConfiguration.

Sign up to request clarification or add additional context in comments.

2 Comments

If it complains about raw types, write ServletRegistrationBean<DispatcherServlet> instead of ServletRegistrationBean.
Or just DispatcherServletRegistrationBean. At the time I wrote this answer there was no that generic type on the ServletRegistrationBean.
4

Even though I am late to the party I am posting my working solution for posterity.

I had faced the same issue and tried the solution suggested by @Artem Bilan. But after debugging the code, I came to know that the servletRegistrationBean.isAsyncSupported() is by default true.

enter image description here.

The actual error is being generated by the code block is from the method org.springframework.web.context.request.async.StandardServletAsyncWebRequest.startAsync() and we are getting this error when getRequest().isAsyncSupported() is false. Despite the ServletRegistrationBean#asyncSupported value is true, HttpServletRequest#isAsyncSupported() method value is always set to false for all API requests.

The Exerpt of the code block Code Reference

Assert.state(getRequest().isAsyncSupported(),
                "Async support must be enabled on a servlet and for all filters involved " +
                "in async request processing. This is done in Java code using the Servlet API " +
                "or by adding \"<async-supported>true</async-supported>\" to servlet and " +
                "filter declarations in web.xml.");

When analyzing further, I came to know that requestAttributes -> Globals.ASYNC_SUPPORTED_ATTR decides whether the request has to be processed in an Async way or not.

During the normal scenario, the requests do not have Globals.ASYNC_SUPPORTED_ATTR requestAttribute, hence request.asyncSupported will be false, if we manually add that requestAttribute ASYNC_SUPPORTED_ATTR value as true for all the requests using something like filters then request#asyncSupported value will be set to true as per the below code block Code Reference

specialAttributes.put(Globals.ASYNC_SUPPORTED_ATTR,
                new SpecialAttributeAdapter() {
                    @Override
                    public Object get(Request request, String name) {
                        return request.asyncSupported;
                    }

                    @Override
                    public void set(Request request, String name, Object value) {
                        Boolean oldValue = request.asyncSupported;
                        request.asyncSupported = (Boolean)value;
                        request.notifyAttributeAssigned(name, value, oldValue);
                    }
                });

Though there is no explicit answer, I have implemented a workaround for this issue. To Summarize all the required changes are listed below:

  1. Add a filter to set the request attribute as request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)

@Component
public class AsyncSupportFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true);
        chain.doFilter(request, response);
    }
}

  1. Add filter to into your filter chain, in my case I have registered my filter into the spring context as below.

@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   @Autowired
   private AsyncSupportFilter asyncSupportFilter = null;
   @Override
   public void configure(HttpSecurity httpSecurity) {

       httpSecurity.addFilter(asyncSupportFilter);
       // we can add the filter before any filter like as httpSecurity.addFilterBefore(asyncSupportFilter , BasicAuthenticationFilter.class);
   }
}

1 Comment

I used this filter and it solved my problem. Note that, I didn't add the filter into the filter chain. Spring is handling that automatically.
2

Maybe it worth to mention that Spring Boot has async-support enabled by default for dispatcher servlet and all filters

Quotation from github:

We automatically set async-supported to true because the DispatcherServlet knows how to work with such requests and it's basically up to individual controller methods to return something like a DeferredResult. Furthermore, we also set async-supported for any filter (provided via getServletFilters) along with setting the DispatcherType for each filter to include ASYNC.

At least my practice supports this statement

5 Comments

Does that practice include deploying Spring app on tomcat and not standalone app?
@Antoniossss that my answer was made during a migration project of a monolith spring (not spring boot) application to a set of standalone spring boot services. Previous, monolithic, non-spring boot version of the app had to turn on async-supported explicitly as far as I remember.
This turns out not to be true.
The behavior has changed in Spring Boot? The abovementioned statement was made by Spring Boot authors in 2014.....
I say it too but too but it was just not wrorking. We had to ass something like @EnableAsync somewhere - cannot remember exactly, but it was a matter of singe annotation (that does not relates to this at first glance)
0

mysolution:

@WebFilter(value = {"/subscriberinfo/query/single/*"})
public class ...Filter{
} 

TO

@WebFilter(value = {"/subscriberinfo/query/single/*"}, asyncSupported = true)
public class ...Filter{
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.