RichWeb GUI Components with (Plain-Old) JSP TagLibs

Jorge Simão

Abstract

I show how to implement JSP taglibs to support RichWeb GUI components in JSP pages. The custom tags map to (X)HTML and Javascript code using some JavaScript widget framework. To illustrate the approach, I use as example the definition of a <ui:input> tag similar to the Standard (X)HTML input tag, except that the type attribute can be a calendar date. This custom tag maps to an equivalent definition in (X)HTML+Javascript using the popular framework JQuery-UI. An additional custom <ui:head> tag is also defined to automatically include the resources required by the framework. I will also show how the optional use of a Filter can be used for post-process the generated (X)HTML pages and add extra functionality to the approach, such as moving Javascript to the head section of the (X)HTML document and removing duplicated Javascript code.

Keywords: JSP taglib, RichWeb GUI Components, JavaScript widget Framework, post-processing Filter

Introduction

Modern web applications often rely on rich GUI widgets that go beyond what is provided by standard HTML. A variety of approaches have been used to implement rich GUI components (widgets). With the DHTML approach, a Javascript framework is used to implement the widgets using a mix of HTML elements, CSS stylesheets, and plenty of JavaScript. Otherwise regular HTML elements are decorated so that extra behavior and styling is used to implement the widgets. Frameworks, such as JQuery-UI and Dojo are among the most popular frameworks for DHTML. DHTML is a natural extension to plain HTML, and is probably the most widely used approach. However, DHTML requires the web-designer or application developer to know the details of the selected JavaScript framework and often requires substantial JavaScript coding.

Some web frameworks take the approach of providing a set of custom XML tags in the server side, and a view-component renderer to generate the required JavaScript code and HTML markup. This approach reduces the amount of JavaScript coding that application developer need to do. Java web frameworks and view technologies such as JSF and ZK, among other, follow this approach. The drawback of this approach is that it increases the dependency of the application on specific server infra-structure. It also tends to make the designer much dependent on the specific set of components provided by the framework. Custom extension are usually possible, but this often requires intimate knowledge of the framework and advanced programming.

JSP is probably the most widely used web view technology in the Java world. JSP custom taglibs can be used to extend the basic functionalities of JSP and standard taglibs with additional types of content and behavior. One possible use of custom JSP taglibs is to define tags for GUI widgets that map to existing JavaScript frameworks. This kind of custom taglibs allows views to be written mostly in XML, with limited use of JavaScript for widget definition.

In this article, I show how custom JSP taglibs can be used to define custom GUI components that map to some JavaScript framework. We selected JQuery-UI as target framework, but adaptation of the approach to other frameworks should be straightforward. JSP taglibs are defined with tag files. Each tag generates JavaScript code and HTML markup. JavaScript code is used to decorate HTML elements, and possibly include required resources. Tag attribute values are used to customize this markup and code generation.

Simple generation of code and markup may lead to duplication when the same tag and attributes is used in the same JSP page. We show how a post-processing Filter can be used to remove this duplication. The same post-processing Filter can also be used to restructure the generated markup, such as moving all generated JavaScript code to the document head section.

The rest of the article is organized as follows: first, we demonstrate the basic approach by defining a custom <ui:input> tag that extends the behavior of the standard HTML input tag to support other data formats such as dates or numbers. A custom <ui:head> tag is also defined to automatically include the required resources as mandated by the selected JavaScript framework. Then, we rewrite the <ui:input> tag to make it easier for the post-processing Filter to move JavaScript code to the head section of the document. The Java code implementation of the post-processing Filter is discussed afterwards. The last sections compare custom the JSP tags approach to RichWeb GUI components with other approaches and summarize the main points of the article. Several resources and exercises to the reader are also presented at the end of the article.

Using TagLibs for Rich GUI Components

To motivate the use of a JSP taglib we use start by showing how a customs tags could be used to write JSP pages with produce RichWeb GUI components without the need to be write Javascript code in every page. JSP standard defines two basic mechanisms for defining taglibs. This can be done by using a Java class per custom tag plus a TLD file (Tag Library Descriptor). Alternatively, its possible to write one JSP Tag file per defined tag with all tag files of the same tag library stored in the same directory. In this article, we follow this later and simpler approach.

