Servlet 2.3: New features exposed

A full update on the latest Servlet API spec

1 2 3 Page 2
Page 2 of 3
public class MyConnectionManager implements ServletContextListener {
  public void contextInitialized(ServletContextEvent e) {
    Connection con = // create connection
    e.getServletContext().setAttribute("con", con);
  }
  public void contextDestroyed(ServletContextEvent e) {
    Connection con = (Connection) e.getServletContext().getAttribute("con");
    try { con.close(); } catch (SQLException ignored) { } // close connection
  }
}

This listener ensures that a database connection is available in every new servlet context, and that all connections are closed when the context shuts down.

The HttpSessionActivationListener interface, another new listener interface in API 2.3, is designed to handle sessions that migrate from one server to another. A listener implementing HttpSessionActivationListener is notified when any session is about to passivate (move) and when the session is about to activate (become live) on the second host. These methods give an application the chance to persist nonserializable data across JVMs, or to glue or unglue serialized objects back into some kind of object model before or after migration. The interface has two methods:

  • void sessionWillPassivate(HttpSessionEvent e): The session is about to passivate. The session will already be out of service when this call is made.
  • void sessionDidActivate(HttpSessionEvent e): The session has been activated. The session will not yet be in service when this call is made.

You register this listener just like the others. However, unlike the others, the passivate and activate calls here will most likely occur on two different servers!

Select a character encoding

API 2.3 provides much-needed support for handling foreign language form submittals. There's a new method, request.setCharacterEncoding(String encoding), that lets you tell the server a request's character encoding. A character encoding, also known as a charset, is a way to map bytes to characters. The server can use the specified charset to correctly parse the parameters and POST data. By default, a server parses parameters using the common Latin-1 (ISO 8859-1) charset. Unfortunately, that only works for Western European languages. When a browser uses another charset, it is supposed to send the encoding information in the Content-Type header of the request, but almost no browsers do. This method lets a servlet tell the server what charset is in use (it is typically the charset of the page that contains the form); the server takes care of the rest. For example, a servlet receiving Japanese parameters from a Shift_JIS encoded form could read the parameters like this:

  // Set the charset as Shift_JIS
  req.setCharacterEncoding("Shift_JIS");
  // Read a parameter using that charset
  String name = req.getParameter("name");

Remember to set the encoding before calling getParameter() or getReader(). The setCharacterEncoding() call may throw java.io.UnsupportedEncodingException if the encoding is not supported. This functionality is also available for users of API 2.2 and earlier, as part of the com.oreilly.servlet.ParameterParser class. (See Resources.)

JAR dependencies

Often, a WAR file (Web application archive file, added in API 2.2) requires various other JAR libraries to exist on the server and operate correctly. For example, a Web application using the ParameterParser class needs cos.jar in the classpath. A Web application using WebMacro needs webmacro.jar. Before API 2.3, either those dependencies had to be documented (as if anyone actually reads documentation!) or each Web application had to include all its required jar files in its own WEB-INF/lib directory (unnecessarily bloating each Web application).

Servlet API 2.3 lets you express JAR dependencies within the WAR using the WAR's META-INF/MANIFEST.MF entry. That is the standard way for jar files to declare dependencies, but with API 2.3, WAR files must officially support the same mechanism. If a dependency can't be satisfied, a server can politely reject the Web application at deployment time instead of causing an obscure error message at runtime. The mechanism allows a high degree of granularity. For example, you can express a dependency on a particular version of an optional package, and the server has to find the right one with a search algorithm. (See Resources for a link to documentation that explains in detail how the manifest versioning model works.)

Class loaders

Here's a small change with a big impact: In API 2.3, a servlet container (a.k.a. the server) will ensure that classes in a Web application not be allowed to see the server's implementation classes. In other words, the class loaders should be kept separate.

That doesn't sound like much, but it eliminates the possibility of a collision between Web application classes and server classes. That had become a serious problem because of XML parser conflicts. Each server needs an XML parser to parse web.xml files, and many Web applications these days also use an XML parser to handle reading, manipulation, and writing of XML data. If the parsers supported different DOM or SAX versions, that could cause an irreparable conflict. The separation of class scope solves this issue nicely.

New error attributes

The previous API version, Servlet API 2.2, introduced several request attributes that could be used by servlets and JSPs acting as targets of an <error-page> rule. If you don't remember <error-page> rules, they let you configure a Web application so that certain error status codes or exception types cause specific pages to be displayed:

<web-app>
    <!-- ..... -->
    <error-page>
        <error-code>
            404
        </error-code>
        <location>
            /404.html
        </location>
    </error-page>
    <error-page>
        <exception-type>
            javax.servlet.ServletException
        </exception-type>
        <location>
            /servlet/ErrorDisplay
        </location>
    </error-page>
    <!-- ..... -->
</web-app>

A servlet in the <location> for an <error-page> rule could receive the following three attributes:

  • javax.servlet.error.status_code: An Integer telling the error status code, if any
  • javax.servlet.error.exception_type: A Class instance indicating the type of exception that caused the error, if any
  • javax.servlet.error.message: A String telling the exception message, passed to the exception constructor

