JSP 2.0
By Dan Troesser, OCI Principal Software Engineer
September 2003
Introduction
Java web development has come a long way. We have been introduced to applets, servlets, JavaServer Pages, WAR files, servlet filters, JSTL, JavaServer Faces (see last month's SETT article), and the list goes on. These technologies have aimed to make web development easier, more portable, more powerful, and more maintainable. The coming release of JSP 2.0 and Servlet 2.4 continues that tradition. Both specifications are currently in proposed final draft version 3 stage and the final versions will likely be released soon. This month's Java News Brief discusses the most notable new features of JSP 2.0.
"Easier to use" was the main goal for JSP 2.0. The main features added include integrated support for JavaServer Pages Standard Tag Library (JSTL) expression language, easier use and development of custom tags, and new dynamic attributes.
Scriptlets and Expressions ... "the old days"
Scriptlets and expressions are snippets of Java code that you embed in a JSP. Many consider them undesirable because they clutter the presentation layer (HTML) with ugly Java code and cause grief for HTML authors. Below is an example JSP that finds a customer based on a userName
request parameter and shows data about that customer. It uses scriptlets and expressions to perform its task.
Note: The complete examples used in this article are available in customer_example.war. Place customer_example.war
in Tomcat 5.0's webapps
directory. Start Tomcat and enter http://localhost:8080/customer_example
in the browser. Port 8080 assumes you're using the defaults. Feel free to modify the examples and experiment.
- <%@ page import="com.ociweb.customer.*"%>
- <html>
- <!--The following is a scriptlet. You can put any arbitrary Java code here. -->
- <%
- String userName = request.getParameter("userName");
- Customer customer = new Customer();
- CustomerDAO.populateCustomer(userName, customer);
- %>
- <head><title>Customer Profile for <%= userName %></title></head>
- <body>
- <h1>Customer Profile for <%= userName %></h1>
- <table>
- <!-- The following lines contain expressions. The result of the code within them
- is evaluated to a String and inserted into the HTML. -->
- <tr><td>First Name:</td><td><%= customer.getFirstName() %></td></tr>
- <tr><td>Last Name:</td><td><%= customer.getLastName() %></td></tr>
- <tr><td>Birth Date:</td><td><%= customer.getBirthDate() %></td></tr>
- <tr><td>Phone:</td><td><%= customer.getPhone() %></td></tr>
- <tr><td>Email:</td><td><%= customer.getEmail() %></td></tr>
- </table>
- </body>
- </html>
As we see above the request is processed and the Customer
object is populated in the JSP. An experienced web developer would probably say "tisk tisk" to that. In a robust web application, a servlet would handle request processing and any calls to a data access layer. The servlet would then forward to a JSP. Nevertheless, for learning's sake, we will proceed with this design (or lack thereof) and see what new features of JSP 2.0 will help us remove the scriptlets and expressions.
JSTL Expression Language Support
Expression language (EL) is a syntax that allows easy access to objects and properties of objects (JavaBeans) in a JSP without using scriptlets or traditional JSP expressions. It was inspired by ECMAScript and XPath and introduced in JSTL 1.0. See the September 2002 Java News Brief for a more complete discussion of JSTL. The JSTL expression language was integrated into JSP 2.0. It was also updated with new features including support for functions and the ability to output expressions into template text. Integrating JSTL EL into the JSP specification removes the need for scriplets in a JSP page.
Here is a version of our customer profile JSP that uses expression language.
- <%@ page import="com.ociweb.customer.*"%>
- <html>
- <jsp:useBean id="customer" class="com.ociweb.customer.Customer"></jsp:useBean>
- <%
- String userName = request.getParameter("userName");
- CustomerDAO.populateCustomer(userName, customer);
- %>
- <head><title>Customer Profile for ${param["userName"]}</title></head>
- <body>
- <h1>Customer Profile for ${param["userName"]}</h1>
- <table>
- <tr><td>First Name:</td><td>${customer.firstName}</td></tr>
- <tr><td>Last Name:</td><td>${customer.lastName}</td></tr>
- <tr><td>Birth Date:</td><td>${customer.birthDate}</td></tr>
- <tr><td>Phone:</td><td>${customer.phone}</td></tr>
- <tr><td>Email:</td><td>${customer.email}</td></tr>
- </table>
- </body>
- </html>
EL expressions begin with ${
and end with }
. Notice they are included inline with template text. In JSTL 1.0 this wasn't possible; you had to use c:out
tags. EL allows access to:
- PageContext attributes. The
jsp:useBean
tag created and insertedcustomer
into thePageContext
object. This allowed us to access it later in an EL expression. - Nested properties and accessors to collections.
firstName
andlastName
are properties (JavaBean conventions) ofcustomer
and accessed using the dot operator. A collection's members can be accessed using square brackets as shown by retrieval of theuserName
parameter from theparam
object. Theparam
object is aMap
mapping parameter names to parameter values. Members of an array orList
can be accessed if the value in square brackets can be coerced to anint
.Map
s can be accessed using the dot operator or square brackets. For example,${param.userName}
is equivalent to${param["userName"]}
. - Relational, logical and arithmetic operators.
${8 * 8}
evaluates to 64.${2 > 1}
evaluates totrue
.${customer.firstName == "Bugs"}
would evaluate totrue
in our example. - Access to public static methods in Java classes. This is covered in the next section.
- A set of implicit objects.
param
in the above example is an implicit object already created and available for use. Other implicit objects includepageContext
,pageScope
,requestScope
,sessionScope
,applicationScope
,paramValues
,header
,headerValues
,cookie
, andinitParam
.
EL Functions
JSP 2.0 added the ability to call public static methods in EL expressions. To illustrate their use, we will replace the remaining scriptlet in our customer profile example with an EL function. In other words, we want to replace
- <%
- String userName = request.getParameter("userName");
- CustomerDAO.populateCustomer(userName, customer);
- %>
with
- <%@ taglib prefix="cu"
- uri="http://www.ociweb.com/jnb/customer_example-taglib" %>
- ${cu:populateCustomer(param["userName"], customer)}
-
The populateCustomer
method we've been using all along looks like this.
- public static void populateCustomer(String userName,
- Customer customer) {
- if (customer == null) {
- customer = new Customer();
- }
- Customer tempCustomer = (Customer) customers.get(userName);
- if (tempCustomer != null) {
- customer.setUserName(tempCustomer.getUserName());
- customer.setFirstName(tempCustomer.getFirstName());
- customer.setLastName(tempCustomer.getLastName());
- customer.setBirthDate(tempCustomer.getBirthDate());
- customer.setEmail(tempCustomer.getEmail());
- customer.setPhone(tempCustomer.getPhone());
- }
- }
In order to use this method we have to add a function
element to our tag library descriptor (TLD). You'll have to create a TLD file if it doesn't already exist. A tag library descriptor defines and configures tags in a tag library.
Download customer_example.war to see the full TLD used in our example.
- <function>
- <description>Finds the customer for the given username
- and populates the customer's properties</description>
- <name>populateCustomer</name>
- <function-class>com.ociweb.customer.CustomerDAO</function-class>
- <function-signature>void populateCustomer(java.lang.String,
- com.ociweb.customer.Customer)</function-signature>
- </function>
We also added a taglib
element to the deployment descriptor (web.xml
).
- <taglib>
- <taglib-uri>http://www.ociweb.com/jnb/customer_example-taglib</taglib-uri>
- <taglib-location>/WEB-INF/customer_example-taglib.tld</taglib-location>
- </taglib>
Notice the taglib-location
specifies the location of the TLD. The taglib-uri
is, for the most part, an arbitrary name given to the tag library. The name you give it can't conflict with other tag libraries in your deployment descriptor.
That's it. In fact, adding the taglib
element to the deployment descriptor is actually optional. You could instead reference the TLD directly in the taglib
directive.
<%@ taglib prefix="cu" uri="/WEB-INF/customer_example-taglib.tld" %>
This isn't recommended because it reduces flexibility if you ever choose to rename or move the TLD. The uri
would have to be changed in every JSP that used it.
The new customer profile JSP looks like the following.
- <%@ taglib prefix="cu" uri="http://www.ociweb.com/jnb/customer_example-taglib" %>
- <html>
- <jsp:useBean id="customer" class="com.ociweb.customer.Customer"></jsp:useBean>
- ${cu:populateCustomer(param["userName"], customer)}
- <head><title>Customer Profile for ${param.userName}</title></head>
- <body>
- <h1>Customer Profile for ${param.userName}</h1>
- <table>
- <tr><td>First Name:</td><td>${customer.firstName}</td></tr>
- <tr><td>Last Name:</td><td>${customer.lastName}</td></tr>
- <tr><td>Birth Date:</td><td>${customer.birthDate}</td></tr>
- <tr><td>Phone:</td><td>${customer.phone}</td></tr>
- <tr><td>Email:</td><td>${customer.email}</td></tr>
- </table>
- </body>
- </html>
The prefix given in the taglib
directive is whatever you choose to distinguish it from tags and functions in other tag libraries used in the JSP.
EL Conclusions
Yes, you could remove scriptlets in JSP 1.2 with creative use of JavaBeans or custom tags. You could also use JSTL and its expression language to help remove those nasty scriptlets. However, that required downloading and installing JSTL for your one or more web applications. Unfortunately, the tag libraries in JSTL were not integrated into JSP 2.0, just the expression language. Therefore, if you want to use JSTL tags you still need to do a little extra legwork. Nevertheless, expression language support is a step in the right direction especially with its added support for functions and inclusion in template text.
Simpler Custom Tag Development
Tag development in JSP 2.0 is easier and more powerful. The new features include:
- Simple Tag Handler. Now that scriptlets are no longer necessary in JSPs, a simpler tag extension API could be created that didn't need to support scriptlets in the body of tags.
- Dynamic Attributes. Dynamic attributes can be passed to custom tags. They are attributes whose names are not known until runtime.
- .tag files. .tag files are probably the simplest way to create a custom tag. They allow a JSP developer to create custom tags using JSP syntax.
- JSP Fragments. Fragments allow passing of customized or pre-styled snippets of output to a custom tag. Fragments deserve a bit more explanation and are deemed beyond the scope of this article. Nevertheless, they are powerful and worth checking out.
Simple Tag Handler and Dynamic Attributes
The next version of the customer profile example illustrates the use of the simple tag handler anddynamic attributes. We will replace the populateCustomer
EL function with a simple tag. First we create our tag class.
- package com.ociweb.customer;
-
- import javax.servlet.jsp.*;
- import javax.servlet.jsp.tagext.*;
- import java.util.*;
- import java.io.*;
-
- public class CustomerDAOTag extends SimpleTagSupport implements DynamicAttributes
- {
- private Map attributes = new HashMap();
-
- public void doTag() throws JspException, IOException {
- String userName = (String) attributes.get("userName");
- Customer customer = (Customer) attributes.get("customer");
- if (userName == null || customer == null) {
- throw new JspException("Both userName and customer attributes "
- + "must exist and be non-null");
- }
- CustomerDAO.populateCustomer(userName, customer);
- }
-
- public void setDynamicAttribute(String uri, String localName,
- Object value) throws JspException {
- attributes.put(localName, value);
- }
- }
To use dynamic attributes we simply implement the DynamicAttributes
interface and provide a setDynamicAttribute
method. We also need to make an entry in the TLD for our new tag. Note that dynamic-attributes
are set to true
.
- <tag>
- <description>Finds the customer for the given username and populates the customer's properties</description>
- <name>populateCustomer</name>
- <tag-class>com.ociweb.customer.CustomerDAOTag</tag-class>
- <body-content>empty</body-content>
- <dynamic-attributes>true</dynamic-attributes>
- </tag>
If we wanted to output HTML in the doTag
method we could insert
getJspContext().getOut().write("some HTML");
Using dynamic attributes for our simple example is probably overkill. We could very easily provide setUserName
and setCustomer
methods instead and do away with our Map
. If we did that, we would also need to add attribute
elements to the tag
element in our TLD.
The new version of our customer profile JSP using our custom tag is shown below.
- <%@ taglib prefix="cu" uri="http://www.ociweb.com/jnb/customer_example-taglib" %>
- <html>
- <jsp:useBean id="customer" class="com.ociweb.customer.Customer"></jsp:useBean>
- <cu:populateCustomer userName="${param.userName}" customer="${customer}"></cu:populateCustomer>
- <head><title>Customer Profile for ${param.userName}</title></head>
- <body>
- <h1>Customer Profile for ${param.userName}</h1>
- <table>
- <tr><td>First Name:</td><td>${customer.firstName}</td></tr>
- <tr><td>Last Name:</td><td>${customer.lastName}</td></tr>
- <tr><td>Birth Date:</td><td>${customer.birthDate}</td></tr>
- <tr><td>Phone:</td><td>${customer.phone}</td></tr>
- <tr><td>Email:</td><td>${customer.email}</td></tr>
- </table>
- </body>
- </html>
.tag Files
Our next example illustrates the use of a .tag file to create a custom tag. This is probably the easiest way to create a custom tag. No TLDs or deployment descriptors need to be modified. Just create a file with a .tag extension and place it in the /WEB-INF/tags
directory. You might have to create the tags
directory if it doesn't exist. A .tag file uses JSP syntax so it's very simple to write. Shown below is customerDetailTable.tag
. It will assume responsibility for looking up a customer and displaying his or her detail in an HTML table.
- <%@ taglib prefix="cu" uri="http://www.ociweb.com/jnb/customer_example-taglib" %>
- <%@ attribute name="userName" %>
- <jsp:useBean id="customer" class="com.ociweb.customer.Customer"></jsp:useBean>
- <cu:populateCustomer userName="${userName}" customer="${customer}"></cu:populateCustomer>
- <table>
- <tr><td>First Name:</td><td>${customer.firstName}</td></tr>
- <tr><td>Last Name:</td><td>${customer.lastName}</td></tr>
- <tr><td>Birth Date:</td><td>${customer.birthDate}</td></tr>
- <tr><td>Phone:</td><td>${customer.phone}</td></tr>
- <tr><td>Email:</td><td>${customer.email}</td></tr>
- </table>
The attribute
directive tells the JSP container what attributes the tag should accept. It takes userName
as an attribute and passes that into the populateCustomer
tag we created earlier. The customer profile JSP becomes even simpler.
- <%@ taglib prefix="cu" tagdir="/WEB-INF/tags" %>
- <html>
- <head><title>Customer Profile for ${param.userName}</title></head>
- <body>
- <h1>Customer Profile for ${param.userName}</h1>
- <cu:customerDetailTable userName="${param.userName}"></cu:customerDetailTable>
- </body>
- </html>
To use our newly created tag we used the taglib
directive and the tagdir
attribute to specify where the .tag file is located. It's that easy.
Other New Features
JSP 2.0 provides some other new features including:
- The ability to disable scriptlets completely so you can really crack down on yourself or others.
- The ability to disable EL expressions if, for some reason, you or your organization wants to prevent use of this new feature.
- A simpler XML syntax for those who prefer the XML JSP syntax over the traditional JSP syntax. The syntax is not as awkward as before and
jsp:root
is no longer required to be the root element. - New .jspx and .tagx filename extensions were added. They are the extensions used for JSPs and tags using XML syntax.
Servlet 2.4
Accompanying JSP 2.0 will be the release of Servlet 2.4, which JSP 2.0 depends on. It's most notable changes are:
- New request listeners API:
ServletRequestListener
,ServletRequestAttributeListener
and associated event classes - The ability to use filters with the Request Dispatcher
- Servlets can now be welcome files
- Internationalization enhancements
- The
SingleThreadModel
interface was deprecated
Relationship to JavaServer Faces
JavaServer Faces (JSF) relies on JSP 1.2 and thus does not take advantage of JSP 2.0's features. JSF should work with JSP 2.0 as it is backward compatible with JSP 1.2. Future versions of JSF will likely use at least some of JSP 2.0's new features.
Summary
This SETT article illustrated some of JSP 2.0's new features using a simple customer profile example. With EL, EL functions, simple tag handlers, and .tag files, we have shown various new ways to remove annoying scriptlets and expressions from JSPs. Remember, the goal is to make web developer's lives easier. If you don't think it's easier, hopefully you have learned about at least one new feature you like. Whatever your impressions, you can't argue we have even more options available to us and more decisions to make as we pursue web development.
References
- [1] JavaServer Pages 2.0 Specification Proposed Final Draft 3
http://jcp.org/aboutJava/communityprocess/first/jsr152/index3.html - [2] Java Servlet Specification 2.4 Proposed Final Draft 3
http://jcp.org/aboutJava/communityprocess/first/jsr154/index3.html - [3] Tomcat 5.0
http://jakarta.apache.org/tomcat/index.html - [4] Developing Web Applications With JavaServer Pages 2.
http://developer.java.sun.com/developer/technicalArticles/javaserverpages/JSP20/ - [5] JavaServer Faces Technology
http://java.sun.com/j2ee/javaserverfaces/
Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.