To make this even better I will combine this with Apache MyFaces Extensions Validator. This framework can be used with JSF ( there is a generic library and a special one for Trinidad ) and its supports the Bean Validation. ExtVal also has some extra features.
- Type-safe group validation
- Model validation
- Severity aware validation
- Client-side validation
- Sorted violation messages
- Dependency injection support for constraint validators
- Mapped constraint source (e.g. for using DTO's with BV)
- Support of @Valid
In this blog I won't give you all the possible validations options you can have in the Bean Validation or in ExtVal framework but I will try to give you a jumpstart, how to setup this up and get everything working.
Before I start I got this working with WebLogic 12c and use OEPE 12c as my IDE. For the JSF part I use Apache MyFaces Trinidad 2.0.0 which support JSF 2.0.
Let's start with the JPA part.
Here I have created a department entity which is based on the department table of the HR demo schema in the Oracle database.
On the departmentName attribute I added some validation annotations like NotNull , Size and Pattern.
On the Pattern annotation I added a custom resource bundle message ( use {} ), I only do it for pattern annotation because Size or NotNull will have it's own default message in JSF.
Bean validation is the default now so we would see the Eclipselink error anymore. So when we try to persist an entity which violates these annotations we get an error like this.
With a javax.validation.ConstraintViolationException on a prePersist callback event. This error won't give you a lot of information about the error.
You got the option to disable the Bean Validation in Eclipselink by setting the validation-mode to NONE. This way you will get the constraint error.
Like this.
So how we can retrieve these validations errors. For example I can do this on the client side before I invoke the EJB Session bean or do it inside the Session Facade methods.
We need to use the resource annotation to retrieve the Validator.
(@Resource Validator validator; )
In the department persist we can pass on the department entity to the validatior.
Set<ConstraintViolation<Department>> violations = validator.validate(department);
And loop through the violations with this as result.
error size: 2
invalid value for: 'departmentName': Name must between 2 and 30 characters
invalid value for: 'departmentName': {departmentNameValidation}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.amis.mode.hr.services; | |
import java.util.List; | |
import java.util.Set; | |
import javax.annotation.Resource; | |
import javax.ejb.Stateless; | |
import javax.persistence.EntityManager; | |
import javax.persistence.PersistenceContext; | |
import javax.persistence.Query; | |
import javax.validation.ConstraintViolation; | |
import javax.validation.Validator; | |
import nl.amis.model.hr.entities.Department; | |
import nl.amis.model.hr.entities.Employee; | |
/** | |
* @generated DT_ID=HrSessionBean | |
*/ | |
@Stateless(name = "HrSessionBean", mappedName = "HrEar-HrJPA-HrSessionBean") | |
public class HrSessionBean implements HrSession, HrSessionLocal | |
{ | |
@Resource | |
Validator validator; | |
@PersistenceContext(unitName="HrJPA") | |
private EntityManager em; | |
public HrSessionBean() { | |
} | |
public Department persistDepartment(Department department) { | |
Set<ConstraintViolation<Department>> violations = validator.validate(department); | |
System.out.println("error size: "+violations.size()); | |
for (ConstraintViolation<Department> violation : violations) { | |
String propertyPath = violation.getPropertyPath().toString(); | |
String message = violation.getMessage(); | |
System.out.println("invalid value for: '" + propertyPath + "': " + message); | |
} | |
em.persist(department); | |
return department; | |
} | |
} |
Now we can go the JSF part.
Here I do the same but then from a managed bean which is called from a commandButton.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private Validator validator; | |
public void addDepartment(ActionEvent event) { | |
if (validator == null) { | |
validator = Validation.buildDefaultValidatorFactory().getValidator(); | |
} | |
Department dept = new Department(); | |
dept.setDepartmentName("1234567890123456789012345678901234567890"); | |
Set<ConstraintViolation<Department>> violations = validator.validate(dept); | |
for (ConstraintViolation<Department> violation : violations) { | |
System.out.println("error size: "+violations.size()); | |
String propertyPath = violation.getPropertyPath().toString(); | |
String message = violation.getMessage(); | |
FacesMessage message2 = | |
new FacesMessage(FacesMessage.SEVERITY_ERROR, | |
"Invalid value for: '" + propertyPath + "': " + message, | |
"Validation Error "); | |
FacesContext context = FacesContext.getCurrentInstance(); | |
context.addMessage(event.getComponent().getClientId(), message2); | |
} | |
if ( violations.size() > 0 ) { | |
return; | |
} | |
getSessionFacade().persistDepartment(dept); | |
} | |
I changed the violations to Faces Messages and skip the persist part.
This is all standard Bean Validation stuff so let's check out the ExtVal part.
I added the following libraries to the lib folder of the WEB-INF
The jsr303-tck and the validation-api jars are from Hibernate Validator and the rest is from MyFaces ExtVal. Where I remove the myfaces-extval-generic-support-2.0.5.jar because I could use the trinidad one.
To test the validation I made a simple JSF Trinidad page where you can see there are no validator or convertors defined.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="iso-8859-1"?> | |
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0" | |
xmlns:f="http://java.sun.com/jsf/core" | |
xmlns:h="http://java.sun.com/jsf/html" | |
xmlns:trh="http://myfaces.apache.org/trinidad/html" | |
xmlns:tr="http://myfaces.apache.org/trinidad"> | |
<jsp:directive.page contentType="text/html;charset=utf-8"/> | |
<f:view> | |
<tr:document title="Bean and ExtVal validation"> | |
<tr:form> | |
<tr:messages/> | |
<tr:panelGroupLayout layout="vertical"> | |
<f:facet name="separator"> | |
<tr:separator/> | |
</f:facet> | |
<tr:inputText label="Test for bean validation (departmentName)" | |
value="#{dataBean.departmentName}" autoSubmit="true"/> | |
<tr:inputText label="Test for extval validation (departmentLocation)" | |
value="#{dataBean.departmentLocation}" autoSubmit="true"/> | |
<tr:commandButton text="Test JPA Validator" | |
actionListener="#{dataBean.addDepartment}"/> | |
<tr:table allDetailsEnabled="true" var="row" | |
rowBandingInterval="2" value="#{dataBean.allDepartments}" | |
rows="10" | |
summary="Department JPA validation"> | |
<f:facet name="actions"> | |
<tr:outputText value="(Actions)"/> | |
</f:facet> | |
<tr:column headerText="Id"> | |
<tr:outputText value="#{row.departmentId}"/> | |
</tr:column> | |
<tr:column headerText="Names"> | |
<tr:inputText value="#{row.departmentName}" autoSubmit="true"/> | |
</tr:column> | |
<f:facet name="detailStamp"> | |
<tr:panelGroupLayout layout="vertical"> | |
<tr:outputText value="Id: #{row.departmentId}"/> | |
<tr:outputText value="Name: #{row.departmentName}"/> | |
</tr:panelGroupLayout> | |
</f:facet> | |
</tr:table> | |
</tr:panelGroupLayout> | |
</tr:form> | |
</tr:document> | |
</f:view> | |
</jsp:root> |
In the Trinidad table I show all the departments and change for example the department Name to 1 char which violates the Size annotation.
When I use invalid characters for the department Name then I get the resource bundle message.
To make this resourcebundle work I need to set a context parameter in the web.xml which points to our own resourcebundle.
<context-param>
<param-name>org.apache.myfaces.extensions.validator.CUSTOM_MESSAGE_BUNDLE</param-name>
<param-value>resources.application</param-value>
</context-param>
The last part is to show you the ExtVal part. I used this managed bean which are used in the JSF page.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.amis.web.beans; | |
import java.util.List; | |
import java.util.Set; | |
import javax.ejb.EJB; | |
import javax.faces.application.FacesMessage; | |
import javax.faces.bean.ManagedBean; | |
import javax.faces.bean.ViewScoped; | |
import javax.faces.context.FacesContext; | |
import javax.faces.event.ActionEvent; | |
import javax.validation.ConstraintViolation; | |
import javax.validation.Validation; | |
import javax.validation.Validator; | |
import javax.validation.constraints.Null; | |
import javax.validation.constraints.Size; | |
import org.apache.myfaces.extensions.validator.baseval.annotation.Length; | |
import org.apache.myfaces.extensions.validator.baseval.annotation.Pattern; | |
import nl.amis.mode.hr.services.HrSessionLocal; | |
import nl.amis.model.hr.entities.Department; | |
@ManagedBean(name = "dataBean") | |
@ViewScoped | |
public class DataBean { | |
private static final long serialVersionUID = -6996992412087723373L; | |
@Null | |
@Size(min=2,max=10) | |
private String departmentName; | |
@Length(minimum=2 , maximum=50) | |
@Pattern(value="[A-Za-z ]*" , validationErrorMsgKey="locationValidation") | |
private String departmentLocation; | |
} |
departmentName use the Bean Validation framework and departmentLocation use the ExtVal framework.
With this as result.
When you want to know more about ExtVal or Bean Validation you definitely should read this example chapter of Bart Kummel's Book about MyFaces Development.
Here is the example project on github. https://github.com/biemond/OEPE_examples/tree/master/beanValidation
greate post! do I have to use hibernate inoreder to use it?
ReplyDeletewhere can I download this example from??
great post! do I have to use hibernate with this example? where can i download this example from?? thank's!
ReplyDeletegreat post! do I have to use hibernate for it? where can I downloadit from? thank's!
ReplyDeletegreat post! do I have to use hibernate for it? where can I downloadit from? thank's!
ReplyDeleteHi,
ReplyDeletethe hibernate is the reference implementation for the bean validation.
I think apache extval can work without.
thanks