The demo JSP page shown below uses the attribute tagdir of the JSP directive <%@ taglib %> to include our custom taglib. We locate the taglib in a directory under /WEB-INF/tags (this is the location suggested by the JEE documentation, and some JSP containers actually required for this - e.g. Tomcat). We select as prefix for the custom taglib ui as it pertains to GUI components.

<%@ page %>
<%@ taglib prefix="ui" tagdir="/WEB-INF/tags/xpress" %>
<html>
<ui:head>
  <title>Testing XPress TagLib</title>
</ui:head>
<body>
  <p> Text: <ui:input type="text"/> </p>
  
  <p> Date: <ui:input type="date"/> </p>

  <p> Number: <ui:input type="number"/> </p>
</body>
</html>

The structure of the demo JSP page is somewhat typical of a JSP page generating (X)HTML. Namely, the head and body elements are enclosed with the top-level element html. In this case, the body contains the markup for three paragraphs each with an input field. This is similar to what would be used in a HTML data form, except that here we are not using the <form> element. In a real-world application, the <form> element would typically be present. The aim of this particular page would be to input three values of types: text, for plain text; date, for a calendar date; and number, for a numeric (decimal) number.

One important think to notice about the page markup is that the input elements <ui:input> are defined in our custom taglib. The attribute type is also used in a somewhat similar way as is done in a standard HTML input element. They key difference is that we are using types that are not allowed in standard HTML. While the type="text" is valid in HTML, the types type="date" and type="number" are not valid in HTML.

In modern web applications a variety of widget frameworks are used to implement the input elements for the types not supported by standard HTML. These frameworks use a mix of fixed and generated HTML, CSS styling, and plenty of JavaScript, to implement the desired GUI widgets, thus overcoming the limitation of plain HTML. Custom frameworks, for specific site were also very popular in the early days of rich web apps, but as general frameworks abound and become more powerful and flexibly this approach is less common these days. Popular JavaScript widget frameworks include JQuery-UI, Dojo, YUI (Yahoo solution), Spring-JS (mostly a convenient wrapper on Dojo), and many others. Extensions or plug-in for some of these frameworks are also available.

The behavior we want to implement in our custom tag lib, is to replace the <ui:input> tags with the required HTML markup and JavaScript code, targeting JQuery-UI as our selected widget framework. This allows a simpler use o JQuery-UI, with minimal coding, since we follow a more declarative approach. While some frameworks can emulate this declarative approach by recognizing made-up (non-standard) attributes in HTML elements, this is not always possible or is not available. Emulation also tends to produce a lot of complains (warnings) in the JavaScript Error console of browsers, which makes debugging the views and the application harder.

An additional advantage to define and import custom taglibs (XML elements) in JSP pages, is that this allow for the transparent replacement of one framework by another. For example, the same set of tags could be defined and recognized by two different taglibs that map to, say: JQuery-UI and Dojo. This implies that replacing one framework for another would be a simple matter of changing the Xmltagdir of the <%@ taglib %> directive.

A further point to note in the above JSP page is that the element <ui:head> is also custom tag. This should be interpreted as a meta-data grouping element, like in standard (X)HTML, except that by using a custom tag it additionally gives a chance to insert the resources required by selected JavaScript widget toolkit. This includes, CSS stylesheets, core JavaScript libraries of the framework, and optional extensions (plug-ins).

Defining TagLibs for Rich GUI Components

Defining the <ui:input> Tag

In this section, we show how the custom <ui:input> element (tag) can be implemented with a JSP tag file. Each JSP tag file defines only one tag, so if a taglib contains multiple tags then multiple tag files should be available (in the same directory). The implementation of <ui:input>, will check the type of the input desired and generates appropriate HTML markup and JavaScript code. For the purpose of this article, we use JQuery-UI as a widget framework.

A JSP tag file follow a structure and content similar to a regular JSP file. A key difference is that the directive <%@ tag%> should be used for definitions, rather <%@ page%>. Moreover, since a custom tag can have attributes, the directive <%@ attribute%> should be used to declare each of these tag attributes. The attribute name is used to defined the name of the tag attribute, while the optional attribute required is used to specify if the attribute is mandatory.

