Spring Webflow: How to establish my own ReloadableResourceBundleMessageSource for a flow - spring

Spring Webflow 2.5.1.
I have my own implementation of ReloadableResourceBundleMessageSource. I can successfully establish it in Spring MVC via:
#Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource source = new AwareReloadableResourceBundleMessageSource();
source.setCacheSeconds(cacheSeconds);
source.setBasename("WEB-INF/i18n/messages");
source.setUseCodeAsDefaultMessage(true);
source.setDefaultEncoding("UTF-8");
return source;
}
My application uses both Spring MVC and Webflow.
I would like to have instances of the same AwareReloadableResourceBundleMessageSource in place for the individual per-flow messages.properties files.
I have tried:
#Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
...
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource ms = new AwareReloadableResourceBundleMessageSource();
ms.setBasename("messages");
System.out.println("MESSAGE SOURCE AwareReloadableResourceBundleMessageSource");
return ms;
}
}
But the messageSource() method is not called.
I have seen: https://stackoverflow.com/a/8126164
Any pointers/techniques/code snipppets very gratefully accepted.

Try the following:
Configure messageSource() within a ValidatorFactory bean, AND
Configure the ValidatorFactory bean within FlowBuilderServices
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setValidator(getValidator())
.build();
}
#Bean
public LocalValidatorFactoryBean getValidator() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}

Related

Spring Boot, Hibernate validator language based on LocaleContextHolder

I've a Spring Boot 2.4.2 REST application using JPA, Hibernate, etc.
So far I use a MessageSource for applications errors (located in i18n/messages), and the default ValidationMessagesfor bean validations.
This is part of my configuration:
public static Set<Locale> LOCALES = Set.of(new Locale("en"), new Locale("it"));
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver localeResolver = new SmartLocaleResolver();
return localeResolver;
}
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
#Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
Locale locale = Locale.lookup(list, LOCALES);
return locale;
}
}
#Primary
#Bean("messageSource")
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setAlwaysUseMessageFormat(true);
messageSource.setBasenames("classpath:/i18n/messages");
// set to true only for debugging
messageSource.setUseCodeAsDefaultMessage(false);
messageSource.setFallbackToSystemLocale(false);
return messageSource;
}
My application supports 2 languages so far: it and en.
The problem right now is that application's messages are correctly localized in the agent's language (browser) but Validations errors are not.
I found out that Hibernate uses the default locale (Locale.getDefault()) and to customize the behaviour I should customize the locale resolution.
So I tried creating a custom hibernateValidator (that I set in my entityFactory) :
#Bean
public MessageSource validationMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setAlwaysUseMessageFormat(true);
messageSource.setBasenames("classpath:/ValidationMessages");
// set to true only for debugging
messageSource.setUseCodeAsDefaultMessage(false);
messageSource.setFallbackToSystemLocale(false);
return messageSource;
}
#Bean("hibernateValidator")
public LocalValidatorFactoryBean hibernateValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setValidationMessageSource(validationMessageSource());
return factoryBean;
}
and the resolver:
public class HibernateLocaleResolver implements LocaleResolver {
#Override
public Locale resolve(LocaleResolverContext context) {
return LocaleContextHolder.getLocale();
}
}
Doing this, the locale resolution works fine, but the parameter replacement doesn't. What I mean is for messages like this:
server.validators.ArraySize.message = The number of values must be between [{min}] and [{max}].
I've an exception:
"exception": "java.lang.IllegalArgumentException", "message": "can't parse argument number: min"
So I changed the configuration above adding the MessageInterpolator:
factoryBean.setMessageInterpolator(new ResourceBundleMessageInterpolator(LOCALES, Locale.ENGLISH, new HibernateLocaleResolver(), false));
At this point the parameters are resolved correctly, but again the locale resolution doesn't work.
Can you point me out in the right direction, trying to explain the best practice to follow for the combination Spring Boot - Hibernate Validator?
I solved the problem. I hope this can help someone else. This is my configuration file:
#Primary
#Bean("messageSource")
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setAlwaysUseMessageFormat(true);
messageSource.setBasenames("classpath:/i18n/messages");
// set to true only for debugging
messageSource.setUseCodeAsDefaultMessage(false);
messageSource.setFallbackToSystemLocale(false);
return messageSource;
}
#Bean
public MessageSource validationMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setAlwaysUseMessageFormat(true);
messageSource.setBasenames("classpath:/ValidationMessages");
// set to true only for debugging
messageSource.setUseCodeAsDefaultMessage(false);
messageSource.setFallbackToSystemLocale(false);
return messageSource;
}
#Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setValidationMessageSource(validationMessageSource());
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
and the LocaleConfiguration:
#Configuration
public class LocaleConfiguration implements WebMvcConfigurer {
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver localeResolver = new SmartLocaleResolver();
return localeResolver;
}
public class SmartLocaleResolver extends AcceptHeaderLocaleResolver {
#Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isBlank(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
Locale locale = Locale.lookup(list, Constants.LOCALES);
return locale;
}
}
}
the important part I saw made the difference are these lines:
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
In this way the interpolator works fine as well as the localization of the message.

Spring 4 #PropertySource not working when ResourceBundleMessageSource bean is registered

