JSP Standard Tag Library eases Webpage development

Learn how JSTL improves upon JSP for simpler Webpage implementation

The 1996 introduction of Java servlets made Java a reasonable choice for dynamic Webpage development. The subsequent debut of JavaServer Pages (JSP) followed by the inclusion of support for JSP tags were logical evolutionary steps toward fast, maintainable Java Webpage implementation. But the mid-2002 release of JSTL (JSP Standard Tag Library) represents perhaps the biggest step yet in speeding and simplifying the development process further.

In this article, I explain JSTL's capabilities and cover everything you need to get started with JSTL. It's assumed you have a basic understanding of Java, JSP, XML, and setting up a Web container. If you're not comfortable with these topics, you might want to browse the background references in Resources. Additional assumed knowledge is described in the XML and SQL sections below.

Installing JSTL support

For our JSTL installation example, we use Tomcat 4.1 (although any servlet container that supports the Servlet 2.3 and JSP 1.2 specifications should work). First, download Tomcat and follow the setup instructions. (Note that JSTL requires a JSP 1.2 Web container.)

Start Tomcat with tomcat4 start and load the index.html page to make sure Tomcat is alive and well.

Next, you'll need to install JSTL support. You can download JSTL support from the Jakarta Website then follow these steps:

  1. Download the JSTL archive (binaries not source) from the Jakarta Website. Unzip/untar the file.
  2. Copy the jar files you've extracted to common/lib in your Tomcat installation (although you won't need all the jar files for our project). This makes the JSTL jar files available to any of your Web applications.
  3. For any Web application for which you want to use JSTL, copy the .tld files to the WEB-INF directory in your Web application.
  4. For your JSTL Web application, edit your web.xml file and add the following entries:

      <taglib>
        <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
        <taglib-location>/WEB-INF/fmt.tld</taglib-location>
      </taglib>
      <taglib>
        <taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
        <taglib-location>/WEB-INF/c.tld</taglib-location>
      </taglib>
      <taglib>
        <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
        <taglib-location>/WEB-INF/sql.tld</taglib-location>
      </taglib>
      <taglib>
        <taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
        <taglib-location>/WEB-INF/x.tld</taglib-location>
      </taglib>
    

    These entries let your Web application use the expression language (EL) versions of the JSTL tag libraries. Position of these entries matters! If you're not sure where to put them, the definitive guide to web.xml options and ordering is defined in the document type definition (DTD) at: http://java.sun.com/j2ee/dtds/web-app_2_2.dtd.

  5. When you create a JSP page that uses JSTL, put it in your Web application's main directory, just like other JSP and HTML pages. You can name this page whatever you want, but it should have a .jsp extension.

The basics

First, all JSTL pages are also JSP pages. JSTL is just a superset of JSP functionality.

Also, all JSTL tags are valid XML. That means if you treat the context of a page outside the JSTL tags as template text (which will normally be HTML), the remaining JSTL tags must parse as valid XML. This has some important implications, most of which are good.

JSTL provides a set of four standard tag libraries (core, internationalization/format, XML, and SQL) and support for an EL. A primary design goal for JSTL and the EL was to simplify Webpage development and implementation.

In this article, we follow the JSTL specification's naming convention and refer to JSTL tags as actions. A JSTL tag corresponds to some action; calling them actions explicitly remind us that they add dynamic behavior to an otherwise static page.

The JSTL tag library comes in two versions: one lets you plug in the standard JSP expressions you've used in the past, such as <%= . . . %>, and one uses a JSTL EL. I further discuss EL support in JSTL below.

EL support

To understand the current state of EL support in JSTL, let's examine how the relevant specifications have been handled. The Java Specification Request (JSR) expert group members decided, for good reasons, that an expression language specification should be part of the JSP, not the JSTL, specification. Complete specification of an expression language will be part of the JSP 2.0 specification. Since JSTL 1.0 was finalized before JSP 1.3, the JSTL authors had to make an educated guess (which will probably turn out to be quite good) on an EL support implementation for JSTL 1.0. A JSTL maintenance release will coincide with finalization of the JSP 1.3 specification and will contain any tweaks to the EL required for consistency with the final JSP 1.3 specification.

