RSS feed
Home | Better webapp config: Spring integration and an example >>

A better way to configure Java Web Applications

Anyone who's installed a few Java EE webapps in their life knows what pain it can be to establish and maintain a working configuration. It's usually far from the it just works experience I've gotten used to and tends to involve mocking around  quite a few files.

Sure, most applications these days do their best in reducing the number of settings you need to grok in order to get started.  Some even provide nice setup wizards that lets you do the configuration in the browser the first time you use the application.

But these startup wizards tend to be just that - for startup. As soon as you need to do something a bit off-track, the convention seems to be to fall back on tweaking a file somewhere inside the WEB-INF folder.

Atlassian, the makers of great software, have made a really nice setup interface for their Jira product. They also bundle Tomcat with their applications so all you need to do is to unzip, start Tomcat, fire up you browser and boom! you're ready. It's almost to good to be true. Or actually, it is.

Try do something that's a bit more complicated, like integrating with LDAP or connecting Jira to MySQL and you'll soon find yourself reading about how to «edit the field-type-name attribute in the atlassian-jira/WEB-INF/classes/entityengine.xml file».

Another example is Pebble, the blogging software powering this site. The reason I chose Pebble was because of its «unrivalled ease of installation and use».  But what what if you want to use the multi-blog feature of Pebble? You guessed it, it's done by «changing the multiBlog property in the /WEB-INF/applicationContext-pebble.xml file to true».

