Modification of properties in Spring PropertyPlaceholderConfigurer
August 25, 2009 1 Comment
A while back I was using Spring to manage my properties using the PropertyPlaceholderConfigurer and I needed to provide an interface to manipulate the properties. Normally, I would store properties in a database or in a JNDI server, but I wanted something much quicker and the problem with using the database is that some properties such as the JDBC configuration is read as properties so it was really a chicken and egg problem.
The problem with the Spring PropertyPlaceholderConfigurer is that it won’t let you modify the properties that are set when Spring initializes. I can see the reasons for this, but I needed to expose the properties, be able to set them and persist the changes if I chose to. So I ended up extending PropertyPlaceholderConfigurer.
public class ExposablePropertyPlaceholderConfigurer extends
PropertyPlaceholderConfigurer {
private Map resolvedProps;
private Resource[] locations;
@Override
protected void processProperties(
ConfigurableListableBeanFactory beanFactoryToProcess,
Properties props) throws BeansException {
super.processProperties(beanFactoryToProcess, props);
resolvedProps = new HashMap();
for (Object key : props.keySet()) {
String keyStr = key.toString();
resolvedProps.put(keyStr, parseStringValue(props
.getProperty(keyStr), props, new HashSet()));
}
}
public Map getResolvedProps() {
//return Collections.unmodifiableMap(resolvedProps); //We want to be able to dynamically set
return resolvedProps;
}
@Override
public void setLocations(Resource[] locations){
this.locations = locations;
super.setLocations(locations);
}
@Override
public void setLocation(Resource location){
//If a single location is configured, we'll need to add it to our array in
//sposition #1
this.locations[0] = location;
super.setLocation(location);
}
public Resource[] getLocations(){
return locations;
}
}
<bean id="propertyConfigurer"
class=”com.springproject.spring.ExposablePropertyPlaceholderConfigurer”>
WEB-INF/application.properties
WEB-INF/jdbc.properties
WEB-INF/mail.properties
I expose the properties as a Map for display and I also fixed it so that the map that the properties is stored into is modifiable. Now all I had to do was write some quick code to display and edit the properties in a grid and save them back to the file. Easy peasy.
I did disover something really nice about Spring properties while doing this. You can nest properties and they will process inside out. I used this technique to provide properties for each environment I am working in so that I can switch a single property to switch the environment I am working in. Let’s look at an example.
site=dev
dev.webmethodsserver=dev.mydomain.com
test.webmethodsserver=dev.mydomain.com
prod.webmethodsserver=dev.mydomain.com
<property name="wsdlDocumentUrl"
value=”http://${${dev}.webmethodsserver}/foo?WSDL” />
Spring resolved this perfectly to my satisfaction in it’s own configuration, but I had to write a little code modification in my properties mechanism to reach a property regardles of the site.
private static ExposablePropertyPlaceholderConfigurer getConfigurer(){
return (ExposablePropertyPlaceholderConfigurer)SpringLoader.getApplicationContext().getBean(BeanName.PROPERTY_CONFIGURER);
}
public static String getSite() {
return getConfigurer().getResolvedProps().get("site");
}
public static String getProperty(String key) {
return getConfigurer().getResolvedProps().get(key);
}
public static String getPropertyBySite(String key) {
String value = getConfigurer().getResolvedProps().get(getSite() + "." + key);
if (StringUtil.isEmpty(value))
value = getConfigurer().getResolvedProps().get("default." + key);
return value;
}
If I had more time or I was developing a mechanism for a newer project, I wouldn’t use this method anymore. Instead I would either create a JNDI server using spring and a file and intialize that first and use it for my properties or I would leave my jdbc properties in the properties placeholder and put my other properties in the database and read those into the Map at startup. Either way, it would be a lot more work, but would eliminate my having to change the site key on each deployment.
Hi
Just to be sure: you persist all the changes of
ExposablePropertyPlaceholderConfigurer#getResolvedProps() to one of the resources objects from ExposablePropertyPlaceholderConfigurer#getLocations(), am i right?
To make these changes be seen by Spring itself (to resolve ${…} ), you need to restart your application, don't you?
In your xml snippet it should be
not http://${${dev}.webmethodsserver}
I really like reading your blog, but as a suggestion pls use any syntax highlighter( for example http://code.google.com/p/syntaxhighlighter/) to make your code readable