2. New Features

Developers fearing revolution, need not be worried by the 2.4 servlet specification as it introduces no new functionality. However, existing mechanisms have been extended and enhanced with sometime powerful results. The short list of new features includes:

  • XML Schema definition of the deployment descriptor.

  • Increased (improved?) integration with other J2EE specification and components.

  • Filtering of RequestDispatchers and Error pages.

  • Event listeners for requests and request attributes.

  • Methods to query client connection details.

  • Improved internationalization methods.

Of these changes, only those to the RequestDispatcher and internationalization methods are likely to be relevant to your average servlet developer.

2.1. XML Schema for web.xml

The 2.4 servlet deployment descriptor is now defined by an XML schema rather that a DTD (as used for all previous versions). Relatively few new elements are introduced in 2.4, but the schema's means that there are some subtle yet significant changes to all elements in the descriptor.

2.1.1. Schema's Declaration

For many developers the change to schema's will mean little more than an change of the mantra used at the start of the web.xml file ( and an increase in the number of times they download xercesImpl.jar). To use the 2.4 schema's, the XML envelope of the deployment descriptor must change from the DTD form of:



    

to the even less readable:


 
    

Most developers need not concern themselves with the exact meaning of this envelope and can simply copy it verbatim or indeed have it generated for them from their IDE, by Xdoclet or similar. The older DTD based descriptors are still supported, but the new features of the 2.4 deployment schema will not be usable unless the 2.4 envelope is used.

2.1.2. Validation

With previous DTD based versions of the deployment descriptor, validation was optional and many containers defaulted to no validation. But with the introduction of schema's, validation has been made a requirement for all J2EE containers:

SRV.13.2 ?The containers and tools that are part of J2EE technology-compliant implementation are required to validate deployment descriptor against the XML schema for structural correctness. The validation is recommended, but not required for the web containers and tools that are not part of J2EE technology-compliant implementation.?

Schema validation is significantly more powerful than DTD validation and more than simply document structure is checked:

  • String values such as mime-types and URL patterns are checked for allowed characters.

  • Numerical values are check for numeric only characters and for the allowed sign.

  • Names for elements such as Servlet, Filter, EJB-Ref, etc. are checked for uniqueness.

  • Enumerations (e.g. user-data-constraints: NONE, INTEGRAL & CONFIDENTIAL) are checked for allowed values.

The increase rigour of schema validation should assist development by detecting errors earlier in the process and reduce portability issues as less container specific validation implementation is required. Because of the accepting nature of unvalidated XML, it is likely that many validation errors will be detected when converting descriptors that were generated by hand and never validated. For developers that generate their descriptors or use XML aware editors, the transition should be mostly painless.

2.1.3. Order Independence

One of the reasons that validation is often turned off for previous servlet versions, is that the DTDs impose a strict order of top level elements in the web.xml. For example, it is not legal to place a servlet mappings directly after the servlets declaration if you have more than one servlet. The following descriptor has a logical ordering that is illegal with the DTD based validation:


  ...
      
    ServletA
    org.mortbay.servlet.ServletA
  

  
    ServletA
    /aaa/*
  

       
    ServletB
    org.mortbay.servlet.ServletB
  

  
    ServletB
    /bbb/*
  
  ...

The 2.4 schema has been written so that all top level elements may now occur in any order and in any number. So the above descriptor will now be legal and more logical descriptor layouts and simpler generation will be possible. It still would have been better if the original descriptor specification had allowed url-patterns in the element, but that's history now.

As a side effect of arbitrary element ordering, all top level elements may now occur 0 or many times. While this is logical for most elements, some additional interpretation is required for some elements:

  • Multiple instances of the list elements like welcome-file-list and the new locale-encoding-mapping-list result in concatenated lists.

  • Multiple instances of , and are errors that must be detected and reported by the container rather than XML validation.

  • Multiple instances of the distributable element are treated the same as a single occurrence.

It is hoped that this relaxation of ordering will avoid a major source of invalid descriptors now that validation is required for J2EE containers.

2.1.4. New Descriptor Elements

There are only a few new elements in the 2.4 deployment descriptor, which are summarized below.

Table 1. New Deployment Descriptor Elements

New Element Parent Element Description
Defines the request type that a filter mapping applies to (see 2.2 below).
Defines list of locale to character encoding mappings.
Defines entry in locale encoding list.
The name of a locale in a locale encoding mapping.
The character encoding of a locale encoding mapping.
Declaration of a message destination resource. Defined in J2EE specification.
Declaration of a reference to a message destination resource. Defined in J2EE specification.
Declares a reference to a Web service. Defined in J2EE specification.

The nested elements of the J2EE elements (e.g. ) and JSP related elements (e.g. ) are no longer defined in the servlet specification itself but by other specifications from J2EE and the JCP.

2.1.5. J2EE Schema's

The design of the deployment descriptor schema's for servlets represents more than just technical changes. The decision to make the schema's dependant on other J2EE schema's represents a significant and not completely welcome change in the specification process.

Prior to 2.4, the web application deployment descriptor was specified entirely by servlet specification and was the responsibility of the relevant JSR. With the 2.4 schema definitions, significant portions of the definitions are imported from the schema's produced by other parts of the J2EE specification and under the guidance of other JSRs.

The practical benefit of this change is that a single consistent definition of deployment descriptor elements may be used for JSPs, EJB, Web Services etc. Furthermore, these elements will be defined by the JSRs responsible for the technologies to which they apply to (e.g. the JSP descriptor schema is produced by JSR152 (JSPs) not JSR-154 (Servlets)). But some major concerns about this approach have been expressed regarding how this change effects the Servlet specification and the whole JCP process.

Importing J2EE schema's means that the servlet specification is no longer stand alone and can only be considered as a subordinate specification of J2EE. Apart from the questionable object oriented design of a component having dependencies on it's container, this represents an administrative change that was forced on JSR-154 by the managers of the Java Community Process. The result being that the complete contents of the servlet deployment descriptor is now beyond the control of the servlet JSR and expert panel.

Servlets are much more than just a component of J2EE and many (if not most) deployments are on J2SE. There are even implementations for some J2ME profiles. By making J2EE the controlling specification for servlets, it has left J2SE implementation with a less definitive specification and with concerns that their interests may not be given equal consideration in future revisions of the specification.

This conflict of interests may have be the result of earlier mistakes in the servlet specification and is very much related to the discussion of extensible schema's later in this article. It could be argued that EJB elements should not have been added to the servlet specification, nor should JSPs have been given "favoured son" status over other content generation technologies. The WEB-INF directory could easily have been used as the home of separate deployment descriptors for servlet related technology without extending servlet deployment descriptor and specification itself.

The next revision of the specification may be a difficult one, as it has both significant technical and political issues that it must deal with. It is the authors hope that the result will be a servlet specification with clear J2ME, J2SE and J2EE profiles and a clear and reasonable dependency tree between them.

2.2. Filter Mapping Enhancements

When Filters were introduced in the 2.3 specification, they only applied to requests as they enter the container. Thus this powerful feature was not available to forwarded or included requests. Additionally it was undefined how they applied to error pages and welcome files. The 2.4 specification corrects this by allowing a filter to be mapped to specific types of requests and by clarifying the specification of the error page and welcome file mechanisms.

2.2.1. The Problem

Consider the use of a filter to transform xml content as it is served from a web application. The transform could be to HTML, WML or whatever mark up is appropriate for the client. Using 2.3, the following descriptor elements could be defined:

  
    Transform
    com.acme.TransformFilter
  

  
      Transform
      *.xml
  

  
    404
    /error404.xml
    
  
    java.lang.Exception
    /except.xml
  

  
    index.xml
  

A request for an XML resource that exists (e.g. /foo/bar.xml) will match the Transform filter the content served will be handled by that filter as desired. Additionally, for many 2.3 containers this also worked for non-existent XML URIs (e.g. /doesNotExist.xml) which also matched the filter mapping and thus transformed the error404.xml error page that was served from the error-page mapping.

Unfortunately, this configuration fails for errors for non XML URIs. If a request for /doesNotExist.JPG is received, this does not match the *.xml filter mapping so the Transform filter should not be applied. This results in the error404.xml file being served without transformation.

A similar problem exists for welcome files, as a request to a URI like /foo/ may be handled by serving the /foo/index.xml resource, but it is unclear if the *.xml filter mapping should apply. For a container that uses a HTTP redirect to a welcome file, the filter would be applied as that results in a new request entering the container. For containers that serve welcome files directly, the filter should not be applied.

Finally, if a controller style servlet is introduced, where requests to URIs like /controller/info are dispatched as forwards or includes to the xml generating resource. The decision whether or not to apply the Transform filter cannot be made until the Controller servlet has selected a resource to which to dispatch.

Developers wanting to use 2.3 filters for such uses have often ended up applying the filter to all URIs and the filter itself decides if it applies to any particular request. This works well if your application mostly serves content that requires transformation, but is very inefficient in any mixed media environment. It also makes poor usage of the filter mapping mechanism.

2.2.2. The Solution

To better support flexible filter usage, the 2.4 specification now allows one or more sub elements to optionally be added to a element. Each element specifies a value of REQUEST, FORWARD, INCLUDE or ERROR to indicate to which type of request the filter mapping applies.

For the above example, the filter mapping in the deployment descriptor can now be written as:

  
      Transform
      *.xml
      REQUEST
      FORWARD
      INCLUDE
      ERROR
  

This specifies that the Transform filter applies to any *.xml request that is received as a normal request, is the target of a RequestDispatcher.forward, is the target of a RequestDispatcher.include or is served as an error page.

2.2.3. No Silver Bullet

The enhanced filter mapping mechanism will make it easier to use filters for clever applications, but it does not absolve the developer totally from the practise of careful design. The main problem now is to avoid situations where the same filter is applied more than once. For example, if an exception was thrown during the generation of a XML resource, then the Transform filter may get applied twice: once for the original request and again for the error page dispatch. Thus simply applying filters to all types of requests will not always be the solution.

2.3. RequestDispatcher Forward Attributes

The current specification of the RequestDispatcher.forward method does not well define the request methods that need to change when a request is forwarded:

SRV.8.4 ?The path elements of the request object exposed to the target servlet must reflect the path used to obtain the RequestDispatcher.?

This has resulted various implementation for the getRequestURI() method as it was unclear if getRequestURI was a path method or not. However, the issue is resolved elsewhere in the specification where it states:

SRV.4.4 ?It is important to note that, except for URL encoding differences between the request URI and the path parts, the following equation is always true: requestURI = contextPath + servletPath + pathInfo?

Thus the value returned by HttpServletRequest.getRequestURI() must change after a forward to reflect the resource used to obtain the RequestDispatcher. This resulted in the situation where the target of a forward was unable to easily determine the original URI of the request, as used by the client. This made it very difficult to generate URL links based on the request that the client had issued.

To resolve this, the 2.4 specification has added a set of forward attributes that are set the first time a request is forwarded:

javax.servlet.forward.request_uri
javax.servlet.forward.context_path 
javax.servlet.forward.servlet_path 
javax.servlet.forward.path_info
javax.servlet.forward.query_string

These attributes capture the path values and query string as originally received by the container and are not hidden by any subsequent forwards or includes of the request. Thus the original request paths will always be available.

Unfortunately, the result is that if a servlet is to be portable and reusable, it now needs to double check getRequestURI to determine the URI that the client used and the URI (or context, servlet or info path) of the resource to be served:

public void doGet(HttpServletRequest request, HttpServletResponse response)
{
  String uriUsedByClient=(String)
    request.getAttribute("javax.servlet.forward.request_uri");
  if (uriUsedByClient==null)
    uriUsedByClient=request.getRequestURI();
  String uriToServe=(String)
    request.getAttribute("javax.servlet.include.request_uri");
  if (uriToServe==null)
    uroToServer=request.getRequestURI();
  // ...
}

2.4. New Event Listeners

The servlet event model has been rounded out with new listeners defined for requests and request attributes. However, there is probably very little need for any of these new listeners other than completeness and do not provide anything that could not simply be implemented with a filter.

2.4.1. Request Event & Listener

The javax.servlet.ServletRequestListener and javax.servlet.ServletRequestEvent classes have been introduced for handling events when a request enters and leaves the scope of a web application, which is defined in the javadoc as:

?A request is defined as coming into scope when it is about to enter the first servlet or filter in each web application, as going out of scope when it exits the last servlet or the first filter in the chain.?

Unfortunately the events themselves were named without considering that a web application can dispatch a request to another, resulting in a request being in scope of more than one web application. Thus instead of names such as enterContext/exitContext, the events are defined as:

public interface ServletRequestListener extends EventListener {
    /** The request is about to come into scope of the web application. */
    public void requestInitialized ( ServletRequestEvent sre );

    /** The request is about to go out of scope of the web application. */
    public void requestDestroyed ( ServletRequestEvent sre );
}

