NEWER is BETTER


Explore the new Servlet 2.3 and JavaServer Pages 1.2
specifications and how they can help make programming easier

by Kevin Jones

he Servlet 2.3 and JavaServer Pages 1.2 specifications will soon be released. They contain many minor and several major changes to the previous specifications. This article will examine these changes and the reasoning behind them.

The major differences between the current Servlet 2.2 specification and the 2.3 proposed final draft (PFD) are the addition of filters and events to the servlet model. Filters provide a way for applications to provide standard actions both before and after the execution of any servlet. Events have long been missing from the servlet specification. An event provides notification of specific actions, notably application start/end and session start/end, allowing applications to perform one-off initialization.

Event Handlers
On many occasions, an application is required to perform some initialization as soon as it starts. For example, this can occur when creating and initializing JDBC connections or creating a JDBC connection pool, reading in resources that will be used throughout the application's lifetime, or simply checking that certain resources exist before the application can execute. An application that runs in a 2.2-compliant container has no guaranteed way of being able to perform this type of initialization.

In the 2.2 specification, a servlet can be deployed with a <load-on-startup> entry in the web.xml file. The element should contain a positive number. This is a hint to the container as to when the servlet should be loaded. The lower the number, the bigger the hint. This solution has a number of problems, however. First, the specification says the following: "The load-on-startup element indicates that this servlet should be loaded on the startup of the Web application." Container vendors could read this as "should be loaded, but don't have to be loaded" (so the servlet may not be loaded when the application starts). Another problem is that we are using a servlet in a way for which it is not really intended.

Servlets have "request" scope. That is, they are designed to be executed once per request from the client. In this case, we have a servlet that has "application" scope (it executes once and hangs around).

In the 2.2 specification, servlets can also be unloaded—a container may look at the initialization servlet, decide it hasn't been used in a while, and unload it. If the servlet "owns" the resources it initialized at start-up, it should then free those resources.

Another problem is that a servlet won't be told when the application is ending. Its destroy method will get called at shutdown time, but there is no way for it to know if all the other servlets have also been unloaded. Other servlets may need to use the resources created by the initialization servlet when they are destroyed. If the initialization servlet is unloaded first, then the other servlets are out of luck.

The exact same issues arise with sessions. It is reasonable that an application does some initialization/tidy up for each session as it is created and destroyed.

To address these issues, the Servlet 2.3 specification introduces the concept of event handlers. Event handlers are code that can respond to specific events that are initiated by the container. In the Servlet 2.3 specification two types of events are defined: application events and session events. For both the event types, handlers can catch start and end events, and handlers can catch attribute change events.

We will concentrate on application start and end events but the code is very similar for the other event types.

Let's take a look at the event handler code. An application event handler has to extend javax.servlet.ServletContextListener. This interface has two methods,
contextDestroyed() and contextInitialized(), that both take a ServletContextEvent parameter. As you can imagine, the contextInitialized() method is called at application start time, and contextDestroyed() when the application ends. The ServletContextEvent class has a method that returns a reference to the ServletContext being created or destroyed.

As an example, imagine that you are building a simple Web-based white pages application where users can look up a name and phone number based on an e-mail (see Listing 1). In this example you will hold the white page entries in a hashtable. The hashtable needs to be created at application startup time and stored in the application's ServletContext.

The code is very straightforward. The class implements ServletContextListener. A constructor creates the hashtable. The contextDestroyed() method does nothing since the application is being unloaded anyway—we don't need to tidy up. The interesting work is in contextInitialized(). This method stores the data we need in the hashtable, then stores the hashtable in the ServletContext. When contextInitialized() is called, it is passed a reference to a ServletContextEvent object. The ServletContextEvent class has a single getServletContext() method that returns a reference to this application's ServletContext. Once that reference has been retrieved, the hashtable is stored.

An application has to be configured to use this event handler. This is done by amending the application's deployment descriptor (the web.xml file):

  <web-app>
<listener>
<listener-class>
WhitePagesListener
</listener-class>
</listener>
...

Any number of listeners can be configured in this way. Listeners are invoked in the order in which they appear in the deployment descriptor. Listeners are singletons, and the developer is responsible for synchronization. This isn't particularly important for ServletContextListeners but will be for all the other types.

Filters

Filters are the biggest addition to the Servlet 2.3 specification. Filters allow a servlet developer to intercept a request both before it reaches a servlet and to modify the response after the servlet has processed the request. Creating and installing a filter is very straightforward, as we will see.

All Filters implement the javax.filter.Filter interface. This interface has three methods, setFilterConfig() (in the final specification, this method is likely to be replaced by two methods, probably init(Filterconfig config) and destroy()), getFilterConfig() (this method will also disappear in later versions of the specification), and doFilter(). The setFilterConfig() method is called when the filter is first instantiated and when the filter is being removed from the call chain. At instantiation time, the filter is passed a non-null FilterConfig object. This object gives the filter access to its name and to the current ServletContext. As part of the configuration of filters, initialization parameters can be specified. These parameters are also available through the FilterConfig. When the filter is removed, setFilterConfig() is passed a null FilterConfig.

