Reverse ajax using DeferredResult of Spring 3.2.0.

2020-07-29 17:32发布

问题:

I am trying to create a demo of Group Chat using reverse ajax in Spring. I am using Spring 3.2.0.RELEASE version.

I am using DeferredResult to perform reverse ajax in my controller. Following is the snippet of my Controller class.

@Autowired
private AsyncRepository asyncRepository;

Map<Integer, List<DeferredResult<String>>> watchers = new ConcurrentHashMap<Integer, List<DeferredResult<String>>>();

@RequestMapping(value="/asyncRequest/getMessages/{id}", method=RequestMethod.GET)
@ResponseBody
public DeferredResult<String> getMessages(final @PathVariable("id") Integer id){
    final DeferredResult<String> deferredResult = new DeferredResult<String>(null, Collections.emptyList());

    if(watchers.containsKey(id)) {
        watchers.get(id).add(deferredResult);
    } else {
        watchers.put(id, new ArrayList<DeferredResult<String>>());
        watchers.get(id).add(deferredResult);
    }

    deferredResult.onCompletion(new Runnable() {
        @Override
        public void run() {
            watchers.get(id).remove(deferredResult);
        }
    });

    return deferredResult;
}

@RequestMapping(value="/asyncRequest/setMessages/{id}/{message}", method=RequestMethod.GET)
@ResponseBody
public String setMessage(@PathVariable("id") Integer id, @PathVariable("message") String message) {
    asyncRepository.setMessage(id, message);

    return "";
}

@Scheduled(fixedRate=1000)
public void processQueues() {
    for (Map.Entry<Integer, Queue<AsyncDataBean>> entry : asyncRepository.getAsyncBeans().entrySet()) {
        while(entry != null && entry.getValue() != null && !entry.getValue().isEmpty()) {
            AsyncDataBean asyncDataBean = entry.getValue().poll();
            for (DeferredResult<String> deferredResult : watchers.get(asyncDataBean.getId())) {
                deferredResult.setResult(asyncDataBean.getMessage());
            }
        }
    }
}

And below is the Repository class which holds the Map of GroupID and its relevant messageQueue. And it also has the functions for getting and setting the messages for relevant group id.

@Repository
public class AsyncRepository {

    private Map<Integer, Queue<AsyncDataBean>> asyncBeans = new ConcurrentHashMap<Integer, Queue<AsyncDataBean>>();

    public String getMessages(Integer id) {
        StringBuilder stringBuilder = new StringBuilder();
        while (asyncBeans.get(id) != null && !asyncBeans.get(id).isEmpty()) {
            stringBuilder.append(asyncBeans.get(id).poll().getMessage()).append("~");
        }

        return stringBuilder.toString();
    }

    public void setMessage(Integer id, String message) {
        if(asyncBeans.containsKey(id)) {
            asyncBeans.get(id).add(new AsyncDataBean(id, message));
        } else {
            Queue<AsyncDataBean> messageQueue = new ConcurrentLinkedQueue<AsyncDataBean>();
            messageQueue.add(new AsyncDataBean(id, message));
            asyncBeans.put(id, messageQueue);
        }
    }

    public Map<Integer, Queue<AsyncDataBean>> getAsyncBeans() {
        return asyncBeans;
    }

    public void setAsyncBeans(Map<Integer, Queue<AsyncDataBean>> asyncBeans) {
        this.asyncBeans = asyncBeans;
    }
}

And below is the data bean I am using to store each message with its group id.

public class AsyncDataBean {
    private Integer id;
    private String message;

    public AsyncDataBean() {
    }

    public AsyncDataBean(int id, String message) {
        this.setId(id);
        this.setMessage(message);
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

And then comes the jsp page for group chat. which looks like below.

<script type="text/javascript">
    var messagesWaiting = false;
    function getMessages(){
        if(!messagesWaiting){
            $.ajax({ url: "${pageContext.servletContext.contextPath}/asyncRequest/getMessages/${id}", 
                     dataType:"text",
                     success: function(data,textStatus,jqXHR) {
                         if(textStatus == 'success'){
                             messagesWaiting = false;
                             var arr = data.split("~");
                             for(var i=0; i<arr.length; i++)
                             {
                                    try
                                    {
                                        if(arr[i] != '') {
                                            $("#txtaMessages").val($("#txtaMessages").val() + "\n\n" + arr[i]);
                                            document.getElementById("txtaMessages").scrollTop = document.getElementById("txtaMessages").scrollHeight;
                                        }
                                    }
                                    catch(e){
                                     alert(e.message);
                                    }
                             }
                         }
                     }, 
                     complete: function(j) {
                     },
                     error: function(xhr) { 
                     }
            });
            messagesWaiting = true;
        }
    }
    setInterval(getMessages, 1000);
    getMessages();

    function sendMessage() {
        var xmlhttp1 = new XMLHttpRequest();
        xmlhttp1.open("GET", '${pageContext.servletContext.contextPath}/asyncRequest/setMessages/${id}/' + $("#txtMessage").val(), true);
        xmlhttp1.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xmlhttp1.send();
        $("#txtMessage").val("");
        $("#txtMessage").focus();
    }
</script>

</head>
<body>
    <h1>Hello World!</h1>
    <table>
        <tr>
            <td>Messages :: </td>
            <td>
                <textarea cols="100" rows="10" id="txtaMessages"></textarea>
            </td>
        </tr>
        <tr>
            <td>Send Message :: </td>
            <td><input type="text" id="txtMessage"/></td>
        </tr>
        <tr>
            <td><input type="button" value="Send" onclick="sendMessage();"/></td>
        </tr>
    </table>
</body>
</html>

That is what I have coded till now to get this working. And everything is working finw in FF and Chrome. But in IE it is not working as expected. The request is never gets hold on the server and it always gets executed every second as configured in the javascript code. And it always returns the same result as previous. I have tried to use several other methods to send ajax request for IE but its not working. Can anyone get it working for me?

Since everything works fine in FF and Chrome, I suspect the problem is with javascript code to send the request to get messages.

Please help me.

Thanks in advance.

回答1:

This is very very frustrating.

To get this thing work properly in IE I need to set cache:false attribute in the ajax request I am creating with jquery for getMessages. Otherwise IE will not hold the request in pending status and always returns back with the old response text.

Its a very big issue with IE. I hope no one face the problem again or finds this answer as early as possible.

:)