Spring ResourceBundle Property Configurer
I haz a problem!
I had a problem, as I often do with the frameworks I work on. I wanted to set properties in my spring context configuration file with externalized strings à la i18n. It seemed like a reasonable request at the time – and then I had several hours sucked out of my life by internet searches with terms like “propertyplaceholderconfigurer i18n”, “propertyplaceholderconfigurer externalized strings”, and near the end when my brain cells started committing seppuku out of loathing of whining forum posters and rage at bofh post answerers, “job openings mcdonalds”.
Yes – apparently there is no standard way to do this. With all the shame and ridicule heaped upon the unwashed Java geeks that are found out to NOT use the spring framework and its “should be part of the Java spec” IOC functionality, and after almost eight years of heavy development and even heavier adoption in the enterprise-and-everything-smaller market, you’d think someone would have come up with a “ResourceBundlePlaceholderConfigurer”. They didn’t.
Here’s where I would have said “but I did”, but the workaround is so trivial and my opensource spring utilities library is so not existing that I figured I’d just show you the code and let you use/modify/steal as needed. That said, bring on the hacking!
The Java part
First up, a little helper class you’re going to need to provide you with the gluecode-ish link between the standard Java ResouceBundle class functionality and Spring’s already gluecode-ish PropertyPlaceholderConfigurer class.
public class ResourceBundleConverter { private String bundleLocation; public void setLocation(String location) { this.bundleLocation = location; } public String getLocation() { return this.bundleLocation; } public Properties getProperties() { ResourceBundle rb = ResourceBundle.getBundle(this.bundleLocation); Enumeration enume = rb.getKeys(); Properties newProps = new Properties(); String key = null; while(enume.hasMoreElements()) { key = enume.nextElement(); newProps.setProperty(key, rb.getString(key)); } return newProps; } }
Not that there’s so much code that you couldn’t figure it out for yourself, but I’ll explain it anyway because I like the sound of my own voice.
It helps drown out the other voices in my head…
The main glitch in our master plan is that there seems to be no transport path from the ResourceBundle class to the PropertyPlaceholderConfigurer class. Said another way, there’s no way to get the resolved property values from the bundle to get spit out by the configurer, and so we use the above code to do an extraction of all the resolved bundle properties and place them into a standard Java properties file. With that properties file, we then have our transport path.
The Spring part
Now we switch to our Spring context file where we immortalise our converter class as a bean.
<bean id="labelBundle" class="com.facets.examples.ResourceBundleConverter" p:location="menu-labels" /> <bean id="labelProps" factory-bean="labelBundle" factory-method="getProperties" /> <bean id="labelPropertiesConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="properties"> <ref local="labelProps" /> </property> </bean> <bean id="menuItem" class="com.facets.examples.MenuItem" p:label="${menu.label}" />
From that bean, we use a factory bean to grab the externalised strings property file and inject it into the “setProperties” method of the standard PropertyPlaceholderConfigurer Spring class. That’s the magic right there – the setProperties method is inherited from the PropertiesLoaderSupport Spring class, the javadoc of which states:
Set local properties, e.g. via the “props” tag in XML bean definitions. These can be considered defaults, to be overridden by properties loaded from files.
Ignore the first part – its the blurb about “considered defaults” that we care about. What we’ve done is we have set the property/value pairs from the bundle class as defaults on our configurer instance. Now when you go to use a placeholder on the string property of one of your beans, the configurer will find that it has no value for it and use the default – which came from the resource bundle. That’s all there is to it.
A few notes…
I’m pretty sure there’s got to be an even better way to do this, and maybe even a Spring utility class buried in the recesses of the Spring libraries that already does this. If there is and you are reading this wondering why I even bothered getting up on the morning of the day I wrote this post, then please – enlighten me! Otherwise, if a geek like me can spend literally eight hours reading docs, posts, source code, and towards the end randomly clicking on google results page numbers hoping to catch a glimpse of something that looks even related to the answer, then the solution to this problem just isn’t ubiquitous enough. Hence this post. Happy hacking!
