I am using spring mvc 3.2.4 and jquery 1.9.0 for long polling. My application is deployed on Tomcat 7.0.42. My spring configuration files are as below:
Application Web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee"
version="3.0">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/app-servlet.xml
</param-value>
</context-param>
</web-app>
Spring Configration xml as:-
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:annotation-config/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<context:component-scan base-package="com.webchat"/>
<bean id="defferedResult" class="com.exp.DeferredResultContainer"></bean>
</beans>
Controller for Posting data looks as
@RequestMapping(value = "/postComment", method = RequestMethod.POST)
public @ResponseBody String postComment(HttpServletRequest request) {
deferredResultContainer.updateAllResults(request.getParameter("comment"));
return "success";
}
Deferred Result container class
public class DeferredResultContainer {
private final Set<DeferredResult<String>> deferredResults = Collections.synchronizedSet(new HashSet<DeferredResult<String>>() );
public void put(DeferredResult<String> deferredResult){
deferredResults.add(deferredResult);
}
public void updateAllResults(String value){
for (DeferredResult<String> deferredResult : deferredResults){
deferredResult.setResult(value);
}
}
public void remove(DeferredResult<String> deferredResult){
deferredResults.remove(deferredResult);
}
public int determineSize(){
return deferredResults.size();
}
}
Controller for Deferred Result looks as
@RequestMapping(value = "/getComments", method = RequestMethod.GET)
@ResponseBody
public DeferredResult<String> getComments() throws Exception{
final DeferredResult<String> deferredResult= new DeferredResult<String>();
deferredResultContainer.put(deferredResult);
deferredResult.onTimeout(new Runnable() {
@Override public void run() {
deferredResultContainer.remove(deferredResult);
}
});
deferredResult.onCompletion(new Runnable() {
@Override public void run() {
deferredResultContainer.remove(deferredResult);
}
});
return deferredResult;
}
When I am trying to long poll it through ajax i am getting following response :-
{"setOrExpired":false}
And onCompletion method is also not getting executed.
To Simply things below controller gives perfect response as {"1":"2"}
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public Map test1() throws Exception{
Map m1 = new HashMap<String, Object>();
m1.put("1", "2");
return m1;
}
Once i change it to below and add Deferred result i get response as {"setOrExpired":true}
@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public DeferredResult<Map> test() throws Exception{
DeferredResult<Map> result = new DeferredResult<Map>();
Map m1 = new HashMap<String, Object>();
m1.put("1", "2");
result.setResult(m1);
return result;
}
Polling code
$(document).ready(function() {
longPoll();
function longPoll(){
$.support.cors = true;
var path = "http://localhost:8080/WebChatExp/rest";
$.ajax({
url: path + "/getComments",
cache:false,
success: function(data){
//To Do
alert("Data" + JSON.stringify(data));
},
error: function(err, status, errorThrown ) {
},
type: "GET",
dataType: "json",
complete: longPoll,
timeout: 60000 // timeout every one minute
});
}
I have searched various examples but cannot figure out if any extra configuration is required for deferred result. Please advise.
The response body you are getting
{"setOrExpired":true}
indicates that Spring serialized your DeferredResult
(which has various properties including setOrExpired
) to JSON instead of handling it with a DeferredResultMethodReturnValueHandler
. In other words, it used another HandlerMethodReturnValueHandler
, most likely RequestResponseBodyMethodProcessor
(which handles @ResponseBody
), to handle the value returned from your @RequestMapping
annotated handler method. (The simplest way to test this is to see what happens when you remove the @ResponseBody
annotation.)
Looking at the 3.2.x source code of RequestMappingHandlerAdapter
, which registers the default HandlerMethodReturnValueHandler
instances, the DeferredResultMethodReturnValueHandler
is registered before RequestResponseBodyMethodProcessor
and therefore will handle the DeferredResult
return value first.
Since you're seeing different behavior, we must assume your configuration is not what you've shown here. (Note that <mvc:annotation-driven/>
registers a RequestMappingHandlerAdapter
.)
Also note that you are currently loading the configuration in /WEB-INF/app-servlet.xml
twice, once by the ContextLoaderListener
and once by the DispatcherServlet
.
Get rid of your ContextLoaderListener
and the corresponding context-param
entirely. They are not needed in your example.
I know is impossible to be the problem in this case (spring mvc 3.2.4) but just for future references: I get the same issue with spring boot 2.2.0.BUILD-SNAPSHOT (spring 5.1.5). This error happens when you try to return DeferredResult using webflux instead of the traditional spring mvc app. Remember, in the new webflux api you should use mono/flux, not DeferredResult.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With