I am enabling message i18n in my Spring web app. For this, I have below code in my servlet.xml
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages/message"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
After adding above code, as soon as I hit my application in browser, I get below exception log:
SEVERE: Servlet.service() for servlet [default] in context with path [/ERP-Web] threw exception [Filter execution threw an exception] with root cause
java.lang.StackOverflowError
at org.springframework.context.support.ReloadableResourceBundleMessageSource.getMergedProperties(ReloadableResourceBundleMessageSource.java:235)
at org.springframework.context.support.ReloadableResourceBundleMessageSource.resolveCodeWithoutArguments(ReloadableResourceBundleMessageSource.java:176)
at org.springframework.context.support.AbstractMessageSource.getMessageInternal(AbstractMessageSource.java:209)
at org.springframework.context.support.AbstractMessageSource.getMessageFromParent(AbstractMessageSource.java:257)
where last 2 lines were repeated 100s of times and gives me StackoverflowException.
Exactly same exception is coming when I am using ResourceBundleMessageSource class.
My spring version is 4.3.6.RELEASE.
Below is content of my properties file
action.add.success = New {0} added successfully.
action.add.failure = Some error occurred in adding new {0}. Please try again later or contact administrator.
Sample project is on GitHub
I have tested your sample code in github and when running it has shown the error described, then modify the following:
Config:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages/message"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
Class:
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public String getMessage() {
String msg = messageSource.getMessage("hello.world", null, LocaleContextHolder.getLocale());
return msg;
}
hello.word is the property with text in your file with name message_en.properties.
With this modifications the code run.
Edit for unknown message codes:
I tried with unknown message codes and the error was repeated, so I look at the registry and found that there could be more than one beans with the same name (potential circular references), but I have not detected why this happens, but if you It needs to work you have to rename the beans like this.
<bean id="myMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages/message"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
and then use:
#Autowired
private MessageSource myMessageSource;
but i think this not resolve the principal problem with the circular potential error.
Very strange situation ;-)
The fundamental problem is that the messageSource gets auto-wired to itself (in the parentMessageSource property) because you use default-autowire="byType" this causes the stackoverflow exceptions for unknown message codes that messes up everything. Must say that logback adds mess to the mess as sometimes seems that the exception happens in its code, Log4J handles it better.
Autowiring is not good for big projects, and this your situation is a classical case of why, however if you must use it change the messageSource bean adding the following:
<property name="parentMessageSource"><null/></property>
In this way you wire yourself the parent and no autowiring happens.
This restores the normal situation in which not found messages are reported with a NoSuchMessageException
Then:
In controller you must request hello.world message, not message
You are missing a default resource, that is a no-locale-suffix file that represents the default locale or your application. In your case would be messages/message.properties To be simple the default locale of your application is that one for which you have all messages. Start with that and then add new languages (that might be incomplete).
Update
As far as I run your demo project:
Remove default-autowire="byType", which will set your message source's parent to itself, which causes stackoverflow;
Avoid LocaleContextHolder.getLocale(), which relay on system default locale;
Use right basename, which is different for ReloadableResourceBundleMessageSource and ResourceBundleMessageSource which solves following warning;
ResourceBundle [messages/message] not found for MessageSource: Can't find bundle for base name messages/message, locale en_US
Runnable example
StackTrace
As far as I could see from your stack trace, you may have three problems:
You don't supply arguments, but your property needs it;
The message source you have has cyclic dependency with its parent, which causes the StackOverflow (because AbstractMessageSource has a model like classloader, i.e. delegation to parent if it can't resolve);
I am not sure whether your properties is really found by message source, if it found, even with cyclic dependency, it will not StackOverflow;
Suggestions
When it comes to why there exists cyclic dependency, I can't tell whether it is a bug of spring or mis-configuration according to current info;
If you are not convenient to provide a example project, you may try 4.1.6 Relase, which I tried, works fine;
You may set log level to DEBUG or set breakpoint, to see whether you property file is really loaded;
The way you should use the ResourceBundleMessageSource is setting the path to the messages files on the basename property.
For example, if you have two messages files:
messages_en.properties
messages_es.properties
located at resources folder. Your bean configuration should be something like:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
Where messages is the prefix of the name of both files.
Maybe the Exception is thrown because Spring id trying to load the classpath automatically, and you have it included too, so it tries to load it again and again...
You can find a working example on Mkyong's.
This is what worked for me in my applicationContext.xml file:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:i18n/message" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en"/>
</bean>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<ref bean="localeChangeInterceptor" />
</property>
</bean>
Note that i put the properties files in the follwoing path :
src/main/resources/i18n/message_en.properties
Related
I am trying to do internationalization in Spring-MVC for the first time and I'm having what I assume to be a configuration issue. I have a NLS file that I named NLS_en.properties which I placed in my application's WEB-INF\classes directory. The file contains the following NLS string:
MSG_HELLO = Hello to the Internationalized World
In my application's servlet.xml file I've defined the following beans:
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="WEB-INF/classes/NLS"/>
</bean>
In my JSP file I have the following tag:
<p><spring:message code="MSG_HELLO" text="You should not be seeing this text" />
When the JSP displays, of course, the message I see is the one I should not be seeing, so how do I have to configure my application so that I do see my HELLO message?
ResourceBundleMessageSource basename (as opposed to ReloadableResourceBundleMessageSource) refers by default to the classpath, so you should have it like :
<property name="basename" value="NLS" />
Now, depending on how you build, even if configuring correctly the message source, it may have been erased at the time you run the application.
Do not place resources directly into classes (or any target directory in general). If you use maven place it directly into resources. If you dont use any build framework put it in the root of the source directory.
I'm learning to use resouce bundle for printing locale-dependent messages in my project.
application.xml:
<bean class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="ru.bpc.svat.svnr.integration.banking.messages" />
</bean>
<bean class="ru.bpc.svat.svnr.integration.banking.application.SpringContextMessageFactory" />
Directory src\main\resources\ru\bpc\svat\svnr\integration\banking contains files:
messages.properties
messages_ru.properties
messages_ru_RU.properties
each having line:
test = testmessage
In my test class I get an instance of message factory and try to get a message:
#Autowired private MessageFactory messageFactory;
...
messageFactory.getMessage("test", new Object[]{}, Locale.getDefault());
And then I get an exception:
No message found under code 'test' for locale 'ru_RU'.
What's wrong with it? This might be a foolish question, but it's my first experience. Thank you for your answers.
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>locale\customer\messages</value>
</property>
</bean>
</beans>
finally use SpringContext context.getMessage() method read your value.
Note: Assume your properties files are located at “resources\locale\customer\” folder.as mentioned in value tag above.
Basically those files needs to be in class-path of your project.
Let me know if it solves your problem, then accept my answer!
what if you place you files directly into src/main/resources (& not in subdirectory of src/main/resources? Just a thought.Normally, it should be able to find the file if it is there on classpath.
Try to replace:
<bean class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="ru.bpc.svat.svnr.integration.banking.messages" />
</bean>
on
<bean class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="ru\bpc\svat\svnr\integration\banking\messages" />
</bean>
make sure that the path relative from your class. In other case put resource in src/main/resources and use classpath:\ suffix
Below is the project-structure showing where I keep my messages files:
Web
Java Resources
src/main/resources
messages_en.properties
messages_fr.properties
Bean configuration file where I define message source looks like as shown below:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages_fr</value>
</list>
</property>
</bean>
I am new to this concept of internationalization. So don't know much except that if you put your .properies file inside the tag it will be picked and changes will be reflected. But I am facing problem in dealing with multiple .properties files.
Say If I have more than one .properties file then how should I implement locale changes? How would the program know which file to take?
You should use :
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>messages</value>
</list>
</property>
</bean>enter code here
Spring will use the local specified when you are calling the message bundle, so you don't have to specify it the declaration of your bundle.
Note : I think that you have to name your file :
messages_fr_FR.properties or messages_en_EN.properties.
Edit :
You can specify a local using :
messageSource.getMessage("messageKey", args, Locale.FRANCE);
to use messages_fr_FR
or
messageSource.getMessage("messageKey", args,LocaleContextHolder.getLocale());
to use the local selected by the user.
I hope it may help you
i have the next properties files with Spring Framework
config.properties
with content
environment=devel //posible values: devel, testing, prod
and with the previous environment property, choose some of the following files to load dynamically
config-service1-devel.properties
config-service1-testing.properties
config-service1-prod.properties
config-serviceN-devel.properties
config-serviceN-testing.properties
config-serviceN-prod.properties
and then, with spring i want load the properties, i'm solve to load the first properties file but i dont understand how to use expression language to complete the values of the dependent properties.
<bean id="MainApplicationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"
value="file://#{systemProperties['jboss.server.home.dir']}/conf/services.properties" />
<property name="placeholderPrefix" value="$mainProperty{" />
<property name="placeholderSuffix" value="}" />
</bean>
<bean id="SecondApplicationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
depends-on="MainApplicationProperties">
<property name="locations">
<list>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceOne/service1-$mainProperty{environment}.properties</value>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceTwo/service2-$mainProperty{environment}.properties</value>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceN/serviceN-$mainProperty{environment}.properties</value>
</list>
</property>
</bean>
the error output is the next,
java.io.FileNotFoundException: /..../conf/serviceOne/service1-$mainProperty{environment}.properties (No such file or directory)
my opinion is, the value has not replaced
helpme, thanks
The problem is that when BeanFactoryPostProcessors are starting to be invoked, they are already instantiated. So even thou the first PropertyPlaceholderConfigurer modifies the bean definition of the second PropertyPlaceholderConfigurer, it has no effect as both beans have been already instantiated.
I am having trouble getting PropertyPlaceholderConfigurer to work in my current configuration. Given the following block of code in my applicationContext.xml:
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<!-- Order matters, last one to create a property wins! -->
<value>classpath:default.properties</value>
<value>file:${MYAPP_PROPERTIES_LOCATION:badurl}/application.properties</value>
<value>file:${user.home}/developer.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertiesPlaceholderConfigurer">
<property name="properties" ref="myProperties"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="searchSystemEnvironment" value="true"/>
</bean>
Then in the default.properties files I have the following (these are test properties):
property1=prop1val
property2=${property1}
What works: the propertyConfigurer correctly resolves both the environment variable MYAPP_PROPERTIES_LOCATION and the System variable user.home. However, then the final properties object is created by the factory bean, the resulting properties are [property1=prop1val, property2=${property1}].
With this configuration, how can I get the properties inside the myProperties bean to resolve their placeholders?? I have done plenty of research to include tracing through the spring code - and I can see how and why this isn't being done. I'm hoping there is some setting I'm just missing! This is my first post so be easy on me :)
You have proper configuration of spring and the property files. That why you are being able to read the data from the file. Everything is proper with what spring is doing too, let me explain a bit more....
As you told below is your property file,
property1=prop1val
property2=${property1}
Remember, it is a pretty text file with key value pairs and it can not take variables. Here, if you are intended to copy the value of property1 to property2 dynamically it is not going to happen. That is not the way we are supposed to use a property file.
Property file is supposed to be simple text file with key-value pairs. So, keep the keys atomic so that you can construct the required data logically from the application or inside your applicationcontext.xml file.
I am not sure why you are using PropertiesFactoryBean. Can you please try just the following (Not sure if you already tried it and any issues you faced)
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- Order matters, last one to create a property wins! -->
<value>classpath:default.properties</value>
<value>file:${MYAPP_PROPERTIES_LOCATION:badurl}/application.properties</value>
<value>file:${user.home}/developer.properties</value>
</list>
</property>
</bean>