Search This Blog

Tuesday 3 July 2012

Spring and Message Loading

One of the advantages of using the application context is that we can load locale specific messages using the ApplicationContext. More simply put Spring includes Localization and Internationalization support.
Internationalization or I18N is defined in wikipedia as
the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.
Localization or L10N is defined as
process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text.
The ApplicationContext implements the MessageSource interface for resolving messages. It includes support for parameters and also locale based selection of such messages. To start of I created a set of property files:
Sample Project configuration
The contents of the property files is as below:
standard.properties
rockMessage=Alligators rock!
failure.properties
argument.required=The \''{0}\'' argument is required.
failure_en_GB.properties
argument.required=This is Great Britan and the \''{0}\''is required, I insist required.
Next the step is to create a MessageSource bean and pass it the list of our property files.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>standard</value>
                <value>com/resources/failure</value>
            </list>
        </property>
    </bean>
</beans>
The implementation used here is ResourceBundleMessageSource. This class is built on the top of the Java resource bundle. The class uses JDK's standard message parsing provided by MessageFormat. The bean has a property called "basenames" which takes a list of property files. The failure property file needs to be included only once. Both “failure_en_US.properties” and “failure_en_GB.properties” are consider one file in Spring. We just need to include the file name once, and Spring will find the correct locale automatically.
I decided to test the working of the bean:
public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            "spring-locale.xml");
    System.out.println(applicationContext.getMessage("rockMessage", null,
            "Default", null));
}
The application context's getMessage method takes four parameters:String code, Object[] args, String defaultMessage, Locale locale
  • The code is the name of the property. 
  • If the message is parametrized, then the parameter values are passed in the second array. 
  • The third parameter is the default message used when the property is not found. 
  • The fourth indicates the locale to use. 
The application context internally delegates the call to the MessageSource implementation. For rendering of no-arg messages, it does not use MessageFormat class.The output of the code is as below:
Alligators rock!
When the ApplicationContext gets loaded, it automatically searches for a MessageSource bean defined in the context. The bean has to have the name/id 'messageSource'.If such a bean is found, all calls to the methods described above will be delegated to the message source that was found. I tested the same method for parametrized message
public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
        "spring-locale.xml");
    //passing parameters to the message
    System.out.println(applicationContext.getMessage("argument.required",
        new Object[] { "Aplication Context" }, "Required", null));
}
The output of the same is :
The 'Aplication Context' argument is required.
I tested the code for UK locale:
public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
        "spring-locale.xml");
    // passing parameters to the message for a specific locale
    System.out.println(applicationContext.getMessage("argument.required",
        new Object[] { "Aplication Context" }, "Required", Locale.UK));
}
The output of the same is
This is Great Britan  and the 'Aplication Context' is required, I insist required.
As per the Spring documentation,
The caching provided by this MessageSource is significantly 
faster than the built-in caching of the java.util.ResourceBundle class.
In the next post we will have a look at reloading the message bundles.

4 comments:

  1. There are two problems with ResourceBundleMessageSource implementation.

    1. Locking contention - getResourceBundle() and getMessageFormat() are both synchronized.
    2. MissingResourceException - Resolving a message involves looping through all the resource bundles defined in the application and calling bundle.getString(key). bundle.getString(key) method call throws an MissingResourceException if the key is not found. Searching for the key until we find the message for the given key. Since exception construction is a slow process and could eat up the CPU (which is what I observed in our load tests) this looks like a bottleneck.

    Though there are workarounds for both of the above problems (by extending the class and overridding the behavior) I just wanted to share what I observed in load tests

    ReplyDelete
    Replies
    1. Hi Amit,
      That was very interesting about the synchronization part. I did a quick look around in the source code and yes - the call to get the resource bundles is synchronized. As the ResourceBundleMessageSource class loads the bundles only once during its lifetime, I am not entirely sure why the synchronized block is there (and that too for the read calls). Maybe some more analysis is required there.
      As for MissingResourceException - This is a run time exception and I wouldn't handle it in my code. I'd rather that the code crashes due to such an error during development/test phase and any missing messages be resolved.
      Nice Observations. Keep sharing :)

      Delete
  2. Hi Robin,

    Nice article.. very informative.. thanks for such a beautiful explanation :)

    I've question. If I've two properties "UserName.properties" and "UserAddress.properties" how can I tell spring to look a key in only specific property .
    Example :
    consider both the properties have same key value pair city=Bangalore, But I want to get city from "UserName.properties". How to achieve this ? I don't see any option in applicationContext.getMessage() method

    ReplyDelete
  3. Robin, you can translate .properties files easily using a software localization tool like htps://poeditor.com/
    If you need such a tool to translate software strings, this would be a good choice.
    Cheers!

    ReplyDelete