Long polling freezes browser and block other ajax request - java

I am trying to implement long polling in my Spring-MVC Web App but it freezes my browser and other request after 4-5 continues AJAX requests.I have no clue whats goin on here is my relevant code.
The controller method:(Server Side):-
#Asynchronous
#RequestMapping("/notify")
public #ResponseBody
Events notifyEvent(HttpServletRequest request) {
Events events = null;
try {
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
System.out.println("Request Came from" + ((com.hcdc.coedp.safe.domain.User) request.getSession(false).getAttribute(Constants.KEY_LOGGED_IN_USER)).getLoginId());
if (!events.getTypeOfEvents().isEmpty()) {
System.out.println("Removing older entries");
events.getTypeOfEvents().clear();
}
while (!events.isHappend()) {
//Waiting for event to happen.
}
events = Events.getInstance();
events.setHappend(false);
request.getSession(false).getServletContext().setAttribute("events", events);
}catch (Exception e) {
e.printStackTrace();
}
return events;
}
The long-polling script(Client Side):-
$(document).ready(function() {
$.ajaxSetup({
async:true//set a global ajax requests as asynchronus
});
alert('Handler for .onload() called.');
waitForMsg();
});
function waitForMsg(){
xhr= $.ajax({
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false,
success: function(data){ /* called when request to notifier completes */
/* Doing smthing with response **/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
}
});
};
UPDATE:
function updateFeed(event, data) {
var f=eval(data);
alert(f.typeOfEvents.length);
}
function catchAll(event, data, type) {
console.log(data);
alert("error");
console.log(type);
}
$.comet.connect('<%=request.getContextPath()%>/notification/notify');
$(document).bind('feed.comet', updateFeed);
$(document).bind('.comet', catchAll);
Neither alert box pops up..:(

Seems like you experienced the session file lock
For PHP
Use session_write_close() when you don't need session value

It seems you have an empty while loop in your browser code.. this is a very CPU instensive way to wait for an event.
If no events happen the client will kill the request after your desired timeout of 50 seconds. But I'm not sure if the server thread is killed too, or if it "whiles" on forever (unless there is an event). The next request will start a second server thread that hangs in the while loop too then. Maybe the amount of empty while loops is an overkill for the server, so that it stops accepting any more requests. So after some requests (that each triggered an endless server thread) the client waits forever on a new request.. because it can't be handled by the server.
ps: on success you commented to wait 1 second, but set the timeout to 10000 (10 seconds)

I've met similar problem, my browser was stucked somehow with AJAX requests. Hint: instead using waitForMsg() directly, try setTimeout("waitForMsg()",10).

FYI, here is a project that might help you: https://github.com/SeanOC/jquery.comet
In general, I would search for JavaScript comet APIs that can support web sockets if available on client / server with graceful fallback to long polling. The API should handle all the gory details, allowing you to focus on the application.
Here's a link to an old dojo article on the topic: http://dojotoolkit.org/features/1.6/dojo-websocket
Good luck.

You can try to rewrite the behaviour using jQuery deferred:
function setShortTimeout() {
setTimeout(waitForMsg, 1000);
}
function setLongTimeout() {
setTimeout(waitForMsg, 15000);
}
$(document).ready(function() {
$.ajaxSetup({
async:true//set a global ajax requests as asynchronus
});
alert('Handler for .onload() called.');
$.when(waitForMsg())
.done(successHandler, setShortTimeout)
.fail(errorHandler, setLongTimeout);
});
function waitForMsg(){
return $.ajax({
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false
});
};
errorHandler and successHandler will be your success: and error: callbacks, which I omitted for clarity, with their setTimeout part removed (since it is now part of the deferred.done() and .fail() callbacks).
Let me know if it works.

I am a PHP developer but I met your problem and it could be the same behaviour. So I give you my 2 cents and hope it'll help you.
The line that does make me suspect a problem is :
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
In PHP, sessions are stored in files, and if we are long-polling on a php script while the session is open, we meet a race condition problem.
The principle is quite simple :
When a request opens the session, file is locked until the session
is closed.
If other requests comes to the server, they will be locked until the
session is released from the previous request.
In a case of long polling, if the session is opened and not closed just after getting information (at least, just before waiting for events), all requests are just locked, you can't go anywhere else in the website if you're using sessions on other pages. Even if you open a new tab, because for one browser there is only one session, you're locked.

It may be this:
xhr= $.ajax({ (...)
in your waitForMsg function.
Try
var xhr = (...)
It may be that you are declaring xhr in the global object, thus making it impossible to respond to two different requests.

Related

jquery/angular scheduler implementation

I have a chat application written on jquery. When user sends a message, this message is being sent to java REST endpoint. But I also want to listen for response. I think it would be good to listen other REST endpoint every second, because I have no idea how to implement it like in Facebook. So if there new message, get them and show. How to do that in jquery?
There are couple of solutions for that, but I'd suggest you do not create chat application using REST. If you want pure JS think about firebase from google (it will be realtime)/meteor or definitely using Websockets (you'll need a websocket server and you can use authobahnjs to subscribe to that server).
I do realize it's solution that's more complicated than REST, but if you ask me and probably your customers REST is really not the best choice for chat/realtime applications...
Just think of situation where you will have lots of users online and each of them will not only be sending a lot of queries (if they write fast) but as well checking for response every second. Now think if something goes wrong on the network what will happen with your server... you'll get tons of 'hanging' connections etc.
btw. ppl who tried it (like me in past) will tell you the same, no chats with polling for messages over REST! ;)
JQuery way...
Ok, just watch out for defining multiple events on the same element (that’s classic one when working with Jquery), here’s a function that should do what you want (its a post, but you can adapt it):
var interval = 1000; // 1000 = 1 second, 3000 = 3 seconds
function doAjax() {
$.ajax({
type: 'POST',
url: 'chatController.php',
data: {action: 'getMessagesForUser', user: {user: 'data'}},
dataType: 'json',
success: function (data) {
//do something with results
},
complete: function (data) {
// Schedule the next
setTimeout(doAjax, interval);
}
});
}
setTimeout(doAjax, interval);
You could also introduce some variable that would make sure you're not making another request before the previous one has completed etc.

How do I renew a spring session?

I have a web application running spring MVC on Websphere, and I am trying to implement a session expiration confirm. I now where to redirect if they choose to log out but I don't know how to renew a session if the choose to do that.
So far I understand I am using jQuery and ajax to send a request somewhere to the server but do not know where (the url), nor the arguments or the expected response. Does anyone know have a general idea how I find this out? Is it a standard function of spring session or am I going to have to write a custom function to fix this?
I was able to figure it out by combining information from multiple sources out on there. I came up with the following code:
function timeoutMessage(){
var popupdate = new Date();
var renewSession = confirm('Your Session is about to expire!\n\nYou will be logged out in 2 minute.\nDo you want to stay signed in?');
if(renewSession){
var response = new Date();
if(response - popupdate > 120000){
alert("Response took too long, current session has ended. \nRedirecting to login.");
}else{
pingServer();
resetTimeout();
}
}else{
window.location.href = "{app logout url}";
}
}
function pingServer(){
jQuery.ajax({url: "{valid server page url}",type: "HEAD",complete: function (XMLHttpRequest, textStatus) {}});
}
function resetTimeout(){
window.setTimeout(function(){timeoutMessage();},1080000);
}
$(document).ready(function()
resetTimeout();
});
The function sets a timer that coincides with two minutes before the session timeout. Most sites will offer most of the above code but what that in the pingServer() function; in order to extend the session on the server you can send a simple ajax call of type 'HEAD' to the server without interrupting the applications primary flow. It is seen as an action by the server toward the session and thus resets/renews the session's timeout. You don't need to do anything with the server response as seen in the code; just as long as you send the request the session timeout will be reset/renewed.
I hope this helps. It took me a while to completely piece together.
Why not set a bigger number for max_inactive_interval so the session will last longer?
Spring Session automatically renews if the user is active. Take a look at the table created by Spring Session for storing the session data: there is a column max_inactive_interval which basically means that the session will only expire if the user is not active for that interval of time.