Using those attributes, a servlet could generate an error page customized to the error, as shown below:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse res)
                               throws ServletException, IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    String code = null, message = null, type = null;
    Object codeObj, messageObj, typeObj;
    // Retrieve the three possible error attributes, some may be null
    codeObj = req.getAttribute("javax.servlet.error.status_code");
    messageObj = req.getAttribute("javax.servlet.error.message");
    typeObj = req.getAttribute("javax.servlet.error.exception_type");
    // Convert the attributes to string values
    // We do things this way because some old servers return String
    // types while new servers return Integer, String, and Class types.
    // This works for all.
    if (codeObj != null) code = codeObj.toString();
    if (messageObj != null) message = messageObj.toString();
    if (typeObj != null) type = typeObj.toString();
    // The error reason is either the status code or exception type
    String reason = (code != null ? code : type);
    out.println("<HTML>");
    out.println("<HEAD><TITLE>" + reason + ": " + message + "</TITLE></HEAD>");
    out.println("<BODY>");
    out.println("<H1>" + reason + "</H1>");
    out.println("<H2>" + message + "</H2>");
    out.println("<HR>");
    out.println("<I>Error accessing " + req.getRequestURI() + "</I>");
    out.println("</BODY></HTML>");
  }
}

But what if the error page could contain the exception stack trace or the URI of the servlet that truly caused the problem (since it's not always the originally requested URI)? With API 2.2, that wasn't possible. With API 2.3, that information is available with two new attributes:

  • javax.servlet.error.exception: A Throwable object that is the actual exception thrown
  • javax.servlet.error.request_uri: A String telling the URI of the resource causing problems

Those attributes let the error page include the stack trace of the exception and the URI of the problem resource. The servlet below has been rewritten to use the new attributes. (It fails gracefully if they don't exist, for backward compatibility.)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
  public void doGet(HttpServletRequest req, HttpServletResponse res)
                               throws ServletException, IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    String code = null, message = null, type = null, uri = null;
    Object codeObj, messageObj, typeObj;
    Throwable throwable;
    // Retrieve the three possible error attributes, some may be null
    codeObj = req.getAttribute("javax.servlet.error.status_code");
    messageObj = req.getAttribute("javax.servlet.error.message");
    typeObj = req.getAttribute("javax.servlet.error.exception_type");
    throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
    uri = (String) req.getAttribute("javax.servlet.error.request_uri");
    if (uri == null) {
      uri = req.getRequestURI();  // in case there's no URI given
    }
    // Convert the attributes to string values
    if (codeObj != null) code = codeObj.toString();
    if (messageObj != null) message = messageObj.toString();
    if (typeObj != null) type = typeObj.toString();
    // The error reason is either the status code or exception type
    String reason = (code != null ? code : type);
    out.println("<HTML>");
    out.println("<HEAD><TITLE>" + reason + ": " + message + "</TITLE></HEAD>");
    out.println("<BODY>");
    out.println("<H1>" + reason + "</H1>");
    out.println("<H2>" + message + "</H2>");
    out.println("<PRE>");
    if (throwable != null) {
      throwable.printStackTrace(out);
    }
    out.println("</PRE>");
    out.println("<HR>");
    out.println("<I>Error accessing " + uri + "</I>");
    out.println("</BODY></HTML>");
  }
}

New security attributes

Servlet API 2.3 also adds two new request attributes that can help a servlet make an informed decision about how to handle secure HTTPS connections. For requests made using HTTPS, the server will provide these new request attributes:

  • javax.servlet.request.cipher_suite: A String representing the cipher suite used by HTTPS, if any
  • javax.servlet.request.key_size: An Integer representing the bit size of the algorithm, if any

A servlet can use those attributes to programmatically decide if the connection is secure enough to proceed. An application may reject connections with small bitsizes or untrusted algorithms. For example, a servlet could use the following method to ensure that its connection uses at least a 128-bit key size.

public boolean isAbove128(HttpServletRequest req) {
  Integer size = (Integer) req.getAttribute("javax.servlet.request.key_size");
  if (size == null || size.intValue() < 128) {
    return false;
  }
  else {
    return true;
  }
}

Note: The attribute names in the Proposed Final Draft use dashes instead of underscores; however, they're being changed, as shown here before the Final Release, to be more consistent with existing attribute names.

Little tweaks

A number of small changes also made it into the API 2.3 release. First, the getAuthType() method that returns the type of authentication used to identify a client has been defined to return one of the four new static final String constants in the HttpServletRequest class: BASIC_AUTH, DIGEST_AUTH, CLIENT_CERT_AUTH, and FORM_AUTH. This allows simplified code like:

if (req.getAuthType() == req.BASIC_AUTH) {
  // handle basic authentication
}

Of course, the four constants still have traditional String values, so the following code from API 2.2 works too, but is not as fast or as elegant. Notice the reverse equals() check to avoid a NullPointerException if getAuthType() returns null:

if ("BASIC".equals(req.getAuthType())) {
  // handle basic authentication
}

Another change in API 2.3 is that HttpUtils, also known as "the class that never should have been made public," has been deprecated. HttpUtils has always stood out as an odd collection of static methods -- calls that were useful sometimes, but might have been better placed elsewhere. In case you don't recall, the class contained methods to reconstruct an original URL from a request object and to parse parameter data into a hashtable. API 2.3 moves this functionality into the request object where it more properly belongs, and deprecates HttpUtils. The new methods on the request object are:

  • StringBuffer req.getRequestURL(): Returns a StringBuffer containing the original request URL, rebuilt from the request information.
  • java.util.Map req.getParameterMap(): Returns an immutable Map of the request's parameters. The parameter names act as keys and the parameter values act as map values. It has not been decided how parameters with multiple values will be handled; most likely, all values will be returned as a String[]. These methods use the new req.setCharacterEncoding() method to handle character conversions.
1 2 3 Page 2
Page 2 of 3