Note that the functionality provided by this listener is also provided by the extended filter mechanism. A trivial filter installed at the default / path equally allows for handling of requests as they enter and exist a web application.

2.4.2. Request Attribute Event & Listener

The javax.servlet.ServletRequestAttributeListener and javax.servlet.ServletRequestAttributeEvent classes have been introduced for handling events when a request attribute is modified while the request is within the scope of a web application:

public interface ServletRequestAttributeListener extends EventListener {
    /** Notification that a new attribute was added to the
     ** servlet request. Called after the attribute is added.
     */
    public void attributeAdded(ServletRequestAttributeEvent srae);

    /** Notification that an existing attribute has been removed from the
     ** servlet request. Called after the attribute is removed.
     */
    public void attributeRemoved(ServletRequestAttributeEvent srae);

    /** Notification that an attribute was replaced on the
     ** servlet request. Called after the attribute is replaced.
     */
    public void attributeReplaced(ServletRequestAttributeEvent srae);
}

As for the ServletRequestListener, a request can be in scope of multiple web applications and events are generated for all in-scope web applications. Thus if ServletA in context /webappA dispatches a request to ServletB in context /webappB, then ServletRequestListeners in both web applications will receive events if ServletB modifies an attribute.

As the value of the attribute is passed in the event object, use of this listener may lead to some interesting class loader issues as web applications are passed an object of a class instance that is only available to another web application. However, this is unlikely to ever be an issue, as there is very little need for this event and the author will buy a large beer to anybody that can suggest a real world use-case. Note that this listeners functionality could also have been provided with the extended filter mechanism and request wrapping.