Spring MVC, best practice how to often polling server

Im working on web application using the following stack of technologies: Spring, Hibernate, JSP. I have a task to make one of user social element - messages. As standard of implementation message system i take facebook system. On of the problem i faced is a polling server every 1-5 seconds (what period i have to take?) to retrieve an information about unread messages. Also i want to polling server to retrieve new messages at conversation page (like a chat). What i did:
Example code of get count unread messages.
Server side:
#RequestMapping(value = "/getCountUserUnreadMessages", method = RequestMethod.POST)
public #ResponseBody Callable<Integer> getCountUserUnreadMessages(#ActiveUser final SmartUserDetails smartUserDetails) {
// TODO add additional security checks using username and active user
return new Callable<Integer>() {
#Override
public Integer call() throws Exception {
Integer countUserUnreadMessages = messageService.findCountUserUnreadMessages(smartUserDetails.getSmartUser());
while (countUserUnreadMessages == 0) {
Thread.sleep(1000);
countUserUnreadMessages = messageService.findCountUserUnreadMessages(smartUserDetails.getSmartUser());
}
return countUserUnreadMessages;
}
};
}
Client side:
(function poll(){
setTimeout(function () {
$.ajax({
type: "post",
url: "/messages/getCountUserUnreadMessages",
cache: false,
success: function (response) {
$("#countUnreadMessages").text(response);
}, dataType: "json", complete: poll, timeout: 1000 });
}, 3000);
})();
So client send a request to retrieve count unread messages every second with a timeout in 3 seconds (is it good decision?).
But i think that is the worst callable code ever :-P
Tell me how to do better? What technique use?
Additional information:
Yeah, i supposed that it would be highload, many users service in the Internet.
Try Spring 4 WebSocket support:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
WebSockets support full duplex communication over a dedicated TCP connection that you establish over HTTP.
If you are expecting this application to have to scale at all I would make that timing interval more like every 30 - 90 seconds. Otherwise you are basically going to be designing your own built in DOS attack on your self.
You might look into Spring Sockets. It sounds like it's long polling option might work better for you.

