Friday, September 12, 2008

Generic support for dynamically reloadable web frameworks

One little feature that I think we're close to pulling off with Impala is the ability to embed any Servlet-based web application and have it dynamically reloadable as a module. I'm thinking of the likes of Tapestry, Struts, etc.

Up to version 1.0M2, this capability has only really existed for Spring MVC. However, to make Impala really attractive to users of other frameworks, you'd want to support these as well. After all, not every Spring user is also using Spring MVC as the web framework.

More recent changes now allow you to embed any Servlet into a the application context XML belonging to an Impala web module, using code such as:

<bean id = "delegateServlet" class="org.impalaframework.web.integration.ServletFactoryBean">
<property name = "servletName" value = "delegateServlet"/>
<property name = "servletClass" value = "servlet.SomeFrameworkServlet"/>
<property name = "initParameters">
<map>
<entry key="controllerClassName" value = "servlet.ServletControllerDelegate"/>
</map>
</property>
</bean>

Note how the init parameters, the servlet name and the servlet class have been specified, as they would be for entries in web.xml.

The InternalFrameworkIntegrationServlet, whose definition is shown below, is what allows the servlet to "live" within an Impala module. It contains the glue code
that ties an invocation from outside of a module (within the context of a servlet container) to the servlet within the module.

<bean class="org.impalaframework.web.integration.InternalFrameworkIntegrationServletFactoryBean">
<property name = "servletName" value = "myservlet"/>
<property name = "servletClass"
value = "org.impalaframework.web.integration.InternalFrameworkIntegrationServlet"/>
<property name = "delegateServlet" ref = "delegateServlet"/>
</bean>

Finally, to communicate with the module itself, there is the ModuleRedirectingServlet, which simply delegates to the a servlet which is registered in the ServletContext using a name which corresponds with the name of the module (and happens to correspond with the first part of the URL's servlet path).

Here's an example configuration in web.xml:

<servlet>
<servlet-name>module-redirector</servlet-name>
<servlet-class>org.impalaframework.web.integration.ModuleRedirectingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>module-redirector</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

Together, these offer the capability dynamically embedding applications using arbitrary web frameworks. The idea is that you can register and load a Struts, Webwork, Tapestry or other module after the JVM has started, without impacting any existing code or without having to restart the JVM or reload the entire web application within the application server. A powerful concept.

There are definitely some gotchas to take care of. For one, it relies on object instances being loaded using the classloader obtained via Thread.currentThread().getContextClassLoader(). Also, how can we make sure that objects saved to sessions don't cause ClassCastExceptions when the module is reloaded? Also, how can we make sure that the same result does not occur because of one module attempting to access objects saved to the session by another module? And of course, we'd need to prove it working with real applications based on these frameworks, not example code assuming noddy pseudo frameworks. These kinds of problems will need to be taken care of before the problem can be considered to be fully solved. It should be fun figuring it out, though.

No comments: