In my previous blog entry I wrote about adding security to the shopping cart web application. Part of the change I made to the application was adding a form to register users and a form to add and update items. It made sense to add validation to the fields so that we could maintain the integrity of our data.
I currently maintain two different versions of the application:
- ShoppingCartSecurity.zip – A standard web application that runs off Tomcat 6 and is built with Hibernate, Spring, JPA and RichFaces. This is an Eclipse archive file that contains two Eclipse projects.
- ShoppingCartGF.zip – A JEE application that runs off GlassFish vs 3 and is built with EJB 3.0, JPA and RichFaces.
There are a number of different approaches one can take when doing validation. A common approach is using client side form validation which uses JavaScript to validate the form fields. There is no round trip to the server so the validation is nice and fast. The jQuery Javascript library has a nice plugin to use if you are interested in this approach. Even though client side validation is nice it isn't essential however server side validation is not an option and should be implemented to ensure data integrity. For the shopping cart application I decided to only use server side validation so that I was using a common validation implementation. In order to get the same user experience as client side validation I chose to use the Ajax JSF RichFaces library.
The validation framework I have chosen to use is the standard validation framework (JSR 303: Bean Validation) implemented in Java EE 6. As of version 4.x Hibernate Validator is based on a new code base which is the reference implementation for JSR 303.
"JSR 303 defines a metadata model and API for JavaBean validation. The default metadata source is annotations, with the ability to override and extend the meta-data through the use of XML validation descriptors. The API is not tied to a specific application tier or programming model. It is specifically not tied to either the web tier or the persistence tier, and is available for both server-side application programming, as well as rich client Swing application developers."
What we will cover
- Defining constraints
- Custom validation
- Spring injection into custom validators
- Validating constraints
- RichFaces Ajax validation for JSF inputs
Constraints
Constraints in Bean Validation are expressed via Java annotations. There are three different types of constraint annotations, namely: field, property, and class-level annotations. An example of property annotations can be seen in the User class:
@Column(name="username", nullable=false, unique=true) @NotEmpty @Length(min=4, max=12) @Pattern(regexp="^[a-zA-Z\\d_]{4,12}$", message="{validator.username.invalid}") @Username(message="{validator.username}") public String getUsername() { return username; }
The getter method for the username property specifies the following validation annotations:
- @NotEmpty – The username cannot be null or empty.
- @Length(min=4, max=12) – The length of the username must be at least 4 characters long and no more than 12 characters long.
- @Pattern(regexp="^[a-zA-Z\\d_]{4,12}$", message="{validator.username.invalid}") – Match a regular expression. The username must contain alphanumeric characters. The default message is overridden with a key mapped to a message in the validation resource file.
- @Username(message="{validator.username}") – Custom validator to compares the usernames already stored in the database to check for duplicates.
An example of where I have used class-level annotation validation can be found in the Register.java JSF backing bean class:
@FieldMatch.List({ @FieldMatch(first="password", second="confirmedPassword", message="{validator.fieldmatch.password}") }) public class Register extends BasePage { private Validator validator; private String password; private String confirmedPassword; @NotEmpty @Length(min=6, max=12) @Pattern(regexp = "(?=.*\\d)(?=.*[a-zA-Z]).{6,12}", message = "{validator.password.insecure}") public String getPassword() { return password; } public void validateUsername() { Set<ConstraintViolation<User>> constraintViolations = validator.validateProperty(getUser(), "username"); if (constraintViolations.size() > 0) { String msg = constraintViolations.iterator().next().getMessage(); addError(getUiUsername().getClientId(getFacesContext()), msg); } } public void validatePassword() { Set<ConstraintViolation<Register>> constraintViolations = validator.validateProperty(this, "password"); if (constraintViolations.size() > 0) { String msg = constraintViolations.iterator().next().getMessage(); addError(getUiPassword().getClientId(getFacesContext()), msg); } } public void validateConfirmedPassword() { Set<ConstraintViolation<Register>> constraintViolations = validator.validate(this); if (constraintViolations.size() > 0) { ConstraintViolation<Register> cv = constraintViolations.iterator().next(); if (cv.getMessageTemplate().equals("{validator.fieldmatch.password}")) { addError(getUiConfirmedPassword().getClientId(getFacesContext()), cv.getMessage()); } } } ... }
- @FieldMatch – Custom cross field validation class-level annotation used to compare the values of two fields, namely password and confirmed password.
- @NotEmpty – The password cannot be null or empty.
- @Length(min=6, max=12) – The length of the password must be at least 6 characters long and no more than 12 characters long.
- @Pattern(regexp = "(?=.*\\d)(?=.*[a-zA-Z]).{6,12}", message="{validator.password.insecure}") – Match a regular expression. The password must contain alphanumeric characters. The default message is overridden with a key mapped to a message in the validation resource file.
Custom Validators
As mentioned above I have two custom validators, namely Username and FieldMatch. Creating a custom validator is actually very simple and requires the following three steps:
- Create a constraint annotation
- Implement a validator
- Define a default error message
I won’t go over this in too much detail as the Hibernate Validator documentation is pretty good and covers this nicely.
Username constraint annotation (Username.java)
@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy=UsernameValidator.class) public @interface Username { String message() default "Username already exists"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
Username validator (UsernameValidator.java)
public class UsernameValidator implements ConstraintValidator<Username, Object> { @Autowired private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void initialize(Username parameters) { } public boolean isValid(final Object value, ConstraintValidatorContext context) { try { User user = userDao.findByUsername((String) value); if (user == null) { return true; } } catch (Exception e) { e.printStackTrace(); } return false; } }
Spring injection
You will notice I am injecting the UserDao class into this validator using Springs @Autowired annotation. This is made possible thanks to Spring’s LocalValidatorFactoryBean class.
"By default, the LocalValidatorFactoryBean configures a SpringConstraintValidatorFactory that uses Spring to create ConstraintValidator instances. This allows your custom ConstraintValidators to benefit from dependency injection like any other Spring bean."
I only needed to add the following line of code to the shoppingcart-database.xml Spring bean configuration file in order to use the SpringConstraintValidatorFactory:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
The Register.java JSF backing bean mentioned earlier gets a reference to the Spring created Validator by declaring the managed property in the faces-config.xml:
<managed-bean> <managed-bean-name>pc_Register</managed-bean-name> <managed-bean-class>com.mydomain.shoppingcart.view.Register</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>shoppingViewHelper</property-name> <value>#{shoppingViewHelper}</value> </managed-property> <managed-property> <property-name>validator</property-name> <value>#{validator}</value> </managed-property> </managed-bean>
ValidationMessages.properties
I created a ValidationMessages.properties file and saved it in my src folder of my project. Hibernate Validator uses this file to retrieve the messages used by the validators.
Tying it together with the user interface
The login-form.jspx page includes a form for registering users. Instead of explaining each input field I am only going to step through one of them (username) and explain the validation process for it:
<h:inputText id="usrnameInTxt" binding="#{pc_Register.uiUsername}" value="#{pc_Register.user.username}" label="usrnameOutTxt" tabindex="1"> <a4j:support id="usrnameSpt" event="onblur" reRender="usrnameMsg" limitToList="true" action="#{pc_Register.validateUsername}" /> </h:inputText> <rich:message id="usrnameMsg" for="usrnameInTxt" errorClass="error" />
The a4j:support tag is a RichFaces tag that provides Ajax support to JSF input fields. Here it defines that the validateUsername method in the Register backing bean will be invoked when the onblur event of the input text field is triggered.
public void validateUsername() { Set<ConstraintViolation<User>> constraintViolations = validator.validateProperty(getUser(), "username"); if (constraintViolations.size() > 0) { String msg = constraintViolations.iterator().next().getMessage(); addError(getUiUsername().getClientId(getFacesContext()), msg); } }
As you can see from the above code the validator class in the validateUsername method invokes the validateProperty method. The single property username is validated and if any constraint violations are raised an error message is added to the FacesContext.
The RichFaces rich:message tag behaves the same way as the standard JSF h:message tag except it will auto re-render itself after an Ajax request.
RichFaces Ajax Validator
RichFaces also has an Ajax validator tag that ties nicely into Hibernate Validator. I use this tag in the Items Admin modal panel of the items.jspx page.
<h:inputText id="nameInTxt" value="#{pc_Items.selectedItem.name}" label="nameOutTxt" tabindex="1"> <rich:ajaxValidator event="onblur" /> </h:inputText> <rich:message id="nameMsg" for="nameInTxt" errorClass="error" />
As you can see there are no a4j:support tags or validation methods that get called. RichFaces will create its own Validator instance and call the validate property methods automatically for you. This works pretty nicely in the above example however for the registration form we are not using this tag because the validator is created for us by the Spring LocalValidatorFactoryBean class.
Registration screen shot
Registration screen shot
3 comments:
testing comment
Hi,
Thanks for such a good article. Need clarification in case if i keep the properties file in resource package i'm getting validation message as a key.. '{validator.password.insecure}'.. instead of 'password is not secure enough'.how to inform the validator that the properties files is in Resource package.Look forward for your kind response...Thanks Lokesh K.
Hi, sorry for not getting back sooner I have been away from my computer for a couple of days on holiday. According to the Hibernate docs it seems the ValidationMessages.properties file has to be in the root of the classpath.
Post a Comment