Introducing the new Servlet API 2.1

A complete description of what's changed since 2.0

On November 6, Sun Microsystems released the specification for Servlet API 2.1. (See Resources for a link to the formal spec.)

This is the first time the Servlet API has had an official specification, and it's the first new release of the Servlet API since announcements of version 2.0 were stuffed in our stockings back in December 1997. This article describes what has changed from version 2.0, explains why the changes were made, and demonstrates how to write servlets using 2.1. To keep the article to a reasonable length, I'm going to assume you're familiar with the classes and methods of Servlet API 2.0. If that's not the case, you can peruse the Resources section for links to sites that will help get you up to speed.

Comparing Servlet API 2.1 to 2.0, how does the new version differ from the previous one?

  • The API has been "cleaned up" to make method names more consistent and predictable

  • Servlets can now delegate request handling to other server components

  • Servlets can now share information using their ServletContext

  • There's a new way to abstract servlet resources

  • Servlets now have more control over session management

Before we begin our examination of these differences, let me point out that version 2.1 has been released as a specification only. No Web server yet supports 2.1. Even the reference servletrunner implementation is still some weeks away, and commercial Web server support may be even further away release-wise. But on the bright side, servletrunner has been entirely rewritten and, when released, should be far more robust than its previous versions.

Cleaning house

One of the nicest features of Java is its consistency of naming conventions and design. The Servlet API 2.1 includes a number of small "housecleaning" API changes designed to maintain that consistency -- both internally and with other Java APIs. The following is a rundown of these small changes:

More consistent logging

To begin with, the method ServletException.log(Exception e, String msg), used to log servlet events, has been deprecated and replaced with log(String message, Throwable t). This one deprecation fixes two problems. First, it moves the optional Exception parameter to the end of the argument list, as is the custom in Java. Second, it allows the log() method to take a Throwable object, a more general type of exception. This should make the API more predictable and robust.

In addition, the method log(String message, Throwable t) has been added to the GenericServlet class. This lets you call log() directly without first having to get a handle to a ServletContext. Previously, in 2.0, GenericServlet only supported the one-argument log(String msg) method. Now it conveniently supports both log() methods.

Removed redundancy

The ServletRequest.getRealPath(String path) method, used to determine the actual filesystem location for a given URL path, has been deprecated in favor of a method by the same name in ServletContext. API 2.0 included both methods and defined them to perform the same task. Redundancy, though good when it comes to kidneys, isn't good in an API. Accordingly, getRealPath() in ServletRequest has been removed. Why wasn't the method in ServletContext deprecated instead? Because path mapping rules depend on a servlet's context, not on any individual client request.

More consistent URLs

So, which way do you abbreviate Uniform Resource Locator, Url or URL? The Java API tends to choose the uppercase presentation, as in: java.net.URL. The Servlet API, well, it seems it couldn't decide. In 2.0, some methods, like getRequestURL(), choose uppercase while others, like encodeUrl(), choose lowercase. In 2.1, URL is always uppercase. Every method containing the lowercase presentation has been deprecated and replaced. The affected methods are HttpServletRequest.isRequestedSessionIdFromUrl(), HttpServletResponse.encodeUrl(), and HttpServletResponse.encodeRedirectUrl().

Easier initialization

One of the things you have to remember when writing servlets with version 2.0 of the API is that anytime you override init(ServletConfig config) you must first call super.init(config). It's annoying, but it has to be done in order to give the GenericServlet superclass a chance to save a reference to the config and perform other preparatory work.

Version 2.1 removes the need for this irritating task. You can now override a no-argument init() method and avoid the mandatory super.init(config) call. You'll never even miss the ServletConfig parameter because GenericServlet itself implements the ServletConfig interface: a servlet can call ServletConfig methods directly on itself.

This change lets you write an init() method as simple as this:

public void init() throws ServletException {
  String greeting = getInitParameter("greeting"); // a ServletConfig method
}

Behind the scenes, the GenericServlet class supports the no-arg init() method with code similar to this:

public class GenericServlet implements Servlet, ServletConfig {
  ServletConfig _config = null;
  public void init(ServletConfig config) throws ServletException {
    _config = config;
    log("init called");
    init();
  }
  public void init() throws ServletException { }
  public String getInitParameter(String name) {
    return _config.getInitParameter(name);
  }
  // etc...
}

Notice the Web server still calls a servlet's init(ServletConfig config) method at initialization time. The change in 2.1 is that GenericServlet now passes on this call to the no-arg init(), which you can override without worrying about the config.

If backward compatibility is a concern, you should continue to override init(ServletConfig config) and call super.init(config). Otherwise you may end up wondering why your no-arg init() method is never called.

A simpler way to get a session

Version 2.1 also adds a no-arg version of getSession() to the HttpServletRequest class. This is a convenience method that has the same behavior as getSession(true), the call used most often when getting a user's session. Using it saves you the four keystrokes t-r-u-e.

More consistent setting of status codes

Another change is that the method HttpServletResponse.setStatus(int sc, String sm) has been deprecated in favor of setStatus(int sc) and sendError(int sc, String msg). This modification was necessary in order for the API to conform to the desirably simple rule that setStatus() sets the response's status code and nothing more, while sendError() sets the status code and, additionally, provides a server-generated explanation of the error in the servlet's response. Following this rule, there is no point in having a setStatus() that accepts a message.

More well-defined parameter receiving

Finally, something only a true servlet aficionado would notice! Version 2.1 has clarified what happens when getParameter(String name) is called on a parameter with multiple values -- as happens when two form fields have the same name. It must now return the same thing as the first value in the array returned by getParameterValues(String name). Previously, the behavior was left unspecified, causing some servers to return a comma-separated list of parameter values (i.e., value1, value2, value3). In 2.1, you may not know for sure which value you'll get, but you at least know it will be a legitimate value, not some server-specific hybrid.

Limiting exposure

A few additional methods have been deprecated in 2.1, not because of API clean-up, but because in 2.0 they exposed more of the underlying Web server implementation than was necessary or proper. Here's a rundown of methods deprecated in version 2.1:

No direct servlet references

The first of these is ServletContext.getServlet(Stringname), called to get a reference to another servlet instance loaded in the Web server. This method has been deprecated and defined to return null in 2.1 because direct access to another servlet instance opens up too many opportunities for error. The ServletContext.getServletNames() method has been deprecated as well. The reasoning goes that servlets may be destroyed by the Web server at any time, so nothing but the server should hold a direct reference to a servlet. Also, on a Web server that supports load balancing where servlets are distributed across a number of servers, it may be difficult even to return a local servlet reference.

The getServlet() method has always been known to be dangerous, and Sun has officially recommended against using it, but it survived until now because there wasn't a good alternative for interservlet communication. The method gets the official boot now because servlets have a new way to collaborate using a shared ServletContext, as we'll see later.

No more session forgery

Also deprecated in 2.1 is the HttpSession.getSessionContext() method. In 2.0, this method returned an HttpSessionContext object that could be used for perusing the current sessions (and corresponding session IDs) managed by the server. This method wasn't useful for much except debugging -- but that's not why it's deprecated. The reason is that session IDs must be carefully guarded. They're kind of like social security numbers. Any unscrupulous individual with access to another user's session ID can, with a forged cookie, join the session of that user. Thus, in 2.1, getSessionContext() now returns an empty HttpSessionContext containing no session information. The entire HttpSessionContext class has been deprecated as well.

On to the enhancements

Enough with the removal of functionality! Let's take a look at what's been added to the Servlet API 2.1. First, the little things:

What API is this, anyway?

The API adds two methods you can use to determine which Servlet API version your Web server supports. These methods are ServletContext.getMajorVersion() and ServletContext.getMinorVersion(). For API 2.1, getMajorVersion() returns 2, and getMinorVersion() returns 1.

Nested exceptions

ServletException, the exception thrown by init(), doGet(), and doPost() to indicate a servlet error, has been enhanced to support a "root cause" exception. This lets ServletException act as a "wrapper" around any type of exception, giving the Web server a way to know what "root" exception caused the ServletException to be thrown. To support this ability, ServletException has two new constructors: ServletException(Throwable rootCause) and ServletException(String message, ThrowablerootCause).

The following code snippets demonstrate how you can use a nested exception. First, here's servlet code that catches an InterruptedException and throws a ServletException, according to the 2.0 API. Notice that the server catching the ServletException won't be able to examine the underlying InterruptedException or view its stack trace.

try {
  thread.sleep(60000);
}
catch (InterruptedException e) {
  throw new ServletException(e.getMessage());  // no type or stack trace
}

In 2.1 you can pass on the underlying exception:

try {
  thread.sleep(60000);
}
catch (InterruptedException e) {
  throw new ServletException(e);  // includes full underlying exception
}

The server can retrieve the underlying exception by calling e.getRootCause(). The call returns null if there is no nested exception.

My personal advice: Ignore this new feature and deal with exceptions yourself. You'll have to write a little extra code, but by handling the exception yourself you can guarantee consistent and proper error handling.

How to let someone else do the work

A more exciting addition to 2.1 is the ability for servlets to programmatically delegate request handling to other components on the server. This serves two purposes. First, a servlet can forward an entire request, doing some preliminary processing and then passing off the request to another component. This ability may remind you of the "NameTrans" feature in Netscape's NSAPI. Second, a servlet can include in its response a bit of content generated by another component, essentially creating a programatic server-side include.

This delegation ability gives servlets more flexibility, and allows for better abstraction. Using delegation, a servlet can construct its response as a collection of content generated by various Web server components, all located using the Web server's URI (a fancy word for URL) namespace. This functionality is especially important to JavaServer Pages, where it often happens that one servlet preprocesses a request, then hands off the request to a .jsp page for completion.

To support request delegation, 2.1 includes a new interface called RequestDispatcher. A servlet gets a RequestDispatcher using the getRequestDispatcher(String uripath) method of its ServletContext. This method returns a RequestDispatcher that can dispatch to the component found at the given URL.

RequestDispatcher has two methods, forward(ServletRequest req, ServletResponse res) and include(ServletRequest req, ServletResponse res). The forward() method hands off the entire request to the delegate. To ensure the delegate has complete control over the response, forward() must be called before the first servlet gets the ServletOutputStream or PrintWriter for the response. The include() method adds the delegate's output to the calling servlet's response, but leaves the calling servlet in control. This method may be called at any time. The delegate, however, because it may be used anywhere in a page, has no ability to change the status code or HTTP headers sent in the response.

The following code shows how a servlet can include content from another component.

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more