The bottom line is that the EL described here may change a bit in an upcoming JSTL release. However, any changes will likely be small.

The EL simply defines a powerful language for expressing simple expressions in a syntax that's easy to learn. It has the flavor of a cross between JavaScript and the better parts of Perl. EL expressions, combined with JSTL tags from the four standard tag libraries, provide a large, flexible feature set.

All EL expressions are enclosed by ${ }. Expressions in JSTL are always part of attribute values in JSTL tags. The expressions may be the only part of the attribute or may be combined and embedded in string literals. JSTL attributes may also contain simple string literals. In the following JSTL, we show each of these cases in a c:out action, which is an action from the core library that spews the contents of its value attribute to the JSP's output:

<c:out value="${anExpression}"/>
<c:out value="literalText${anExpression}${anotherExpression}"/>
<c:out value="literalText"/>

The EL also defines a set of rules for coercing values in an expression to a type that corresponds to the context in which the values are used. We won't cover these rules in detail here; however, the approach is very similar to that defined in Perl. (As with most things Perl, this approach works reasonably well, but, on occasion, gives results the language happily accepts, but may not be quite what you expect.)

The EL provides support for accessing object properties and collection elements, a set of implicit objects, and using relational, logical, and arithmetic operators. For indexed properties, including arrays and java.util.List classes, elements can be accessed with syntax like the following:

${alist[4]}
${aList[someVariable]}

