Categories
Coding

Ajax and Struts

The current project that I am working on makes very heavy use of Ajax. In fact, the application UI has developed to the point where the logical flow of the application has begun to resemble a thick-client (e.g. Swing) application, with the various JSP UI elements firing off asynchronous notifications from within event handlers and updating the UI when data becomes available. This has made a huge difference to the usability of the UI from the end user’s point of view. The application makes heavy use of Prototype, the Javascript framework, and we have had great success with its XmlHttpRequest handling capabilities. The controller tier is built on Struts, which delegates to a Spring-managed service layer. For instance, a sample request on the client might be invoked via Javascript as follows:

/**
* Update the list of unit names, given a handle to a <select> element with a currently
* selected unit type value
*/
fireUnitNameUpdate: function(sel) {
var unit = sel.options[sel.selectedIndex].value;
var url = Utils.contextRoot() + actionPath;
var pars = "action=showApprovalTree&type=" + unit;

if (unit == '' || unit == null) {
return;
}

var myAjax = new Ajax.Request (
url,
{
method: 'post',
postBody: pars,
onComplete: updateApprovalTree
});
}

The contextRoot() and actionPath variables construct a path to a Struts handler, which picks up the request and unpacks the parameters (Note that extra validation could be done on the client at this point). It then calls the service layer to create an XML document (constructed using Dom4J) and transforms it via an XSLT stylesheet:

public ActionForward showApprovalTree(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
String xml = approvalChainService.buildXmlApprovalTree(new Event());

// Transform the xml
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(new File(Constants.APPROVAL_TREE_XSL)));
response.setContentType(Constants.HTML);
transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(response.getWriter()));
response.getWriter().flush();
response.getWriter().close();
return null;
}

The Struts action returns an XML tree which is deserialized in the Ajax handler callback method on the client:

updateApprovalTree: function(originalRequest) {
var dom = XMLUtil.createXMLDom(originalRequest.responseText);
var nodes = XMLUtil.parseXPath(dom, "//unit");
UIUtils.updateDropDownWithXML('approvalTree', nodes);
}

This approach has proven to be very flexible and allows us to easily construct simple event handlers for UI event triggers. Building reusable libraries of cross-browser JavaScript code is now (with the help of Prototype and Behaviors) very easy. The only downside is the reduced debgging capabilities on the client side.