Using AJAX With Struts and Tiles

 

This post explains a technique for building websites using Struts and Tiles templates, in such a way that AJAX can be integrated into the application. When the user does not support AJAX or JavaScript, the website will still function because it will fall back on the standard HTTP protocols and the standard Struts framework.

Graceful degradation in this way is often referred to as “Hijax”.

This post assumes that the reader is familiar with the Struts and Tiles frameworks.

 

 

What is AJAX?

AJAX is an acronym for Asynchronous JavaScript and XML. AJAX is a scripting technique for silently loading new data from the server. Although AJAX scripts commonly use the soon to be standardised XMLHttpRequest object, they could also use a hidden <iframe> or <frame>, or even dynamically embedded <script> tags.

Although the term AJAX is in common usage, and implies the use of XML, XML is not always used. The XML component is a method of transmitting data between the server and the client. There are alternative methods for doing this, such as JSON (JavaScript Object Notation), plain text, or even HTML.

What is Hijax?

The idea behind Hijax is to build a website with AJAX in mind, but not to implement AJAX functionality until the final stages of the project. When implemented properly, this should ensure that the site degrades gracefully when AJAX or JavaScript are not available.

Ideally this should be done using Unobtrusive JavaScript techniques. Unobtrusive JavaScript works by running function(s) when an (X)HTML page has finished loading. These functions look for classes and id’s assigned to (X)HTML tags and add JavaScript behaviour to those tags.

For example, when the document loads the script could look for all (X)HTML “<A>” elements with a class named “ajax” and apply “onclick” events to those links.

What is Struts?

Struts is an open source framework for building Servlet/JSP based web applications based on the Model-View-Controller (MVC) design paradigm. Struts is packaged for use in Java and J2EE applications.

Struts is used to define the various actions which may be undertaken within the system.

What is Tiles?

“Tiles” is a component of the Struts framework which allows for templating of websites. Templates easily allow us to create consistent websites and improve re-use.


Tiles is used to define the layout of the system.

Designing the Tiles Templates

Tiles can be used to define the layout of the website. It is a templating system, which means that individual Tiles are created to represent different areas of the website. These are then brought together in a Layout file, which defines where each tile is placed.

A sample Tiles layout design

 

The Tiles shown above would be represented in a tiles-defs.xml file as follows:



<!– Filename: tiles-defs.xml –>

<definition name=“.default” path=“/jsp/layout/layout.jsp”>

<put name=“menu” value=“/jsp/menu.jsp”/>

<put name=“graphic” value=“/jsp/graphic.jsp”/>

<put name=“content” value=“/jsp/content.jsp”/>

<put name=“footer” value=“/jsp/footer.jsp”/>

</definition>


Below are some sample JSP files which could be used to represent each Tile.



For example, the Menu Tile would be a simple JSP file which renders an Ordered List of links, for example:


<%– Filename: menu.jsp –%>

<ol id=”menu”>

<li><a href=”showPage.do?link=1″>Menu Item 1</a></li>

<li><a href=”showPage.do?link=2″>Menu Item 2</a></li>

<li><a href=”showPage.do?link=3″>Menu Item 3</a></li>

</ol>


(Obviously in a real scenario the links would be driven by some form of data, such as a JavaBean)


The Graphic Tile would be a simple JSP which outputs an image, for example:


<%– Filename: graphic.jsp –%>

<img id=”maingraphic” src=”welcome.gif” alt=”Alt Text” />


The Footer Tile would also be a simple menu-like JSP, for example:


<%– Filename: footer.jsp –%>

<ol>

<li><a href=”sitemap.do”>Site Map</a></li>

<li><a href=”help.do”>Help</a></li>

<li><a href=”terms.do”>Terms and Conditions</a></li>

</ol>


The content tile would contain the main information being displayed on the site, for instance, features and specifications information for a vehicle.


