ReloadablePropertiesFactoryBean: Beware of Caching

As Glenn pointed out, reloading properties is futile if the servlet engine returns an unchanged copy of the property file from its resource cache.

We need to find the real file anyway to check its last modification date, so overriding “loadProperties” to read from the file system absolutely makes sense.

Here’s an updated version, which correctly handles reloading of properties also from a ServletContextResource.

Download

Spring Properties Reloaded

Some time ago, I promised I would describe how to make spring configuration properties reloadable.

When using a standard spring PropertyPlaceholderConfigurer, properties will be read from a file, and their values can be referenced using a ${…} macro syntax. The expanded property values are usually assigned to bean properties in the xml application context.

Now what I would like to have is this: When the file changes, the properties should be read again, and the updated values should be assigned to the original beans’ properties. The standard Spring answer would be “shut down the application context and launch a new one”, but often we can be much more flexible without disrupting operation (e.g. changing the size of a cache etc.)

I came up with the following design goals:

  • Syntax and usage should be as close as possible to the non-reloading standard spring variant
  • Not all placeholders should be dynamic – some bean properties cannot meaningfully be changed at runtime, and the configuration should make this appearant.
  • The same conversions should take place during initial configuration, and when a property is reloaded
  • No extra demon threads – the XML configuration should control when files are checked for updates
  • Spring best practices: programming to interfaces, testability.
  • Singleton beans are enough – there’s no point in reconfiguring instances of templates.

As an example, let’s take a bean whose “cachesize” property is configured using a placeholder “my.cache.size” taken from a property file “config.properties”. The standard spring way to describe this looks as follows:

<bean id="configproperties"
      class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="location" value="file:config.properties"/>
</bean>

<bean id="propertyConfigurer"
     class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="properties" ref="configproperties"/>
</bean>

<bean id="mybean" class="net.wuenschenswert.spring.example.MyBean">
  <property name="cachesize" value="${my.cache.size}"/>
</bean>

The dynamic variant looks very similar, with only some class names changed, and a slightly different placeholder syntax:

<bean id="configproperties"
      class="net.wuenschenswert.spring.ReloadablePropertiesFactoryBean">
  <property name="location" value="file:config.properties"/>
</bean>

<bean id="propertyConfigurer"
      class="net.wuenschenswert.spring.ReloadingPropertyPlaceholderConfigurer">
  <property name="properties" ref="configproperties"/>
</bean>

<bean id="mybean" class="net.wuenschenswert.spring.example.MyBean">
  <property name="cachesize" value="#{my.cache.size}"/>
</bean>

<!-- regularly reload property files. -->
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
   ...(see complete file for details)...
<bean>

If you’re interested – just dive into the code, or download the jar. Thanks to my colleagues at coremedia for discussion and insights – the hacking is all mine, so I take the blame.

I used Spring 1.2.8 plus commons-logging for development (don’t know about licensing, so I’ll rather not bundle the binaries). Try running the example, change the config file and observe the log output. Whoa.

java -cp spring-reloaded.jar:spring.jar:commons-logging.jar   \\
          net.wuenschenswert.spring.example.Main

Implementation

How does it work? In order to get dynamically reloaded properties, we need the following ingredients:

  • a factory bean that detects file system changes
  • an observer pattern for Properties, so that file system changes can be propagated
  • a property placeholder configurer that remembers where which placeholders were used, and updates singleton beans’ properties
  • a timer that triggers the regular check for changed files

The observer pattern is implemented by the interfaces and classes ReloadableProperties, ReloadablePropertiesListener, PropertiesReloadedEvent, and ReloadablePropertiesBase. None of them are especially exciting, just normal listener handling. The class DelegatingProperties serves to transparently exchange the current properties when properties are updated. We only update the whole property map at once, so that the application can avoid inconsistent intermediate states (more on this later).

Now the ReloadablePropertiesFactoryBean can be written to create a ReloadableProperties instance (instead of a Properties instance, as the PropertiesFactoryBean does). When prompted to do so, the RPFB checks file modification times, and if necessary, updates its ReloadableProperties. This triggers the observer pattern machinery.

In our case, the only listener is the ReloadingPropertyPlaceholderConfigurer. It behaves just like a standard spring PropertyPlaceholderConfigurer, except that it tracks all usages of placeholders. Now when properties are reloaded, all usages of each modified property are found, and the properties of those singleton beans are assigned again.

If it doesn’t work for you, I’d love to read your comments. If it does, even more so!

Spring configuration properties: Defaults and Reloading

We’re using the spring framework a lot at work, and it’s generally an enormous improvement over our diverse previous practices for configuration and inter-component relations. However, there are two points where Spring seemed to be a step backwards compared to our previous practice, so I set out to fix them. I hope this will be interesting to others as well – this be my christmas gift…

Default values

Using Spring’s PropertyPlaceholderConfigurer, we separate the configuration of our software into an XML file containing the detailed wiring, and a standard Java Properties file containing simple parameters that can be adjusted by customers (such as cache sizes, TCP/IP port numbers, database connect strings, authentication names and passwords).

Now let’s assume some customer has adapted the properties file, while we ship a new version of our software, which introduces new configuration parameters. The customer shouldn’t have to manually merge and extend his property file; instead, the XML file contains reasonable default values for those properties missing from the customer’s properties file.

In out-of-the-box Spring, there are two ways to do this with a PropertyPlaceholderConfigurer: either put a list of default property bindings into the XML file itself, or put them into another Properties file, which may be contained as a resource in the jar file. Stefan Kleineikenscheidt has a good description of this approach.

What I found somewhat inconvenient is that each property name is listed twice: once in the default values, once when it is assigned to some bean property. So here’s how Stefan’s example would look using my DefaultPropertyPlaceholderConfigurer:

<bean id="propertyConfigurer"
    class="net.wuenschenswert.spring.DefaultPropertyPlaceholderConfigurer">
  <property name="location" value="file:custom/my.properties"/>
</bean>

<bean id="mybean">
  <property name="cachesize" value="${my.cache.size=100}"/>
</bean>

What happens here? The property configurer will load the properties from the given location as usual. Then when it comes to replacing a placeholder, and that placeholder is not defined in the properties file, the default value specified after the equals sign is used!

  ${placeholder=defaultvalue}

Reloading properties

When loading configuration properties from a file, our software would regularly check whether the file’s modification date has changed, and reload the properties if necessary.

I’ll go into this more deeply into the next post… until then, I’ll give you the syntax:

<bean id="configproperties" 
     class="net.wuenschenswert.spring.reconfiguration.ReloadablePropertiesFactoryBean">
  <property name="location" value="file:custom/my.properties"/>
</bean>

<bean id="propertyConfigurer" 
     class="net.wuenschenswert.spring.reconfiguration.ReloadingPropertyPlaceholderConfigurer">
  <property name="properties" ref="configproperties"/>
</bean>

<bean id="mybean">
  <property name="cachesize" value="#{my.cache.size=100}"/>
</bean>

The hash mark identifies the property as reloadable; When the file changes, and the value of the property my.cache.size defined inside that file changes, the setter for the cacheSize property is invoked on the singleton bean named “mybean”. Of course this includes default values, as above.

  #{reloadableplaceholder}
  #{reloadableplaceholder=defaultvalue}

And it actually works!