2.5. New APIs

A few minor improvements have been made to the servlet APIs of existing interfaces.

2.5.1. Client Connection Methods

The javax.servlet.ServletRequest interface in 2.3 had a partial set of methods that describe the logical and actual connection of a request. In 2.4 four additional methods have been added to make this part of the API more complete:

public interface ServletRequest 
{ 
    // Methods about the logical name of the server, as seen by the client.
    public String getServerName();    
    public int getServerPort();

    // Methods about the local end of the actual TCP/IP connection
    public String getLocalName();  // New in 2.4
    public String getLocalAddr();  // New in 2.4
    public int getLocalPort();     // New in 2.4
    
    // Methods about the remote end of the actual TCP/IP connection
    public String getRemoteAddr();
    public String getRemoteHost();
    public int getRemotePort();    // NEW in 2.4


    ...

The ServerName is the name used by the client to create the connection with the server. The LocalName is the DNS name of the interface that the connection was received on, which may have been proxied or otherwise mapped and thus can often be different from the ServerName. The same difference applies to the ServerPort and LocalPort.

These APIs will not be of much use for most web applications, but are vital for those that need to be aware of their network environment.

2.5.2. Internationalization

Prior to 2.4, the content type and character encoding of a response could be set with a call to setContentType like:

response.setContentType("text/html; charset=Shift_JIS");

The same result could be achieved with better separation of concerns using setLocale:

response.setContentType("test/html");
response.setLocale(Locale.JAPAN);

Unfortunately the mapping between Locale and character encoding is not well defined and it was up to each container to provide a reasonable mapping. There was no way to tell if a Locale was known to the container, nor to retrieve the resulting combined content-type to check if a character encoding had been set.

The 2.4 specification addresses this by firstly allowing the Locale to encoding mapping to be specified in the descriptor using the new element. Locales may be mapped with either just a language code (e.g. zh), or a language and country code (e.g.. zh_TW):


  
    jaShift_JIS
  
  
    zhGB2312
  
  
    zh_TWBig5
   

The encoding for a particular locale may still be set by a container default, but the webapp now has the option to override the container or ensure that the locale is supported. There is also a new getContentType method on ServletResponse that allows the resulting content-type string to be observed:

response.setContentType("test/html");
response.setLocale(Locale.JAPAN);
assert response.getContentType().indexOf("charset=Shift_JIS")>0;

Finally for those that want direct control of the character encoding and do not wish to go via the Locale mechanism, there is a new response setCharacterEncoding method:

response.setContentType("test/html");
response.setCharacterEncoding("Shift_JIS");