ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Advanced Configuration of the Spring MVC Framework

by Dejan Bosanac
03/22/2006

In this article I will present some configuration tips for the Spring MVC framework that could help you manage multiple instances of your Spring-based web application. The configuration management topic is often neglected in the literature, but as we will see, it is very important for real-life web development. It is not directly related to any particular technology, so we will start by explaining the basic concepts of the problem. Next, we will focus on the Spring MVC framework and offer a few solutions for projects developed using this technology.

Spring Configurations

It is common for a web application to be deployed on more than one host. Take, for example, a website that will have only one instance in production. Beside that instance, you (as its developer) will probably have another (development) instance on your development machine. You may also find useful to maintain another installation of your application on some local development server inside of your company (organization). The purpose of this instance would usually be to give access to web designers, for quality assurance stuff, to people that should document the application, etc.

As you can see, even in this simplest possible scenario there are three instances of the application that we should install, configure, and maintain. The situation is even worse for geographically dispersed teams that work on a project like this. For any non-trivial web application project, you will usually have more that one developer with his local installation of the project and local settings, one installation for running unit tests, and so on.

Related Reading

Spring: A Developer's Notebook
By Justin Gehtland

Many organizations create their products as web applications. You may find examples of such products in various e-commerce systems, content management systems (CMSes), or even blog publishing platforms. Products like these are intended for deployment on as many servers as possible. For successful acceptance of general-purpose web applications, their developers must ensure that their apps are easy to install and integrate well with other web applications. After this discussion, it should be clear that application configuration, which is the topic of this article, is one of the very important issues for developers of general-purpose web application projects.

Version control systems, such as CVS or Subversion, are one of the standard tools used by development organizations. This kind of tool represents a central source code repository of some organization and is used to keep the source code in order. Users are able to track changes of the application's source, show differences among file versions, and make project branches. Also, they make partial updates on the application deployments possible.

It is clear that version control system software is necessary for keeping track of your source code and that it can help in a great deal with the application configuration problems discussed here. Still, in this article, we will not focus on version control systems, as there are lots of great materials that cover this topic. Here, we will focus on just a small subtopic of the version control issue: how to ease the configuration of web applications (particularly those written using the Spring MVC framework).

The question is: what kind of configuration we are talking about here? Well, any web application needs resources that are usually specific to the host on which it is running, such as the database URL, the SMTP server that will send emails, folders that will contain some application-specific documents, etc. Settings like these should be centralized so that application configuration could be as easy as possible.

But this describes only the simplest version of the problem. Sometimes, you need more complex configuration differences among application deployments. This means that you will have to make different bean wirings among deployments, which complicates this issue even further.

Solutions to these application configuration issues have many benefits, ranging from easier installation and configuration of your application to easier source version control, which leads to fewer conflicts in your source repository. Now let's discuss this topic in more detail and through examples.

Problem

Let's start with demonstrating the simpler version of the problem we described above. In this scenario, all that you want to change among application deployments are simple configuration parameters, such as URLs, passwords, etc. If you have ever tried to develop a web application using the Spring MVC framework, you know that there are two configuration files that should be used:

So what's the problem here? The problem is that your applicationContext.xml will contain some bean definitions that are host-specific. The most obvious example of this issue is a bean that holds JDBC connection information, but any non-trivial application will have a dozen such beans. Take a look at the following example:

<bean id="dataSource" 
 class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName">
       <value>org.postgresql.Driver</value>
   </property>
   <property name="url">
       <value>jdbc:postgresql://localhost/test</value>
   </property>
   <property name="username">
       <value>postgres</value>
   </property>
   <property name="password">
       <value></value>
   </property>
</bean>

The problem with this solution is in hard maintenance of the applicationContext.xml file. For starters, imagine that you have your project in the source code version control system, such as CVS. Now let's say that you want to add new functionality to your website, and for that, you need to put some extra bean definitions in the application context definition. The problem is how to reflect those changes on the production server.

Usually your local instance of the application will not use the same database as the live site, so the applicationContext.xml file will contain settings to access your local database. When you want to commit your changes in the source repository, you have to take care of synchronization of these host-specific properties. The file in the repository will probably end up with configuration from your local settings. Now, when you want to update the configuration on the production server, you will have to manually synchronize the values of the properties. This can be a very tedious task, and is also very prone to errors.