Both JavaBean properties and java.util.Map elements (which represent a set of name/value pairs) can be accessed using one of the following ways. In the first two expressions below, we can access a property named aProperty in a JavaBean or a Map entry with the key aProperty. In the third expression (note I've left out the quotes), we access an element in anObject with a name held in the variable aVariableContainingPropertyName:

${anObject.aProperty}
${anObject["aPropertyName"]}
${anObject[aVariableContainingPropertyName]}

There are a number of implicit varibles defined in the EL:

  • pageContext: the pageContext object for that Webpage
  • pageScope, requestScope, sessionScope, and applicationScope: these are Map collections that map variable names in each of these scopes to values
  • param and paramValues: parameters passed with the page request; same as in JSP
  • header and headerValues: headers passed with the page request; same as in JSP
  • cookie: Map that maps cookie names to a particular cookie object

The EL defines a full set of operators that corresponds closely to those you're familiar with in Java. Arithmetic operators include +, -, *, / (or div), and % (or mod). Relational operators include ==, !=, <, >, <=, >=, which correspond to eq, ne, lt, gt, le, and ge, respectively. I won't elaborate on these operators because they are all self-explanatory.

JSTL tag libraries

Now that I've covered some basics and looked at EL syntax, I can discuss the four JSTL tag libraries specifically. I discuss the core library most since it's the one you'll certainly use; but I'll also cover the rest in enough detail to get you started.

First, though, I should talk more about the JSTL tag libraries' two flavors. I mentioned above that each JSTL tag library comes in two versions: one that supports expressions in the EL and one that supports standard JSP expressions. When you import any tag library into a JSP page, you define a prefix that designates a namespace corresponding to the tags in that library.

The four standard tag libraries, with their JSTL spec-defined prefix conventions, are listed below. Note that you could define your own prefixes, but there is absolutely no good reason for this.

Four standard tag libraries

LibraryEL library prefixRequest-time (RT) library prefix
Corecc_rt
Internationalization/formatfmtfmt_rt
SQL/DB supportsqlsql_rt
XMLxx_rt

To use the EL core tag library in your page (you're really just giving your page visibility into the namespace defined in the library), include the following example directive at the top of your page:

<%@ taglib prefix="c" uri=http://java.sun.com/jstl/core %>

To use the tags in that core library, prefix each tag in your page with the prefix you've designated in your include statement:

<c:out value="${anExpression}"/>

The core tag library

Let's examine the core tag library in more detail. We look at the most commonly used functionality first.

Displaying/setting values and exception handling

The core library's most basic tag is the c:out tag, which displays an EL expression's value in a page. An example expression that uses c:out might look like this:

 We have <c:out value="${applicationScope.product.inventoryCount}" escapeXml="true" default="0"  /> of those items in stock.

In the above, the value attribute is the expression we send to the page output. I've also shown the optional escapeXml attribute, which specifies whether XML characters (<, >, &, and .) should convert to corresponding character entity codes (so they show up as those characters in an HTML page), and the default attribute, which is used if the EL can't evaluate the value or the value evaluates to null.

Note that when EL support is fully implemented in JSP 2.0, you won't need to use the c:out action; you can just embed JSP expressions directly in the page.

Another commonly used core action is c:set, which sets a variable in a page. You can use c:set in two ways. The first way sets the variable defined in the var attribute to the value defined in the value attribute, as shown below:

<c:set var="customerID" value="$param:customerNumber" scope="session" />

The optional scope attribute above specifies that we want to set the variable customerID in the session scope; if scope is not specified, it defaults to page scope.

Another powerful usage of c:set assigns the contents of the c:set tag's body to a specified variable:

<c:set var="cellContents">
<td>
<c:out value="${myCell}"/>
</td>
</c:set>

In the above example, a c:set action defines a variable named cellContents (in page scope) that holds the contents defined in the tag's body. In this case, the body defines an HTML table cell element. The c:out action in the body is evaluated, and that evaluation's results are included in the string literal in the body.

As you might expect, JSTL has made exception handling a bit easier. In typical JSP pages, you have two approaches for handling exceptions: try/catch blocks in scriptlet code embedded directly in the page or with a JSP errorPage directive that provides a nice catch-all way to handle any possible exception on a page. JSTL offers a good alternative with the c:catch action, which provides an effective way to handle exceptions with a bit more granularity without embedding Java code in your pages. A c:catch action might look like this:

<c:catch>
     <!--. . . some set of nested JSTL tags below which would be hit on an exception-->
</c:catch>

The c:catch action has an optional attribute, a variable that references a thrown exception.

You're less likely to use the c:remove tag. This tag has attributes for a variable name and a scope, and removes the specified variable from the specified scope.

Flow control

Let's move on to JSTL's flow control and conditional tags. If you've used conditional and flow control statements in any language, conceptually there's not much new here.

The c:if action handles simple conditional tests. The Boolean expression's value in the test attribute is evaluated; if true, the body's contents are evaluated. In the action below, we also show the optional var attribute that stores the test results for later use in the page (or elsewhere, if the other optional scope attribute is specified):

<c:if test="${status.totalVisits == 1000000}" var="visits">
   You are the millionth visitor to our site!  Congratulations!
</c:if>

Below we show JSTL's support for switching logic with c:choose, c:when, and c:otherwise. A set of c:when actions may be included within a choose tag; if any of the expressions in the c:when blocks evaluate to true, the following tests in the c:choose action are not evaluated. If none of the tests in the c:when blocks evaluate to true, c:otherwise action's contents, if present, are evaluated:

<c:choose>
<c:when test="${item.type == 'book'}">
...
</c:when>
<c:when test="${item.type == 'electronics'}">
...
</c:when>
<c:when test="${item.type == 'toy'}">
...
</c:when>
<c:otherwise>
...
</c:otherwise>
</c:choose>

The c:foreach action provides an easy way to iterate through a collection's elements. If you want to only iterate over part of the collection, you can specify starting and ending indices and an increment value with the optional begin, end, and step attributes, respectively. In the following example, we iterate through the contents of a collection in the variable customerNames; in each loop, the next element is put in the variable name and evaluated in the c:forEach action's body.

<table>
<c:forEach var="name" items="${customerNames}">
<tr><td><c:out value="${name}"/></td></tr>
</c:forEach>
</table>

Remember Java's StringTokenizer class? With the c:forTokens action, you can obtain similar functionality in JSTL. This fragment iterates through items in the items String attribute using the delimiter(s) defined in the delims attribute. Note that the items attribute doesn't need to be a string literal; it can be any valid EL expression:

<table>
<c:forTokens items="47,52,53,55,46,22,16,2" delim="," var="dailyPrice">
<tr><td><c:out value="${dailyPrice}"/></td></tr>
</c:forTokens>
</table>

In the following complete JSTL page, I list all passed parameters that have been passed to the page. The param and paramValues implicit objects are Java Map collections that map keys to one or more values. In this example, we find the key for each MapEntry in the collection, which is the parameter name, and use the key to look up all the parameter values associated with the key:

<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
<body>
<head>
<title>Parameter Listing Example</title>
</head>
<br>
<b>Parameter values passed to this page for each parameter: </b>
<table border="2">
<c:forEach var="current" items="${param}">
    <tr>
    <td>
    <b><c:out value="${current.key}" /></b>
    </td>
        <c:forEach var="aVal" items="${paramValues[current.key]}">
            <td>
            <c:out value="${aVal}" />
            </td>
        </c:forEach>
    </tr>
</c:forEach>
</table>
</body>
</html>

Other actions

There are some other important core tag library actions we need to cover. One potential problem area in Webpage implementation relates to URL encoding. Without URL encoding, certain characters in URLs that pass between Webpages, like spaces, can confuse Web servers. URL encoding ensures that these special characters are replaced with characters that are not confusing. The following example defines a URL in the variable myUrl, which consists of a URL and a set of parameters. The URL action (note we refer to "action" in the JSTL sense here) ensures that all characters are properly encoded:

<c:url value="http://acme.com/exec/register" var="myUrl">
<c:param name="name" value="${param.name}"/>
<c:param name="country" value="${param.country}"/>
</c:url>
<a href='<c:out value="${myUrl}"/>'>Register</a>

In the code above, the param action simply defines a set of name/value pairs.

JSP allows page designers to include other pages' content with the <jsp:include> directive. JSTL extends this concept with the JSTL c:import action. c:import's big advantage is that you can specify an arbitrary URL and include page content outside of your Web application (anywhere on the World Wide Web) or within another Web application on your server.

A set of HTTP-related tags wouldn't be complete without a way to handle HTTP redirection. JSTL supports that with the c:redirect action.

The internationalization tag library

In covering JSTL support for internationalization, I assume you have a reasonable understanding of the following subjects:

  • Resource bundles and how Java finds them
  • The Locale class
  • Resource bundle lookups and the MessageFormat class
  • Character encoding and its relationship to Java internationalization
  • Java text and date formatting

Read the internationalization-related references in the background section in Resources if you need more information on these topics.

I cover the internationalization tag library in two sections below. In the first section, I cover formatting- and parsing-related actions (that roughly correspond to classes in the java.text package) you're likely to use even if you're not developing fully internationalized applications. In the second section, I cover actions more specific to internationalization.

If there's a remote possibility your application will be used outside your native country, life will be easier if you build in internationalization support from the beginning. This is true even with an implementation approach as easy to use as JSTL.

Format tag library: Format actions

If you've used the Java DateFormat and NumberFormat classes, the approach used in the tags below should look familiar, because JSTL format actions are built on top of these classes. These Java classes generally provide a format( ) method, which converts a Java type to a formatted String and a parse( ) method, which attempts to parse a String and creates a Java object corresponding to that String.

The fmt:formatNumber action has a value attribute, which is an EL expression or literal just like the other value attributes we've seen, and a pattern attribute. This attribute mirrors patterns as defined in the NumberFormat class. The following action sends a formatted String to the JSP page's output:

<fmt:formatNumber value="1000.001" pattern="#,#00.0#"/>

In this fmt:formatNumber action, we use the type attribute to specify that we want the formatted value formatted as a currency value. We save the formatted result in a variable name dollars. In a US locale, the following would generate the string 456.79 (note that it appropriately rounds the value for the currency used):

<fmt:formatNumber value="3456.789" type="currency" var="dollars"/>

Possible values for the type attribute above include currency, number, and percent.

In this example, we go the other way—we start with a text string containing a formatted (currency, per the type attribute) field in the value attribute and parse it to obtain a number. The result is typically stored in a variable specified by the var attribute. Although this attribute is optional, you will usually use it; otherwise, the parsed value is simply sent to the page output:

<fmt:parseNumber value="${currencyInput}" type="currency" var="parsedNumber"/>

The fmt:formatDate action has a value attribute, a format attribute, and an attribute that refers to the format class that handles the formatting (typically java.util.Date):

<jsp:useBean id="now" class="java.util.Date" />
<fmt:formatDate value="${now}" timeStyle="long"
dateStyle="long"/>

Like number formatting, JSTL also provides a mechanism to parse a string representing a date into a Date object:

<fmt:parseDate value="${dateInput}" pattern="MM dd, YYYY" />

Refer to the java.util.DateFormat class for more details on how to handle formatting and patterns.

Format tag library: Internationalization actions

A critical piece of Java localization is the ResourceBundle class. JSTL actions allow you to work with this class simply. This example uses the fmt:bundle action to obtain a ResourceBundle corresponding to the current Locale and fmt:message actions to look up localized strings in that resource bundle:

<fmt:bundle basename="myBundle">
<%-- Use values in myBundle --%>
<fmt:message key="Introduction">
   <fmt:param value="${loginName}"/>
   <fmt:param value="${loginCount}"/>
</fmt:message>
<fmt:formatDate value="${now}" var="parsedDate"/>
</fmt:bundle>

Often, the fmt:message action simply looks up a string corresponding to a key. In the example above, the string in the ResourceBundle contains placeholders into which two values are substituted. These values are specified in the fmt:param actions. This is just like the approach used by the Java MessageFormat class.

There's a similar action to specify a time zone. The time zone applies to anything evaluated in the tag's body:

<fmt:timeZone value="someTimeZone">
<!-- actions in this context will be evaluated using someTimeZone  -->
</fmt:timeZone>

The fmt:bundle and fmt:timeZone actions above have corresponding fmt:setBundle and fmt:setTimeZone actions. These actions add optional scope attributes; thus, you can use these actions to set a resource bundle or a time zone in any scope up to application scope.

If you work with non-European locales, you'll probably have to worry about encoding. JSTL supports encoding with the fmt:requestEncoding action.

The SQL tag library

JSTL allows easy database integration. It's important to note, however, that the out-of-the-box JSTL implementation has some limitations. The main issue relates to connection pooling. Establishing—and maintaining—a connection to a database is resource-intensive. JSTL SQL actions result in many database connections being established, generally, at least one per user. Therefore, JSTL SQL tags are great for prototyping or for low-volume, Web-based applications, but aren't suitable for large-scale applications. A scalable production application would typically wrap database access in a layer that hides database access and handles details like connection pooling. There are, however, approaches that allow you to implement connection pooling and use JSTL SQL actions with a bit of custom code (see "JSTL 1.0: What JSP Applications Need, Part 2" in Resources).

We look at some simple examples that use JSTL tags from the SQL library. If you're familiar with SQL basics, you should be able to adapt these for your own applications.

In the following snippet, we set up a connection to a database, select a set of order items matching an order ID, and display item attributes in a table:

<sql:setDataSource
      driver="com.cheapDrivers.jdbcDriver"
      url="jdbc:cheapDrivers:."
      user="guest"
      password="password"
      var="dataSource" />
    
<sql:query var="orderItems" dataSource="${dataSource}">
SELECT * FROM items
WHERE order_id = <cout value="${orderID}"/> 
ORDER BY price
</sql:query>
<table>
<c:forEach var="row" items="${orderItems.rows}">
<tr>
<td><c:out value="${row.itemName}"/></td>
<td><c:out value="${row.price}"/></td>
<td><c:out value="${row.weight}"/></td>
</tr>
</c:forEach>
</table>

In the next example, I show how JSTL supports database transactions. In the database world, many operations involving multiple changes to tables must be all or nothing: if a problem happens, the changes must be undone. In the example below, the sql:update action is wrapped in a sql:transaction action; if any SQL errors occur in the course of the transaction, all operations performed in the transaction scope roll back.

The name of the sql:update action is a bit misleading; in addition to SQL UPDATE, sql:update also supports INSERT and DELETE, and even SQL CREATE. In fact, it supports any SQL operation that doesn't produce a result. In the following example, the sql:update performs an UPDATE by inserting variable values into a PreparedStatement. In this code fragment, we transfer money between two accounts (a classic example of something that needs to be wrapped in a transaction):

<sql:transaction dataSource="${dataSource}">
<sql:update>
UPDATE account
SET account_balance =account_balance -?
WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${sourceAccount}"/>
</sql:update>
<sql:update>
UPDATE account
SET account_balance =account_balance +?
WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${destAccount}"/>
</sql:update>
</sql:transaction>

The XML tag library

A complete treatment of what you can do with the standard XML tag library, in particular Extensible Stylesheet Language Transformations Specification (XSLT) transformations, is a good subject for another article. I cover enough to get you started below.

XML support in JSTL conforms to the XPath specification. One of XPath's important functions is providing a clean syntax for accessing hierarchical information for which XML is famous. Perhaps the easiest way to see how everything works is to look at the way we use XML tags in a fragment from a real JSTL page:

<!-- Find and parse our XML document (somewhere on the WWW) -->
<c:import url="http://www.cheapstuff.com/orderStatus?id=2435" var="xml"/>
<x:parse xml="${xml}" var="doc"/>
<!-- access XML data via XPath expressions -->
<x:out select="$doc/name"/>
<x:out select="$doc/shippingAddress"/>
<x:out select="$doc/deliveryDate"/>
<!-- Set a scoped variable -->
<x:set var="custName" scope="request" select="$doc/name"/>

In the import and parse actions above, we load and parse a specified XML document into a variable doc. In each of the x:out actions above, we access the parsed XML document's elements using an XPath expression and send the results to the JSP page output.

The set expression above evaluates an XPath expression and puts the results into a scoped variable (in the case above, it's in request scope).

Both the x:out and x:set actions can throw a JspTagException if they don't complete successfully (most likely because the XPath expression referred to a tag that doesn't exist). Your page, as in all other cases, should handle these exceptions intelligently (either via the traditional JSP errorPage directive or with JSTL c:catch actions).

JSTL handles XSLT transforms easily. In the following example page, we use the x:transform action from the XML tag library to create a formatted page from an XML source document using an XSLT stylesheet. The x:transform action's most important attributes are the xml and xslt attributes. In the example below, we set the xslt attribute from a variable we initialize on the same page; we set the xml attribute in the body of the action, which is the default for the x:transform action.

By default, the results of the transformation are sent to the page output; you can also save the results in a variable with the x:var attribute:

<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/xml" prefix="x" %>
<c:set var="xsltSource"> 
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
      <xsl:apply-templates/>
</xsl:template>
<xsl:template match="music">
<html>
<head></head>
<body marginheight="0" marginwidth="0" topmargin="0" leftmargin="0">
<table cellpadding="0" cellspacing="0" border="1" bgcolor="#ffffff">
      <tr>
            <td><STRONG>Artist</STRONG></td>
            <td><STRONG>Album</STRONG></td>
            <td><STRONG>Year</STRONG></td>
            <td><STRONG>Genre</STRONG></td>
      </tr>
      <!---Set up for loop to collect all the artist information //-->
      <!-- <xsl:for-each select="./*[name()='artists']"> -->
      <xsl:for-each select="artists">
            <tr>
                  <td><xsl:value-of select="artist"/></td>
                  <td><xsl:value-of select="album"/></td>
                  <td><xsl:value-of select="year"/></td>
                  <td><xsl:value-of select="genre"/></td>
            </tr>
      </xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
</c:set> 
<x:transform xslt="${xsltSource}" >
<music>
    <artists>
            <artist>Jonny B</artist>
            <album>Feedback and Distortion</album>
            <year>2001</year>
            <genre>Rock</genre>
      </artists>  
        
      <artists>
            <artist>Harmony's Nieces</artist>
            <album>Sappy Pop Ballads</album>
            <year>2002</year>
            <genre>Pop</genre>
      </artists>  
</music>
</x:transform>

You can also use the c:import action to specify an external source document and stylesheet, as shown in this example code snippet:

<c:import var="${xmlSource}" url="${someDocumentURL}" />
<c:import var="${xsltSource}" url="${anotherDocumentURL}" />
<x:transform xml="${xmlSource}" xslt="${xsltSource}" >

Closing remarks

If you've made it this far, you should have a good understanding of JSTL, its four standard tag libraries, and how it can make Webpage development easier. Now start writing some JSTL!

Thanks to Randy Hanford for reviewing this article.

Steve Small has been developing with Java for many years in technical leadership and development positions for companies including Boeing, Amazon.com, and currently at PictureIQ in Seattle, Wash. He has developed and taught off-hours Java courses for the University of Washington since 1998.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies