Servlets.com

Home

What's New?

com.oreilly.servlet

Servlet Polls

Mailing Lists

Servlet Engines

Servlet ISPs

Servlet Tools

Documentation

Online Articles

The Soapbox

"Java Servlet
Programming,
Second Edition"

"Java Enterprise
Best Practices"

Speaking & Slides

About Jason

XQuery Affiliate

Introducing the new Servlet API 2.1
A complete description of what's changed since 2.0

(Originally published in JavaWorld, December 1998)

Summary
Sun has just released the specification for Servlet API 2.1. Regular JavaWorld contributor and servlet aficionado Jason Hunter explains the differences between 2.0 and 2.1, discusses the reasons for the changes, and shows you how to write servlets using 2.1.

Plus, for the servlet developer on the run, Jason provides the "2.0 to 2.1 Cheat Sheet," a quick-list of changes to Servlet API 2.1. (3,800 words with sidebar)

By Jason Hunter

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.


// Show an item in an online catalog
out.println("Feast your eyes on this beauty:");

RequestDispatcher dispatcher = getServletContext()
  .getRequestDispatcher("/servlet/CatalogDisplay?item=156592391X");
dispatcher.include(req, res);

out.println("And, since I like you, it's 20% off!");

A servlet can pass information to the delegate using a query string as shown above, or it can put "attributes" in the request using ServletRequest's new setAttribute(String name, Object object) method. For example,


// Show an item in an online catalog
RequestDispatcher dispatcher = getServletContext()
  .getRequestDispatcher("/servlet/CatalogDisplay");

out.println("Feast your eyes on this beauty:");
req.setAttribute("item", new Book("156592391X"));
dispatcher.include(req, res);

out.println("Or how about this one:");
req.setAttribute("item", new Book("0395282659"));
dispatcher.include(req, res);

out.println("And, since I like you, it's 20% off!");

CatalogDisplay can receive the "item" request attribute by calling req.getAttribute("item"), or it can receive the names of all the request attributes using req.getAttributeNames(). Using attributes instead of parameters gives you the ability to pass objects instead of simple strings.

Shared attributes
To further help servlets cooperate on tasks, the Servlet API 2.1 includes a new method for servlets to share information. Previously, it was possible for servlets to share information, but they had to use homegrown mechanisms like a Singleton object or shared files. In 2.1, servlets have been given the ability to share information by setting and getting attributes in their ServletContext.

Several methods have been added to ServletContext to support the setting and getting of attributes. There's setAttribute(String name, Object object), which sets an attribute. Another method is Object getAttribute(String name), which gets an attribute, and a third is EnumerationgetAttributeNames(), which gets a list of attribute names. (Actually, the getAttribute() method has existed since Servlet API 1.0, but until now it could only read attributes hard coded into the server.) Finally, there's removeAttribute(String name), which removes an attribute.

Using these methods, a servlet can easily share information with any other servlets that live in its ServletContext. This functionality helps tremendously when load balancing, because servlets may find themselves spread across a number of back-end servers, making it especially challenging for servlets to use a homegrown mechanism to share information.

How servlets are divided into ServletContext groups depends on the server configuration. Historically, Web servers have put all servlets in the same context. However, now that each context has a "shared state," it's likely servers will begin to segment servlets into individual contexts, with each context thought of as a single "Web application."

To support communication between contexts, there's a new method, ServletContext.getContext(String uripath). This method returns the ServletContext for the given URI, subject to security constraints. You can use this method to share information with server components outside your context.

Resource abstraction
Another new feature in Servlet API 2.1 is resource abstraction, which allows servlets to access a resource without knowing where the resource resides. This abstraction makes servlets into mobile objects that can be moved between servers -- a useful ability when load balancing.

In 2.1, all resources are abstracted into URLs on the server.

Getting an abstract resource

A servlet gains access to an abstract resource using ServletContext.getResource(String uripath). This method returns a URL that can be used to investigate the specified resource and read its content. How the URI path parameter maps to an actual resource (file, database entry, or other) is determined by the Web server. The one restriction is that the URI should not be an active resource (servlet, CGI program, and so on). For active resources you should use a RequestDispatcher.

To demonstrate how to read from an abstract resource, the following code fetches and prints the server's /includes/header.html file:


URL url = getServletContext().getResource("/includes/header.html");
out.println(url.getContent());

The header.html file should be found somewhere under the server's document root. It may exist on a server machine other than the one hosting the servlet, but conveniently that doesn't matter.

Using the returned URL object you can investigate the attributes of the abstract resource. Here's code that examines the Web server's front page.


URL url = getServletContext().getResource("/");  // front page
URLConnection con = url.openConnection();
con.connect();
int contentLength = con.getContentLength();
String contentType = con.getContentType();
long expiration = con.getExpiration();
long lastModified = con.getLastModified();
// etc...

Remember, the content served for the / URI path is entirely determined by the server.

Getting an abstract resource as a stream

The 2.1 API also includes a convenient method, called getResourceAsStream(String uripath), for reading resources as a stream. This method returns an InputStream, which lets you type


InputStream in = getServletContext().getResourceAsStream("/")

instead of


URL url = getServletContext().getResource("/");
InputStream in = url.openStream();

The method doesn't save much typing, but perhaps it can help people who aren't familiar with URL objects.

Writing to an abstract resource

Here's a useful tip: You can also use getResource() to write output, for those resources that permit it. The trick is to get the URL's corresponding URLConnection and write to the connection's OutputStream. For example:


URL url = getServletContext().getResource("/custom.log");
URLConnection con = url.openConnection();
con.setDoOutput(true);
OutputStream out = con.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
pw.println("That wasn't so hard.");
pw.close();
out.close();