These separate tiles are then brought together inside a Tiles Layout. A Tiles Layout is simply a JSP file which defines the (X)HTML structure in which the Tiles are placed.


<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>


<%@ taglib uri=“http://struts.apache.org/tags-tiles” prefix=“tiles” %>

<%@ page language=“java” contentType=“text/html; charset=UTF-8″ %>


<html:html xhtml=“true”>

<head> … </head>

<body>

<div id=”menu”>

<tiles:insert attribute=“menu”/>

</div>

<div id=”graphic”>

<tiles:insert attribute=“graphic”/>

</div>

<div id=”content”>

<tiles:insert attribute=“content”/>

</div>

<div id=”footer”>

<tiles:insert attribute=“footer”/>

</div>

</body>

</html>


The last step is to define instantiations of this template. Using the example of a “Features and Specifications” website, the site could consist of the following pages:


  • Exterior Dimensions

  • Interior Dimensions

  • Engine Data

  • Safety and Security

  • Audio Systems


These need to be added to the tiles-defs.xml file.


<!– Filename: tiles-defs.xml –>

<definition name=“.exterior” extends=“.default”>

<put name=“graphic” value=“/jsp/ext_graphic.jsp”/>

<put name=“content” value=“/jsp/ext_content.jsp”/>

</definition>


<definition name=“.interior” extends=“.default”>

<put name=“graphic” value=“/jsp/int_graphic.jsp”/>

<put name=“content” value=“/jsp/int_content.jsp”/>

</definition>


<definition name=“.engine” extends=“.default”>

<put name=“graphic” value=“/jsp/engine_graphic.jsp”/>

<put name=“content” value=“/jsp/engine_content.jsp”/>

</definition>



These definitions extend the default so that when the exterior view is requested, exterior graphics and content are displayed.

 

 

Struts Configuration

One or more actions now need to be defined which will display the various pages of the site.

In this simple example, we will look at just one action, “ShowPageAction”. This action will display a requested page, in the standard, non-AJAX way.


<action path=“/showPage”

type=“com.ford.it.ajaxdemo.ShowPageAction”

scope=“request”

unknown=“false”

validate=“true”>


<forward name=“exterior” path=“.exterior” />

<forward name=“interior” path=“.interior” />

<forward name=“engine” path=“.engine” />

<forward name=“safety” path=“.safety” />

<forward name=“audio” path=“.audio” />

</action>


A Struts Action class must now be created which will forward to the relevant page, based on a parameter; the URL to view the Exterior Dimensions would then be:


http://www.url.com/myapp/showPage.do?page=exterior


The Action would then forward to “exterior” and display the exterior page as defined by the .exterior Tiles Definition (using “ext_graphic.jsp” and “ext_content.jsp”).


Implementing AJAX

Once a working website has been constructed, it is time to implement the AJAX layer. Unobtrusive JavaScript will be used to progressively enhance the website by adding AJAX calls to the hyperlinks within the page.


The AJAX XML Document

The AJAX messages are defined as Tiles Layout JSP files. These JSP files contain XML – not (X)HTML. The file must therefore begin with the standard XML header,



<?xml version=“1.0″ encoding=“UTF-8″ ?>


<escape>


<graphic alt=text“ id=“tagid”>http://myurl.com/img.jpg</graphic>


<html id=“livearea”><![CDATA[

<tiles:insert attribute="content"/>

]]></html>


<javascript description=“start page” when=“start”><![CDATA[

alert("hello");

]]></javascript>


</escape>



The document root is called “escape”. There are then 3 types of element – html, javascript, and graphic.


<graphic>

The <graphic> tag is used to represent an image on the page. The “id” attribute should correspond to the ID of an (X)HTML <img/> tag somewhere in the web page. The content of the <graphic> element is used to replace the image source (src attribute).


<html>

The HTML tag works in a similar way. The id attribute is used to locate an (X)HTML element somewhere on the page. The content of that element is then replaced with the content in the AJAX document.


The <![CDATA[ tags are used to ensure that it is safe to place HTML fragments inside the XML document. Note that this means any included documents may notthemselves include <![CDATA or ]]> tags.


<javascript>

The <javascript> tag does not have an id attribute. Instead it has a “when” attribute which can take the values “start” and “end”. These indicate whether the javascript should be run before or after the AJAX document has updated the (X)HTML document in the web browser.


Again, <![CDATA[ … ]]> elements are used to ensure the content of the <javascript> tag does not interfere with the AJAX XML document.


JSP Tags

Note that it is perfectly acceptable to use JSP Tags in the XML document. An example if a <tiles:insert /> tag is included in the html example. Equally, a <bean:write/> or <bean:message/> tag could have been used for the alt text on the <graphic> image.


The AJAX Processor

JavaScript functions are required in order to send AJAX messages and to process the response from the server.


These methods are implemented in an “ajax.js” file. The AJAX JavaScript may be customised for each application, but the core of the code is reproduced here.


/*

(c)Copyright 2007 Ford Motor Company. All rights reserved.

*/


var req;


The “req” variable is used to hold the XML Request object.


function startRequest(url) {

 

req = getXMLRequestObject();


if (req) {

req.onreadystatechange = processStateChange;

req.open(“GET”, url, true);

sendXMLRequest(req);

return false;

} else {

return true;

}

}


function sendXMLRequest(obj) {

if (window.XMLHttpRequest) // Non-IE browsers

obj.send(null);

}

else if (window.ActiveXObject) // IE

obj.send();

}

}


function getXMLRequestObject() {

var obj;


if (window.XMLHttpRequest) // Non-IE browsers

obj = new XMLHttpRequest();

//req.overrideMimeType(‘text/xml’)

}

else if (window.ActiveXObject) // IE

obj = new ActiveXObject(“Microsoft.XMLHTTP”);

}

return obj;

}


The 3 functions above are used to generate the AJAX request.


The following function is called when an AJAX response is received, it processes the AJAX XML document.


function processStateChange() {

if (req.readyState == 4) // Complete

if (req.status == 200) // OK response


var dom = req.responseXML;

 

// If response is non-XML, simply overwrite the current

// document contents with what was received (html expected).

// This caters for displaying fatal error pages, for example.

if(dom==null || !dom.hasChildNodes()) {

document.write(req.responseText);

} else {

// —————————-

// Process start JavaScript

// —————————-

processJavaScript(“start”, dom);

 

 

// ————————–

// Update graphic

// ————————–

try {

var graphicElems = dom.getElementsByTagName(“graphic”);

for(var i=0; i<graphicElems.length; i++) {

var alt = graphicElems[i].getAttribute(“alt”);

var imagePath =

graphicElems[i].firstChild.nodeValue;

var id=graphicElems[i].getAttribute(“id”);

 

var image = document.getElementById(id);

if(!isUndefined(image) && image!=null{

image.src = imagePath;

image.alt = alt;

image.title = alt;

}

}

}

catch(ex) {// If there is an exception, continue anyway since

// there may not be a graphic element in the returned 
// XML. This caters for the AJAX Error page which simply

// displays an error notable.

}

 

// —————————-

// Update HTML

// —————————-

var i;

for(i=0; i<dom.getElementsByTagName(“html”).length; i++) {

var htmlElem = dom.getElementsByTagName(“html”)[i];

 

// Handle empty tags e.g. <html id=”x”></html>

var html = “”;

if(htmlElem.firstChild) {

html = htmlElem.firstChild.nodeValue;

}

 

var id = htmlElem.getAttribute(“id”);

var tag = document.getElementById(id);

if(!isUndefined(tag)) {

tag.innerHTML = html;

}

}

 

// —————————-

// Process end JavaScript

// —————————-

processJavaScript(“end”, dom);

}

} else {

// If the request status returned was not 200 (OK), display the

// standard locale-specific error page, since the request

// cannot be processed.

document.location = “error.do”;

}

}

}