#Configuration
#PropertySource("classpath:service.properties")
public class ApplicationConfig{
#Value("PROPERTY_MYSQL_JNDI_NAME")
private String jndiName;
}
this was working fine. But when I register
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("resources/authentication");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
bean in the ApplicationConfig class previous property loading not working. is there any thing i can do to test it
I was facing the same issue. In my case, I had missed adding the following bean:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
which made the property loading work correctly.
You messageSource is wrong it should be as below assuming your authentication.properties is in src/resources:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("authentication");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
You can still use
#PropertySource("classpath:config.properties")
Assuming config.properties is in src/resources

Spring 4 & Hibernate 5 validator messageSource does not work

I can't get use to work hibernate validation 5.1.0.Final with Spring MVC 4.2.5.RELEASE.
My WebConfig:
#Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
#Bean
#Autowired
public MethodValidationPostProcessor getValidationPostProcessor(LocalValidatorFactoryBean validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Override
public Validator getValidator() {
return validator();
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor l = new LocaleChangeInterceptor();
l.setParamName("lang");
return l;
}
#Bean
public SessionLocaleResolver localeResolver(){
SessionLocaleResolver s = new SessionLocaleResolver();
s.setDefaultLocale(Locale.ENGLISH);
return s;
}
I have an ExceptionHanlder which gets validation messages and push it back as a json:
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
#ResponseBody
public ValidationError handleConstraintViolation(final ConstraintViolationException exception) {
ValidationError v = new ValidationError();
exception.getConstraintViolations().forEach(violation -> {
v.addError(violation.getPropertyPath().toString(), violation.getMessage());
});
logger.warn(exception, exception);
return v;
}
I have 3 files in src/main/resources/i18n/: messages_en_EN.properties, messages_pl_PL.properties, messages.properties.
My model class with validation has one validated parameter:
#Column(name = "VALUE_", nullable = false)
#Email(message = "{Email.contractorContactEmail.value}")
#NotNull(message = "{NotNull.contractorContactEmail.value}")
private String value;
What I see is that hibernate validator look into classpath:ValidationMessages properties not into my spring message source. It may be ok for me but Hibernate does not want to translate those messages - locale is always server default. What am I doing wrong?? How can I fix it?
PS. In controller I use #org.springframework.validation.annotation.Validated.
PS2. I am sure that my messageSource is working correctly because if I add this code into ExceptionHandler it translates perfectly but I know that it is bad practice.
exception.getConstraintViolations().forEach(violation -> {
String messageTemplate = violation.getMessageTemplate();
if (messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
String key = StringUtils.substring(messageTemplate, 1, messageTemplate.length() - 1);
v.addError(violation.getPropertyPath().toString(), messageSource.getMessage(key, null, LocaleContextHolder.getLocale()));
}
});
What I can tell you right away, judging from your configuration, your messages are in the wrong place - you've configured the message source to look at i18/messages but your messages are in the classpath root. So, your message source definition should look like:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
Aside from that, the rest of the configuration looks pretty much okay. I'm guessing that you're using the WebMvcConfigurerAdapter class?
EDIT:
For future reader's reference, the issue was not configuration-related, JPA entity was missing a #Valid annotation on a #OneToMany field so controller-level validation failed to pick it up and Hibernate's JPA validator used default messages.

Spring Hibernate Validator custom MessageSource

This is my first question here, so please be gentle with me :)
I'm using Spring Boot, HIbernate Validator and no XML-config
I'd like to have my Hibernate Validator to read the error messages out of my messages_xx_XX.properties files. To accomplish this I used Java config in the following way, but it doesn't work:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Override
public Validator getValidator() {
return validator();
}
#Bean(name = "validator")
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(messageSource());
return localValidatorFactoryBean;
}
#Bean(name = "messageSource")
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setBasename("classpath:messages/messages");
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
reloadableResourceBundleMessageSource.setCacheSeconds(-1);
return reloadableResourceBundleMessageSource;
}
}
My normal Spring messages are working fine this way, but the output for my ValidatorMessages are in the form of {errorMessage}.
I've tried many solutions on the net, but nothing worked for me.
I hope you guys can help me out.
edit:
The idea to have all messages in one file comes from this post: http://www.silverbaytech.com/2013/04/16/custom-messages-in-spring-validation/
I did everything just like he did, but still no luck.

Overriding default ValidationMessages.properties from hibernate-validation

I'm trying to override the ValidationMessages.properties from default hibernate-validation (5.0.2 version) in a Spring MVC application.
But it keeps using the internal ValidationMessages.properties from jar.
UPDATE: it works (but it should be another way) when I copy the *.properties files to org\hibernate\validator ...
This is my configuration:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.blueliv.search.*"} )
public class WebConfig extends WebMvcConfigurerAdapter {
static Logger log =LoggerFactory.getLogger(WebMvcConfigurerAdapter.class);
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new GsonHttpMessageConverter());
//converters.add(new MappingJackson2HttpMessageConverter());
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
#Bean
public ExceptionHandlerExceptionResolver exceptionResolver() {
return new ExceptionHandlerExceptionResolver();
}
#Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.setValidationMessageSource(getBundle());
return validatorFactoryBean;
}
public ReloadableResourceBundleMessageSource getBundle(){
log.debug("get bundle!");
ReloadableResourceBundleMessageSource bundle = new ReloadableResourceBundleMessageSource();
bundle.setBasenames("classpath:/i18n/validation/ValidatonMessages");
return bundle;
}
#Override
public Validator getValidator() {
return validator();
}

Resources