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!