Script-Free JavaServer Pages with the Standard Tag Library (JSTL)

Script-Free JavaServer Pages with the Standard Tag Library (JSTL)

by Dean Wette, Senior Software Engineer

September 2002


Overview and Background

Java Servlets

For some time now web application developers have utilized Sun's Java Servlet APIs for processing HTTP query and submission requests from client browsers, and generating dynamic HTML (or XHTML) content as a response. Put plainly, Servlets are services, implemented in the Java programming language, that run inside a J2EE-compliant "container," typically a web or application server. A container hosts Servlets by handling the low level details of the HTTP protocol, managing the Servlet service lifecycle, and executing Java code written by developers to handle the HTTP request/response cycle associated with user requests. Furthermore, the Servlet API provides developers with high-level Java interfaces that expose lower level aspects of the HTTP protocol and service requests in a more object-oriented manner. This includes accessing the values of user query and form parameters; other information about client and HTTP requests; and facilities for managing HTTP forwards, HTTP redirects, and user state; among other things.

An example of a simple Servlet follows:

  1. import java.io.*;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4.  
  5. public class HelloWorldServlet extends HttpServlet {
  6. protected void doGet(HttpServletRequest req, HttpServletResponse res)
  7. throws IOException, ServletException {
  8. res.setContentType("text/html");
  9. PrintWriter out = res.getWriter();
  10.  
  11. // "name" comes from the HTTP GET request
  12. // e.g. http://localhost:8080/servlets/hello?name=Dean
  13. String nameParam = req.getParameter("name");
  14. if (nameParam == null) nameParam = "World";
  15.  
  16. out.println("<html>");
  17. out.println("<head><title>HelloWorld Output</title></head>");
  18. out.println("<body>");
  19. out.println("<h1>Hello, " + nameParam + "!</h1>");
  20. out.println("</body>");
  21. out.println("</html>");
  22. }
  23. }

Several observations can be made about this example. One, Servlets are easy to write. The HttpServlet, HttpServletRequest, and HttpServletResponse objects provide access to most of what a developer needs to create simple Servlets like this, as well as ones considerably more complex, like a web-based shopping cart, for example. The example above doesn't reveal the rich set of features the Servlet API provides, but one only needs to examine the API documentation briefly to understand that Servlets can be quite sophisticated.

The second point to note illustrates a relative shortcoming of Servlets. The HTML for the response sent back to the HTTP client is embedded as a series of strings emitted in print statements to an IO stream. This is problematic for a couple of reasons. Responsibility for creating HTML lies in the hands of the Servlet developer, meaning greater expense in terms of training and engineering costs. Furthermore, web site maintenance becomes more difficult since changes to the content and/or appearance of the web site requires changes to the Java code and all those print statements, which get quite ugly with any but the simplest Servlets. Early solutions to this problem include delegating production of HTML fragments to dedicated Java objects, but fail to address another concern.

Generally, content authors working within a more limited context of HTML, JavaScript, CSS, and related technologies are less costly to utilize than server-side engineers working in Java. The approach illustrated above virtually eliminates using content authors for anything but prototyping. To involve them in creation of production code requires additional training and expense. Solving this problem requires an approach inspired by classical Model-View-Controller (MVC) architecture, an object-oriented framework pattern that separates data (the Model), presentation (the View), and application logic (the Controller) in a loosely-coupled design with well-defined interfaces.

Using XML and XSLT

One solution that utilizes MVC involves eXtensible Markup Language (XML) and eXtensible Stylesheet Language Transformation (XSLT). This approach has Servlet logic that creates an XML representation of the dynamic content (data) intended for presentation, and uses an XSLT stylesheet to transform the XML data into HTML sent as a response to the HTTP client. The presentation logic moves from the Servlet code to the XSLT stylesheet, which implements a series of production rules for embedding the data into HTML and emitting the result HTML document the client sees.