With every instance of the application, this issue becomes more important. Imagine that three developers work on the code base and that they each use their local database. When you commit your changes, each of them must be very careful when updating source code on their local hosts. Their manual synchronization of your changes, followed by committing of their work, makes the version control system useless for these configuration files. If you've ever used Spring MVC, you know that applicationContext.xml is the crucial element of your application, since it is the glue that holds things together. Therefore, it is very important to find a mechanism that helps us keep things organized in our applications.

As we said earlier, this is the easier configuration problem that you can experience. The harder version of the problem is when you need to have different bean wirings on different hosts. Examples of this kind of problem are very easy to find in everyday software development tasks. For instance, imagine that you have a custom authentication module for your product that is able to authenticate users from relational databases or LDAP servers. Naturally, the authentication module could be configured with the bean that abstracts a certain repository. If you want to change how users are authenticated among different application deployments, you will need to make different bean wirings in your applicationContext.xml file. This kind of configuration issue could be seen in all applications that have configurable features among deployments.

In the rest of this article, we will focus on these two configuration issues. We will focus first on the problem of synchronizing bean properties and its solutions, and then move to the more complex issues of synchronizing bean wirings.

Solutions

Synchronizing Bean Properties

One possible solution to this issue is to put all host-specific parameters in a regular Java properties file and use Spring's PropertyPlaceHolderConfigurer class to map these parameters to your bean properties.

Using this solution, we will have a properties file (/WEB-INF/jdbc.properties) that looks like this:

jdbc.driver=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost/test
jdbc.user=postgres
jdbc.password=

and our bean configuration will look like this:


<bean id="propertyConfigurer" 
 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
       <value>/WEB-INF/jdbc.properties</value>
    </property>
</bean>

<bean id="dataSource" 
 class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName">
         <value>${jdbc.driver}</value>
    </property>
    <property name="url">
         <value>${jdbc.url}</value>
    </property>
    <property name="username">
         <value>${jdbc.user}</value>
    </property>
    <property name="password">
         <value>${jdbc.password}</value>
    </property>
</bean>

As you can see, we defined an instance of the PropertyPlaceholderConfigurer class and set its location property to our properties files. This class is implemented as a bean factory post-processor and it will replace all placeholders (${...} values) with the properties defined in the file.

With this technique, we can remove all host-specific configuration properties from the applicationContext.xml file. In that way, we can freely add new beans to that file without worrying about synchronization of host-specific properties, which can simplify product deployment and maintenance.

Synchronizing Wirings

The above technique solved the first problem, but still it is not suitable if you plan to modify bean wirings among application deployments. One solution to this problem could be to create one more XML definition file named applicationContext-[hostname].xml, where [hostname] is actually the name of the host on which you will deploy your application. For example, on your local machine, this file would be usually called applicationContext-localhost.xml, where in deployment it could be something like applicationContext-somehost.com.xml.

As you can guess, this file should contain all configuration beans that are specific to a certain host. In this article, we will assume that our definition of the dataSource bean will be located in this kind of file instead of the general-purpose applicationContext.xml definition. Of course, this mechanism in not incompatible with the previous one, but for simplicity reasons we will now focus entirely on this method.

Now that we have our specific configuration, we should discuss how we can integrate it into the overall Spring MVC configuration concept. There are number of ways to achieve this, and we are going to discuss all of them in detail soon. But first, it is important to note that because some beans will be now located in the separate configuration file, all local references to them (in applicationContext.xml) must be converted to the global one.

For example, references like this one:

 <property name="someProperty">
   <ref local="someBean"/>
 </property>

should be changed to the following:

 <property name="someProperty">
   <ref bean="someBean"/>
 </property>

When this is done, there are numerous ways in which we can add an additional resource for configuration purposes. The first and most obvious is to use the <import> tag to include this additional resource in the applicationContext.xml configuration file. You usually put this tag at the beginning of the applicationContext.xml file. The example could look something like this:

<import resource="applicationContext-somehost.com.xml"/>

Now we have all host-specific wirings in the separate XML definition file and all general-purpose bean definitions in the regular application context definition file. Because the most of the beans are not host-specific, we are free to handle the applicationContext.xml file just as any other resource in our web application and synchronize it through the appropriate version control system.

But the aforementioned approach also has its drawbacks. If you want to keep your different configurations in different XML files, you will still have to worry about applicationContext.xml synchronization, since the name of the resource must be changed for every server. It is much better than the original solution, since now there is only the file name that should be taken care of, but it still requires manual assistance from the developer.