The next function handles and <javascript> tags in the AJAX XML response.


function processJavaScript(when, dom) {

var i;

for(i=0; i<dom.getElementsByTagName(“javascript”).length; i++) {

var jsElem = dom.getElementsByTagName(“javascript”)[i];


// description used for information purposes only (e.g. debugging)

var jsWhen = jsElem.getAttribute(“when”);

 

if(jsWhen==when) {

var jsDescription = jsElem.getAttribute(“description”);

var js = jsElem.firstChild.nodeValue;


try {

eval(js);

} catch(e) {

// If the JavaScript fails to evaluate, ignore since this

// is not a critical error. Line below can be used for

// debugging, if necessary, by uncommenting.

 

// alert(“AJAX Error (” + jsDescription + “) ” + e.message);

}

}

}

}


It is possible to write JavaScript in a JSP page and include that in the <javascript> tag. For example,


<javascript when=”start” description=”some description”><![CDATA[

<jsp:include page="javascript.jsp" flush="true" />

]]></javascript>


JavaScript which is run at AJAX runtime cannot include other JS files. To get around this restriction, the “include” function (below) may be called.


// This function can be used to include .js files

// It should be used by JSPs in place of the <script src=”"/> tag.

function include(scriptpath) {

var jsReq = getXMLRequestObject();

jsReq.open(‘GET’, scriptpath, false);

sendXMLRequest(jsReq);

eval(jsReq.responseText);

}

AJAX Struts Configuration

An AJAX Action class should be created. This may extend or re-use functionality from a Struts Action class already defined, such as ShowPageAction in our example. The suggested naming convention used for AJAX Struts Action replaces the word “Action” with the word “AJAX”. So in this example we would name our AJAX Action “ShowPageAJAX.java”.


The AJAX action should forward to an AJAX forward, as defined in the struts-config.xml file.


<action path=“/ajaxShowPage”

type=“com.ford.it.ajaxdemo.ShowPageAJAX”

scope=“request”

unknown=“false”

validate=“true”>


<forward name=“AJAXexterior” path=“AJAX.exterior” />

<forward name=“AJAXinterior” path=“AJAX.interior” />

<forward name=“AJAXengine” path=“AJAX.engine” />

<forward name=“AJAXsafety” path=“AJAX.safety” />

<forward name=“AJAXaudio” path=“AJAX.audio” />

</action>



The tiles-defs.xml file also needs to be updated with AJAX tiles definitions. In this simple example, we are only updating the “content” area of the website using AJAX.


<!– Filename: tiles-defs.xml –>

<definition name=“AJAX.default” path=“/jsp/layout/AJAXlayout.jsp”>

<put name=“view” value=“”/>

<put name=“startJavaScript” value=“/jsp/startJavaScript.jsp”/>

<put name=“endJavaScript” value=“/jsp/endJavaScript.jsp”/>

</definition>


<definition name=“AJAX.exterior” extends=“AJAX.default”>

<put name=“view” value=“/jsp/ext_content.jsp”/>

</definition>


<definition name=“AJAX.interior” extends=“AJAX.default”>

<put name=“view” value=“/jsp/int_content.jsp”/>

</definition>


<definition name=“AJAX.engine” extends=“AJAX.default”>

<put name=“view” value=“/jsp/engine_content.jsp”/>

</definition>



Next we need to define a Tiles layout file which will output the XML. Create a new JSP File named “/jsp/layout/AJAXlayout.jsp”. This file should be set to have a content type of “text/xml” – this is important as some Web Browsers will not properly process the XML if it does not have this content-type.


This can be done using the <%@ page contentType=”text/xml; charset=UTF-8″ %> at the top of the JSP file.


The layout file could look something like this:


<?xml version=“1.0″ encoding=“UTF-8″ ?>


<%– —————- –

AJAX LAYOUT FILE

– —————- –%>


<%@ page language=“java” contentType=“text/xml; charset=UTF-8″ %>


<%@ taglib uri=“http://struts.apache.org/tags-bean” prefix=“bean” %>

<%@ taglib uri=“http://struts.apache.org/tags-tiles” prefix=“tiles” %>

<%@ taglib uri=“http://struts.apache.org/tags-logic” prefix=“logic” %>


<escape>

 

<html id=“content”><![CDATA[

<tiles:insert attribute="view" />

]]></html>

 

<javascript description=“start page” when=“start”><![CDATA[

<tiles:insert attribute="startJavascript"/>

]]></javascript>

 

<javascript description=“Page refresh” when=“end”><![CDATA[

<tiles:insert attribute="endJavascript"/>

]]></javascript>

</escape>




Adding the AJAX calls

The final step is to call the AJAX “startRequest” function instead of doing a normal HREF call.


Take another look at the Menu XHTML:


<%– Filename: menu.jsp –%>

<ol id=”menu”>

<li><a href=”showPage.do?link=1″>Menu Item 1</a></li>

<li><a href=”showPage.do?link=2″>Menu Item 2</a></li>

<li><a href=”showPage.do?link=3″>Menu Item 3</a></li>

</ol>



The simplest way to add AJAX is to use the “onclick” attribute, for example,


<li><a href=”showPage.do?link=1″

onclick=”return startRequest(‘ajaxShowPage.do’+this.search)”>

Menu Item 1

</a></li>


This could be further improved by writing a simple function to start the AJAX request, and add it to “ajax.js”, for example:



function ajaxShowPage(link) {

var url = “ajaxShowPage.do”+link.search;

return startRequest(url);

}



Then the onclick JavaScript event would become:


<li><a href=”showPage.do?link=1″

onclick=”return ajaxShowPage(this)”>

Menu Item 1

</a></li>


Unobtrusive AJAX Calls

Ideally the “onclick” event would not be present in the (X)HTML; instead it should be added using unobtrusive JavaScript. Please see the references section for a tutorial on Unobtrusive JavaScript; however, a summary of the steps required is included here for completeness.

First you will need an “onload” hook to ensure that JavaScript is run once the XHTML document has loaded. In our example this hook is named “addLoadEvent”. This could be added to the “ajax.js” file in our example. Ideally it would be included in a general library file.


// Add a window.onLoad event. This is an unobrusive function:

// it will maintain any other onload events required by other JS code.

function addLoadEvent(fn) {

return addEvent(window, “load”, fn);

}


// Cross browser function for maintaining a list of events to be fired

// (e..g onload, on resize

// obj - DOM which the event will fire upon

// evType - e.g. “load”, “resize” (without a leading “on”)

// fn – Function to execute when the event fires

function addEvent(obj, evType, fn){

if (obj.addEventListener){

obj.addEventListener(evType, fn, false);

return true;

} else if (obj.attachEvent){

var r = obj.attachEvent(“on”+evType, fn);

return r;

} else {

return false;

}

}


Next we need to create our hook by adding it to the beginning of the “ajax.js” file.



addLoadEvent(function(){ ajaxMenu(); });



Finally, the ajaxMenu() function needs to be included in “ajax.js”. This function will simply look for an element with the id of “menu”, then grab all of the <a> tags within it and add “onclick” events.



function ajaxMenu() {


var undef; // undefined variable

var menu = document.getElementById(“menu”);

if(menu!=undef) {

var tags = menu.getElementsByTagName(“a”);

var len = tags.length;


for(var i=0; i<len; i++) {

tags[i].onclick=function() {return ajaxShowPage(this);};

}

}

}



This ensures that the onclick events are attached to the XHTML.


For more information, see the Unobtrusive JavaScript tutorial.



References

 

 

 

blog comments powered by Disqus