The details of XML/XSLT are beyond the scope of this article, but a Servlet using this approach, along with the Java API for XML Processing (JAXP), might look something like this:

  1. import java.io.*;
  2. import javax.servlet.*;
  3. import javax.servlet.http.*;
  4. import javax.xml.transform.*;
  5. import javax.xml.transform.dom.*;
  6. import javax.xml.transform.stream.*;
  7. import org.w3c.dom.*;
  8.  
  9. public class XSLTServlet extends HttpServlet {
  10. protected void doGet(HttpServletRequest req, HttpServletResponse res)
  11. throws IOException, ServletException {
  12. res.setContentType("text/html");
  13. ServletContext context = getServletContext();
  14.  
  15. // get the data as XML DOM document
  16. Document xmlDoc = ...
  17. // get the XSLT stylesheet
  18. InputStream xsltStream = context.getResourceAsStream("stylesheet.xslt");
  19. Source stylesheet = new StreamSource(xsltStream);
  20. Source xml = new DOMSource(xmlDoc);
  21. // result of transform goes to response
  22. Result result = new StreamResult(res.getWriter());
  23.  
  24. TransformerFactory factory = TransformerFactory.newInstance();
  25. Transformer transformer = factory.newTransformer(stylesheet);
  26.  
  27. // emit result of XML/XSLT transformation to HTTP response
  28. transformer.transform(xml, result);
  29. }
  30. }

Clearly this is a improvement since no HTML is embedded in the Servlet code. If changes need to be made to the resulting HTML or how the dynamic content is handled, that requires nothing more than editing the XSLT stylesheet residing in the web application directory on the server. The servlet itself does not need to be changed, nor recompiled and redeployed. The Servlet developers focus on production of application and data logic, leaving content authors to maintain the presentation logic separately. However, this approach also requires XML/XSLT expertise on the part of the content authors, and XSLT (along with XPath that it depends on) has a steeper learning curve than HTML and associated technologies that content authors use traditionally. Nevertheless, it's one that works quite well and sees wider use all the time.

JavaServer Pages (JSP)

There is an alternative solution that is adopted widely. JavaServer Pages (JSP), a technology that parallels Java Servlets, addresses another approach to the problem of embedding HTML content into application code implemented in Java. Using JSP, content authors develop regular HTML documents, but add JSP scripting elements and tags for specifying the dynamic aspects of the content. Unlike JavaScript, JSP pages are processed on the server (rather than in the client browser), and in fact are translated into Servlets by the JSP container. This implies that JSP containers are also Servlet containers, which is indeed the case. It also means that JSP pages have access to the same information the Servlet API provides to Servlets. JSP pages can also collaborate with JavaBeans (Java classes with interfaces conforming to well-defined conventions) residing on the server.

A typical JSP page might look something like the following. It creates a web page for looking up an employee's information and displaying phone numbers in a table.

  1. <%@ page import="java.util.*" %>
  2. <!-- instantiate a JavaBean object -->
  3. <jsp:useBean id="employee" class="Employee"></jsp:useBean>
  4. <!-- set the id property on the JavaBean from a request parameter
  5. which results on setting the other bean data -->
  6. <jsp:setProperty name="employee" property="id"></jsp:setProperty>
  7.  
  8. <html>
  9. <head><title>
  10. <!-- like calling employee.getFullName() on the bean -->
  11. <jsp:getProperty name="employee" property="fullName"></jsp:getProperty>'s Phone Numbers
  12. </title></head>
  13. <body>
  14. <h2>
  15. <jsp:getProperty name="employee" property="fullName"></jsp:getProperty>'s Phone Numbers
  16. </h2>
  17. <table>
  18. <tr><th>Type</th><th>Number</th></tr>
  19. <!-- use a JSP scriptlet to iterate over a Map -->
  20. <% Iterator iter = employee.getPhones().entrySet().iterator();
  21. while (iter.hasNext()) {
  22. Map.Entry entry = (Map.Entry)iter.next(); %>
  23. <tr>
  24. <td><%= entry.getKey() %></td>
  25. <td><%= entry.getValue() %></td>
  26. </tr>
  27. <% } %>
  28. </table>
  29. </body>
  30. <html>