Below, we show a simplified implementation of the custom tag <ui:input>. The standard JSTL core tag lib is included to help in the implementation of the custom tag.

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ attribute name="type" required="true" %>
<c:choose>
	<c:when test="${type=='text'}">
		<input type="text" />
	</c:when>
	<c:when test="${type=='date'}">
		<script type="text/javascript">
		$(function() {
			$(".date").datepicker(); //decorate elements
		});
		</script>
		<input type="text" class="date"/>
	</c:when>
	<c:when test="${type=='number' or type=='decimal'}">	
		<script type="text/javascript">
			//TODO: implement
		</script>
		<input type="text" class="number"/>
	</c:when>
	<c:otherwise>
		<%-- fallback to plain (X)HTML --%>
		<input type="${type}" />
	</c:otherwise>
</c:choose>

The tag attribute type is defined for our custom tag with an <%@ attribute%> directive. In a real-world implementation, many more attributes should be defined -- at least those already supported by standard HTML.

The body of the tag definition contains a <c:choice> as single top-level element, with multiple cases selected according to the value of the type attribute. For type="text", we simply rewrite the element in plain HTML (without the ui prefix). This is also the behavior for the fall-back (<c:otherwise>) case. We put this code duplication to better highlight the tag coding pattern. Moreover, it could be used to add more behavior to text input (e.g. using an addition al pattern attribute, for a regular expression constraint).