As part of their configuration, filters can be associated with a single servlet or with a group of servlets. Whenever a request is dispatched to a servlet with an associated filter, that filter is executed. Filters are executed as part of a chain, the order of execution being determined by the order of the filters in the deployment descriptor (the web.xml file). The container executes the filter by calling its doFilter() method. The method takes a reference to a ServletRequest, a ServletResponse, and a javax.servlet.FilterChain object.

FilterChain has a single method, doFilter(), that the filter calls to pass the request and response further down the chain. This also means, of course, that the filter can block the call by not passing it down the chain. In this case the filter is responsible for generating the correct response.

As an example, we will look at a filter that can be useful when debugging servlets (see Listing 2). This filter will dump the HttpRequest headers and the HttpResponse headers that are set by downstream servlets and filters. Making this filter the first filter in the chain should allow us to catch all of the response headers, but containers may decide to set headers in such a way that the filter never gets to see them.

The filter implements the javax.servlet.Filter interface, and so must implement setFilterConfig() and getFilterConfig() as shown previously. In setFilterContext(), a reference to the ServletContext is saved for later use.

Implementing doFilter() is trickier. The first part of doFilter() is easy: grab the request object, get the HTTP method and query string, and enumerate through the headers. The code is shown in Listing 3. Handling the response is the tricky part, and leads to another new aspect of servlets—wrapping the request and response objects. In previous versions of the servlet specification, when calling any method that required a [Http]ServletRequest and/or a [Http]ServletResponse, you had to pass the reference to the request and response objects that were passed to the servlet's service method. The most obvious example of this is when dispatching requests:


  RequestDispatch rd = getServletContext().getRequestDispatcher("foo");
rd.forward(request, response);
// or rd.include(request, response);

Suppose that you wanted to do an include but then you wanted to process the response before sending it back to the client, or maybe look at the response from a RequestDispatcher.forward() and throw it away before returning to the client. In previous versions of the servlet specification this was not possible. All of these things are now possible in the Servlet 2.3 specification.

The new specification defines two new class javax.servlet.http.HttpServletRequestWrapper and javax.servlet.http.HttpServletResponseWrapper (these derive from their non-Http versions). The constructors for these classes look like this

	HttpServletRequestWrapper(HttpServletRequest request)
HttpServletResponseWrapper(HttpServletResponse response)

The default behavior of each method in these classes is to pass the call onto the objects they wrap. To use these classes you typically derive from the class and override the methods you are interested in. For our filter, we need to create a response wrapper that logs calls to addXXXHeader() and setXXXHeader() methods. We also want to trap calls to setStatus(), setContentLength(), setContentType(), and setLocale(). The code is shown in Listing 4. It simply logs calls to each method. To use the object in the filter we do this:

  HttpServletResponse resp = (HttpServletResponse)response;
HeaderResponseWrapper hrespw = new HeaderResponseWrapper(resp, ctx);
System.out.println("********");
chain.doFilter(request, hrespw);

Notice that after creating the wrapper the chain.doFilter() method is called. It is during the processing of this method (the processing of the filter chain and the final servlet) that the wrapper methods will be called.

Once the filter has been defined it has to be installed. To do this, a filter element has to be defined in web.xml.

<filter>
<filter-name>Headers Filter
</filter-name>
<filter-class>DumpHeaders
</filter-class>
<!— optional <init-params> —>
</filter>

Once that is done, you need to associate the filter with the resources you are filtering. You have two options here: Associate the filter with a single named servlet or associate the servlet with a URL. This is done in web.xml. The two options are shown here:


<filter-mapping>
<filter-name>Headers Filter
</filter-name>
<servlet-name>
AddressBookServlet
</servlet-name>
</filter-mapping>

or

<filter-mapping>
<filter-name>Header Filter
</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

The request header ouput looks like this:


  GET/AddressBook/Browse.jsp HTTP/1.1
