Results 1 to 2 of 2
  1. #1
    StormyWaters is offline Senior Member
    Join Date
    Feb 2009
    Posts
    304
    Rep Power
    6

    Default Server Side Events - Detecting closed page

    Hello all,

    I've been working on a little side project to familiarize myself with Server Side Events. They seem to me like it would be very useful when there is a need for a client to receive information from the Server, and it would most likely replace AJAX in most places that I currently use it, however, I'm having a problem getting the server recognize when the client closes or leaves the page. It's most likely something stupid. I know the infinite loop in the Server jsp is probably where the issue is, I just don't see how to replace it or break out of it.

    Here's the sample code:

    Client JSP
    Java Code:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    	<head>
        </head>    
    	<body>
    		<p>Client Side</p>
      		<script>
        		var source = new EventSource('http://localhost:8082/test/serverEvents.jsp');
        		source.onmessage = function(e) {
          			document.body.innerHTML += e.data + '<br>';
        		};
      		</script>  
    	</body>  
    </html>
    Server's JSP
    Java Code:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    
    <%
    	response.setContentType("text/event-stream");
    	int messagesSent = 0;
    	while (true) {
    		System.out.println("Sending message: " + String.valueOf(messagesSent));
    		out.print("data: Message: " + messagesSent++ + "\n\n");
    		out.flush(); 
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    %>
    The problem I'm seeing is that when I close the page and have a new page connect to the Server, it is creating all new Threads and the old ones are never getting destroyed. The only thing I can think of right now to address this is having a Unique Id for the Client JSP, and use AJAX to poll the Server with the Unique Id to let it know if it is alive and update a timestamp for the Id. The Server JSP can then check the timestamp, and use that to exit the loop if it hasn't been updated in awhile.

    Is there any other way that the Server could check if the client closed the page? Is there anyway for the Client to send a message to the Server to let it know the page is closing?

    Any help/insight would be appreciated.
    Thanks,

  2. #2
    StormyWaters is offline Senior Member
    Join Date
    Feb 2009
    Posts
    304
    Rep Power
    6

    Default Re: Server Side Events - Detecting closed page

    Hello again,

    Since I haven't heard from anybody regarding this, I decided to try to implement the polling system to let the Server know that the Client is still connected, and use it to timeout the SSEs being sent. I had to add a manager class in for handling the generation and storing of the Unique Ids for the Server Side Event pages and a JSP used to update the timestamp for the Unique Ids. This all seems to be working well and good, but I still have some questions/concerns.

    1) Is this the best approach for handling breaking out of the loop for the SSE? I'm curious if there are any other ways.

    2) Are there any problems with the way I've handled the generating, updating and removing of the SSE Unique Ids? I was thinking of using a separate Thread interior to the SSE_TimeoutManager class which occasionally goes through and removes the timed out ids, but I figured this would be better handled inside the Server's SSE JSP as this way depending on the JSP page, the timeout could be different. Should I use a sorted list for managing the taken Unique Ids, would that help with performance? Should I be concerned with the performance?

    Again, any help/insight would be appreciated.
    Thanks,

    Below is the updated code:

    Client's SSE JSP
    Java Code:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    <%@ page import="test.SSE_TimeoutManager" %>
    
    <%
    	final Long UNIQUE_ID = SSE_TimeoutManager.SINGLETON.getNextUniqueIndex();
    %>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    	<head>
        </head>    
    	<body>
    		<p>Client Side</p>
      		<script>
        		var source = new EventSource('http://localhost:8082/test/serverEvents.jsp?SSE_ID=<%= String.valueOf(UNIQUE_ID) %>');
        		source.onmessage = function(e) {
          			document.body.innerHTML += e.data + '<br>';
        		};
      		</script>
      		
      		<script language="Javascript" type="text/javascript">
    			function createRequestObject() {
        			var tmpXmlHttpObject;    
        			//Depending on what the browser supports, use the right way to create the XMLHttpRequest object
       				if (window.XMLHttpRequest) { 
            			//Mozilla, Safari would use this method ...
           				tmpXmlHttpObject = new XMLHttpRequest();	
        			} else if (window.ActiveXObject) { 
            			//IE would use this method ...
            			tmpXmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP");
        			}    		
       				return tmpXmlHttpObject;
    			}
    
    			//Call the above function to create the XMLHttpRequest object
    			var http = createRequestObject();
    
    			setInterval(function makeGetRequest() {
        			//make a connection to the server ... specifying that you intend to make a GET request 
        			//to the server. Specifiy the page name and the URL parameters to send
        			http.open('get', 'ssePinger.jsp?SSE_ID=<%= String.valueOf(UNIQUE_ID) %>');
    	
        			//actually send the request to the server
        			http.send(null);
    			}, 1000);
    		</script>
    	</body>  
    </html>
    Server's SSE JSP:
    Java Code:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    <%@ page import="test.SSE_TimeoutManager" %>
    
    <%
    	String uniqueIdS = request.getParameter("SSE_ID");
    	if (uniqueIdS != null) {
    		long temp = 0l;
    		try {	
    			temp = Long.parseLong(uniqueIdS);
    		} catch (Exception e) {
    			e.printStackTrace();
    			return;
    		}
    		
    		final long UNIQUE_ID = temp;
    		try {
    			response.setContentType("text/event-stream");
    			int messagesSent = 0;
    			
    			final long TIMEOUT = 30000;
    			long TIMESTAMP = 0l;
    			while ((System.currentTimeMillis() - (TIMESTAMP = SSE_TimeoutManager.SINGLETON.getTimestamp(UNIQUE_ID))) <= TIMEOUT) {
    				String message = "Message Number: " + messagesSent++;
    				System.out.println("Sending '" + message + "'");
    				out.print("data: " + message + "\n\n");
    				out.flush(); 
    				try {
    					Thread.sleep(1000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		} finally {
    			SSE_TimeoutManager.SINGLETON.removeUniqueIndex(UNIQUE_ID);
    		} 
    	}
    %>
    JSP for updating Timestamp for Unique Ids
    Java Code:
    <%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
    <%@ page import="test.SSE_TimeoutManager" %>
    
    <%
    	String uniqueIdS = request.getParameter("SSE_ID");
    	if (uniqueIdS != null) {
    		try {
    			long uniqueId = Long.parseLong(uniqueIdS);
    			SSE_TimeoutManager.SINGLETON.updateTimestamp(uniqueId);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    %>
    SSE_TimeoutManager.java
    Java Code:
    package test;
    
    import java.util.Enumeration;
    import java.util.Hashtable;
    
    public class SSE_TimeoutManager {
    	
    	public static final SSE_TimeoutManager SINGLETON = new SSE_TimeoutManager();
    	
    	private final Object UNIQUE_LOCK = new Object();	
    	
    	private long uniqueIndex = -1l;
    	
    	private final Hashtable<Long, Long> indexToTimestampHT = new Hashtable<Long, Long>();
    	
    	private SSE_TimeoutManager() {
    		super();
    	}
    	
    	public long getNextUniqueIndex() {
    		long retVal = 0l;
    		synchronized (UNIQUE_LOCK) {
    			retVal = getNextUniqueIndex_Internal();
    		}
    		return retVal;
    	}
    	
    	public void updateTimestamp(long uniqueIndex) {
    		synchronized (UNIQUE_LOCK) {
    			Enumeration<Long> keys = indexToTimestampHT.keys();
    			while (keys.hasMoreElements()) {
    				Long key = keys.nextElement();
    				if (key.longValue() == uniqueIndex) {
    					indexToTimestampHT.remove(key);
    					indexToTimestampHT.put(key, System.currentTimeMillis());
    				}
    			}
    		}
    	}
    	
    	public long getTimestamp(long uniqueIndex) {
    		synchronized (UNIQUE_LOCK) {
    			Enumeration<Long> keys = indexToTimestampHT.keys();
    			while (keys.hasMoreElements()) {
    				Long key = keys.nextElement();
    				if (key.longValue() == uniqueIndex) {
    					return indexToTimestampHT.get(key);
    				}
    			}
    		}
    		
    		return 0l;
    	}
    	
    	public void removeUniqueIndex(long uniqueIndex) {
    		synchronized (UNIQUE_LOCK) {
    			Enumeration<Long> keys = indexToTimestampHT.keys();
    			while (keys.hasMoreElements()) {
    				Long key = keys.nextElement();
    				if (key.longValue() == uniqueIndex) {
    					indexToTimestampHT.remove(key);
    					return;
    				}
    			}
    		}
    	}
    	
    	private long getNextUniqueIndex_Internal() {		
    		do {
    			incrementUniqueIndex();
    		} while (isUniqueIndexTaken());		
    		indexToTimestampHT.put(uniqueIndex, System.currentTimeMillis());		
    		return uniqueIndex;
    	}
    	
    	private void incrementUniqueIndex() {
    		if (uniqueIndex == Long.MAX_VALUE) {
    			uniqueIndex = Long.MIN_VALUE;
    		} else {
    			uniqueIndex = uniqueIndex + 1;
    		}
    	}
    	
    	private boolean isUniqueIndexTaken() {
    		Enumeration<Long> keys = indexToTimestampHT.keys();
    		while (keys.hasMoreElements()) {
    			Long key = keys.nextElement();
    			if (key.longValue() == uniqueIndex) {
    				return true;
    			}
    		}
    		return false;
    	}	
    
    }
    Last edited by StormyWaters; 08-15-2012 at 05:34 PM.

Similar Threads

  1. Client-side or server-side XML parsing?
    By noodle_variation_187-X in forum JavaServer Pages (JSP) and JSTL
    Replies: 11
    Last Post: 05-31-2012, 09:06 AM
  2. Replies: 1
    Last Post: 03-20-2010, 07:03 PM
  3. Replies: 0
    Last Post: 03-02-2010, 08:28 AM
  4. detecting mail server in network
    By iqcst in forum Networking
    Replies: 4
    Last Post: 11-26-2008, 07:49 AM
  5. Replies: 0
    Last Post: 07-25-2008, 02:24 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •