AJAX bit by byte – Part II
Posted On August 14, 2007 by Priyadarshan Roy filed under Internet
In the earlier article, we used the XMLHTTP object to check for a duplicate login id. But it ran into browser caching issues. Try opening temporary internet files for IE browser, you would see user1.xml cached.
So what’s the problem if the file is cached, the application is still working, isn’t it?
The problem will occur when you want to submit the login-id to the XML file and try to fetch it once again. Suppose you have updated the xml file with the new login id. Now, when you again re-type your login id, there should be an error message. But nothing of that sort happens, this is because the old xml file is cached, it does not contain the new id you had just submitted so the application would use that file instead of the newer one. If you delete user1.xml from the cache, it will work just fine.
All this trouble was because we were using a GET request, to rectify this we need to use POST -
This article would primarily focus on POST method calls to servlet along with an addition of Auto ZIP feature, so you can expect a lot of code.
It is divided into two sections –
1. Using a POST request to prevent browser caching and updating the xml file with servlets.
2. Adding a Auto ZIP feature that shows the name of the location when the user types the ZIP code.
SECTION 1- Using POST Method:
We would have to redesign the way our small application behaves. This can be further divided into three steps,
1. Use the post method to call the servlet.
2. Use our servlet to check for the duplicate login-id –
A. If login-id is present, servlet returns “invalid”.
B. If login-id is absent, servlet updates the xml file with the new login-id and returns “valid”.
3. Finally, modify the AJAX layer to deal with the response.
Step 1: using POST to call a servlet
In the last article we had seen the listing of the function init_request () (Code 3). We need to modify it a little bit to serve our purpose.
CODE 9
|
The function is modified to accept a parameter called ‘choice’. This choice is actually the login text box/object‘s name as you can see below.
| <INPUT TYPE="text" NAME="user" onBlur=' init_request (this.name)' onFocus='clear_text()' > |
Getting back to our function, we have made use of another function called setURL (), this function sets the URL to be called by open () method of the xmlhttp object.
CODE 10:
|
{ if (choice=="user") return ("http://localhost/hello/servUpdateXML?search="+escape(document.frm.user.value)); } |
The URL that is returned calls the servUpdateXML servlet with an URL parameter as search which contains the value typed in the login id text box.
* The escape (string) encodes the string to make it portable i.e. in ASCII format. For example a blank space is represented by %20 etc.
Finally the open () is configured to call the server’s servUpdateXML with a POST method.
Apart from these changes, the rest of the function is same as you had seen in the first article. A possible question may arise, why are we using setURL () when we can hardcode the URL? things will be clearer as we progress...
Step 2: Servlet Description
A servlet needs to be constructed which would respond to the call, lets call it servUpdateXML.
| CODE 11 servUpdateXML import java.io.IOException; public void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException, IOException try { if(req.getParameter("search")!=null) // duplicate user id search flag=xmlfile.SearchUserName(req.getParameter("search")); /************ after pressing SUBMIT BUTTON ************* */ if(req.getParameter("name")!=null) // updating the XML file with the new user name String strUpdateValue=req.getParameter("name"); res.setContentType("text/xml"); |
Getting into the servlet, a POST call would be handled by doPost () method, here if a request parameter named ‘search’ is present (refer CODE 10: setURL ()) an object of a class named UpdateXML is instantiated. This class contains two methods:
|
After the instantiation, the object’s SearchUserName () method is called and value of the request parameter – ‘search’ i.e. the value in the login id text box is passed as the argument.
Since this returns a boolean value, this is caught in boolean variable named flag. If value of flag is true,
‘INVALID’ is written to the response i.e. a duplicate of the login-id exists, otherwise ‘VALID’ is written. The rest part of the servlet will be dealt later on this article.
Let see the UpdateXML class which we are using.
|
Coming to the SearchUserName (String name) part first, Parsing of the user1.xml is needed for searching its content. In our case, JDOM is used as an alternative to DOM or SAX. This article would need you to have basic idea about DOM, SAX XML parsing in order to better grasp what’s going on.
In a simplest form, you can say - SAX and DOM are two different techniques that XML/XSL parsers like Xalan and Xerces use. While DOM requires the entire document to be parsed and stored into memory, hence not good when you work on huge documents (use SAX instead). While SAX is a serial access parser API that uses an event based approach (not good when lot of to and fro traversing is required).
For more info about SAX refer to http://www.saxproject.org/
JDOM on the other hand is java specific. It’s a java based representation of a XML document, it works well with SAX and DOM and uses parsers (default being JAXP) to build the document and is simpler to use than rest of the two. For more into JDOM refer http://www.jdom.org/
Getting back to our code, the UpdateXML () constructer creates a SAXBuilder and declares a String named URL. SAXBuilder is a class that builds a JDOM document from streams, files and readers etc using a SAX parser.
Inside our SearchUserName (String name) we have created a JDOM document object, ‘doc’, which refers to the JDOM doc that is built from our user1.xml
| Document doc = builder.build(new File(URL)); |
The ‘URL’, is the path of user1.xml in the server. The second step creates an org.jdom.Element called root and assigns to it the root element of the document. In our case the root is ‘login’. See code 5:user1.xml
| Element root=doc.getRootElement(); |
We use this root element to get all the children of the xml doc. In our case it is all the ‘data’ nodes in user1.xml.
Once we get this, we just iterate through the list of children and if any of the value matches with ‘name’ part of the method argument, we break and return a flag whose value is true.
STEP 3: Dealing with the response
Coming back to our JavaScript AJAX layer:
| var status=search(document.frm.user.value,xmlhttp); if(status) update (); |
We have defined a search () function, which takes in the same parameters as the search () function defined in the earlier article, but this time we have to change the logic to suit our needs.
| function search(str,xmlhttp) { if(xmlhttp.responseText=="INVALID") return true; else return false; } |
Notice the use of responseText property; we have used this as the response is in form of text and not xml as was the case in our earlier article. When ever ‘status’ is true, update () function is called which is exactly the same as defined in code 6.
This sums up our searching part. The browser wont catch our little user1.xml file.
Notice how we have used more of serverside processing. This not only prevents browser caching but improves security and robustness. A simple viewsource would have earlier revealed a lot about our working logic. This application mind you is not foolproof yet; these security concerns would be addressed in forthcoming issues.
UPDATING THE XML FILE:
OK, now what if search returns false i.e. the name typed by user is unique and not present in user1.xml?
The submit button would now be enabled and user can submit the details. Hence we need to update the xml file with this new id so that when user retypes this id, he/she should get a duplicate id error message.
The UpdateXML class contains another method called createNewDoc () which accepts a string value, ‘updateValue’ – the new login id name. Notice that this method is called only if the user has clicked on the submit button which is only enabled when the SearchUserName () returns false – i.e. valid.
The working is almost same, we create and build a JDOM document from user1.xml and get the root element i.e. ‘login’, next we create a new element called data and add a text content to it whose value is the new login-id name i.e., ‘updateValue’ that we want to update in our xml file.
| Element data=new Element("data").addContent(updateValue); |
We have to now add this new element to the JDOM doc. Since this element ‘data’ is the child of the root element ‘login’, we append this to the root element.
The Element addContent (Content child) serves our purpose. This appends the child to the end of the element list.
| root.addContent(data); |
We use this to append our new data element to the root element so that it becomes the child of the root element. The only thing left is to save this JDOM doc to the user1.xml.
| XMLOutputter outputter=new XMLOutputter(" ",true); |
JDOM provides an XMLOutputter class for this purpose. The XMLOutputter class is used to output the doc to streams.
This class has some formatting options, we have made sure by our above declaration that the child elements would be indented two spaces from the parent element. If you look at code 5:user1.xml the difference between starting of the <login> tag and <data> are exactly two spaces, the “true” parameter forces the outputter to output each new element in a new line.
Finally we output our JDOM document ‘doc’ to the File stream ‘fw’ where we mentioned the path of our user1.xml file.
Now the user1.xml file can be updated at will and is ready to use.
SECTION 2: Adding a ZIP code feature:
This feature displays the corresponding area associated to the ZIP code whenever the user types the ZIP code. The idea is to store some zip code and associated area as key value pairs in a properties file, so that whenever the user has finished typing the zip code, the servlet is queried and if the zip code matches with the content of the properties file, the corresponding area name is sent back to the AJAX layer for display.
If no matches are found, a ‘NOT FOUND’ message is passed to the layer.
The screenshot of the process is shown below.

So how do we accomplish this? First we modify the HTML page by adding the below mentioned lines -
|
The working is same as that of the user text box, when the textbox loses focus, onBlur event is fired which calls init_request () with the value of ‘zip’ as the argument. Note that we have assigned a maxlength of 6 as no zip code in INDIA can be more than that and we have made the default value as 4000 as Mumbai zip codes begin with this.
Next, we modify the AJAX layer to support this feature. We start with the setURL () function.
| CODE 13:
{ if (choice=="user") return ("http://localhost/hello/servUpdateXML?search="+escape(document.frm.user.value)); return ("http://localhost/hello/servUpdateXML?zip="+escape(document.frm.zip.value)); } |
We have added another return statement that gets into action when the ‘choice’ parameter is not equal to ‘user’. In our case it is ‘zip’.
Now let’s move on to our main function i.e. init_request ()
| CODE 14: var status=search(document.frm.user.value,xmlhttp);
} updateZipSTATE(xmlhttp); } |
Adding the above lines marked blue/shaded now checks for ‘choice’ against ‘zip’, if this is true, updateZipSTATE (xmlhttp) is called.
Even we have added a whole new feature, there is a small amount of coding done and this doesn’t affect our old application.
Quickly moving into the updateZipSTATE () – we just display the result of the response.
| CODE 15: function updateZipSTATE(xmlhttp) // for showing the zip state value { document.frm.zipstate.value=xmlhttp.responseText; } |
Before proceeding an important thing to note is:
Whenever expecting a server response in terms of xml add-
xmlhttp.setRequestHeader ("Content-Type", "application/x-www-form-urlencoded");
Otherwise the server would discard the request.
The client side is ready now to accept the server response, but we need the server response right?
For this we modify our servlet a bit. When the following lines of code are added to the servUpdateXML servlet-
| CODE 16 : servUpdateXML servlet
if(req.getParameter("zip")!=null) // to reflect the state from the zip code { String zipcode=getZIP(req.getParameter("zip")); res.getWriter().write(zipcode); } |
We test for the request parameter called ‘zip’ that we had sent earlier.
If such parameter is found, then getZIP () function is called with takes the request parameter’s value i.e. the zip code supplied by the user
| CODE 17 : public String getZIP(String lzip) } catch(Exception ex) |
Moving on to the getZIP (), this creates an object of the ResourceBundle class. This class lets you work with locale-specific data, with that I mean – geographical/country specific data.
An example where you generally use this is for displaying country/region specific error messages in a distributed application. In our case we use default Locale.
getBundle() method is called which takes as an argument String basename, which can be a class file or a properties file, in our case we use a properties file called zip.properties. As I had already mentioned a properties file is nothing but a text file with a .properties extension with data in key-value pairs.
The getBundle () is a static method of ResourceBundle class, this first looks for a class file, if it doesn’t find one it looks for a properties file, if this is found it returns a PropertyResourceBundle object (which is subclass of ResourceBundle class ) containing the key-value pairs of properties file. If this is also not found, well, it just throws a MissingResourceException.
A look at zip.properties file will I guess clear all the doubts.
| Code 18 400005= Colaba // key = 400005 value = Colaba |
The rbzip object now contains key-value pairs of the above properties file. The getString () method takes as an argument the key and returns the value of that key, in our case, key is the value the user typed in the zip text box. So a 400005 would return “COLOABA”, 400008 would return “Mumbai Central”.
In case of any exception the servUpdateXML servlet returns “NOT FOUND”.
This wraps up the whole article. This is just a small step towards AJAX which is growing by leaps and bounds. Many AJAX toolkits are available like SAJAX, DOJO but, I would advise you manually code a few applications while using them.
A FINAL NOTE: JSON
Just for the record, XML though very popular, is not the only way to interchange data across AJAX layer and server. JSON (JavaScript Object Notation) can be used for this purpose and it has already gained many admirers.
JSON is a lightweight data interchange format. The major advantage it has over XML is that it is easier to parse and is less verbose.
The same user1.xml that we had used can be re-written in JSON –
| user1.xml <?xml version="1.0" encoding="UTF-8" ?> <login> <data>gaurav</data> <data>archita</data> <data>deepak</data> <data>subhasish</data> <data>anudweipayan</data> </login> |
| user1.xml in JSON { "login": [ "gaurav","archita","deepak","subhasish","anudweipayan" ] } |
This may not be of much difference, but when you consider complex structure, JSON has a slight advantage
JSON is data oriented as opposed to xml which is more into document structure, so JSON is being touted as being better data interchange format. But there is still a question that hasn’t been answered… what about parsing JSON?
JSON can be parsed using JavaScript eval ()
eval ( ‘ (‘ + xmlhttp.responseText +’)’ )
Getting deep into JSON is beyond the scope of this article and would require another article dedicated to this topic so to know more about JSON refer http://www.json.org/
Hopefully in my forthcoming articles, I would present deeper insight into AJAX and JSON. Any comments/suggestions about this article would be greatly appreciated.
********** ***********
ABOUT Author:
He is have been working at TATA Consultancy Services Ltd, TCS, for the past seven months as a web developer on J2EE after completing my B.E on 2005 at ITER, Bhubaneshwar.
Mail: gaurav_devdutt@yahoo.co.in
