spring validation with #valid where/how custom error messages - spring

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>

Related

How do you create a subclass of PropertySourcesPropertyResolver in spring 4?

I'm trying to figure out a way to store certain properties in an encrypted form while they are at rest, and have them transparently decrypted before the property is injected into any beans, whether they are using #Value or they are defined in xml by setting properties. We're not using spring-boot yet. The property file would look like this:
db_password={aes}some_encrypted_value
I can see in the logs where the PropertySourcesPropertyResolver gets the value for my property. It should be pretty simple to create my own implementation of the PropertySourcesPropertyResolver.getProperty method that looks for the "{aes}" prefix, decrypting it if necessary, but I can't figure out how to use my subclass in my application.
Does anyone have any idea how I can get spring to use my implementation instead of Springs?
I initially tried to use the PropertySourcesPlaceholderConfigurer that worked for me in Spring 3, but I couldn't figure out how to make it work in spring 4. I also couldn't get the newer PropertySourcesPlaceholderConfigurer to work either.
Can anyone point me in the right direction?
We did it as follows with Spring 4.0.3 RELEASE
public class MyPropertyConfigurer extends PropertyPlaceHolderConfigurer{
protected void convertProperties(Properties props){
Enumeration<?> propertyNames = props.propertyNames();
while(propertyNames.hasMoreElements()){
String propName = (String)propertyNames.nextElement();
String propValue = (String)props.getProperty(propName);
if(propName.indexOf("db_password") != -1){
setPropertyValue(props,propName,propValue);
}
}
}
private void setPropertyValue(Properties props,String propName,String propValue){
String decryptedValue = PasswordUtility.decrypt(propValue);
props.setProperty(propName,decryptedValue);
}
}
In xml, it was configured as below
<bean id="dbPropertyPlaceholder" class="mypackage.MyPropertyConfigurer">
<property name="locations">
<list>
<value>file:myProp.properties</value>
<list>
</property>
</bean>

Spring Boot JSR303 message code in annotation getting ignored

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;
}

Spring: How do I set the pattern of a #DateTimeFormat from a properties file?

I'm using Spring 3.1.1.RELEASE. I have a model that I submit to one of my controllers. In it, is the following field
#DateTimeFormat(pattern = "#{appProps['class.date.format']}")
private java.util.Date startDate;
However, the above doesn't work (the EL isn't being interpreted), in as far as every time I submit my form, I get an error. If I use the following
#DateTimeFormat(pattern="yyyy-MM-dd")
private java.util.Date startDate;
everything works fine. But ideally, I'd like to drive the pattern from a properties file. Is this possible and if so, what's the correct syntax?
Dave
Right now it seems only to work with Property Placeholders.
Have a look at this:
https://jira.springsource.org/browse/SPR-8654
I would use PropertySourcesPlaceholderConfigurer to read my system properties. Then you can use this syntax to resolve placeholders : ${prop.name}.
Your annotated filed should work like this then:
#DateTimeFormat(pattern = "${class.date.format}")
private java.util.Date startDate;
To configure your PropertySourcesPlaceholderConfigurer for your application in xml, try this:
<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
<property name="location">
<list>
<value>classpath:myProps.properties</value>
</list>
</property>
<property name="ignoreUnresolveablePlaceholders" value="true"/>
</bean>
Or, with JavaConfig:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
//note the static method! important!!
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] { new ClassPathResource("myProps.properties")};
configurer.setLocations(resources);
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

Reading a "properties" file

I have a Rest service using Resteasy (running on top of Appengine), with the following psuedo code:
#Path("/service")
public class MyService {
#GET
#Path("/start")
public Response startService() {
// Need to read properties file here.
// like: servletContext.getResourceAsStream("/WEB-INF/config.properties")
}
}
However its obvious that the servlet context cannot be accessed here.
And code like:
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("/WEB-INF/config.properties");
Can't be executed within the Appengine environment.
EDIT:
I have tried doing it with Spring like:
appContext.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="/WEB-INF/auth.properties"/>
</bean>
Then, put this on the actual class fields:
#Path("/service")
public MyService{
#Autowired
#Value("${myservice.userid}")
private String username;
#Autowired
#Value("${myservice.passwd}")
private String password;
// Code omitted
}
However, part of the code of the MyService complains because the username and password was not "injected", I mean its empty although its on the auth.properties file
In RESTEasy you can easily inject Servlet context via #Context annotation: http://docs.jboss.org/resteasy/docs/2.3.1.GA/userguide/html_single/index.html#_Context
Examples can be found here: Rest easy and init params - how to access?
This should work if you put the file in /WEB-INF/classes/ (which, importantly, is on the classpath), specifying config.properties as a file at the top-level.
this.getClass().getClassLoader().getResourceAsStream("/config.properties");
See this similar question: How to load properties file in Google App Engine?
Edit: Now you've edited, I'll respond & answer the Spring-related question. So, put the auth.properties into /WEB-INF/classes/ , and then specify classpath as follows.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:auth.properties"/>
</bean>

spring mvc annotation validation integer

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;
}

Resources