This example illustrates how easy it is to access Java objects conforming to the JavaBeans conventions without direct knowledge of the Java programming language. Unfortunately, the <jsp:useBean> and associated JSP tags don't support indexed bean properties nor other Java collections. Iteration and branching logic, as well as other common tasks such as date and number formatting, require using JSP expressions and scriptlets implemented with Java code. The example gets even more complicated if one changes it to check whether the map of phone numbers is non-empty before outputting the HTML table at all. This typically involves using a scriptlet with conditional logic where the HTML table tags are emitted as Java print statements within the scriptlet. It doesn't take a long stretch of imagination to realize this approach to separating presentation and application logic has shortcomings. Where the early Servlets suffered from HTML print statements embedded in Java code, JSPs evolve easily into the opposite and equally undesirable problem of embedding too much Java code in HTML.

But the facilities in JSP for authoring dynamic web pages don't end here. Using APIs in the JSP specification for creating custom actions, one can delegate branching, iteration, and other Java scriptlet code to dedicated Java classes separate from the JSP page, and define JSP tag interfaces to that code. A JSP page can then call the Java code indirectly by using the custom JSP tag element, rather than defining the code directly. Entire custom tag libraries can be created to remove Java code from JSP pages. With custom tags, authoring presentation content can be put back in the hands of non-programmer content authors.

The previous example implemented with custom tags might look something like this:

  1. <%@ taglib uri="/ocitaglib" prefix="oci" %>
  2. <jsp:useBean id="employee" class="Employee"></jsp:useBean>
  3. <jsp:setProperty name="employee" property="id"></jsp:setProperty>
  4.  
  5. <html>
  6. <head><title>
  7. <jsp:getProperty name="employee" property="fullName"></jsp:getProperty>'s Phone Numbers
  8. </title></head>
  9. <body>
  10. <h2>
  11. <jsp:getProperty name="employee" property="fullName"></jsp:getProperty>'s Phone Numbers
  12. </h2>
  13. <!-- insert the table of phone numbers using custom tag
  14. passing it the employee bean object as an tag attribute value -->
  15. <oci:empPhoneTable emp="<%= employee %>"/>
  16. </body>
  17. <html>

The <oci:empPhoneTable> tag calls Java code that translates the map of phone numbers for the given employee into a HTML table that is inserted into the output of the JSP. Custom tags can come in many forms - with or without attributes, body content, and so on - and perform any tasks one might expect regular Servlet code to handle. The above tag might be implemented differently, where it only performs iteration over the map key/value pairs and HTML for the table remains in the JSP code. So instead of the <oci:empPhoneTable> tag, one might create something more reusable, a tag for iterating over key/value pairs of any map:

<table>
  <tr><th>Type</th><th>Number</th></tr>
  <!-- use a JSP custom tag to iterate over a Map -->
  <oci:mapEntryIterator map="<%= employee.getPhones() %>">
    <tr>
      <td><%= key %></td>
      <td><%= value %></td>
    </tr>
  </oci:mapEntryIterator>
</table>

In either case, most of the pure Java code is removed from the JSP, which is desirable. It's why custom tag libraries are very popular and in widespread use. However, a few problems remain. Many types of actions perform very common and useful tasks, such as the map entry iterator above. However, since they are implemented as custom tags, they vary in specifics and syntax from project to project, enterprise to enterprise, and so on. Furthermore, as one sees from the example, a Java expression is still used to supply the attribute value in the <oci:mapEntryIterator> tag. There's a lack of standardization for common and useful tasks, and some dependence on use of Java code for expressions is still necessary. Until now that is.

JavaServer Pages Standard Tag Library (JSTL)

Some time ago the developer community recognized this lack of standardization and other problems reaching the goal of code-free JSP pages, so an expert group in the Java Community Process (JCP) was formed to address this as the Java Specification Request (JSR) 52 for the JavaServer Pages Standard Tag Library (JSTL). The result of this is the first Specification and Reference Implementation of JSTL 1.0, released in the summer of 2002. JSTL includes a set of four standard JSP tag libraries for performing general purpose tasks, URL-related tasks, relational database access, XML processing, and internationalization & formatting. It also introduces Expression Language (EL) - for specifying tag attributes values - that is higher level and simpler than the Java programming language.