long polling using JSON not working

I am trying to build chat application with long polling mechanism on Google app engine server.
HTTPRequest has default time out of 30 seconds, so I am sending polling request to server every 28 seconds if there is no update from server (so that I wont miss any message from other clients).
First request gets registered, but second request sent after 28 seconds is not reaching server.
function loadPage(query){
$.get({ url: query, success: function(events){
updated = 1;
//events data processing
createServerChannel();
});
}
function createServerChannel(){
var query='/ChatController?&user='+userName+'&sessionName='+sessionName+'&register=true';
loadPage(query);
updated = 0;
setInterval(function() { poll(query); }, 28000);
};
function poll(query){
if(updated==0){
loadPage(query);
}
}
I am using thread.wait() for request to wait on server. Is there any way to consume first pending request when next request from same client is available.
Please help.
I think web sockets might be a better approach as this keeps a continuous connection open to the server and waits for the server to push data to the client.
http://www.html5rocks.com/en/tutorials/websockets/basics/

Browser timeout and page cannot be displayed

I have written within a JSP page to have Thread.sleep() for more than 1 hour. The JSP is deploayed on WebLogic server 9.2. I am trying to check the browser timeout and page cannot be displayed error.
As per the below documentation, the default timeout for IE6 is 60 seconds. However, I was testing the above JSP and did not get any timeout or page cannot be displayed even beyond 30 minutes. I am doing this to the user experience for a requirement that can take long time to execute. Thanks.
http://support.microsoft.com/kb/181050
If you want to run and control a long-running process, better let it run in its own Thread instead of the request's Thread. Store a reference to this Thread in the session scope so that the client can use ajaxical requests (using the same session!) to request the server side for the current progress (and automagically also to keep the session alive so that it doesn't timeout).
Here's a basic example of such a servlet:
package mypackage;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RunLongProcessServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
if ("XMLHttpRequest".equals(request.getHeader("x-requested-with"))) {
LongProcess longProcess = (LongProcess) request.getSession().getAttribute("longProcess");
response.setContentType("application/json");
response.getWriter().write(String.valueOf(longProcess.getProgress()));
} else {
request.getRequestDispatcher("runLongProcess.jsp").forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
LongProcess longProcess = new LongProcess();
longProcess.setDaemon(true);
longProcess.start();
request.getSession().setAttribute("longProcess", longProcess);
request.getRequestDispatcher("runLongProcess.jsp").forward(request, response);
}
}
class LongProcess extends Thread {
private int progress;
public void run() {
while (progress < 100) {
try { sleep(1000); } catch (InterruptedException ignore) {}
progress++;
}
}
public int getProgress() {
return progress;
}
}
..which is mapped as follows:
<servlet>
<servlet-name>runLongProcess</servlet-name>
<servlet-class>mypackage.RunLongProcessServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>runLongProcess</servlet-name>
<url-pattern>/runLongProcess</url-pattern>
</servlet-mapping>
And here's a basic example of the JSP (with a little shot jQuery, an ajaxical JS framework which I by the way greatly recommend):
<!doctype html>
<html lang="en">
<head>
<title>Show progress of long running process with help of Thread and Ajax.</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(init);
function init() {
if (${not empty longProcess}) {
$.progress = 0;
checkProgress();
}
}
function checkProgress() {
$.getJSON('runLongProcess', function(progress) {
$('#progress').text(progress);
$.progress = parseInt(progress);
});
if ($.progress < 100) {
setTimeout(checkProgress, 1000);
}
}
</script>
</head>
<body>
<form action="runLongProcess" method="post">
<p>Run long process: <input type="submit"></p>
<p>Current status: <span id="progress">0</span>%</p>
</form>
</body>
</html>
Open it at http://localhost:8080/yourcontext/runLongProcess and click the button.
If this is a really, really long running process, you may improve "efficiency" by increasing the ajax intervals to 5 seconds or so so that the server doesn't feel getting DDOS'ed ;)
Hope this helps.
Your page may have returned some amount of data (such as headers) which convinced IE to leave the socket open and wait for more data.
To see what's actually going on, try running Wireshark (or Fiddler) on the machine with IE. You could also telnet to port 80 on your server and request the page manually (type in GET /path/to/your.jsp HTTP/1.0 followed by 2 line breaks).
I think you need to use AJAX technology to solve this kind of long operation problem, ask Server to maintain a connection over an hour is not an efficient decision.
What if more than hundred users connect to your server?
The concept of a simple AJAX solution is like this:
you start a timer using Javascript's setTimeInterval().
in timer's invocation, you compose a URL and use a XmlHttpRequest to request your server.
this async request will trigger(or just ask) a progress of a server-side back-end service.
every time the JS timer ticks, server-side should respond the progress(or result) to give user a visual response.
I think this is a better way to do your job. you can build this mechanism by your own or use some existing AJAX Framework like GWT, DWR even ZK to accomplish it.
Typically the "this page cannot be displayed" message occurs when the browser fails to make a connection to the host. In the scenario you've presented, the browser manages to make a connection and holds that connection open until the connection finishes or the server side hangs up.
If you are trying to test the functionality of the browser, you'll need to play some tricks on the network to have the connection fail. For example, firewall port 80 on your server such that it doesn't respond to any packets sent its way. The browser should leave the socket open for 60 seconds waiting for a response. When the response doesn't arrive the "this page cannot be displayed" message should be displayed.
Thanks for all your answers. We have a requirement where the users can wait longer. We are limiting the number of parallel requests by configuration in server (using Work Manager in WebLogic). If the number of parallel requests is more than the max no of threads, the requests are placed in queue. My questions if if the browser times out after 60 seconds as specified in the link - http://support.microsoft.com/kb/181050. We have done few tests and observing that page cannot be displayed message is displayed after 1 hour. We also checked response headers and no response content or response headers are returned back. So, we are not sure if either the server is timing our or browser is timing out. And also would like to check if IE6 times out after an hour or after 60 seconds as per the documentation.
Thanks

Categories

Resources