Saturday, September 27, 2008

Configuring applications with Spring

If you’ve used Spring before, you’ve almost definitely used a PropertyPlaceholderConfigurer to inject settings from external sources — most likely properties files — into your application context. The most common use cases include JDBC and Hibernate settings, but it’s not that uncommon to also configure Lucene index, temp file, or image cache directories as well. The simplest case looks something like this:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:application.properties"/>
</bean>   <!-- A sample bean that needs some settings. -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

And application.properties might look like this:

jdbc.driver=org.h2.Driver
jdbc.url=jdbc:h2:mem:example
jdbc.username=sa
jdbc.password=

Note, you can achieve the same simple configuration using the new spring 2.x style schema configuration, but it doesn’t allow for any further customization so we’re going to use the old style.

<!-- Example of new Spring 2.x style -->
<context:property-placeholder location="classpath:application.properties"/>

This handles the simple case of replacing placeholders (e.g. ${jdbc.url}) with values found in a properties files (e.g. jdbc.url=jdbc:h2:mem:example). In a real-world application, we not only need to collect settings, but also override them in different environments. Many of our applications are deployed in 4 or more environments (developer machine, build server, staging server, and production), each requiring different databases at the very least.

There are a few ways to enable overriding of properties. Let’s take a look at them in turn:

1. Setting the system properties mode to override (default is fallback)
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="location" value="classpath:application.properties"/>
</bean>

When configured in this mode, any value specified as a system property to the JVM will override any values set in properties files. For example, adding -Djdbc.url=jdbc:h2:mem:cheesewhiz to the JVM arguments would override the value in the file (jdbc:h2:mem:example). On a Java 1.5 or newer platform, Spring will also look for an environment variable called jdbc.url is no system property was found.

2. Specifying an optional properties file
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="locations">
        <list>
            <value>classpath:application.properties</value>
            <value>classpath:local.properties</value>
        </list>
    </property>
</bean>

When ignoreResourceNotFound is set to true, Spring will ignore resources that don’t exist. You can imagine application.properties, containing all of the default settings, versioned in your SCM system. Developers have the option of creating a properties file called local.properties to override any settings that differ in their environment. This file should be unversioned and ignored by your SCM system. This works because properties are loaded in order and replace previous values.

3. Web Application overrides

In a web application environment, Spring also supports specifying values in web.xml as context params or in your application server specific meta-data as servlet attributes. For example, if you’re using Tomcat you can specify one or more parameter elements in your context.xml, and Spring will can inject those values into placeholders.

<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
    <property name="location" value="classpath:application.properties"/>
</bean>

The ServletContextPropertyPlaceholderConfigurer conveniently works in non servlet environments by falling back to the behavior of a PropertyPlaceholderConfigurer. This is great when running unit tests.

4. Combining techniques

There’s no reason why these techniques can’t be combined. Technique #1 is great for overriding a few values while #2 is better for overriding many. #3 just expands the field of view when Spring goes to resolve placeholders. When combined, system properties override those in files. When using technique #3, there are some settings available for adjusting the override behavior (see contextOverride). Test the resolution order when combining to ensure it’s behaving as expected.

Optional External Properties

There’s another use case that applies to some projects. Often in non-developer environments, system admins want to keep properties for the environment outside of the deployable archive or the application server, and they don’t want to deal with keeping those files in a Tomcat context file; they prefer a simple properties file. They also don’t want to have to place the file in a hard-coded location (e.g. /var/acmeapp/application.properties) or they may keep configuration for multiple servers in the same network directory, each file names after the server. With a little trickery, it’s easy to support an optional external properties file that isn’t in a hard-coded location. The location of the file is passed as a single system property to the JVM, for example: -Dconfig=file://var/acmeapp/server1.properties. Here’s the configuration to make it happen:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="location" value="${config}"/>
</bean>

The first definition enables basic property resolution through system properties (in fallback mode). The second bean loads the resource from the location resolved from the system property -Dconfig. All spring resource urls are supported, making this very flexible.

Putting it all together

Here’s a configuration that does more than most people would need, but allows for ultimate flexibility:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>   <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="searchContextAttributes" value="true"/>
    <property name="contextOverride" value="true"/>
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="locations">
        <list>
            <value>classpath:application.properties</value>
            <value>classpath:local.properties</value>
            <value>${config}</value>
        </list>
    </property>
</bean>

Every placeholder goes through the following resolution process. Once a value is found it’s set and the next placeholder is resolved:

  1. (optional) Property value specified as a system or environment property; useful for overriding specific placeholders (e.g. -Djdbc.host=devdb / -Djdbc.username=carbon5)
  2. (optional) Context parameters located in web.xml or context attributes specified in application server meta-data (e.g. a Tomcat context.xml).
  3. (optional) Properties file located by the system/environment variable called “config”; useful for externalizing configuration. All URL types are supported (e.g. -Dconfig=c://hmc.properties).
  4. (optional) Properties file identified by classpath:local.properties; useful for specific developer overrides.
  5. (required) Properties file identified by classpath:application.properties, which contains default settings for our application.
Best Practices
  • Deploy the same exact artifact (e.g. war, ear, etc) across all environments by externalizing configuration. This may seem daunting, but the emergent benefits are huge in terms of simplicity.
  • Only make things that can safely change across environments configurable. Also, only things that need to be configurable should be configurable, it’s easy to go overboard.
  • Configure the minimal properties search path that meets your requirements.
  • When looking for properties files in the project tree, use classpath resources whenever possible. This makes finding those files easy, consistent, and insensitive to the working-dir, which is great when running tests from your IDE and command line.
  • Aim for a zero-configuration check-out, build, run-tests cycle for the environment where its happens most: development.

What other interesting configuration scenarios have you seen?

by christian

Wednesday, September 10, 2008

Lazy loading vs. pre-loading beans with Spring Framework

Spring framework can instantiate and bind (called loading) related Java objects (called beans) according to a given configuration. An XML file can easily be used to define these bindings. Spring framework supports two different types of loading methods; lazy loading and pre-loading respectively managed by BeanFactory and ApplicationContext containers.

Lazy Loading

A bean is loaded only when an instance of that Java class is requested by any other method or a class. org.springframework.beans.factory.BeanFactory (and subclasses) container loads beans lazily. Following code snippet demonstrate lazy loading, concentrate on how "beans.xml" spring configuration file is loaded by BeanFactory container class.
BeanFactory factory = new XmlBeanFactory(
new InputStreamResource(
new FileInputStream("beans.xml"))); // 1
Employee emp = (Employee) factory.getBean("employeeBean"); // 2

Even though "beans.xml" configuration file is loaded with BeanFactory container in line number 1, none of the beans will be instantiated. Instantiation takes place only at line number 2, where bean called "employeeBean" is requested from container. Since the class is instantiated at getBean() method call, time spend to return this method will vary depending on the instantiated object.

Pre-loading

All beans are instantiated as soon as the spring configuration is loaded by a container. org.springframework.context.ApplicationContext container follows pre-loading methodology.
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml"); // 1
Employee emp = (Employee) context.getBean("employeeBean"); // 2

As all singleton beans are instantiated by container at line number 1, this line will take some considerable time to complete. However line number 2 will return the bean instance immediately since instances are already available inside the container.

Point to note

Decision to choose one from these two methods would depend solely on application specific requirements. Some applications need to load as soon as possible while many others would probably willing to spend more time at startup but serve client requests faster. However some of the beans defined in a configuration may only be used rarely, so instantiating such classes at start up would not be a wise decision. Similarly, some Java instances would be highly resource consuming; leading not to instantiate at start up.

By Kamal Mettananda