Search code examples
javaspring-mvctomcat7long-pollinginternal-server-error

Tomcat 7 server error after 10 seconds when long polling


I'm trying to achieve long-polling in Java web application. The application is written using Spring MVC 3.2 and I make use of DeferredResult object (it represents Servlet 3.0 async response capability).

The problem is that when using DeferredResult I get internal server error (code 500) after 10 seconds every time I send request. Expected result is text data after 30 seconds. There is nothing in my app logs, nothing in Tomcat logs but in FireFox network monitor I see this 500 error.

On the other hand when I send sync request and just hold it for 30 seconds in server it completes successfully.

Can anyone help me with this?

There is a test application download link at the end of this post. It is a maven application.

Here is my app code:

ResponseController.java

@Controller
@RequestMapping("responses")
public class ResponseController {

    @Autowired
    private ResponseService messagesService;

    @RequestMapping(value="/async", method=RequestMethod.GET)
    @ResponseBody
    public DeferredResult<String> getAsyncUpdate() {
        return messagesService.getAsyncUpdate();
    }

    @RequestMapping(value="/sync", method=RequestMethod.GET)
    @ResponseBody
    public String getSyncUpdate() {
        return messagesService.getSyncUpdate();
    }

}

ResponseService.java

@Service
public class ResponseService {

    private DeferredResult<String> deferredResult;

    public DeferredResult<String> getAsyncUpdate(){
        deferredResult = new DeferredResult<String>();
        return deferredResult;
    }

    public String getSyncUpdate(){
        long startTime = System.currentTimeMillis();
        while( System.currentTimeMillis() - startTime <30000){

        }
        return "RESULT";
    }

    @Scheduled(fixedDelay=500)
    public void refresh(){
        if(deferredResult != null){
            long startTime = System.currentTimeMillis();
            while( System.currentTimeMillis() - startTime <30000){

            }
            deferredResult.setResult("RESULT");
        }
    }

}

mvc-dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/mvc     
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/task 
        http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <context:component-scan base-package="pl.prv.mra.test.app" />
    <context:annotation-config/>

    <aop:aspectj-autoproxy/>

    <mvc:annotation-driven >
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>

    <task:annotation-driven/>

    <mvc:resources location="/resources/**" mapping="/resources/**"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

JQuery request for async response

$.ajax({
    url : "responses/async",
    type : "GET",
    timeout : 60000,
    dataType : "json",
    success : function(){
        alert('SUCCESS');
    },
    error : function(){
        alert('FAILURE');
    }
});

JQuery request for sync response

$.ajax({
    url : "responses/sync",
    type : "GET",
    timeout : 60000,
    dataType : "json",
    success : function(){
        alert('SUCCESS');
    },
    error : function(){
        alert('FAILURE');
    }
});

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>test-app</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>

Tomcat server.xml file

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.core.AprLifecycleListener"
        SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JasperListener" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
            type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved"
            factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
            pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>

    <Service name="Catalina">

        <Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000" redirectPort="8443" />

        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

        <Engine name="Catalina" defaultHost="localhost">

            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                    resourceName="UserDatabase" />
            </Realm>

            <Host name="localhost" appBase="webapps" unpackWARs="true"
                autoDeploy="true">

                <Valve className="org.apache.catalina.valves.AccessLogValve"
                    directory="logs" prefix="localhost_access_log." suffix=".txt"
                    pattern="%h %l %u %t &quot;%r&quot; %s %b" />

            </Host>
        </Engine>
    </Service>
</Server>

My environment configuration

  • OS: Linux Mint 16

  • Server: Apache Tomcat 7.0.55

  • Java: Java 1.7.0_67

Test application download link

UPDATE 1


Here is a response data:

Response headers:

Connection: "close"

Content-length: "0"

Date : "Mon, 27 Oct 2014 19:48:10 GMT"

Server : "Apache-Coyote/1.1"

UPDATE 2


Here are my application logs. I've configured logging level to ALL. Unfortunatelly there is no error in them at all. (I did the same with tomcat logs with the same result):

2014-11-01 15:33:09 DEBUG RequestMappingHandlerMapping:220 - Looking up handler method for path /responses/async
2014-11-01 15:33:09 TRACE RequestMappingHandlerMapping:264 - Found 1 matching mapping(s) for [/responses/async] : [{[/responses/async],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}]
2014-11-01 15:33:09 DEBUG RequestMappingHandlerMapping:227 - Returning handler method [public org.springframework.web.context.request.async.DeferredResult<java.lang.String> pl.prv.mra.test.app.controllers.ResponseController.getAsyncUpdate()]
2014-11-01 15:33:09 DEBUG DefaultListableBeanFactory:246 - Returning cached instance of singleton bean 'responseController'
2014-11-01 15:33:09 TRACE DispatcherServlet:1122 - Testing handler adapter [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@7e036b3]
2014-11-01 15:33:09 DEBUG DispatcherServlet:912 - Last-Modified value for [/test-app/responses/async] is: -1
2014-11-01 15:33:09 TRACE HandlerMethod:129 - Invoking [getAsyncUpdate] method with arguments []
2014-11-01 15:33:09 TRACE HandlerMethod:135 - Method [getAsyncUpdate] returned [org.springframework.web.context.request.async.DeferredResult@4810881c]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@2a426218] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.method.annotation.ModelMethodProcessor@6c362b54] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@5856cf4c] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@2b43b3e3] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@deae877] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:09 TRACE HandlerMethodReturnValueHandlerComposite:78 - Testing if return value handler [org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@3943e3c4] supports [org.springframework.web.context.request.async.DeferredResult<java.lang.String>]
2014-11-01 15:33:10 DEBUG WebAsyncManager:429 - Concurrent handling starting for GET [/test-app/responses/async]
2014-11-01 15:33:10 TRACE DispatcherServlet:1028 - Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@655d2842
2014-11-01 15:33:10 DEBUG DispatcherServlet:963 - Leaving response open for concurrent processing
2014-11-01 15:33:10 TRACE XmlWebApplicationContext:332 - Publishing event in WebApplicationContext for namespace 'mvc-dispatcher-servlet': ServletRequestHandledEvent: url=[/test-app/responses/async]; client=[127.0.0.1]; method=[GET]; servlet=[mvc-dispatcher]; session=[55143812B6586F66C0001D3F41B1947C]; user=[null]; time=[13ms]; status=[OK]
2014-11-01 15:33:10 TRACE XmlWebApplicationContext:332 - Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/test-app/responses/async]; client=[127.0.0.1]; method=[GET]; servlet=[mvc-dispatcher]; session=[55143812B6586F66C0001D3F41B1947C]; user=[null]; time=[13ms]; status=[OK]

Solution

  • Ok. I've found a solution.

    In Tomcat configuration (in server.xml file) there is a HTTP/1.1 connector. I tought that connectionTimeout parameter defines timeout for every connections but it does not.

    For asynchronous connections (like in my case) there is separate parameter called asyncTimeout. It's default value is 10 seconds.

    Setting it like this:

    <Connector port="8080" protocol="HTTP/1.1" asyncTimeout="60000" connectionTimeout="20000" redirectPort="8443" />
    

    Solved my problem.