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.
On the server side, I had to introduce some technique to intercept the code and The whole process worked as below:
The ResponseWrapper implementation is as below:
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.
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:
- I decided to intercept all AJAX requests processed by the server.
- 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.
publicclass FinalResponseGeneratingFilter implements Filter {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.
@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 {
}
}
The ResponseWrapper implementation is as below:
publicclass DummyResponseWrapper extends HttpServletResponseWrapper {The class collects all the data in a StringWriter.Lastly had to configure my web.xml to have the intercept happen.
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;
}
}
<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.