Drawing from the above examples, a JSP using JSTL looks like this:

  1. <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
  2.  
  3. <jsp:useBean id="employee" class="Employee"></jsp:useBean>
  4. <jsp:setProperty name="employee" property="id"></jsp:setProperty>
  5. <c:set value="${employee.fullName}" var="empName"></c:set>
  6.  
  7. <html>
  8. <head><title>
  9. <!-- use JSTL tag instead of <jsp:getProperty> tag -->
  10. <c:out value="${empName}"></c:out>'s Phone Numbers
  11. </title></head>
  12. <body>
  13. <h2>
  14. <c:out value="${empName}"></c:out>'s Phone Numbers
  15. </h2>
  16. <!-- use a JSTL tag to check if employee phones is non empty
  17. with the 'empty' bean property of the Map interface -->
  18. <c:if test="${not employee.phones.empty}">
  19. <table>
  20. <tr><th>Type</th><th>Number</th></tr>
  21. <!-- use a JSTL tag to iterate over a Map -->
  22. <c:forEach var="entry" items="${employee.phones}">
  23. <tr>
  24. <td><c:out value="${entry.key}"></c:out></td>
  25. <td><c:out value="${entry.value}"></c:out></td>
  26. </tr>
  27. </c:forEach>
  28. </table>
  29. </c:if>
  30. <!-- another test for map content using JSTL 'empty' operator -->
  31. <c:if test="${empty employee.phones}">
  32. <c:out value="No numbers listed for ${empName}"></c:out>
  33. </c:if>
  34. </body>
  35. <html>

This JSP example is entirely free of Java language scripting elements, and somewhat more sophisticated than the previous ones. It performs the map iteration using the JSTL <c:forEach> tag, but also tests that the map of phone numbers is non-empty using the JSTL <c:if> tag. A JSTL tag for "if-else" type conditional testing exists, but this example uses the two "if" tests to show one example that takes advantage of JSTL support for JavaBean conventions and another utilizing the EL empty operator. As with many other languages, there's more than one way to do things with JSTL.

Expression Language

JSTL's Expression Language (EL) has a simple syntax inspired by ECMAScript and XPath. For now EL can only be used in tag attribute values, but the expert groups for the next specifications of JSP and JSTL are defining support for using EL in tag bodies and other template text as well (this will appear in the JSP 2.0 specification). EL is easy to use, hides the details of the page language (Java), and custom tags can be implemented to support it. Expressions may include static text and/or multiple expressions combined in attribute values to form larger expressions, and may include the use of EL operators for boolean expressions. For example:

  1. <some:tag attr="${expr}"></some:tag>
  2. <some:tag attr="${expr1}${expr2}"></some:tag>
  3. <some:tag attr="${expr1} some static text ${expr2}"></some:tag>
  4. <some:tag booleanAttr="${x.a eq y.a}"></some:tag>

EL supports JavaBeans conventions for accessing object properties, like ${employee.address.zip} used to retrieve the nested "zip" property from the employee object. It also provides a rich set of operators including the dot (.) operator used to access bean properties; the subscript ([]) operator to access array and collection indices and for map key lookups; relational, arithmetic, and logical operators similar to those found in most modern programming languages; and the empty operator seen in the example.

The real goal for EL is to provide a way to create JSPs using custom and/or standard tags that are entirely free of Java language declarations, expressions, and scriptlets.

JSTL Tag Libraries

The other major component of JSTL comprises the four tag libraries mentioned above. For the most part, the authors designed them to provide a standard means for implementing common and useful tasks that have been the subject of many different custom tag implementations.

Core Actions

The tag library identified by the URI http://java.sun.com/jstl/core, consists of tags for four categories of core actions. These include:

  1. General Purpose Actions:
    • <c:out> - displays values of EL expressions
    • <c:set> - sets value of JSP scoped variables from EL expressions
    • <c:remove> - removes a JSP scoped variable
    • <c:catch> - catches exceptions thrown by nested tags
  2. Conditional Actions:
    • <c:if> - for simple tests
    • <c:choose>, <c:when>, <c:otherwise> - for mutually exclusive tests, used to implement "if-else" and switch-like branching
  3. Iterator Actions:
    • <c:forEach> - iterates over items in an array, a comma-delimited string, Collection, or Map; or for looping through a specified count
    • <c:forTokens> - tokenizes a delimited string
  4. URL Actions:
    • <c:import> - imports static and dynamic resources into a JSP
    • <c:url> - rewrites URLs for session tracking
    • <c:redirect> - performs HTTP redirects
    • <c:param> - encodes URL parameter names and values, used as a nested action of , , and

Many of the core tags listed here can be seen used in the example given earlier, as well as those that follow.

SQL Actions

In general accessing relational databases using in a web application should be delegated to a separate data tier, but sometimes it can be useful to perform database lookups directly from a JSP. Typical scenarios include web application prototyping, or developing small web applications that are fairly simple, have small user communities, and low security requirements. To facilitate developing JSPs with direct database access using SQL, the JSTL includes a tag library of database actions, identified by the tag URI http://java.sun.com/jstl/sql. This tag library includes actions for creating connections to back-end databases, executing database queries, executing database updates, and performing multiple database operations inside transactions.

The following JSP fragment demonstrates how to create a HTML table with data from a SQL database query:

  1. <SQL:query var="engineers" datasource="${mySqlDataSource}"></SQL:query>
  2. SELECT * FROM employees
  3. WHERE title LIKE '%Engineer'
  4. ORDER BY lastname
  5. </SQL:query>
  6. <table>
  7. <c:forEach var="row" items="${engineers.rows}">
  8. <tr>
  9. <td><c:OUT VALUE="${row.lastname}"></c:out></td>
  10. <td><c:OUT VALUE="${row.firstname}"></c:out></td>
  11. <td><c:OUT VALUE="${row.email}"></c:out></td>
  12. </tr>
  13. </c:forEach>
  14. </table>

The query tag provides access to a JSTL-defined Result object (i.e. engineer.rows in the example) that maps column database names to values, which is simpler than using JDBC ResultSet objects.

Formatting Actions

With increasing globalization of the economy, internationalizing web applications becomes increasingly important. While Java provides full support for internationalization, utilizing this support has been difficult with JSP. And even where internationalization isn't needed, most non-trivial web applications must deal with formatting date and number values correctly for presentation. The JSTL tag library for formatting provides facilities to handle these problems more easily with actions based on Java's resource bundle mechanism for localizing text messages, and numerous tags for parsing and formatting dates and numbers, among other things. Formatting a Java Date object with a particular pattern becomes a trivial task with the action:

  1. <jsp:useBean id="now" class="java.util.Date"></jsp:useBean>
  2. <fmt:formatDate value="${now}" pattern="MM/dd/yyyy"></fmt:formatDate>
  3. <!-- output is: 09/02/2002 -->

XML Actions

The tag library for XML processing and transformation provides one of the most interesting and useful aspects of JSTL beyond the core tag library. Some of these actions are counterparts to the core tags, but they access XML data using XPath expressions and functions in tag attributes, instead of EL. This library, identified by the tag URI http://java.sun.com/jstl/xml, includes three categories of XML actions.

  1. Core XML support.
    • <x:parse> - parses XML data from a variety of sources
    • <x:out> - displays values of XPath expressions
    • <x:set> - sets value of JSP scoped variables from XPath expressions
  2. XML flow control tags corresponding to the core conditional and iteration tags, with usage similar to that found in their XSLT counterparts.
    • <x:if>, <x:choose>, <x:when>, <x:otherwise>, and <x:forEach>
  3. XSLT stylesheet transformations of XML data.
    • <x:transform> - applies an XSLT stylesheet to an XML document
    • <x:param> - sets XSLT stylesheet parameters

The following example demonstrates using a combination of JSP with JSTL and XML with XSLT to produce HTML output from a web application.

The data to be presented is represented by this XML document:

  1. <?xml version="1.0">
  2. <cars>
  3. <car type="roadster">
  4. <make>BMW</make>
  5. <model>Z3 Roadster 3.0i</model>
  6. <price>38000</price>
  7. <car>
  8. <car type="limousine">
  9. <make>BMW</make>
  10. <model>530iA</model>
  11. <price>48000</price>
  12. <car>
  13. </cars>

