Reputation: 1089
I created an async MVC application using Spring 4.0.5 and Servlet API 3.1.0. The async behavior works well in Jetty 8.0 using Firefox 24 but I cannot get it to work in Tomcat 8.0 and Firefox 24. I'm using DeferredResult to manage the async requests. Any idea what I'm missing? It mus be some Tomcat setting or something in the web.xml since the exact same Java code works well in Jetty.
When the async request finally has result and supposedly writes to the response, I see the following messages logged:
WebAsyncManager - Dispatching request to resume processing
RequestResponseBodyMethodProcessor - Written [true] as "application/json" using MappingJacksonHttpMessageConvertor
DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'app': assuming HandlerAdapter completed request handling
DispatcherServlet - Successfully completed request
The long running request never comes back to my browser, and eventually i see this timeoutout error in the Tomcat log:
CoyoteAdapter.asyncDispatch Exception while processing an asynchronous request java.lang.IllegalStateException: Calling [asyncTimeout()] is not valid for a request with Async state [Dispatching]
--server.xml--
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxConnections="100"
maxThreads="100"
connectionTimeout="150000"
asyncTimeout="150000" />
--Tomcat web.xml--
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
</web-app>
--Spring web-app web.xml--
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>my-async-app</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/app-config.xml</param-value>
</context-param>
<!-- Handles all requests into the application -->
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/controllers-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<!-- Maps all /app requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
Upvotes: 2
Views: 5673
Reputation: 1715
I solved (worked around) this by setting the following in my application.properties:
spring.mvc.async.request-timeout=-1
This option sets the amount of time (in milliseconds) before asynchronous request handling times out. Setting it to -1 makes this unlimited.
Be careful with this if you are dealing with many requests, as this can exhaust the request handling threads rather easily.
Upvotes: 0
Reputation: 3154
This issue is related to a bug in Tomcat described here, here and here.
Possible solutions:
Override the HttpServlet as suggested here:
public class AsyncServlet extends HttpServlet {
protected void doGet(final HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.isAsyncStarted()) {
response.getWriter().write("asyncResult=" + request.getAttribute("asyncResult"));
} else {
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.addListener(new AsyncListener() {
public void onTimeout(AsyncEvent event) throws IOException {
request.setAttribute("asyncResult", "timeout\n");
asyncContext.dispatch();
}
public void onStartAsync(AsyncEvent event) throws IOException {
}
public void onError(AsyncEvent event) throws IOException {
}
public void onComplete(AsyncEvent event) throws IOException {
}
});
asyncContext.setTimeout(5000L);
}
}
}
Upvotes: 3