Using Spring themes to override resource bundle messages
We use Spring quite a bit. In one project we use Spring MVC (I won’t get into a rant about whether that’s a good thing or not) and rightly or wrongly we use Spring themes within our UI. Yes I know JSP based UIs are so last century but sometimes if the shoe fits …
One thing that we wanted to do was have themes which override certain resource bundle variables defined in an application wide, internationalized properties file. For example, our login greeting is “Welcome” with an appropriate i18n version for various locales. We also have certain themes based on types of user accounts. Some themes require their own customized login greeting and others will rely on using messages from the default MessageSource.
Our Spring application utilizes two classes for accessing resource bundles:
org.springframework.context.support.ResourceBundleMessageSourceorg.springframework.ui.context.support.ResourceBundleThemeSource
Both these classes provide access to specific resource bundles but do not necessarily know about each other which means the operate in isolation. Ideally we wanted to have a hierarchy of resource bundles such that a resource message defined in the bundle accessed by ResourceBundleMessageSource could be overridden by a message defined in resource bundles accessed by ResourceBundleThemeSource. Whilst the ResourceBundleMessageSource class has a setParentResourceBundle(), there is no way to inject that to the ResourceBundleThemeSource via configuration.
As the resourceful (no pun intended) java developers that we are, we decided to create our own wrapper class for theme bundle resolution. This class will use the application’s ResourceBundleMessageSource definition as a parent for its own resource message source so that we can ensure that if a message cannot be resolved by key in the theme bundle, it will then defer to the parent resource bundle (which is the application level MessageSource) and try and look up the key there. This simple class is outlined below:
public class CustomResourceBundleThemeSource
extends ResourceBundleThemeSource {
private MessageSource parent;
public void setParentMessageSource(MessageSource messageSource) {
parent = messageSource;
}
@Override
protected MessageSource createMessageSource(String basename) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename(basename);
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setParentMessageSource(parent);
return messageSource;
}
}
With this class, we just need the following in our Spring config class:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>bundles/messages</value>
</list>
</property>
</bean>
<bean id="themeSource"
class="CustomResourceBundleThemeSource">
<property name="basenamePrefix" value="/themes/" />
<property name="parentMessageSource" ref="messageSource" />
</bean>
Now whenever we need to resolve resource messages, we use the Spring specific theme tags:
<spring:theme code="login.greeting" />
And if we have a definition in our theme resource bundle we see that else we see the default definition as specified in the underlying MessageSource.
This allows us to override resource bundle messages for various themes as required and removed quite a bit of ugly JSP/JSTL logic for resolving messages conditionally.
Job done.