The next logical idea is to try to move this host configuration to the web.xml file (if possible), since it is usually modified less often than applicationContext.xml. Luckily, there is a solution available for us to use. Take a look at the following example of the web.xml configuration snippet:

  <listener>
   <listener-class>
    org.springframework.web.context.ContextLoaderListener
   </listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/applicationContext.xml 
       /WEB-INF/applicationContext-somehost.com.xml
    </param-value>
  </context-param>

As you can see, beside the usual ContextLoaderListener that is usually found in the web.xml file, we put the contextConfigLocation context parameter configuration. This parameter is used to instruct the framework where to look for these configuration files. If it is omitted, Spring will look only in the applicationContext.xml, but here we defined our host-specific configuration file to be used as well.

With this approach we moved all host-specific configuration from the applicationContext.xml file and thus eased its synchronization among various application deployments.

If this approach becomes your new habit, there is one more thing you can do to make it more flexible. By following the instructions that follow, you can remove host-specific configuration even from the web.xml file.

In order to do that, we need to create our specific application context class:

package net.nighttale.spring.util;

import java.net.InetAddress;

import org.springframework.web.context.support.XmlWebApplicationContext;

public class PerHostXmlWebApplicationContext 
 extends XmlWebApplicationContext {
        
    protected String[] getDefaultConfigLocations() {
            String hostname = "localhost";
            try {
                hostname = InetAddress.getLocalHost().getHostName();
            } catch (Exception e) {
            }
            
            String perHostConfiguration = DEFAULT_CONFIG_LOCATION_PREFIX 
                 + "applicationContext-" 
                 + hostname 
                 + DEFAULT_CONFIG_LOCATION_SUFFIX
                 ;
    
            logger.debug(
                 "Adding per host configuration file: " 
                 + perHostConfiguration
                 );
            
            if (getNamespace() != null) {
                    return new String[] {
              DEFAULT_CONFIG_LOCATION_PREFIX 
                + getNamespace() 
                + DEFAULT_CONFIG_LOCATION_SUFFIX
             , perHostConfiguration};
            }
            else {
                    return new String[] {
             DEFAULT_CONFIG_LOCATION
              , perHostConfiguration};
            }
    }
}

This class extends Spring's XmlWebApplicationContext, which is used by default. The XmlWebApplicationContext class takes the configuration for the web application from XML definition files. By default, it configures the application from applicationContext.xml and [servlet-name]-servlet.xml files. The only additional task that our class performs is getting the name of the host on which it is executed and adding the applicationContext-[hostname].xml file to the list of configuration files.

In order to use this class, we need to compile it, include it in the classpath, and instruct Spring framework to use it. The first two steps are well-known and we will not describe them here. We can instruct Spring to use it through the contextClass context parameter. Instead of the previous configuration in the web.xml file, we can put something like this:

<context-param>
   <param-name>contextClass</param-name>
  <param-value>
   net.nighttale.spring.util.PerHostXmlWebApplicationContext
  </param-value>
</context-param>

If this configuration snippet is used, our class will be used and three files will be used to initialize the framework: [servlet-name]-servlet.xml, applicationContext-[hostname].xml, and applicationContext.xml.

As you can see, the applicationContext.xml and web.xml files are now completely free of any specific configuration details and there is no fear that you will break your configuration during the application update.

However, there is one drawback to this approach. It is reflected in the need to have this third configuration file on the application deployment, whether you will use it or not. In the case that there is no specific host configuration needed, it could look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans></beans>

Finally, you need to know what specific hostname our application context class will look for. The easiest way to check your host's name is to execute the following code on the machine:

System.out.println(InetAddress.getLocalHost().getHostName())

You can execute this as Java code or as a script in your favorite scripting language with a Java-like syntax, such as BeanShell or Groovy. When you have the name of the host, you should create a default empty /WEB-INF/applicationContext-[hostname].xml file (like the one we have defined above) and you'll be ready to go.

Conclusion

In this article, I've presented some configuration tips that could ease your day-to-day work with the Spring MVC framework. If you ever find yourself trying to figure out how to maintain various web application deployments, try to find the most suitable solution for your development process. It will make your life much easier.

Resources

Dejan Bosanac is a software developer, technology consultant and author. He is focused on the integration and interoperability of different technologies, especially the ones related to Java and the Web.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.