(For the record: Atlassian is a truly amazing company and I think they have some top-notch products, what I'm trying to get across here is that parts of their configuration system deserve some amazification.)

So what's the problem?

My main problem with this isn't that there are lot of settings to tweak, but that the configuration files are mixed with the application files inside the WEB-INF directory. You might ask what the problem is with keeping configuration within WEB-INF. The configuration have to reside somewhere right? There are a few reasons this might not be a good idea:

It complicates backup.
As soon as you modify a war, you'll have to back up your changes. In the event of a disaster your users can't simply replace the war with a fresh one since that will kill their configuration tweaks.

It makes software updates a pain.
Consider what happens when you release a new version of your webapp. Your users will typically download the new war file, (hopefully) backup the old one, and then they have to remember and manually merge the all the settings they had in the old install. Anyone who has ever had the responsibility of keeping a mildly customized version of Jira up to date will know the amount of misery this can cause.

It breaks Inversion of Control.
Inversion of Control is about injecting dependencies to a component from outside instead of keeping them inside it. I don't see any reason this shouldn't also be applied at the webapp level. Your webapp is the component and its configuration is a dependency, hence it should be  injected from outside.

It requires write access to the WEB-INF dir.
This isn't necessarily a problem, but it will be if you decide not to unpack your war, if you're trying to tighten security in the file system or you're running from a read-only medium.

It makes throwaway wars impossible
Some times it makes sense to unpack the war to a temporary directory every time you start your application server and then just delete it on shutdown. If you want to bundle Jetty and your webapp in a single, double-clickable jar, this would be a requirement.

How can we solve this?

This post would be just a rant If I didn't back it up with a proposed solution and some code. The one-sentence version of my proposal is that:

We should move all configuration out of the webapp, treat configuration as just another type of data and stick it in a directory that we point our webapp at.

The first part of this proposal is the easy one. Instead of putting configuration files in WEB-INF, put it in some other directory outside your war and application server. This should also be the directory where you put indexes, database files or any other data that your application produces or consumes.

The second part is about pointing your webapp at the data directory. (Remember that configuration is just another type of data). But how will the application know where to look for it's data directory? There are several ways we could implement this:

Setting a system property.
We'll start out application server with a -DdataDirectory=/var/local/application/data flag. The problem with this is that we can only run one instance of an application because the system property can only have a single value.

Setting a JNDI property
Instead of reading a system property, the application can read a JNDI environment property. This enables multiple instances of the application to be run at the expense of more configuration in your application server.

Setting a context parameter
This is quite similar to the JNDI method but we instead configure the application server to set a context parameter. Your application will see this as if it had a context-param set in the web.xml file.

Adding a property file to the classpath
This is a bad solution since it breaks our rule about not writing to the war. Still it could be useful for people who for some reason can't use one of the preferred methods.

Just go for a sensible default
This is the convention over configuration approach. Instead of configuring the data directory, just select a sensible default, typically a directory named like the application in the user's home directory. In order for this to work your application will have to know how create and startup from this empty directory, but that's something it should be able to do anyway. Like the system property solution, this one will only allow once instance of the application on the same system.

The implementation

Checking all these different types of configuration adds quite a bit of complexity to our application. Luckily, Java has this nice thing called interfaces that we can hide all the complexity behind. I've called this interface DataDirectoryLocator and it's dead easy:

/**
* Component responsible for locating a data directory based on some strategy.
* Typically used to cleanly separate configuration and data from a web app.
*/
public interface DataDirectoryLocator {
File locateDataDirectory();
}

Calling the locateDataDirectory() metod is all your application needs to do in order to figure out where to read it's configuration from.

The dirty details of how to find the directory is implemented by the DefaultDataDirectoryLocator. It will check the different configuration options in the following order:

  1. JNDI environment property
  2. Context parameter
  3. System property
  4. Classpath property file

It will pick the first one set, and if none of are set, the configured default location is used.

If you would like to use this code your own projects, feel free to do so by checking out the sources from the Simplericity Subversion repository and build it by running mvn install.

I'll be following up this post  with an example of how to use and configure the data directory locator.
Tags :


Use preferences?

This has been the bane of java web apps for many a year. But also PHP apps typically will instruct you to make the .htaccess file writeable at least during initial configuration; java apps like roller make you go and edit the config files yourself. There's no programmatic access to set things inside the web.xml file or other config files, like let's say on 1st-time use, setting up the db access. So how about using the java preferences api for this type of stuff?

Use preferences?

Guy,

I think you're right that the problem I'm describing in this post isn't specific to Java. However, the solution I've created is.

My key point is that configuration and application code are two very different concerns so we should keep them separated.

I didn't cover how to do the configuration itself. I haven't really used the  Preferences API, but I think it falls through because it  separates  configuration from the application data, and I think it's preferable to keep those in the same directory.

Another point to remember is  that you're not always not in control over the format of configuration and some times you just can't do it programatically. The format could be mandated by the libraries and APIs you're using.

Re: A better way to configure Java Web Applications

Unix and linux have had this separation for ages. Configurations goes into the home or /etc directory, while the application binaries should stay in the /bin and /lib directory. It's sometimes frustrating to see how old wisdom is thrown away like dust every time a new language or technology is created. One could almost be tempted to believe that Java programmers was running windows on their laptops...
However, I think it's important to distinguish between two different types of configuration here. Programmers tend to like to make things configurable, even if the users are not supposed to change them. The Spring application context file is an example. The problem arise when they do not distinguish between the configuration entries the users should be able to change and the entries that only the programmer should change. If the user is supposed to change a configuration entry, then this entry should be regarded as part of the user interface (even if it is located in a file). It should have a nice, well-designed name in the same way as we always label our buttons with nice, well-designed names. It should be documented if it's not self evident what it does. It should be tested etc. On the other hand, if the user is not supposed to change the entry, then it should be hidden away. The best thing would be if it were stored somewhere inside the class hierarchy where the user can't find it. I like to store those kinds of non-user-changeable entries in static fields in a java class. This requires the application to be rebuilt if someone wants to change them. This would require a new version of the application, which again requires you to run the unit tests again, maybe do some integration testing and maybe pass an acceptance test - and that’s exactly the point. Non-user-changeable configuration is a part of the code, even if it's not written in Java.

Add a comment Send a TrackBack