Backward compatibility for resources

For backward compatibility and to ease the transition to servlets for CGI programmers, 2.1 continues to include methods for file access, such as getPathTranslated(). Just remember that anytime you access a resource using a File object you're tying yourself to a particular machine.

Sessions
Servlet developers have been given more control over HttpSession lifecycles in 2.1. Previously, the interval before a session timed out had to be set using administration tools. You can now set the duration of a session programmatically by calling HttpSession.setMaxInactiveInterval(int interval). This method takes an int representing the number of seconds of inactivity that need to occur before the session is timed out and invalidated. A negative value indicates the session should never expire. The current interval can be retrieved using getMaxInactiveInterval().

Future directions: deployment descriptors
Not included in the 2.1 spec, but coming soon, are servlet deployment descriptors. These descriptors, modeled after a similar concept in Enterprise JavaBeans, are a way to simplify the installation of servlets and Web content into a Web server.

Deployment descriptors are expected to allow servlets, support classes, and even content to be packaged in a jar along with a complete declaration of their server configuration requirements. For example, the jar can report where the content should reside, which of the contained classes should be loaded as servlets, what names the servlets should be registered under, what default init parameters the servlets should have, and what ServletContext the servlets should live in. Essentially, with deployment descriptors you'll be able to install an entire "Web application" using just one jar.

Conclusion
The 2.1 API includes a number of changes that make servlet programming easier and more consistent. In exact numbers: version 2.1 offers 1 new class, 24 new methods, 1 deprecated class, and 9 deprecated methods. It's definitely not a large revision, and the core functionality remains the same, but by moving to 2.1 you'll be able to take advantage of consistent APIs and new abilities for request delegation, information sharing, resource abstraction, and session management.


About the author
Jason Hunter works as the chief technology officer of K&A Software, where he specializes in Java training and consulting. He is author of the book Java Servlet Programming (O'Reilly) and publisher of the Web site http://www.servlets.com/. He belongs to the working group responsible for Servlet API development (and has his fingerprints all over the 2.1 specification). If by some miracle you don't find him at work, he's probably out hiking in the mountains.

To be notified when new articles are added to the site, subscribe here.




2.0 to 2.1 Cheat Sheet

New classes:

RequestDispatcher -- Used for delegating requests

New methods:

void GenericServlet.log(String message, Throwable t) -- Replaces log(Exception e, String msg)
  void ServletContext.log(String message, Throwable t) -- Replaces log(Exception e, String msg)
  boolean HttpServletRequest.isRequestedSessionIdFromURL() -- Replaces isRequestedSessionIdFromUrl()
  String HttpServletResponse.encodeURL() -- Replaces encodeUrl()
  String HttpServletResponse.encodeRedirectURL() -- Replaces encodeRedirectUrl()
  void GenericServlet.init() throws ServletException -- Alternative to init(ServletConfig config)
  HttpSession HttpServletRequest.getSession() -- Alternative to getSession(true)
  int ServletContext.getMajorVersion() -- Returns Servlet API major version number
  int ServletContext.getMinorVersion() -- Returns Servlet API minor version number
  ServletException(Throwable rootCause) -- Supports a new nested exception
  ServletException(String message, Throwable rootCause) -- Supports a new nested exception
  Throwable ServletException.getRootCause() -- Returns the nested exception
  RequestDispatcher ServletContext.getRequestDispatcher(String uripath) -- Gets a RequestDispatcher for delegating requests
  void RequestDispatcher.forward(ServletRequest req, ServletResponse res) throws ServletException, IOException -- Sends the request to another component
  void RequestDispatcher.include(ServletRequest req, ServletResponse res) throws ServletException, IOException -- Includes content from another component
  Enumeration ServletRequest.getAttributeNames() -- Returns the request attribute names, to support delegation
  Enumeration ServletContext.getAttributeNames() -- Returns the context attribute names, to support shared information
  void ServletContext.setAttribute(String name, Object object) -- Sets a context attribute, to support shared information
  void ServletContext.removeAttribute(String name) -- Removes a context attribute, to support shared information
  ServletContext ServletContext.getContext(String uripath) -- Returns another component's context, to support shared information
  URL ServletContext.getResource(String uripath) throws MalformedURLException -- Returns an abstract resource
  InputStream ServletContext.getResourceAsStream(String uripath) -- Returns an abstract resource as a stream
  void HttpSession.setMaxInactiveInterval(int interval) -- Sets a session's timeout
  int HttpSession.getMaxInactiveInterval() -- Returns a session's timeout

Newly deprecated methods:

void ServletContext.log(Exception e, String msg) -- Replaced by log(String message, Throwable t)
  String ServletRequest.getRealPath(String path) -- Use ServletContext.getRealPath(String path) instead
  boolean HttpSession.isRequestedSessionIdFromUrl() -- Replaced by isRequestedSessionIdFromURL()
  String HttpServletResponse.encodeUrl() -- Replaced by encodeURL()
  String HttpServletResponse.encodeRedirectUrl() -- Replaced by encodeRedirectURL()
  void ServletContext.setStatus(int sc, String sm) -- Use setStatus(int sc) and sendError(int sc, String message) instead
  Servlet ServletContext.getServlet(String name) -- Removed for safety
  Enumeration ServletContext.getServletNames() -- Removed for safety
  HttpSessionContext HttpSession.getSessionContext() -- Removed for safety

Newly deprecated classes:

HttpSessionContext -- Removed for safety

 


Home   com.oreilly.servlet   Polls   Lists   
Engines   ISPs   Tools   Docs   Articles   Soapbox   Book

Copyright © 1999-2005 Jason Hunter

webmaster@servlets.com
Last updated: March 1, 2009