Node.js: Does this long polling implementation leak: - java

I'm evaluating Node.js for possible replacement of my current push functionality on a Java Web App. I wrote a simple long polling server that works like an intermediary between the client and the Java back-end. The client makes a request to subscribe, and then the Java server can notify subscribed clients by calling Node.js. It seems to be working fine so far, but I got the following message which points to a memory leak:
(node) warning: possible EventEmitter memory leak detected. 11 listeners added.
Use emitter.setMaxListeners() to increase limit.
Trace
at EventEmitter.addListener (events.js:168:15)
at EventEmitter.once (events.js:189:8)
at route (C:\Users\Juan Pablo\pushserver.js:42:12)
at Server.onRequest (C:\Users\Juan Pablo\pushserver.js:32:3)
at Server.EventEmitter.emit (events.js:91:17)
at HTTPParser.parser.onIncoming (http.js:1793:12)
at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:111:23
)
at Socket.socket.ondata (http.js:1690:22)
at TCP.onread (net.js:402:27)
I have a line of code that logs the existing listeners whenever a notify event is emitted. I've had it running for a while and it shows that there is only one listener per subscribed client (as should be), but this line wasn't on the code when I got the warning message. The code was exactly the same except for that line tough.
This is the push server's code (it's a bit rudimentary since I'm still learning Node.js):
var http = require('http');
var url = require("url");
var qs = require("querystring");
var events = require('events');
var util = require('util');
var emitter = new events.EventEmitter;
function onRequest(request, response)
{
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
request.setEncoding("utf8");
if (request.method == 'POST')
{
var postData = "";
request.addListener("data", function(postDataChunk)
{
postData += postDataChunk;
console.log("Received POST data chunk '"+ postDataChunk + "'.");
});
request.addListener("end", function()
{
route(pathname, response, postData);
});
}
else if (request.method=='GET')
{
var urlParts = url.parse(request.url, true);
route(pathname, response, urlParts.query);
}
}
function route(pathname, response, data)
{
switch (pathname)
{
case "/subscription":
emitter.once("event:notify", function(ids)
{
response.writeHead(200, {"Content-Type": "text/html", "Access-Control-Allow-Origin": "*"});
response.write(JSON.stringify(ids));
response.end();
});
break;
case "/notification":
//show how many listeners exist
console.log(util.inspect(emitter.listeners('event:notify'));
emitter.emit("event:notify", data.ids);
response.writeHead(200, {"Content-Type": "text/html", "Access-Control-Allow-Origin": "*"});
response.write(JSON.stringify(true));
response.end();
break;
default:
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain", "Access-Control-Allow-Origin": "*"});
response.write("404 - Not found");
response.end();
break;
}
}
http.createServer(onRequest).listen(8888);
console.log('Server running at http://127.0.0.1:8888/');
I was under the impression that using emitter.once would automatically remove the event listener once it was used, so I don't know how 11 listeners could've been added if there was only one client connected. I'm thinking that perhaps if the client disconnects while waiting for a notification then the associated connection resources are not disposed.
I'm wondering whether I have to manually handle disconnections and if there is actually a leak in there somewhere. Any advice is welcome. Thanks.

If anyone is interested, the above code does leak. The leak occurs when a client disconnects before a notification is sent. To fix this, it is necessary to remove the event listener when a client disconnects abruptly, such as:
case "/subscription":
var notify = function(ids)
{
response.writeHead(200, {"Content-Type": "text/html", "Access-Control-Allow-Origin": "*"});
response.write(JSON.stringify(ids));
response.end();
}
emitter.once("event:notify", notify);
//event will be removed when connection is closed
request.on("close", function()
{
emitter.removeListener("event:notify", notify);
});
break;

Related

Canceling filewatcher run by TimerTask after receiving websocket message in Java

I've been coding around in circles trying to work this one out, new to java and have been lurking here to find things out for a while but I really can't get passed this one. I adapted some code by Desmond Shaw (http://www.codepool.biz/how-to-implement-a-java-websocket-server-for-image-transmission-with-jetty.html) to create a websocket to tranfer jpg images from a server to remote clients. I want to send files from the server to the browser windows of connected clients when some specific files on the server change (they are pages of music scores that are created using Max/MSP in real-time), but I don't seem to be able to cancel the timers I'm creating to watch these files in my home directory for changes.
More specifically I'm sending messages from the remote browser clients (through javascript buttons operated by the users) over a websocket connection to specify which of the files they wish to see updated on their screen (i.e part one, which refers to a file on the server called "1.png" and is the violin part, or part 2 which is the server file "2.png" and is the cello part etc). This is then used within the websocket handler running on my server to send the right files to that client when a filewatcher detects they have changed on the server. I can get everything going except stopping the timers running the filewatchers, when a different part is requested by the client (say the violin player wants to look at the cello players part). Below is the method I have edited to respond to the messages from the clients:
#OnWebSocketMessage //part request from websocket client (remote browser)
public void onMessage( String message) {
System.out.println("Message: '" + message + "' received");
sFclient = message;
if (sFclient == "1" || sFclient == "2" || sFclient == "3" || sFclient == "4") {
System.out.println("Part " + sFclient + " joined");
}
else {
sFclientOut = 0;
}
}
public void onChange( File file ) {
System.out.println( "File "+ file.getName() +" has changed!" );
TimerTask task = new FileWatcher(new File("/Users/benedict/" + message + ".png")) {
try {
File f = new File("/Users/benedict/" + sFclient + ".png");
BufferedImage bi = ImageIO.read(f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(bi, "png", out);
ByteBuffer byteBuffer = ByteBuffer.wrap(out.toByteArray());
mSession.getRemote().sendBytes(byteBuffer);
out.close();
byteBuffer.clear();
}
catch (IOException e) {
e.printStackTrace();
}
Timer timer1 = new Timer(); {
timer1.schedule(task , new Date(), 20 );
if (sFclientOut == 0){
task.cancel();
timer1.cancel();
}
}
}
I had it working mostly using an if statement which I've now abandoned but have been editing and now it doesn't make as much sense probably. Any help at all would be appreciated, but my main question is should I be trying to cancel the threads handling the timertasks or use a completely different approach altogether like a switch statement for example. I have tried sending a message before every new message from the browsers ("0") to cancel the old threads but the Timertasks just don't start at all, which I think is because that cancels the timertask and doesn't let it run again?
Thanks,
Benedict
Ok here's the final working solution, I just had to cancel the tasks based on a message from the clients (in this case a 0)
else if (message.equals("0")) {
zerocounter = zerocounter + 1;
if (zerocounter >= 2) {
task.cancel();
}

Long polling in JQuery + JAVA?

I need to hold my request in the server until new data comes.I am using the Tomcat 6 as my web server . So this is my JQuery code,
function sendMessage() {
var message = $("#message").val();
$.ajax({
type: "POST",
cache: false,
url: "sendMessage.html",
data: "message=" + message,
dataType: "html",
success: function(response) {
},
error: function(e) {
//alert('Error: ' + e);
},
});
}
function startLongPolling(){
$.ajax({
type: "POST",
cache: false,
url: "LongPoll.html",
dataType: "html",
success: function(response) {
if(response != null && response !="" && response !="null")
$("#sucess").html(response);
},
error: function(e) {
//alert('Error: ' + e);
},
complete: function(e) {
startLongPolling();
},
});
}
My java code will be ,
#RequestMapping(value = "LongPoll.html", method=RequestMethod.POST )
public #ResponseBody String longLongPolling(HttpSession session) {
String sessionId = session.getId().toString();
AgentState agentState = ApplicaionManager.agentDetail.get(sessionId);
String message = null;
if(ApplicaionManager.agentDetail.containsKey(sessionId)){
while(true){
if(agentState.isStateChange() == true){
message = agentState.getMessage();
if(message != null)
agentState.setStateChange(false);
System.out.println("Break for session "+sessionId+" due to Agent State changed");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("While exited for session"+sessionId);
return message;
}
But there is a continous request sent to server for every 11 seconds . I don't know how it is possible. I have checked this with chrome developer tools.
Hope our stack users will help me.
This is normal / expected. Depending on your browser and (especially) front-end server (Apache/NGINX) and web-server (Tomcat?) configuration you will have:
maximum wait time for the first response (connection timeout, probably 10 seconds in your case)
maximum wait time for the complete response
These setting basically prevent the server from being spammed with requests that never complete and running out of threads in the thread pool.
You could increase these values, however you should always create your code like this with timeouts in mind. Basically you want to do on the client side:
Open long pull
Wait for response
If received, continue
If (timeout) error, go to step 1
Please note that this solution is not scalable: usually you would have (for example) 200 thread processing incoming requests. The idea is that they finish fast. If the thread pool is exhausted, users will have to wait for a new connection. But with 200 threads you are able to serve well over 2.000 users. But not if threads are blocked because of long pool.
If possible, you should really look into WebSockets, which are now available in new versions of Java.
EDIT As Konrad suggested below, you can use something like socket.io, which falls-back automatically to other mechanisms. There's a Java-based implementation for sever-side available call Atmosphere, but I haven't tried it though.

HTML5 Server Side Events (SSE)

I am working on implementing SSE in a Web Application on Java Stack using Servlets. I have facing 2 key issues currently. Let me first place my code both for the Web page and the Servlet followed by the issue I am facing.
Web Page Code:
<script type="text/javascript">
function registerSSE() {
var source = new EventSource("http://www.sample.com/BootStrap/NotificationServlet");
source.addEventListener('StartProductionRun', function(e) {
// Get the data and identify the instrument Name/Id
var dataReceived = e.data;
document.getElementById(dataReceived + "_button").disabled = true;
}, false);
}
function StartProduction(instrument) {
var dataString = 'instrumentName='+ instrument;
// call ajax to submit the form and start the production run
$.ajax({
type: 'POST',
url: 'http://localhost:8080/BootStrap/ProductionRunServlet',
data: dataString,
success: function() {
$('#Status').html("<div id='message'></div>");
$('#message').html("<h4 aling=\"centre\">Prudction Run for Instrument " + instrument + " initiated.</h4>")
.hide()
.fadeIn(5000);
}
});
}
</script>
Servlet Code :
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/event-stream;charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter out = response.getWriter();
NotificationService notificationService = null;
while (true) {
notificationService = NotificationService.getInstance();
if (notificationService.getNotificationCount() > 0 ) {
String notificationValue = notificationService.getNotification(0);
String[] keyValue = notificationValue.split(":");
out.print("event:" + keyValue[0] + "\n");
out.print("data: " + keyValue[1] + "\n");
out.print("retry:" + 1000 + "\n\n");
out.flush();
}
else {
out.print(": time stream \n");
out.print("retry:" + 1000 + "\n\n");
out.flush();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now the issues:
The Web Page will be viewed by multiple users at the same time. And I want that the data to be pushed to all the users who viewing that page. Currently when I am running locally in my machine, even if I open Chrome and Firefox, I don't get the notification in both the Browsers. It comes only in one.
Also, If I leave the browser running for some time, I find that even if the servlet is pushing out data based on certain events. I don't get the notification on the Browser.
I need to make sure that:
The notification gets pushed to all the clients who are viewing that particular page irrespective of what they are doing on the page or the page is just used for viewing the information.
Looking forward to all the help I can get to make this working. Also, would be interested to know if there are other alternative which I can use.
Your servlet code is working perfectly, although i haven't worked so much on it(once did a jsp project). What i think is, you have missed something in javascript?
I think there should be a timer/thread/loop in javascript too, to get all pushed data continuously. i.e,
setInterval(
function(){
// code which needs to run every 5sec
},5000);
I hope this will help out a bit.
You should check if EventSource is available in that browser before using it. Maybe one of the browsers has not support for it.

WebSocket disconnected when trying to send large size of images

I am testing a WebSocket at localhost using java and javascirpt, running Tomcat 7.0.42 and no proxy in between. It works fine on sending text and small size of image via websocket. However, it will be forced to close the connection on client side(chrome browser) when trying to send a large size of photo (Notice that the tomcat's 'onClose callback in MessageInbound' does not be notified after websocket on browser closed the connection).
how can I solve it? thx.
Here is the capture from chrome development tool
Below is my code on client side:
for (var i = 0, f; f = files[i]; i++) {
// step 1: tell server who the people you want to send
ws.send(JSON.stringify({
action: "binary",
receiver: <%=selectedfriend.getUserId()%>,
timestamp: new Date().getTime()
}));
// step 2: send file
ws.send(f);
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" style="width: 50px;height: 30px;" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
appendImage(span.innerHTML, "pullleft");
};
})(f);
reader.readAsDataURL(f);
}
Finally, I found a solution for my question, what i did on Tomcat is to use:
protected StreamInbound createWebSocketInbound(String string, HttpServletRequest hsr) {
MyMessageInbound inbound = new MyMessageInbound();
inbound.setByteBufferMaxSize(9999999);
inbound.setOutboundByteBufferSize(9999999);
return inbound;
}
But there is another problem is:
what is the proper value I should use, here is 9999999, the value should be tested well when I upload under 8-9MB file using WebSocket, but why, how can I measure it?
Please help and discuss here again, thx!
Try to send as ArrayBuffer:
ws.send(f.readAsArrayBuffer());

How to check whether a port is open at client's network/firewall?

This is solved at last with "timeout" attribute of jQuery AJAX (and JSONP). See my own answer !
Please see the updated part, I have tried with applet too. And will not hesitate to accept your answer if you can give a solution with applet implementation.
I am working with a Java based web application. My requirement is to check whether a particular port (say 1935) is open or blocked at client's end. I have implemented a "jsonp" (why 'jsonp' ? i found that 'http' request through AJAX cannot work for corssdomain for browsers 'same origin policy') AJAX call to one of my server containing particular port. And if the server returns xhr.status == 200 the port is open. Here is a drawback that I can't make the execution-flow wait (synchronous) until the call completes. Here is the JavaScript function I am using.
Any alternative solution (must be a client-sided thing must be parallel with my application, please dont suggest python/php/other languages) is also welcome. Thanks for your time.
function checkURL() {
var url = "http://10.0.5.255:1935/contextname" ;
var isAccessible = false;
$.ajax({
url: url,
type: "get",
cache: false,
dataType: 'jsonp',
crossDomain : true,
asynchronous : false,
jsonpCallback: 'deadCode',
complete : function(xhr, responseText, thrownError) {
if(xhr.status == "200") {
isAccessible = true;
alert("Request complete, isAccessible==> " + isAccessible); // this alert does not come when port is blocked
}
}
});
alert("returning isAccessible=> "+ isAccessible); //this alert comes 2 times before and after the AJAX call when port is open
return isAccessible;
}
function deadCode() {
alert("Inside Deadcode"); // this does not execute in any cases
}
---------------------------------------------------------UPDATE----------------------------------------------------------------
I have tried with Java Applet (thanks to Y Martin's suggestion). This is working fine in appletviewer. But when I add the applet in HTML page, it is giving vulnerable results. Vulnerable in the sense, when I change the tab or resize the browser, the value of portAvailable is being altered in the printed message.
Applet Code :
import java.applet.Applet;
import java.awt.Graphics;
import java.net.InetSocketAddress;
import java.net.Socket;
public class ConnectionTestApplet extends Applet {
private static boolean portAvailable;
public void start() {
int delay = 1000; // 1 s
try {
Socket socket = new Socket();
/*****This is my tomcat5.5 which running on port 1935*************/
/***I can view it with url--> http://101.220.25.76:1935/**********/
socket.connect(new InetSocketAddress("101.220.25.76", 1935), delay);
portAvailable = socket.isConnected();
socket.close();
System.out.println("init() giving---> " + portAvailable);
}
catch (Exception e) {
portAvailable = false;
System.out.println("init() giving---> " + portAvailable);
System.out.println("Threw error---> " + e.getMessage());
}
}
public void paint(Graphics g) {
System.out.println("Connection possible---> " + portAvailable);
String msg = "Connection possible---> " + portAvailable;
g.drawString(msg, 10, 30);
}
}
And this is my HTML page (I am hosting it on same computer with a different Tomcat 6 which runs on port 9090. I can view this page with url ---> http://101.220.25.76:9090/test/):
<html>
<body>
<applet code="ConnectionTestApplet" width=300 height=50>
</applet>
</body>
</html>
And how I am doing the port 1935 blocking and openning ?
I have created firewall rule for both inbound and outbound for port 1935.
I check the port 1935 open/blocked scenario by disabling/enabling both rules.
This is my S.S.C.C.E. Now please help me :)
Gotcha !!! I have solved my problem with JSONP and jQuery AJAX call. I discovered the timeout attribute of jQuery AJAX and my code executed fluently when the port was blocked or opened. Here is the solution for future visitors. Thanks to all answerers for contribution.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="jquery-1.7.2-min.js"></script>
</head>
<body>
<script type"text/javascript">
var isAccessible = null;
function checkConnection() {
var url = "http://101.212.33.60:1935/test/hello.html" ;
$.ajax({
url: url,
type: "get",
cache: false,
dataType: 'jsonp', // it is for supporting crossdomain
crossDomain : true,
asynchronous : false,
jsonpCallback: 'deadCode',
timeout : 1500, // set a timeout in milliseconds
complete : function(xhr, responseText, thrownError) {
if(xhr.status == "200") {
isAccessible = true;
success(); // yes response came, esecute success()
}
else {
isAccessible = false;
failure(); // this will be executed after the request gets timed out due to blockage of ports/connections/IPs
}
}
});
}
$(document).ready( function() {
checkConnection(); // here I invoke the checking function
});
</script>
</body>
</html>
I don't think you understand the use cases for JSONP and it's not possible to test open ports with it. http://en.wikipedia.org/wiki/JSONP
If you want a client side solution it could be possible with websockets, but this is only available on new browsers like chrome or ff. Otherwise request a server side script which does the ping. For example - with a curl script: curl and ping - how to check whether a website is either up or down?
Here is a Java code as an Applet to test server/port connectivity:
import java.io.*;
import java.net.*;
public class ConnectionTestApplet extends Applet {
public void start() {
boolean portAvailable = false;
int delay = 1000; // 1 s
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("server.domain.com", 1935), delay);
portAvailable = socket.isConnected();
socket.close();
} catch (UnknownHostException uhe) {
uhe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("Connection possible: " + portAvailable);
}
}
You still have to get the information out of the applet to do something else with that result. The easiest way is to redirect the browser thanks to getAppletContext().showDocument(url)
Instead of an applet a flash component may be used. Using the Socket class available in ActionCcript one can open a tcp connection from flash to a port on a server to check if its open. But based on the flash player version a policy file needs to be placed on the server to which the socket is opened.
Check this out:
http://blog.andlabs.org/2010/12/port-scanning-with-html5-and-js-recon.html
With JS-Recon, you can do port scanning with javascript. You can simply point it to your local IP address. I believe it works by making a web sockets/cors connection to an arbitrary desintation ip/socket and measuring the timeouts. It is not a perfect approach, but this may be the limit of javascript ultimately.
If you can do it in a java applet/flash application, that may be better ultimately as they have lower-level access.
You cannot do this in JavaScript because it doesn't have true socket support, with JavaScript you can only test for the presence of HTTP socket. You could use Java (JavaScript is not Java) and write a proper Java Applet to do it.
You should also read this Q&A How to Ping in java
Try using isReachable
In JavaScript, you have to work-around the asynchronous issue. Here is a proposal:
The HTML page displays an animated image as a progress bar
You invoke the checkURL
After either receiving the callback or a defined timeout, you change display for an error message or do on with the job to do
Based on the following document with the use of XMLHttpRequest, here is a code example for checkURL:
var myrequest = new ajaxRequest();
var isAccessible = false;
myrequest._timeout = setTimeout(function() {
myrequest.abort();
displayErrorMessage();
},
1000
) //end setTimeout
myrequest.onreadystatechange = function() {
if (myrequest.readyState == 4) { //if request has completed
if (myrequest.status == 200) {
isAccessible = false;
goOnWithTheJob();
} else {
displayErrorMessage();
}
}
myrequest.open("GET", url, true);
myrequest.send(null); //send GET request
// do nothing - wait for either timeout or readystate callback
This code lets 1 second to get the 200 response from a HTTP GET on a basic resource.
In your local test, you get an immediate answer because the system answers connection reset if the port is closed but a firewall just does not answer.
Even if the open method may be used synchronously, I recommend the use of a timer because the code is likely to wait for TCP timeouts and retries (3 x 1 minute ?) as a firewall usually just drops packets on closed ports and may reject ICMP packets, preventing you to test availability thanks to ping. And I imagine such a long wait is not expected for such a check.
I am occasional frontend/javascript/jQuery guy, so this may not be 100% professional, but it is good enough and it solved my similar problem:
ping_desktop_app = $.get({
url: "http://127.0.0.1:#{desktop_app_port}",
dataType: 'jsonp',
})
$(#).parent().find(".click-me-to-use-desktop-app").click ->
if ping_desktop_app.status == 200
$.get({
url: "http://127.0.0.1:#{desktop_app_port}/some_command/123123",
dataType: 'jsonp',
})
else
alert("Please run your desktop app and refresh browser")
I could not check whether port is open (desktop app is running) on server side because views are cached, so I needed to check the localhost/port right before user click in browser
Edits translating to JS are welcome

Categories

Resources