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!