Quantcast
Channel: Learning the code way
Viewing all articles
Browse latest Browse all 231

Reverse AJAX - Using the Piggback Technique

$
0
0
In the previous post we saw how reverse AJAX was achieved using the polling technique. Although easy, this is not a very popular or scalable solution. An alternative approach is the PiggbackTechnique.
In this there is no continuous requests being sent from client to check if any event has occurred. There is no interval here. The client functions as normal. Whenever it needs any specific data, it sends a request to the server. The server processes the client requests and returns the response. But - and this where things get interesting - along with the response data, the server adds any additional information that it wants to provide to the client.
Thus Server is piggybacking its information on the clients request for some other data.
Consider that the client has demanded to be made aware of certain events - e.g.A Meeting Join Invite. In the polling mode, the client would keep pinging the server every x minutes/seconds to check if there are any invites. In this case, whenever a client request for any data arrives, the server will also include any invites if present.
Consider the same index.jsp application from last post.
<html>
<head>
<script>

var pollAndCheck = function() {
document.getElementById("sessionActve").innerHTML = "Checking...";
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {

var xmlDoc = xmlhttp.responseXML;
var value = xmlDoc.getElementsByTagName("value")[0].childNodes[0].nodeValue;
document.getElementById("sessionActve").innerHTML = value;
processForEvents(xmlDoc);
}
}
xmlhttp.open("POST", "ajax/isActive", true);
xmlhttp.send();
};

varprocessForEvents = function(xmlDoc) {
var eventCount = xmlDoc.getElementsByTagName("eventC")[0].childNodes[0].nodeValue
if (eventCount > 0) {
document.getElementById("eventHighlighter").innerHTML = "<b>"
+ eventCount
+ " Event Invites received ! Goto Events page o review. </b>";
}
};

//check every 1 minute
window.setTimeout(pollAndCheck, (1 * 60 * 1000));
</script>

</head>



<%
  session = request.getSession(false);
  if(session !=null){
    session.invalidate();
  }
  session = request.getSession(true);
  System.out.println("Session has been activated at "+ session.getCreationTime());
%>

<body>
<h3>Hello, This is a sample page</h3>
<br/>
<br/> Is your session active ??
<div id="sessionActve"></div>
<div id="eventHighlighter"></div>
</body>
</html>

On the server side, I had to introduce some technique to intercept the code and The whole process worked as below:
  1. I decided to intercept all AJAX requests processed by the server.
  2. Before the response is written back to the client, the code for piggybacking any server data is executed and the result added to the response.
I wouldn't say mine is a very clean technique, rather it is just an approach to demonstrate piggybacking data on server end.
publicclass FinalResponseGeneratingFilter implements Filter {

@Override
publicvoid destroy() {
}

@Override
publicvoid doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain filterChain)
throwsIOException, ServletException {
HttpServletResponse response = (HttpServletResponse) arg1;
HttpServletRequest request = (HttpServletRequest) arg0;
HttpServletResponseWrapper responseWrapper = new DummyResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);

int events = EventUtils.getEvents();//COMPLEX logic to collect events
String data = "<data><value>"
+ StringEscapeUtils.escapeXml(responseWrapper.toString()) + "</value>"
+ "<eventC>" + events + "</eventC>" + "</data>";
response.getWriter().write(data);
response.getWriter().flush();

}

@Override
publicvoid init(FilterConfig arg0) throws ServletException {
}

}
As seen above the Filter uses a ResponseWrapper to collect all the response. It then executes some server logic to collect information on events. This is then written back to the actual stream.
The ResponseWrapper implementation is as below:
publicclass DummyResponseWrapper extends HttpServletResponseWrapper {
protectedStringWriter stringWriter;
protectedPrintWriter writer;
protectedboolean getOutputStreamCalled;
protectedboolean getWriterCalled;

public DummyResponseWrapper(HttpServletResponse response) {
super(response);
stringWriter = newStringWriter();
}

public ServletOutputStream getOutputStream() throwsIOException {
if (getWriterCalled) {
thrownewIllegalStateException("getWriter already called");
}
getOutputStreamCalled = true;
returnsuper.getOutputStream();
}

publicPrintWriter getWriter() throwsIOException {
if (writer != null) {
return writer;
}
if (getOutputStreamCalled) {
thrownewIllegalStateException("getOutputStream already called");
}
getWriterCalled = true;
writer = newPrintWriter(stringWriter);
return writer;
}

publicString toString() {
String streamContent = null;

if (writer != null) {
streamContent = stringWriter.toString();
}
return streamContent;
}
}
The class collects all the data in a StringWriter.Lastly had to configure my web.xml to have the intercept happen.
<servlet>
<servlet-name>SessionCheckServlet</servlet-name>
<servlet-class>com.app.web.SessionCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionCheckServlet</servlet-name>
<url-pattern>/ajax/isActive</url-pattern>
</servlet-mapping>

<filter>
<filter-name>FinalResponseGeneratingFilter</filter-name>
<filter-class>com.app.filter.FinalResponseGeneratingFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>FinalResponseGeneratingFilter</filter-name>
<url-pattern>/ajax/*</url-pattern>
</filter-mapping>

This is how the output would be:
The advantage of this technique is less resource consumption. With no polling requests (thus eliminating the requests that return no data) there is less resource consumption at server. The technique is pure java script that works across browsers and does not require special features on the server side. The disadvantage in this technique is its timeliness. The events accumulated on the server side will be delivered to the client at a time client action is received. It is not immediate.

Viewing all articles
Browse latest Browse all 231

Trending Articles