What's new in Java Servlet API 2.2?

A full update on the latest Java Servlet API

1 2 3 Page 2
Page 2 of 3

It's also defined that Web servers must guarantee that servlets sharing information via user sessions or servlet contexts must not experience unexpected ClassCastExceptions. Such exceptions occurred previously due to servlets being loaded by different ClassLoader instances -- remember, classes loaded by two different class loaders cannot be cast to one another. In effect, this new rule means that, when one servlet class is reloaded, all the classes of the entire context have to be reloaded. This causes an unfortunate performance penalty, but one that should only be paid during development and that can be justified by the easier programming it allows.

Distributed applications

A few clarifications were also made in 2.2 with regards to distributed applications, in which application components can be spread across multiple back-end server machines.

The specification dictates that, for an app marked as distributable in its deployment descriptor, there will be a single ServletContext instance per JVM. This means the context attributes cannot be used to share global information. Global information in a distributed app needs to be stored externally from the Web server, as is the case in database or EJB component.

An app marked as distributable also has special rules for user sessions. Servers will use session affinity to efficiently manage user state across multiple back-end servers. This means that all requests that are part of a single session from a particular user are to be handled by only one JVM at a time. This in turn eliminates the need to constantly replicate session information across all the back-end servers. Responsibility for the session can be moved to another server between user requests, though in practical terms this is unlikely to occur frequently. Still, to enable the moving of a session, all objects placed into a session by servlets in a distributed app must implement Serializable. A Web server can throw an IllegalArgumentException if this condition is not met. Nondistributed apps, of course, can store any objects into the session.

Response buffering

One of the most useful features added in version 2.2 of the servlet API is response buffering. A servlet now has control over whether the server buffers its response, and may dictate how large a buffer the server can use.

In previous versions of the API, most Web servers implemented response buffering as a way to improve performance. Exactly how large that buffer was depended on the server. Generally, servers had buffers in the neighborhood of 8 KB.

The important change for 2.2 is that a servlet now can specify a minimum buffer size for its output. This improves the flexibility of servlet error handling.

How does this work? Well, the structure of HTTP dictates that the first line of an HTTP response includes a status code indicating the success or failure of the client request. To be sure to correctly set the status code, servlets have had to do full error checking before generating any output. If an error was encountered halfway through the response, it was just too bad. The response was already sent (or committed) and the status code and headers could not be changed.

A response buffer allows a servlet to write some amount of output with a guarantee that the response won't be immediately committed. If the servlet finds an error, the status code and headers can still be changed so long as the buffer has not been flushed.

Five new methods

Five methods were added to ServletResponse in order to support response buffering:

  • The setBufferSize(int size) method tells the server the minimum buffer size that the servlet will accept; the value is given in bytes. The server may provide a larger buffer than requested if necessary -- it may want to keep buffers in 8 KB blocks, for example.

  • The getBufferSize() method returns an int indicating how large the current buffer actually is. Larger buffers allow for more flexibility; smaller buffers save server memory and make the page appear to arrive to the client more quickly. Just make sure to specify the buffer size before writing output, as setBufferSize() will throw an IllegalStateException if you don't.

  • The isCommitted() method returns a boolean indicating whether any part of the response has actually been sent. If this method returns true, it's too late to change the status code and headers.

  • The reset() method can be used any time before commit to empty the buffer and unset the headers. reset() is automatically called by methods like sendError() and sendRedirect(). reset() throws an IllegalStateException if you try to reset a committed response.

  • The final newly added method is flushBuffer(). It sends content in the buffer to the client and commits the response.

The following code snippet demonstrates how these methods can be put to use:

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setBufferSize(16 * 1024); // 16K buffer res.setContentType("text/html"); PrintWriter out = res.getWriter();

int size = res.getBufferSize(); // returns 16384 or greater

out.println("The client won't see this"); res.reset(); out.println("Nor will the client see this!"); res.reset(); out.println("And this won't be seen if sendError() is called"); if (req.getParameter("important_parameter") == null) { res.sendError(res.SC_BAD_REQUEST, "important_parameter needed"); } }

This servlet sets the buffer to 16 KB, then checks the buffer size. The returned size should be 16 KB or greater. Then the servlet tests out the reset() method by calling it both directly and via sendError().

Support for double headers

In the new version of the API, servlets have the ability to retrieve more information than ever before, and send a little more as well.

There's a new method in HttpServletRequest called getHeaders() that can return multiple values for a given header. This was needed because some headers, such as Accept-Language, can send multiple header values:

Accept-Language: en
Accept-Language: fr
Accept-Language: ja

Previously, servlets using getHeader() could retrieve only one value per header. With the new version of getHeaders(), a servlet can retrieve multiple values as an enumeration of string objects.

Servlets have also been given the ability to send multiple values for the same response header using methods in HttpServletResponse. The new addHeader(String name, String value) method sets the header to the given value. While the traditional setHeader() method would replace any existing value or values, addHeader() leaves current settings alone and just sets an additional value. There's also addIntHeader(String name, int value) and addDateHeader(String name, long date).

Now this is just temporary

