Saturday, October 3, 2009

Why web.xml makes it hard to write modular web applications

In a typical Java enterprise application, the web.xml is used to define servlets and filters, which are among the main entry points into your application from the outside world. Since web.xml cannot be reloaded, added to or modified without reloading the entire application, it is not a very convenient place to host application configuration and definitions in a dynamic module applications.

Another related problem is the limitations of the request mapping capability of web containers as defined by the current servlet specification. Currently, these make it possible to map requests to servlets and filters using an exact match (e.g. /myservlet/myresource) or either a prefix and wildcard match (e.g /myservlet/*) or using a suffix wildcard match (e.g. *.do). It doesn't allow you to use a combination of prefix and suffix wildcard matches. This means that you cannot, for example, use the path (/myprefix/*) to match application URLs, and at the same time allow your application's CSS files to be accessible in a resource such as /myprefix/styles.css.

Multi-module web applications in Impala

One of biggest changes in the recent 1.0 RC1 release of Impala is the ability to write web applications which are less reliant on web.xml, allowing both dynamic registration of modules containing servlets and filters, and at the same time solving the path mapping limitation described in the previous paragraph.

In an Impala application, you cannot do away with the web.xml altogether. However, you can reduce the request handlers defined in web.xml to the following:

<filter>
<filter-name>web</filter-name>
<filter-class>org.impalaframework.web.spring.integration.ModuleProxyFilter</filter-class>
<init-param>
<param-name>modulePrefix</param-name>
<param-value>urlmapping-web</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</filter>

<filter-mapping>
<filter-name>web</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
In the example above, the ModuleProxyFilter captures requests and routes them into Impala modules. The mapping rules which determine which modules service which requests are contained within modules. Here's an example from the URL mapping sample:

<web:mapping>
<web:to-module prefix = "/webview" setServletPath="true"/>
<web:to-handler extension = "htm" servletName="urlmapping-webview" filterNames = "characterEncodingFilter,sysoutLoggingFilter"/>
<web:to-handler extension = "css" servletName="urlmapping-resources"/>
</web:mapping>

<web:servlet id = "urlmapping-webview"
servletClass = "org.impalaframework.web.spring.servlet.InternalModuleServlet"/>

<web:servlet id = "urlmapping-resources"
servletClass = "org.springframework.js.resource.ResourceServlet"
initParameters = "cacheTimeout=10"/>

<web:filter id = "characterEncodingFilter"
filterClass = "org.springframework.web.filter.CharacterEncodingFilter"
initParameters = "forceEncoding=true,encoding=utf8">
</web:filter>

<web:filter id = "sysoutLoggingFilter"
filterClass = "org.impalaframework.urlmapping.webview.SysoutLoggingFilter">
</web:filter>

This module defines a number of servlets and filters who's life cycles are tied to that of the module, rather than that of web.xml. They can be dynamically registered and removed, and don't require an application restart. The modules can contain all the classes and resources necessary to service requests, without relying on the presence, for example, or resources such as JavaScript files on the context path (e.g. in the WEB-INF directory).

What about Servlet 3.0?

The changes described above are very much in line with the changes in the forthcoming Servlet 3 specification which allow servlets and filters to be added via web.xml fragments, and via annotations. It will also allows you to add Servlet and Filter instances programmatically. I expect that Impala will be able to take advantage of this mechanism when it becomes available, perhaps by wrapping the Servlet or Filter to ensure that it is associated with the originating module's class loader, and not the web application class loader. This will have the advantage of allowing Impala to make use of the web container's invocation infrastructure while still supporting dynamic servlet or filter registration.

No comments: