Spring MessageSource seems to ignore property fallbackToSystemLocale - java

I have problem configuring Spring MessageSource to ignore my system locale. When I call getMessage with null locale parameter I want my MessageSource to choose default property file messages.properties. Instead it chooses messages_en.properties. When I change the name of this property file to messages_fr.properties then default property file is choosen. My system locale is 'en'. So it seems like MessageSource ignores the fallbackToSystemLocale property which I set to false.
This behavior is same with Spring version 4.1.4 as well as 4.1.5.
MessageSource configuration:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="fallbackToSystemLocale" value="false"></property>
<property name="basenames">
<list>
<value>locale/messages</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
Getting message:
String message = messageSource.getMessage("user.email.notFound", new Object[] {email}, null);
Thanks for any advice!

fallbackToSystemLocale is NOT intended to steer what the message source does when you invoke them with locale=null
fallbackToSystemLocale control what to do when you request a message (code) that does not exist for the requested local - either because there is no message properties file for the language at all, or just because the message file does not contain the message code
On the other hand when you invoke getMessage with locale=null (messageSource.getMessage("key", null);) then locale will be set by Locale.getDefault
org.springframework.context.support.AbstractMessageSource:
protected String getMessageInternal(String code, Object[] args, Locale locale) {
...
if (locale == null) {
locale = Locale.getDefault();
}
...
BEFORE the fallbackToSystemLocale property is taken in account.
Therfore the easyest hack-arround (It is not a workarround it is a hack), would be using a language that you not support instead of null:
messageSource.getMessage("key", new Locale("XX"));

Related

What is the relation between #RequestMapping and Locale in Spring Boot?

If my understanding is correct, you can add/remove params for saveEmployee() freely. For example, when you add "loc" as follows, saveEmployee() receives the "non-null object" when the event happens. And the same goes for queryParams.
#Controller
public class Employee {
#RequestMapping("/save")
public void saveEmployee(Locale loc,
#RequestParam Map<String, String> queryParams) {
// saving employee
}
}
How could this method receive non-null Locale object by just adding a param "loc" here?
I would like to know the logic behind this.
Spring does it for you by using LocaleResolver or LocaleContextResolver, for the current request locale, determined by the most specific locale resolver available, in effect, the configured LocaleResolver / LocaleContextResolver in an MVC environment.
21.3.3 Defining #RequestMapping handler methods
An #RequestMapping handler method can have a very flexible signatures. The supported method arguments and return values are described in the following section. Most arguments can be used in arbitrary order with the only exception of BindingResult arguments.
Supported method argument types
java.util.Locale for the current request locale, determined by the most specific locale resolver available, in effect, the configured LocaleResolver / LocaleContextResolver in an MVC environment.
Spring's DispatcherServlet which forwards request from client to your controller gives you that parameters. In order to do that, it search the object from ApplicationContext to which bean(Controller) belongs.
Spring looks at the method arguments, their types and annotations, then determines if it can provide an object of that type/annotation.
If it cannot, it'll throw an exception, otherwise it will call the method with the object it decided fits the type/annotation.
For the list of supported types/annotations, read the documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments
As you can see, java.util.Locale is listed.
I think you need xml setting for your locale
Do you want to try the followning xml setting
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<mvc:interceptors>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
</mvc:interceptors>

localeChangeInterceptor does not work

I have very standard configuration, I kept editing many times, checked other questions on SO but by my final configuration most people had their issues resolved, however no result for me. Whenever firing requests like:
http://localhost:8080/appName/?lang=es_MX
or
http://localhost:8080/appName?lang=es_MX it does not resolve to correct locale, it does just do nothing.
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages" />
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
</mvc:interceptors>
I know that default locale resolver by headers work so is my messageResource and .jsp configuration is correct, since I set up one browser explicitly to have es_MX locale and it resolves with AcceptHeaderLocaleResolver correctly.
Does it have to do with the way my handler mappings are defined?
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home( Model model, Locale locale, HttpServletRequest hr) {
String header = hr.getHeader("User-Agent");
model.addAttribute("header", header);
String contextPath = hr.getContextPath();
model.addAttribute("contextPath", contextPath);
return "index";
}
God I spent so much time on this...please help
I solved it by placing interceptor into servlet-context.xml instead of my other config. Now trying to figure it out why in servlet-context.xml it works and in my dedicated config it doesn't, your insights would be valuable ! :-) I am not accepting my answer, since it does not explain why. Please explain me why this is the case(and so I could test it - ideally resulting in interceptor being placed in my custom config).
For anyone in trouble try this:
<interceptors>
<beans:bean
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="lang" />
</interceptors>
inside your servlet-context.xml
needs namespace:
xmlns:p="http://www.springframework.org/schema/p"
SessionLocaleResolver seems not to care about location and works in my custom config location too.
Check the API docs -> http://static.springsource.org/spring/docs/3.2.4.RELEASE/javadoc-api/org/springframework/web/servlet/i18n/SessionLocaleResolver.html
Also the source of the resolveLocale method of SessionLocaleResolver:
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale == null) {
locale = determineDefaultLocale(request);
}
return locale;
}
It will in turn get the Locale from the request:
protected Locale determineDefaultLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale == null) {
defaultLocale = request.getLocale();
}
return defaultLocale;
}
And in the docs for the ServletRequest.getLocale method:
Returns the preferred Locale that the client will accept content in,
based on the Accept-Language header. If the client request doesn't
provide an Accept-Language header, this method returns the default
locale for the server. Returns: the preferred Locale for the client
It seems you need to change locale by updating the correct session attribute, which is the job of the LocaleChangeInterceptor.
Maybe something is wrong with the interceptor then. Does it set the locale into the correct LocaleResolver? Can you post the whole spring configuration? Is the mvc namespace correct?
There is a complete i18n tutorial here -> http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example/

