Thursday, November 5, 2009

The granularity of change in dynamic Java web applications

When writing Java web applications, you are continually making changes to your application, and to be productive you need to be able to deploy and test these changes quickly. The kinds of changes you make are of all sorts: from changes to resources to changes to markup templates to changes in the way your application is wired to changes in the code itself.

The point of this article is that not all changes are equal, both in their frequency and in the difficulty in applying them dynamically in a running web application. Let's go through some examples, and how the changes might be applied in different types of frameworks:

1. Static resources
The simplest kinds of changes for any dynamic web framework to apply are those of static resources - images, JavaScript files, page templates etc. That's because these kinds of artifacts are inherently free of dependents.

2. Application configuration
Configuration consists of things such as settings for data sources, mail servers, etc, as well as switches in your application itself. While it is in general possible to reflect these kinds of changes dynamically, there is a cost. For example, if you are using database connection pooling, pointing to a different data source dynamically is a non-trivial exercise. Also, if your application checks particular application settings at source each time the affected functionality is used, then the system is more dynamic, but also less performant. By contrast, if you only load particular settings at startup (for example using wired in property placeholders in a Spring application context), the application is more efficient but is more likely to require reloads to reflect changes.

3. Application wiring
Application wiring is a configuration of sorts, but relates more to how parts of the system are composed or wired together to form the whole application. In a Spring application, the application wiring is simply your Spring configuration.

In general, changes to application wiring requires reloads. There are special cases where this doesn't apply. For example, you could introduce new Spring beans without reloading the application context. Changes to existing beans and their collaborators are harder to make.

4. Scripts
Scripts are programs in your application which by definition can be altered without having to reload your entire application or even significant parts of it. However, a scripting infrastructure needs to be in place to allow changes to scripts to be introduced, recognised and reflected in the system.

5. Application code
Application code in a Java application are your Java classes. Actually, considering this group as a single category is an oversimplification, especially in a dynamic module system, where making changes to core interfaces and domain classes will impose much greater requirements in terms of reloading than peripheral or implementation classes with fewer dependants.

6. Third party libraries
The libraries in your application are the jar files containing all the third party dependencies.

The challenge for frameworks

The key productivity challenge with a dynamic application framework is to make it as easy as possible to make the kinds of changes you need to make, while at the same time keeping the framework as lightweight as possible. In the next section I take a look a number of technology stacks, what they do to make different types of reloading possible, what they get wrong, and what they get right.

A. Traditional Java web application
A traditional Java web application might consist, for example, of a Struts or JSF front end, Hibernate back end, all wired together using Spring.

The traditional Java web application has no problem reloading static resources without having to reload any other part of the application. Most web containers are able to reload an entire web application, including third party libraries, Java code, application wiring etc.

The problem is they are not much good at reloading any finer grained changes. Any changes you make to your application wiring or code will normally require a full application reload.

B. Scripted applications
Scripted applications are based on scripting languages such as Groovy (Grails) and JRuby (Rails). As well as explicitly providing support for reloading capabilities, these frameworks rely on the fact that all application functionality is in scripts rather than in compiled Java code, making fine grained reloading of parts of the application possible. The downside (if you think of it this way) is that you have to work with scripted code without any of the type safety checking of a statically typed language such as Java.

C. OSGi applications
OSGi applications offer a fairly comprehensive solution to the reloading problem. All artifacts within the application, from resources to application code to libraries are contained within modules which are treated in a more or less uniform manner by the OSGi container. This is a strength, but it is also a weakness. The strength is that it does allow third party libraries to be reloaded in a fine grained way. The weakness is that in your high level view of the application, OSGi doesn't really allow you to easily distinguish between the parts of your application which should be easy to reload - e.g. resources - and the parts which are harder to reload, but are changed much less frequently during the lifetime of the application (third party libraries).

What about Impala?

Impala tries to find the right balance in the strengths of the various approaches. Resource reloading works as with traditional Java applications - nothing special needs to take place. Impala includes mechanisms which make it easier to change configurations dynamically without requiring any module reloading. For changing static configuration and application wiring, Impala allows you to reload parts of your application at exactly the right granularity. If only a single module is affected, then only that module needs to be reloaded. If the change affects core interfaces, then the reload will automatically ripple through to the right dependent modules.

Impala even allows you to dynamically change the structure of modules within the application. Unlike OSGi, it doesn't support reloading of third party libraries. For this, an entire application reload is required. However, Impala's approach does your application modules in central focus, which is important as these are the normally parts of your application which change most frequently.

6 comments:

Unknown said...

so how is the developer experience like, does impala automatically reload the modifications or does the developer need to trigger a reload, manually specifing what is it exactly to be reloaded?

Phil Zoio said...

Mohamed

You can have it either way. If you set a property called 'auto.reload.modules', which if true will reload modules automatically.

See http://code.google.com/p/impala/wiki/PropertyConfiguration for more details.

Alternatively, you can trigger module reloading manually through a JMX interface. An admin console is planned, but does not exist yet.

Phil

Petr Nevaril said...

How is it with modification of module in production environment (when module is deployed as jar on Tomcat in its WEB-INF/modules directory)? Is it possible to somehow replace the module jar at runtime?

Phil Zoio said...

Petr,

Yes, you can replace the module jar at runtime. You will need to property 'auto.reload.modules=true' set in impala.properties. There are a few other options you can set: whether to use a staging directory, whether to use a 'touch.file' to trigger reloads, etc.

See again the 'auto.reload' properties in http://code.google.com/p/impala/wiki/PropertyConfiguration

You can also use JMX to trigger a reload of a modified jar.

Cheers,
Phil

Petr Nevaril said...

Phil, thanks for aswer. My problem is that when I'm runnig the modules on Tomcat, I can't replace the module jar (WEB-INF/modules/.jar), because it is locked (opened) by the tomcat process. How can I solve this?

Phil Zoio said...

Petr,

Have you tried using the staging directory approach.

Into impala.properties, add
auto.reload.monitoring.type=stagingDirectory

and

use.touch.file=true

You can then place the replacement jar in /WEB-INF/staging

When you want to trigger reloading. then save touch.txt in /WEB-INF/modules

The change in the touch file will trigger reloading of the jar from the staging directory.

Hopefully you won't get any locking issue as the IO ops will take place within the Tomcat process.

Let me know if this works (preferably on the user mailing list if possible).