Another new piece of information was made available in 2.2, but this change didn't require a new method.

The context object now contains a new standard attribute -- available using getAttribute(String name) -- called javax.servlet.context.tempdir. This attribute maps to a temporary directory where short-lived working files can be stored. Each context receives a different temporary directory.

The following code shows how to write to a temp file in the temporary directory.

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // The directory is given as a File object File dir = (File) getServletContext() .getAttribute("javax.servlet.context.tempdir");

// Construct a temp file in the temp dir (JDK 1.2 method)

File f = File.createTempFile("xxx", ".tmp", dir);

// Prepare to write to the file

FileOutputStream fout = new FileOutputStream(f);

// ...

}

First, this servlet locates its temporary directory. Then, it uses the createTempFile() method to create a temporary file in that directory with an xxx prefix and .tmp suffix. Finally it constructs a FileOutputStream to write to the temporary file.

No more amnesia

The last bit of new information available to a servlet is the servlet's own name. In the ServletConfig interface (which GenericServlet implements) there's a new method, getServletName(), that returns the servlet's registered name. If the servlet is unregistered, the method returns the servlet's class name. This method proves useful when logging and storing a servlet instance's state information into such resources as databases or servlet contexts.

As an example, the following code demonstrates how to use the servlet's name to retrieve a value from the servlet context, using the name as part of the lookup key.

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String name = getServletName(); // inherited from GenericServlet ServletContext context = getServletContext(); Object value = context.getAttribute(name + ".state"); }

So, where are you from?

Internationalization has become an important topic on the Web, and the new version of the API includes some enhancements that simplify the challenge of dealing with clients from multiple locales.

With Java Servlet API 2.2, a servlet can determine the preferred locale of the client using a few new convenience methods. HttpServletRequest has a getLocale() method that returns a java.util.Locale object, which in turn indicate the client's most preferred locale. This preference is based primarily on the Accept-Language header. There's a getLocales() method as well that returns an enumeration of Locale objects indicating all the acceptable locales for the client, with the most preferred first. Accompanying these methods is a setLocale(Locale loc) method added to ServletResponse that allows a servlet to specify the locale of the response. The method automatically sets the Content-Language header and the Content-Type charset value. setLocale() should be called after setContentType() and before getWriter(). For example:

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html"); Locale locale = req.getLocale(); res.setLocale(locale); PrintWriter out = res.getWriter();

// Write output based on locale.getLanguage() }

Note that these methods aren't as powerful as the com.oreilly.servlet.LocaleNegotiator class (available in the Resources section).

Hark, who goes there?

Before Java Servlet API 2.2, servlets had very little control over server security. Page access restrictions were set up using server administration tools, and all a servlet could do was view the remote user's basic authentication login name by calling req.getRemoteUser(). Now, however, servlets have slick built-in support for portable role-based user authentication and authorization.

Using tags in the Web application deployment descriptor, security constraints can be set up to indicate that certain pages in the Web app are to be accessed only by users with certain credentials. Specifically, since this is role-based user authorization, access will only be granted to users who are part of a given role. For example, you might want to set up your site so that pages that display salary information can be restricted to only those users who are in a manager role.

The deployment descriptor specifies the type of access granted to each role, but does not specify that role to user or group mapping. That's done during deployment of the Web app, using server-specific tools. The ultimate mapping may come from many locations -- the Web server, the operating system, a relational database, and so on.

Two new methods were introduced in HttpServletRequest to support role-based authorization. The getUserPrincipal() method was added to return a java.security.Principal object holding the name of the current client user (or a null value if the client hasn't logged in). The isUserInRole(String role) method was added to return a boolean indicating if the current client is in the given role. Access can be granted or denied based on the value returned.

The final new security method in HttpServletRequest is isSecure(), which returns true if the request was made over a secure channel such as HTTPS.

Here's a code snippet showing the new methods in action:

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/plain"); PrintWriter out = res.getWriter();

out.println("The current user is: " + req.getUserPrincipal()); out.println("Is the user a Manager? " + req.isUserInRole("Manager")); out.println("Is our connection secure? " + req.isSecure()); }

Nit-picky on parameters

If you look closely at the specification, you'll see that a much-needed clarification was made on how parameters are handled in API 2.2.

It's now guaranteed that parameters specified in the query string of a post request will be read and made available to servlets. Furthermore, in the event that any query string parameter names collide with post parameter names, the query string values are to be made available first via getParameterValues(), followed by the post values. Previously, it was legal for query string parameters to be ignored. Requiring their support allows portable use of post forms whose action tag includes a query string, such as:

<FORM METHOD="POST" ACTION="/servlet/Testing?a=b">

This clarification also allows a special query string parameter to be used for session tracking and passed even during post requests.

Better dispatching

Finally, Java Servlet API 2.2 provides more convenient request dispatching.

There's a new getNamedDispatcher(String name) method in ServletContext that lets a servlet dispatch to a component specified by its registered name instead of a full URI path. This allows dispatching to a component that may not be publicly accessible on any URI path.

1 2 3 Page 2
Page 2 of 3