The most relevant case is type="date", where we actually insert some JavaScript code that invokes JQuery-UI. The approach we use is to generate an HTML text input with CSS class date. The JavaScript code selects the set of elements with this class (using JQuery characteristic syntax, $(".date"), an setups a date input decorator by invoking JQuery-UI function datepicker(). An alternative approach, would be to select the decorated element by ID, as follows:

<script type="text/javascript">
$(function() {
  $("#date").datepicker();
});
</script>
<input type="text" id="date"/>

Selecting elements by require use to check first if an an XML id attribute is already defined. This is the reason why, for the purpose of this article, we selected the simpler approach of selecting by CSS class.

Note that the JQuery elements decoration code is always setup to be run only after the document is loaded. In JQuery jargon this is done by writing the code inside a callback function, as follows: $(function() ... ).

The case for type="number" (and type="decimal") is left deliberately unfinished -- an empty JavaScript block is generated. Only an HTML <input> element with type text is generated. This is intended to show again the coding pattern, and how other input types could be, in principle, be recognized and processed.

Note that JQuery-UI supports a declarative style for defining widgets -- in addition to requesting decoration of HTML elements. This is done by setting regular HTML elements with non-standard attributes that are search in the DOM by the JQuery-UI runtime. For JQuery-UI this declarative approach can, in many cases, be used as alternative to Javascript coding. However, not all features are available by just specifying attribute values (e.g. complex constraints in form field). For those cases, some Javascript coding is always required. In the code example above, we choose to generate JQuery-UI decorations from JavaScript because this is a more general approach. Generating HTML tags with non-standard elements from custom JSP taglibs should not present any extra level of complexity -- and should, in many cases, be even simpler.

Defining the <ui:head> Tag

Because the custom JSP tags make use of some JavaScript widget framework, JQuery-UI in the case, it is essential that the generated code includes the elements and/or coding required to include the desired resources, such as JavaScript modules and CSS Stylesheets. We make this by defining an extra custom tag <ui:head> that is mapped directly to a standard HTML <head> element, but in addition to that it automatically includes the required resources.

The tag file for the custom <ui:head> tag is show below:

<%@ tag language="java" pageEncoding="UTF-8"%>
<head>
  <link type="text/css" href="jquery/jquery-ui/css/smoothness/jquery-ui.css" rel="stylesheet" />
  <script type="text/javascript" src="jquery/jquery.js"></script>
  <script type="text/javascript" src="jquery/jquery-ui/js/jquery-ui.js"></script>

  <jsp:doBody />
</head>

Note that we use two <script> elements, one for JQuery core Javascript library, and another for the JQuery-UI library where GUI widgets are defined. Using JQuery plugin would require additional set of modules to be included. A <link> element is also used to include a CSS stylesheet for the JQuery-UI widgets. In this cases, we are using a fixed theme and filename. Extending the tag definition to allow the theme to be selected as an attribute of <ui:head> is proposed as an exercise to the reader at the end of this article.

The standard tag <jsp:doBody/> is used to include the body of <ui:head>, so that XML elements specified in the the body of <ui:head> in the JSP page appear also in the generated HTML file.

Post-processing Generated Views

The approach outline in previous sections to define RichWeb GUI components using custom JSP tag libs, although functional, presents some limitations. We start this section, by discussion some of these limitations and then show how a post-processing Filter can be used to overcome these limitation.

A first issue to consider when a tag generates markup and/or JavaScript code, is that if a tag is used more than once in the same JSP page than some markup and/or JavaScript code may be duplicated. Sometimes this is exactly what we want, but other times is desirable that a single piece of code or markup be reused across multiple tag instances. For example, in the implementation of <ui:input type="date"> tag we use a JQuery-UI selector that decorated all elements of class date. This is done with JavaScript code $(".date").datepicker(). This code could potentially be generated only once per page. Another example is the inclusion of resource required to implement that tag, such as a JavaScript module or a CSS stylesheet. Even if the same tag is used multiple times in a JSP pages, we would like (for performance sake) that the same resource be included only once. Post-processing the generated views is a possible approach that can be used to remove this kind of code and/or markup redundancy.

Another reason why we may want to so some post-processing is to rearrange the page structure. For example, for aesthetic or technical reasons, we might want to move all <script> elements to the head section of the HTML document. This may result in a page that is more readable and therefore more easy to debug.

In summary, post-processing of a generated view affords the following:

  • Remove redundant markup and/or JavaScript code.
  • Rearrange page structure, such as moving markup and code to the head section.

Rewriting the Tag File

Below, we show how the implementation of our custom <ui:input> tag refactored to more easily afford post-processing.

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>

<%@ attribute name="type" required="true" %>

<c:choose>
	...
	<c:when test="${type=='date'}">
		<%-- markup and code for (X)HTML head --%>
		<head>
			<script type="text/javascript" id="ui-input-date">
			$(function() {
				$(".date").datepicker(); //decorate elements
			});
			</script>
		</head>
		<input type="text" class="date"/>
	</c:when>
	...
</c:choose>

Elements that we want to be moved to the head section of the final HTML document are declared inside a <head> element. Because in (X)HTML standard an <head> element should appear only once in a document, and as first child of the root element <html>, this suggests that a post-processing filter should be used to move and merge the content of all generated <head> elements in a single <head> element set before the body of the document. An XML ID is also set in the <script> element show above. This can be used by the post-processing filter to detect and remove duplicates code. If code is supposed to be duplicated across multiple uses of the same tag, then the ID should not be specified.

Implementing a Post-Processing Filter

In this section, we show the implementation of a Filter that can be used to post-process generated views. This is done with class XPressFilter that extends the standard Filter class and overrides method doFilter(). The code for this class is shown below:

public class XPressFilter implements Filter {
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
	throws IOException, ServletException {
    	ServletResponse wresponse = wrapResponse(response);  	
    	
		// pass the request along the filter chain
		chain.doFilter(request, wresponse);

		PrintWriter out = response.getWriter();

		StringWriter sw = ((StringWriterSource) wresponse).getStringWriter();
		StringBuffer sb = sw.getBuffer();

		new XPressViewDOMProcessor().processAndWrite(sb.toString(), out);

	}

	ServletResponse wrapResponse(ServletResponse response) {
		return new XPressHttpServletResponse((HttpServletResponse)response);
	}

The auxiliary method wrapResponse() is used to produce a ServletResponse object that is a wrapper that prevents the response generated by servlets (such as the JSP renderer, or an MVC framework) to write directly to the response output stream. The otherwise normal processing of the HTTP request is done by invoking chain.doFilter() with the wrapped response object as argument.

After the chain.doFilter() returns we can assume that the response is has been written to the wrapper Writer. The content of the response is get as a (possible long) String. An instance of the custom class XPressViewDOMProcessor is created and method processAndWrite() is invoked to post-processed the buffered response and write the result to the output HTTP stream as it will be delivered to the client browser.

Response Wrapping and Buffering

To create a wrapper around the original javaHttpServletResponse passed by the Servlet container, we define an auxiliary custom class that extends the standard class HttpServletResponseWrapper. This class is shown below:

interface StringWriterSource {
	StringWriter getStringWriter();
}

class XPressHttpServletResponse extends HttpServletResponseWrapper
	implements StringWriterSource {
	StringWriter s = new StringWriter();
    PrintWriter out = new PrintWriter(s);
    	
	public XPressHttpServletResponse(HttpServletResponse response) {
		super(response);
	}

	public PrintWriter getWriter(){
		return out;
	}

	@Override
	public StringWriter getStringWriter() {
		return s;
	}		 
}

The custom class XPressHttpServletResponse create a StringWriter used by a PrintWriter that is returned in method getWriter(). This is the method that is used by Servlets to be able to write a response. The method getStringWriter(), defined in the custom interface StringWriterSource, return the StringWriter whose buffer contains the written response.

Post-Processing the (X)HTML Document

The actual post-processing of the response is done by the custom class XPressViewDOMProcessor. The code for this class is described in this section.

Method processAndWrite() is the entry point for the classes. The response string is first parsed as a XML document and a DOM Tree is built using utility method parse(). Method process() does all the post-processing work. Utility method write() is used to write the post-processed response to the final response stream.

public class XPressViewDOMProcessor {
	Document doc;
	Node doc_html;	
	Node doc_head;
	Node doc_body;	
	Map<String, Node> idMap = new HashMap<String, Node>();
		
	public void processAndWrite(String s, PrintWriter out) {
		try {
			doc = parse(new StringBufferInputStream(s));
			process(doc.getDocumentElement());
			write(doc, out);
		} catch (Exception ex) {
			ex.printStackTrace(out);
		}
	}

	public static Document parse(InputStream in)
	throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();	
		DocumentBuilder docb = fact.newDocumentBuilder();
		return docb.parse(in);
	}

	public boolean process(Node node) {	
		...
	}
	
	static public void write(Document doc, Writer out) {
		...
	}	
	
}

Moving Nodes to the Head Section

Below, we show the code for method process(). This a recursive method that looks for elements with tag name <head>, and clones all its child elements and inserts the clones to a single head section element in the final document. The final head element is stored in data-field doc_head. This is discovered during the traversal of the DOM tree, it it present. If it is not present, than one is created as a child of the root <html> element. If the root <html> element is not present, than it is also created.

public boolean process(Node node) {	
	switch(node.getNodeType()) {
		case Node.ELEMENT_NODE:
			if (node.getNodeName().equalsIgnoreCase("html")) {
				doc_html = node; 
			} else if (node.getNodeName().equalsIgnoreCase("head")) {
				Node phead = node.getParentNode();
				if (doc_head==null && phead!=null &&
				 phead.getNodeType()==Node.ELEMENT_NODE &&
				 phead.getNodeName().equals("html")) {
					doc_head = node;
				} else {
					if (doc_head==null) {
						doc_head = doc.createElement("head");
						if (doc_html==null) {
							doc_html = doc.createElement("html");
							Node root = doc.getDocumentElement();
							doc.removeChild(root);
							doc.appendChild(doc_html);
							doc_html.insertBefore(root, doc.getDocumentElement().getFirstChild());
						}
						doc_html.insertBefore(doc_head, doc_html.getFirstChild());
					}
			        Node child = node.getFirstChild();
			        while (child != null) {
			        	if (isDuplicated(child)) continue;
						doc_head.appendChild(child.cloneNode(true));
			            child = child.getNextSibling();
			         }
			        return true;	
				}
			}
			break;
		case Node.TEXT_NODE:
		default:
			break;
	}
	Node child = node.getFirstChild();

	List<Node> children = null;
	if (child!=null) {
		children = new ArrayList<Node>();
	}
	while (child != null) {
		if (process(child)) {
			children.add(child);
		}
		child = child.getNextSibling();
	}
	if (children!=null) {
		for (Node c: children) {
	        node.removeChild(c);
		}
	}
	return false;
}

The DOM transversal recursion works by self-calling process() for all the child nodes of the current node. process() returns true when the node, or better a clone of node, as been copied to the head section element. The child nodes that were copied are removed from their original location in the DOM Tree. Nodes that are considered duplicated, checked by method isDuplicated(), are not copied to the document head section (although they are always removed from the original location).

Removing Duplicates

Node inside a <head> are considered duplicated if they have the same XML ID. This nodes are moved to the head section of the document only once. The HashMap idMap is used to register the IDs of all moved elements. Method isDuplicated() checks the duplication and keeps track of XML ID of new elements about to be moved to document head section. This is shown below:

public boolean isDuplicated(Node child) {
	if (child instanceof Element) {
		String id = ((Element)child).getAttribute("id");
		if (id!=null && id.length()>0) {
			if (idMap.containsKey(id)) return true;
			idMap.put(id, child);
		}
	}
	return false;
}

Writing the Result

To write the post-processed DOM tree we can use a StreamResult as the destination of an identity XML transform, as follows:

static public void write(Document doc, Writer out) {
	try {
	 	//set up an indenty transformer
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer t = tf.newTransformer(); 
		t.setOutputProperty(OutputKeys.INDENT, "yes");
		t.setOutputProperty(OutputKeys.METHOD, "xml");
		t.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1");
		t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
		t.setOutputProperty(OutputKeys.INDENT, "yes");
	
		StreamResult result = new StreamResult(out);
		DOMSource source = new DOMSource(doc);

		t.transform(source, result);
		
	} catch (Exception ex) {}
}

Configuring the Filter

The post-processing Filter needs to be configured in the web.xml. We have used the following configuration and mapping:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" >

<filter>
	<filter-name>XPressFilter</filter-name>
	<filter-class>xpressjsp.XPressFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>XPressFilter</filter-name>
	<url-pattern>*.jsp</url-pattern>
</filter-mapping>

</web-app>

Sample Processed Page

Below, we show the result of the post-processing of the sample JSP page and taglib used in this article:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>

<head>
  <link href="jquery/jquery-ui/css/smoothness/jquery-ui.css" rel="stylesheet" type="text/css"/>
  <script src="jquery/jquery.js" type="text/javascript"> </script>
  <script src="jquery/jquery-ui/js/jquery-ui.js" type="text/javascript"> </script>

  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
  <title>Testing Custom JSP TagLib for RichWebApps</title>

  <script type="text/javascript">
    $(function() {
      $(".date").datepicker();
    });
  </script>
		
  <script type="text/javascript">
    //TODO: implement
  </script>
</head>

<body>
  <p> Text: <input type="text"/> </p>

  <p> Date:  <input class="date" type="text"/> </p>

  <p> Number: <input class="number" type="text"/> </p>
</body>
</html>

Note how all the <input type=".."> elements where replaced with <input type="text">, according to the implementation of <ui:input>. The required JQuery and JQuery-UI resources are also included, according to the implementation of <ui:head>. Note also that the <script> elements where moved to the document head section. This is because they where defined inside a <ui:head> element in the <ui:input> tag. The post-processing Filter, as described in this section, was involved in doing this work.

Related Work

Custom XML elements and namespaces are widely used and provided by many Web frameworks. In particular, custom XML elements are introduced in many Java Web framework to provide GUI components. On the other hand, projects that use JSP taglibs to map GUI components to existing JavaScript frameworks are not so common. Examples of such Java framework with custom XML elements, include the standard JSF framework. While JSF specification has motivated several independent implementations, the reaction to JSF as been mixed. One issue that has been raised is that using JSF view technology required web-designer to learn a completely new set of tags that are in many respect different from more familiar HTML tags. JSF approach also makes webapps too dependent on the Java infra-structure. When, in fact, there is nothing in webapps, specially views, that suggest a conceptually strong dependency to Java or any other language infra-structure.

ZK is another Java web framework that introduces new XML elements for GUI components. ZK can use JQuery+JQueryUI as a JavaScript framework to which components generate code. But other than that, similar objections put on JSF also apply to ZK.

Many of the tags provided by frameworks such as JSF and ZK do implement features already present, to some extent, in HTML. This suggests that augmenting HTML tags with new attributes and, when needed, extended HTML with some extra tags, might be an approach more easily adapted by web-designer and application developer. The evolution to HTML5 also go in-line with this perspective. Some JavaScript widget frameworks do support this (declarative) approach, such that custom attributes can be used to provided extra information about the behavior of widgets (e.g. JQuery-UI and Dojo). The declarative approach on the client-side can be, however, problematic. Not all features may be equally covered by custom attributes, and non-standard attributes tend to generate errors in the Javascript console of browsers.

Adobe Flex infra-structure provides a XML language named MXML that provides declaritivity in defining GUIs from components. MXML tag names map to Flex/ActionScript classes, and are therefore very much dependent of the details of the used Flex framework(s). Flex is also most based on a proprietary technology, which is a put back in many projects (although a open-source SDK is available). Still, Flex is very popular approach to develop RIA.

Laszlo is another XML language with custom tags for GUI components. Laszlo maps its tags to Flex/ActionScript components or to DHTML components (from Dojo framework, in current implementation).

XUL is another XML language to define GUIs used internally by Mozilla Foundation Browser - Firefox. However, XUL is not a web view technology, as it is used solely to extend the GUI of the Firefox browser.

Summary and Conclusions

I have shown how custom JSP taglibs defined with tag files can be used to implement GUI components to be used in RichWeb Apps (RIA). The custom tags are mapped to plain HTML and existing JavaScript frameworks. With this way, most of the heavy work of providing widgets is deferred to a proven framework. The use of custom tags for GUI components greatly simplifies the work of building views, since most JavaScript coding may be generated automatically by the tag implementation.

I illustrated the approach by showing the implementation of a custom <ui:input> tag similar to the standard HTML input tag, except that other types of inputs are supported such as calendar dates. JQuery-UI was selected as target JavaScript framework. Another custom <ui:head> tag was used to include required resources.

I discussed how the generation of markup and/or JavaScript code can present issues when the same tags are reused in the same JSP page. First, some code or markup may be duplicated. Secondly, aesthetic or technical reason might required restructuring of the generated DOM tree. I showed how a post-processing Filter can be used to overcome these issues in a effective and simple way. The content of generated <head> elements are moved to the document head section, and XML ID are used to detected and avoid duplication of markup and script code.

Exercises to the Reader

Here I propose a set of extensions and developments to the approach layout in this article for defining RichWeb UI components using custom tag files. They are left as exercises to the reader. The exercises assume the reader is somewhat comfortable with programming of JSP+EL, and JQuery+JQuery-UI and/or other JavaScript frameworks. If not, you can check the resources section at the end of the article.

  • Modify the tag file for <ui:head> to accept a theme attribute that specifies the name of the JQuery theme to use. This includes specifying an additional <%@ attribute%> directive, and make the attribute href of the HTML <link> element to evaluate to a dynamic value using a EL expression (syntax ${...}).
  • Complete the implementation of the tag file for <ui:input> to support other values in the type attribute, including: number (or decimal), integer, currency, and any other types that you find useful and easy to map to JQuery-UI.
  • Consider defining new custom tags (each in a dedicated tag file), to use other widgets or features of JQuery+JQuery-UI, including: tabbed panels and accordions, effects, and other. Consider also defining custom tags for features made available only with JQuery plug-ins such as drop-down menus (e.g. consider the Superfish menu plugin).
  • JQuery is only one possible target framework for the defined custom tags. Consider reimplementing the same set of tags and features above, but this time to other frameworks such as Dojo or Spring-JS.

Resources

Local

External

Acknowledgements

If you have any comments or suggestions that you think may improve this document please send an e-mail to the author.

Sponsors
(c) Jorge Simão, 2011