An XSLT stylesheet generates a HTML table for displaying the XML data. It also takes a parameter that specifies a number formatting pattern.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3. <xsl:param name="pattern"></xsl:param>
  4. <xsl:template match="/">
  5. <table border="1">
  6. <tr>
  7. <th>Type</th>
  8. <th>Make</th>
  9. <th>Model</th>
  10. <th>MSRP</th>
  11. </tr>
  12. <xsl:for-each select="//car">
  13. <tr>
  14. <td><xsl:value-of select="@type"></xsl:value></td>
  15. <td><xsl:value-of select="make"></xsl:value></td>
  16. <td><xsl:value-of select="model"></xsl:value></td>
  17. <td><xsl:value-of select="format-number(price, $pattern)"></xsl:value></td>
  18. </tr>
  19. </xsl:for-each>
  20. </table>
  21. </xsl:template>
  22. </xsl:stylesheet>

The JSP generates the actual HTML web page. It might also include a header and/or footer, some boilerplate content, CSS links, and other static and dynamic content such as summary data, but it delegates presentation of the most structured data (tables, lists, etc.) to an XML/XSLT approach. It defines the pattern for number formatting and passes it to the XSLT stylesheet. (Note the use of the <fmt:formatNumber> JSTL tag: another approach involves using the <x:out> tag along with the XPath format-number() function, similar to that used in the XSLT, but version 1.0 of the JSTL reference implementation doesn't support that function in its XPath function library.

  1. <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
  2. <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
  3. <%@ taglib uri="http://java.sun.com/jstl/xml" prefix="x" %>
  4.  
  5. <c:set var="pattern" value="$#,##0"></c:set>
  6. <c:import url="/car.xml" varReader="reader">
  7. <x:parse xml="${reader}" varDom="carxml"></x:parse>
  8. </c:import>
  9. <c:import url="/car.xslt" var="carxslt"></c:import>
  10. <x:set var="total" select="sum($carxml//price)"></x:set>
  11. <html>
  12. <body>
  13. <h1>JSTL XML Test</h1>
  14. <x:transform xml="${carxml}" xslt="${carxslt}">
  15. <x:param name="pattern" value="${pattern}"></x:param>
  16. </x:transform>
  17.  
  18. <h3>Total cost is <fmt:formatNumber value="${total}"
  19. pattern="${pattern}"></fmt:formatNumber></h3>
  20. </body>
  21. </html>

When the JSP is executed, the following web page is generated.

JSTL XML Test


In earlier examples, output is produced using either XML/XSLT or JSP, and in both cases the technique used is responsible for all aspects of the HTML output. The point to this combined approach is to demonstrate how one might choose to use the strengths of JSP and XML/XSLT together. XSLT is excellent for generating presentation of highly structured data in tabular form, but less appealing for generating content based more on static and loosely-structured content. JSP, on the other hand, is somewhat more natural to creating complete HTML and is good for creating the general outline of the result web page.

On the other hand, one might argue this approach requires that content authors learn more technologies: HTML, JSP, etc. plus JSTL and XSLT. This is true, but for large scale applications with bigger development teams, developers can be further segregated into content authors responsible for boilerplate content and style, using JSP and CSS, and those responsible for presentation of reports and other structured data, using XML/XSLT. In the end, deciding which is really the better approach still depends on many factors, including the unique characteristics of a given development and business environment.

In both cases (pure JSTL and combined with XSLT), however, the advantage of creating script-free JSPs is realized. All the JSTL examples presented are entirely void of Java language JSP expressions, declarations, and scriptlets. Future implementations of the JSP and JSTL specifications will carry this even further when JSP 2.0 allows using Expression Language in tag body and template text.

Obtaining the JSTL

The JSTL reference implementation is available from Apache's Jakarta Taglibs project web site, and is also bundled with Sun's Java Web Services Developer Pack (refer to the internet references below). Installation is no different than with any other custom JSP tag library. The JSTL reference implementation is bundled with the tag libraries jar file, additional support jar files, and the TLD (tag library descriptor) files. These are deployed with web applications using JSTL in the same manner as other JSP tag libraries.

One should also be sure to obtain the official specification in final form from the JSTL home page. Although it's about 200 pages long, it consists mostly of syntax references for the tag libraries and an Expression Language reference. As of the date of this article, several books on JSTL are slated for publication, but one should exercise caution selecting early editions to make sure they are based on the official release of JSTL. Numerous changes appeared in the JSTL specification after the initial public review draft was published.

References



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.