Spring Dynamic Validation

I am new to Spring validations. Previously i have used Struts validations. For dynamic validations, we will configure in errormessages.properties file like "errors.required={0} is required." later we will replace {0} with name. Is ther anyway in spring also for doing this. Please help me.
Thanks in advance.
Bellow code snippet may help you.
errormessages.properties
errors.required={0} is required
you need to define ResourceBundleMessageSource bean in spring-context.xml.
<bean id="messageSource" class="org.springframwork.context.support.ResourceBundleMessageSource">
<property name="messages">
<list>
<value>errormessages</value>
<list>
</property>
</bean>
In bean messageSource Member variable to access messages.
#Autowired
private MessageSource messageSource;
Second argument is array of object to pass.
messageSource.getMessage("errors.required",new Object[]{"Name"},"Default Required Error Message",null);
If you are implementing Spring Validator interface ( http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#validator) - you can directly specify message arguments using
void reject(String errorCode, Object[] errorArgs, String defaultMessage);

How do I load a bean value from a file with job parameter substitution?

In my spring batch project I can do something like this:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="message" value="job parameter value: #{jobParameters['arg1']}"/>
</bean>
and the message property will have a value taken from the spring batch job parameters. However, the value that I actually want to assign is very large and I don't want to put it in the xml file. I know this syntax doesn't work, but I would like to do something like:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="message" read-value-from-file="/path/to/file.txt"/>
</bean>
and that file would contain the line "job parameter value: #{jobParameters['arg1']}" which spring will parse as if the file content was in a value="" attribute.
Is there a nice way to do this?
I think what you are looking for is a PropertyPlaceholderConfigurer.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/path/to/file.properties" />
<property name="placeholderPrefix" value="#{" />
<property name="placeholderSuffix" value="}" />
</bean>
This is run by Spring as a bean processor and will attempt to resolve placeholder tokens. There is a default instance that will resolve against system properties, using this notation: ${propertyname}. For your notation, you would need to specify the placeholderPrefix/Suffix. When there are multiple bean processors, the order is determined by the order property. By default, if a processor fails to resolve a placeholder, execution fails, but this can be altered by setting ignoreUnresolvablePlaceholders. Since the mechanism is property driven, you probably want to consider a notation like:
<property name="message" value="job parameter value: #{jobParameters.arg1}"/>
Or, if what you're trying to convey is that arg1 is also a parameter, you might try:
<property name="message" value="job parameter value: #{jobParameters.${arg1}}"/>
Spring loops over the bean processors until no replacements are performed, or an exception is raised. So defining a property as ${something.${orOther}} is valid.
I would suggest you to use a String as file name and in your bean open that file.
I'm not sure if I get your problem right. I'm just suggesting something like Spring MessageBundle
Something like this:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="messagePath" location="/path/to/file.txt"/>
</bean>
And in your exampleTasklet read the file and do your thing (I'm not sure what it is)
If anybody came here to do something like this from a properties-file:
If you want a property from a .properties-file to appear in the JobParameters, you won't find ready-to-use solution. You can do the following:
Wrap a bean around your properties file.
Pass this bean to another one which has access to the JobParameters and can pump the properties from the file into that class.
Then you should be able to access your properties with Spring's Expression Language and do something like:
<bean id="myBean" class="my.custom.Bean">
<property name="prop" value="#{jobParameters['arg1']}"/>
</bean>
Alternatively, I think the solution proposed by Devon_C_Miller is much easier. You don't have the properties in your JobParameters then. But if the replacement in the XML configuration is the only thing you want, you only have to change your placeholders to:
${myPropFromFile}
Happy batching, everyone ;-)

Automatically Trim Trailing White Space for properties in Props file loaded into Spring

I'm using PropertiesFactoryBean to load properties from a typical Properties file. Is there anyway to get Spring to automatically trim trailing white space from the prop value?
As this can often be a source of confusion when using Spring Boot, I want to add that you do not need XML configuration to provide your own PropertyPlaceholderConfigurer.
Simply put this in your main class:
#Bean
public static PropertySourcesPlaceholderConfigurer createPropertyConfigurer()
{
PropertySourcesPlaceholderConfigurer propertyConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyConfigurer.setTrimValues(true);
return propertyConfigurer;
}
This is sufficient for trimming the values from application.properties.
You can customize the Properties loading functionality by passing in a custom PropertiesPersister into your PropertiesFactoryBean configuration. The PropertiesPersister instance is used by the PropertiesFactoryBean to parse the Properties file data. The default implementation follows the native parsing of java.util.Properties. You can customize the parsing logic by providing your own implementation of the PropertiesPersister interface.
As Chad said, Spring solved this problem with version 4.3RC1. But you need to manually set on trim function with parameter "trimValues" like so (default if "false"):
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="trimValues" value="true"/>
<property name="locations">
<list>
...
</list>
</property>
I do not found any documentation about this but I deduce it from Spring API.
With latest Spring version(4.3+), you can simply call setTrimValues() with true when you create PropertySourcesPlaceholderConfigurer bean in your configuration. That will remove any extra leading or trailing spaces from the value you got from the properties file.
You can define your own property configurer:
package your.company.package;
public class TrimPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
#Override
protected String resolvePlaceholder( String placeholder, Properties props ) {
String value = super.resolvePlaceholder( placeholder, props );
return (value != null ? value.trim() : null );
}
}
Then you must define it in your bean_config.xml
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:main.properties" />
</bean>
<bean id="trimPropertyPlaceholderConfigurer" class="your.company.package.TrimPropertyPlaceholderConfigurer">
<property name="properties" ref="applicationProperties" />
</bean>
Another way if you're using #Value annotations to set the properties into the fields:
#Value( value = "#{applicationProperties['my.app.property'].trim()}" )
NullPointerException is thrown if the property doesn't exists in the file
One easy way to do it would be to "hack" the spEl Expression to force the use of the String.trim() function.
Let's say you have a property test.myvalue equal to azerty (with trailing spaces) in the application.properties file, then you could inject this property in your class like this :
#Value("#{'${test.myvalue}'.trim()}")
String myvalue;
The resulting myvalue will be equal to azerty (no trailing spaces) once injected in your class.
Obviously this trimming won't be set globally to all injected values in your app, and you'll have to do it to all injected value, but I think this approach gives more flexibility.

Categories