accept: */*
referer: http://localhost/
AddressBook/
accept-language: en-gb
accept-encoding: gzip, deflate
user-agent: Mozilla/4.0 (
compatible; MSIE 5.5;
Windows NT 5.0)
host: localhost
connection: Keep-Alive
cookie: JSESSIONID=
E0F9646772F4448004C16122020664F1

The response header output looks like this:


Content-Type:
text/html;charset=8859_1
Content-Type: text/plain

If you run a network trace on this, you'll see that several things are missing. For example, the status code is not displayed because there are headers being set by the container either before or after the filter chain is executed.

Filters have many uses such as authentication, transforms, and encryption/decryption. However, there is one note of caution: You can associate a filter with any resource, not just a servlet. If you are using a servlet engine that plugs in to another Web server, it is possible (in fact it is likely) that the Web server will serve resources independent of the servlet container. If this happens, the servlet container will not receive all the requests and so the filter may not always be executed.

Request Dispatching


One of the issues in JSP 1.1 involved request dispatching. If you had a page and did an include, jsp:include had a mandatory flush attribute that had to be set to true.

  <jsp:include page="somePage" flush="true" />
  


This forced the container to flush the current contents of the buffer back to the client. Then the JSP page couldn't set any HTTP response headers. This behavior has been changed in the JSP 1.2 specification so that jsp:include can now specify flush="false".

Tag Libraries
Several aspects of JSP tag libraries have been clarified and the specification has been tidied up in many places. There is also one major addition to tag libraries—the iterator tag. Currently in the JSP 1.1 specification, if a tag wants to perform "looping" it has to be a BodyTag (for more information on Body Tags, see my article "Benefits of Body Tags," Java Pro, April 2001). However, the tag may not need to process its body, so being a BodyTag causes the container to generate inefficient code (setBodyContent() and other methods will still be called). To address this, JSP 1.2 introduces iteration tags.

The JSP 1.2 specification introduces two new constants, EVAL_BODY_BUFFERED and EVAL_BODY_AGAIN. EVAL_BODY_BUFFERED replaces EVAL_BODY_TAG as a more descriptive name; their values are the same, which means that legacy (heritage) code will still work. It means that an implementation of doStartTag() should now return EVAL_BODY_BUFFERED rather than EVAL_BODY_TAG.

EVAL_BODY_AGAIN also has the same value as EVAL_BODY_BUFFERED, but again is a more descriptive name that should be returned from doAfterBody() when the tag wants the container to iterate over its body.

javax.servlet.jsp.IterationTag is the new interface defined by the JSP 1.2 specification that has to be implemented to provide iteration behavior. This interface extends SimpleTag and provides one extra function, doAfterBody(). BodyTag now extends IterationTag rather than SimpleTag.

To iterate, either in a BodyTag or an IterationTag, doAfterBody() should return EVAL_BODY_AGAIN to force the container to evaluate the tag again, and return SKIP_BODY to stop the iteration. The code for an IterationTag is very straightforward (see Listing 5).

doStartTag() returns EVAL_BODY_INCLUDE, and doAfterBody() returns EVAL_BODY_AGAIN to iterate or SKIP_BODY when done. Notice there is currently no IterationTagSupport class to match TagSupport and BodyTagSupport, but that doesn't really cause any difficulties.

The current version of tag libraries allows a very minimal validation mechanism. It is possible to define a TagExtraInfo class with an isValid() method. This method gets called when the page is compiled, and you can use it to check attribute names, values, types, and so on. The JSP 1.2 specification offers a much richer validation mechanism. You can define a validator class with a method that gets passed the XML representation of the entire page that contains the tag (or tags) in your tag library (see Listing 6). This method will be called when the page is compiled (it is a one-off hit, which means you only pay the cost once, not each time the page is requested by the user).

The validator in Listing 6 extends javax.servlet.jsp.tagext.TagLibraryValidator, and overrides the validate() method. This method takes as arguments the prefix of the tag to be validated and the associated URI on the page. It is called once per taglib directive on the page.

PageData gives you access to an InputStream that contains the JSP page in its XML format. This method returns null if everything is OK, or you get an error String in the case of a validation failure. Remember this is a page-translation-time error that should only happen at development time.

More Updates

The new specifications address other issues. For example, the JSP 1.2 specification will finally define an XML representation of a JSP page. This will be extremely useful, both for validation, as we saw previously, and for tool vendors because it will make JSP editors easier to implement.

The specification also allows us to define listeners as tags rather than as separate classes as we saw in this article. The JSP specification now has a chapter on localization, which defines, for example, the ability to specify the character encoding of the page.

A TryCatchFinally interface has also been added. This allows tags to better handle resource cleanup in the face of unexpected exceptions.

The JSP 1.2 and Servlet 2.3 specifications accomplish two goals. They clarify some of the points of confusion in the current specifications and they introduce some of the features requested by developers. The major changes are listeners, filters, and a modified TagLibrary model. The new specifications (as they currently stand) are available from http://java.sun.com/aboutJava/communityprocess/first/jsr053/index.html for anyone to read and comment on. Try them out to see if they make your job easier.

Kevin Jones is a developer with more than 15 years of experience. He has spent the last four years researching and teaching Java programming and most recently investigating HTTP and XML. Kevin lives and works in the U.K. for Developmentor, a training company based in the U.S. and Europe specializing in technical training on the Java and Microsoft platforms. Kevin can be reached at [email protected].