|
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:
Of these changes, only those to the RequestDispatcher and internationalization methods are likely to be relevant to your average servlet developer. 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. 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. 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:
Schema validation is significantly more powerful than DTD validation and more than simply document structure is checked:
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. 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:
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 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:
It is hoped that this relaxation of ordering will avoid a major source of invalid descriptors now that validation is required for J2EE containers. There are only a few new elements in the 2.4 deployment descriptor, which are summarized below. Table 1. New Deployment Descriptor Elements
The nested elements of the J2EE elements (e.g. 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. 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. 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:
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. To better support flexible filter usage, the 2.4 specification now allows one or more For the above example, the filter mapping in the deployment descriptor can now be written as:
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. 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. The current specification of the RequestDispatcher.forward method does not well define the request methods that need to change when a request is forwarded:
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:
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:
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(); // ... } 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. 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:
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. 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. A few minor improvements have been made to the servlet APIs of existing interfaces. 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. 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
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"); |
|
|||||||||||||||||||||||||||||||||
© 2003 Core Developers Network Ltd "Core Developers Network", the stylized apple logo and "Core Associates" are trademarks of Core Developers Network Ltd. All other trademarks are held by their respective owners. Core Developers Network Ltd is not affiliated with any of the respective trademark owners. |