In my Spring Boot app I have a backing bean where I am using JSR303 validation. In the annotation, I have specified the message code:
#NotBlank(message = "{firstname.isnull}")
private String firstname;
Then in my message.properties I have specified:
firstname.isnull = Firstname cannot be empty or blank
My JavaConfig for the messageSource is:
#Bean(name = "messageSource")
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
The validation works correctly but instead of seeing the actual string, I get the message code in my jsp page. In looking at the log file, I see an array of codes:
Field error in object 'newAccount' on field 'firstname': rejected value []; codes [NotBlank.newAccount.firstname,NotBlank.firstname,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newAccount.firstname,firstname]; arguments []; default message [firstname]]; default message [{firstname.isnull}]
If I change the my message code in the message.properties to one of the codes in the array, the string displays correctly in my web form. I didn't even have to change the code in the annotation. This indicates to me the code in the message parameter of the annotation is getting ignored.
I don't want to use the default code. I want to use my own. How can I make this work. Can you please provide a code example.
JSR303 interpolation normally works with ValidationMessages.properties file. However you can configure Spring to change that if you want (I was lazy to do so :)) e.g.
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
<mvc:annotation-driven validator="validator" />
According to JSR-303 specification message parameters are expected to be stored in ValidationMessages.properties files. But you can override the place where they are looked for.
So you have 2 options:
Move your messages to the ValidationMessages.properties file
Or override getValidator() method of your WebMvcConfigurerAdapter's descendant (JavaConfig in your case):
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
Related
Firstly, I am newbie in Vaadin 7. I was just trying out some vaadin demo when I discovered the BeanFieldGroup.class. As I saw ,this class bound a field to a bean property. In the bean the properties are annotated with validation constraints annotations ( JSR 303 ). In this case my pom.xml include hibernate validator dependency:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.CR1</version>
</dependency>
I made ValidationMessage.properties file and put there some messages, ( with Hungarian characters ) and it has been saved UTF-8 format,for example :
validation.NotBlank=Árvíztűrő tükörfúrgép
And here is the annotated bean property:
#NotNull(message = "{validation.NotBlank}")
private String name = null;
I can see, when I change the field value to null, Vaadin show me my previous constraint error message, but with wrong character coding.
In other hand - without Vaadin, but Spring,I can use the next simple formula where the character coding is appropriate me ( as you can see I use Spring ReloadableResourceBundleMessageSource) :
private MessageSource MessageSource(){
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setCacheSeconds(5000);
reloadableResourceBundleMessageSource.setFallbackToSystemLocale(false);
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
Properties properties = new Properties();
properties.setProperty("fileEncodings", "UTF-8");
reloadableResourceBundleMessageSource.setFileEncodings(properties);
reloadableResourceBundleMessageSource.setBasename("classpath:/locales/messages");
return reloadableResourceBundleMessageSource;
}
#Bean
public LocalValidatorFactoryBean validator(){
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setValidationMessageSource(MessageSource());
return factory;
}
My question is: Can Somebody tell me the right solution how do I configure hibernate validator or Vaadin to read property files with UTF-8 encoding ?
By default, ISO-8859-1 encoding is used when reading properties files. Characters not available in that encoding must be converted using Unicode escapes ("\u1234"), e.g. using the native2ascii tool. See this answer to a related question for some more details.
Alternatively, you could plug in a custom ResourceBundleLocator which reads properties using UTF-8 encoding. The Hibernate Validator reference guide describes how to do this. As a starting point, you could take PlatformResourceBundleLocator and adapt it as per your requirements.
Vaadin's BeanFieldGroup uses another Vaadin class BeanValidator for JSR-303 validation. If you take a look at it's source code at BeanValidator.getJavaxBeanValidator() and getJavaxBeanValidatorFactory() methods, wou will see that it's building it's own Validator and ValidatorFactory instances. You'll need to subclass it and override the getJavaxBeanValidatorFactory() method with one that uses a ValidatorFactory obtained from your Spring context, which is properly set up to use Spring's own UTF-8 encoded MessageSource as you already configured it in your example. It will also help you with debugging as Spring's MessageSource can be reloadable, which is a huge saver.
EDIT: following a question in comments on how to override with subclassing I realized that with the current state of this part of the Vaadin API as of version 7.5.9, I can't do it elegantly. Since we can't do it elegantly we could do it the quick and dirty way by reflection with this hack implemented as a spring component:
#Component
public class VaadinValidationHack implements InitializingBean {
#Autowired
private ValidatorFactory validatorFactory;
protected void hack() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = BeanValidator.class.getDeclaredField("factory");
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(null, validatorFactory);
}
#Override
public void afterPropertiesSet() throws Exception {
hack();
}
}
I have this message source bean. It works well for getting messages, e.g. from org.springframework.validation.Validator.
#Bean(name = "messageSource")
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setCacheSeconds(-1);
messageSource.setFallbackToSystemLocale(false);
messageSource.setDefaultEncoding("UTF-8");
messageSource.setBasename("classpath:/locale/messages");
return messageSource;
}
and I would like to use this bean for processing JSR 349 validation messages for such POJO class:
public class AuthorizationRequest {
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("response_type")
private String responseType;
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("client_id")
private String clientId;
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("redirect_uri")
private String redirectUri;
private String scope;
// the rest omitted
}
But error messages from are still (localized) original Hibernate, such {org.hibernate.validator.constraints.NotEmpty.message}. But I would like to use my own error messages. I have tried many options but none of them work.
I would like to remain one message properties file for whole application.
Question
Is there some way how to tell Spring to use my messageSource bean?
Are you sure this is not working(commented lines)? Placing your messages in property file (resources/locale/message.properties) should work...
you need follow this format in Resource Bundle file.
ErrorType.className.fieldName = message.
example:
public class Call{
#Pattern(regexp = "^(http://|https://)?(www.)?([a-zA-Z0-9]+).[a-zA-Z0-9]*.[a-z]{3}.?([a-z]+)?$")
private String site;
}
and just define message in Resource Bundle like this
Pattern.call.site = site address is wrong.
Story
I have a select control that represents user access level. I'm looking for a way to internationalize it. The label should be loaded from a message resource and the value should be used as is. I prepare all my drop down lists in controllers using a simple SelectOption class that has a label and a value properties. This way, my select's look consistent accross all jsp's.
Problem
I've found some examples but they are based on logic within jsp. Developer loops through his labels and manually constructs the option tag using a message resource. While this works, there just has to be a better way. I've also found some comments that Spring 3 will have support for internationalizing option labels but I can't find anything concrete on that.
Controller logic
Collection<SelectOption> optionList = new ArrayList<SelectOption>();
optionList.add(new SelectOption("-SELECT-", "-"));
optionList.add(new SelectOption("Administrator", "ADMIN"));
optionList.add(new SelectOption("Editor", "EDIT"));
bean.setFilterUserAccessLevelOptionList(optionList);
JSP logic
<form:select path="filterUserAccessLevel" items="${bean.filterUserAccessLevelOptionList}" itemLabel="label" itemValue="value"/>
Questions
I would like to add options in my controller in this way: optionList.add(new SelectOption("userAccessLevelAdministratorLabel", "ADMIN")); and have Spring convert userAccessLevelAdministratorLabel to a value from a message resource. Is this possible?
If Spring 3 cannot do this for me, how else can this be achieved without manually constructing the option tag within jsp?
=== 2012-01-15 ==============================================================
Still trying to work out a solution using aweigold's idea.
Controller
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
SelectOptionListBuilder listBuilder;
#RequestMapping("/userIndex/{pageNumber}")
public ModelAndView getUserList(#PathVariable Integer pageNumber, #ModelAttribute("userIndexBean") UserIndexBean phantomBean, Locale locale, Model model) {
UserIndexBean bean = new UserIndexBean();
// prepare filter form
Collection<SelectOption> optionList = listBuilder.getUserAccessLevelOptionList(true, SortOrder.NONE, locale);
bean.setFilterUserAccessLevelOptionList(optionList);
SelectOptionListBuilderImpl
#Component
public class SelectOptionListBuilderImpl implements SelectOptionListBuilder, MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#Override
public List<SelectOption> getUserAccessLevelOptionList(boolean addSelectPrompt, SortOrder sortOrder, Locale locale) {
List<SelectOption> optionList = new ArrayList<SelectOption>();
if(addSelectPrompt) {
optionList.add(new SelectOption(messageSource.getMessage("common.selectPromptLabel", null, locale), "-"));
}
messageSource mapping
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/i18n/messages" />
<property name="defaultEncoding" value="UTF-8"/>
<property name="UseCodeAsDefaultMessage" value="true"/>
</bean>
Exception
org.springframework.context.NoSuchMessageException: No message found under code 'common.selectPromptLabel' for locale 'en_CA'
When I need to do operations like this in a Controller outside of a jsp, I've been making my Controllers MessageSourceAware. Spring will then inject a new MessageSource when they are swapped, and you can interrogate it much like Spring does. In your example, you would do something like this:
#Controller
public class someController implements MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping
// Pass in the locale from the LocaleResolver
public void someMapping(Locale locale){
optionList.add(new SelectOption(
messageSource.getMessage("userAccessLevelAdministratorLabel", null, locale),
"ADMIN"))
}
}
Have a look at a spring roo project. They managed this kind of problem by creating tagx tags. This tags do what you already descibed (it contains a litte logic to load the messages from ressources and build the option tags). But because the logic is witten once and you can use this tags like normal tags in you jspx files, it feels like a tag that do what you want to have.
I have an object.
public class MyObject
{
....
#Column(name = "a_number") #NotNull #NumberFormat(style = Style.NUMBER) #Min(1)
private Integer aNumber;
...
//getters and setters
}
In my controller I have #Valid annotation on my object being posted. I do have validation working on all my other fields in the class (their all Strings) except this number. If I enter a number from my form it works fine and if I violate the #Min(1) it also gives me the correct validation error. My problem however is that if you enter a string instead of a number it throw a NumberFormatException.
I've seen many examples of Integer and validation but no one accounts for if you enter a string into the form being posted. Do I need to do the validation else where? Javascript? I would like a solution that falls in line with the rest of spring validation so I could use this in other classes. I would just like an error stating it must be numeric. Also I tried using the #Pattern annotation but apparently thats just for strings.
Suggestions?
You can add the following to your file which controls your error messages (these are the generic ones it looks for in the case of a type mismatch:
typeMismatch.commandObjectName.aNumber=You have entered an invalid number for ...
typeMismatch.aNumber=You have entered an invalid number for ...
typeMismatch.java.lang.Integer=You have input a non-numeric value into a field expecting a number...
typeMismatch=You have entered incorrect data on this page. Please fix (Catches all not found)
For those who did not get the idea right here is what to do in spring 4.2.0.
Create a file name messages.properties in WEB-INF > classes folder. And put the above type mismatch messages in that file.
In spring configuration or servlet.xml file create the following bean.
<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="messages"></beans:property>
</beans:bean>
And for your model attribute like private Integer aNumber; in the question along with other validation rules this rule is also applied for type mismatch conversion. You will get your desired message in this.
<form:errors path="aNumber"></form:errors>
Hope it helps others.
Still relevant, so I'll add the programmatical approach of message source bean definition:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
I'm trying to do some spring validation with the error messages in properties files.
But the examples I find all seem to have the values hardcoded, or gotten from a properties file but using a validator class and retrieving it there.
My setup is a bit different.
I'm using the #Valid annotation in my requestmapping, and my #Valid class uses #NotNull etc.
I've seen some examples where people do #NotNull(message = "blablabla");
But that's also hardcoded, and I'd like to put the messages in a properties file so I can easily edit it on the fly and so I can easily implement i18n in the future.
Any input on how to achieve this would be appreciated.
It works exactly the same way as with explicit Validator - you declare a MessageSource and write error messages in .properties files. Messages codes are formed as constraintName.modelAttributeName.propertyName:
publib class Foo {
#NotNull private String name;
...
}
.
#RequestMapping
public String submitFoo(#Valid Foo foo, ...) { ... }
messages.properties:
NotNull.foo.name=...
MessageSource declaration:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value = "messages" />
</bean>