therustednail

            Coding tips and generally useful stuff for guys that work for a living. Brought to you from the front lines.

Wednesday, November 16, 2005

AJAX Tip - 01 - Method for Delivery of Complex Data Types

We have been using alot of AJAX lately within our web application. I thought I would share the design pattern we are using to have the web page get multiple dynamic data points delivered through one AJAX call to the servlet. In this case the actual servlet we are using is a Struts Action, however the code is very similar for a generic Servlet as well.

We have a requirement to deliver 5 data points that change on a regular interval to a web page. This will allow the user to have a status indication updated frequently for a batch process that is running in the background. I think this is a fairly general example that many will relate to.

The first piece is the Servlet code. I will start here, as I think this is a easy place where common ground is easily understood. Here we will query the subsystem (the business objects) for the data values, and put them into the HttpServletRequest object, so that the Servlet (ActionForm) can forward to a JSP page that will out put the values for retrieval by the XMLHttpRequest object from the browser.


// this goes inside the doGet() method of a servlet or the perform() method of a ActionForm
// (Struts 1.0)
//if someone is just requesting status
if (status != null && !"".equals(status)) {
httpServletRequest.setAttribute("_filesLeft",
getImportedFileCount() + "");
httpServletRequest.setAttribute("_filesTotal",
getTotalToImportCount() + "");
return (actionMapping.findForward("scanStatus"));
}


Next, I will include the very simple JSP page. Notice that in effect we are just creating a 'CSV like' return string.


<%@ page language="java" buffer="none" import="java.lang.*"%>
<jsp:useBean id="_filesLeft" scope="request" class="java.lang.String" />
<jsp:useBean id="_filesTotal" scope="request" class="java.lang.String" />
<%=_filesLeft%>,<%=_filesTotal%>


Finally, the browser will have the generic AJAX code that we have all become accustomed to. Notice however that we just use some simple JavaScript text parsing attributes to locate some elements in the page and replace their values with those of the returned string.


In this case we will get the:
  • number of files processed as item 1 in the string.
  • the nuber of total files to process.
  • calculate the overall progress and scale a image to reflect that number.


<script language="javascript">
var xmlhttp=false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
// JScript gives us Conditional compilation, we can cope with old IE versions.
// and security blocked creation of the objects.
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
@end @*/
if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
xmlhttp = new XMLHttpRequest();
}

// Now add a timer to continually call this function every 2 seconds
setTimeout("getFilesLeft()",2000);

// function to call the AJAX servlet and return the status for the objects
function getFilesLeft()
{
xmlhttp.open("GET", "viewFilesAction.do?id=&status=true",true);
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
//alert(xmlhttp.responseText);
var str = '';
var str2 = '';
var filesDone;

// get the files left from the process as item 1 in the CSV output, and put it in the page
x = document.getElementById("filesLeft");
str = xmlhttp.responseText.substring(0,xmlhttp.responseText.indexOf(','));
x.innerHTML = str;

// get the total files we processed from the process as item 2 in the CSV output
y = document.getElementById("filesTotal");
str2 = xmlhttp.responseText.substring(xmlhttp.responseText.indexOf(',')+1);
y.innerHTML = str2;

// using that data, parse the values and scale a image in the page
// relative to the total progress
var progress = (parseInt(str2-str)/parseInt(str2));
z = document.getElementById("progress_img");
z.style.width = (progress*100)+""+"%";
filesDone = str2-str;
zz = document.getElementById("filesDone");
zz.innerHTML = filesDone;

if(str==0)
{
//we are done
document.location.replace("viewFileAction.do?id=");
}
}
}
xmlhttp.send(null);
// when we are complete add the timer again.
setTimeout("getFilesLeft()",10000);
}
</script>

I hope this helps anyone that is new to learing AJAX and needs a good example, like I did when I was starting. You should be able to take this same pattern and scale it to as many data points as you need returned from a single object in one request to the browser page. Good Luck!

0 Comments:

Post a Comment

<< Home