With JEE6/JSF2.0, validation can be done by bean validation annotations. these annotations can be put on the JEE6 entity beans OR managed backing beans, as long as they conform to the Java Bean requirement, ie, providing getter and setter methods for instance variables.
Bean validation messages are i18n ready. To internationalize the validation messages, you need to provide the localized messages in a properties file "ValidationMessages.properties" and put it under the applications class path. For web applications, this would be "WEB-INF/classes". This is similar to JSF's i18n solution. But you don't need to configure it in "web.xml" or "faces-config.xml", since it's bean validation framework available to you automatically.
For example, if you want to support both English and Chinese for your bean validation, you can provide the messages in two files and put them under "WEB-INF/classes":
WEB-INF/classes/ValidationMessages.properties
WEB-INF/classes/ValidationMessages_zh.properties
Following is an example of ValidationMessages.properties :
bean.validate.username.required=username is required to login bean.validate.nameFilter.required=name filter is required to search! bean.validate.nameFilter.short=name filter too short, at least 3 chars
And the corresponding message for Chinese support from ValidationMessages_zh.properties :
bean.validate.username.required=\u7528\u6237\u540D\u662F\u5FC5\u987B\u5730\uFF01 bean.validate.nameFilter.required=\u540D\u5B57\u662F\u5FC5\u987B\u5730\uFF0C\u5A9A\u7EB8\uFF01 bean.validate.nameFilter.short=\u540D\u5B57\u592A\u77ED\u4E86\uFF0C\u81F3\u5C11 3 \u4E2A\u5B57\u7B26
Here's an exmple for JEE entity bean:
package com.jxee.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; /** * entity bean to table "test.user" * validation done here as well -- this is excellent! */ @Entity @Table(name="USER", uniqueConstraints = @UniqueConstraint(columnNames = "username")) public class User implements Serializable { @Id @GeneratedValue @Column(name="id") private Integer userid; @Column(name="username") // NotNull validation message are localized and defined in // ValidationMessages.properties/ValidationMessages_zh.properties @NotNull(message="{bean.validate.username.required}") // Size validation message is hard-coded here: @Size(min=4,max=20,message="username must be 4-20 characters") private String username; // other instance variables, getters and setters... }
The following code illustrates how to use it in you bean class (the managed student search bean):
package com.jxee.action.student; import java.io.Serializable; import java.util.List; import java.util.TimeZone; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.Flash; import javax.inject.Inject; // bean validation annotations import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.apache.log4j.Logger; import com.jxee.ejb.student.StudentDAO; import com.jxee.model.student.Student; @ManagedBean(name="ss") @ViewScoped public class StudentSearchUseFlash implements Serializable { private static final Logger log = Logger.getLogger(StudentSearchUseFlash.class); private static final String SEARCH_PAGE = "/student/studentSearch.xhtml"; private List<Student> searchResultList; private @EJB StudentDAO dao; // use bean validation to validate the search filter @NotNull(message="{bean.validate.nameFilter.required}") @Size(min=3, message="{bean.validate.nameFilter.short}") private String nameFilter; private int maxRows = 50; ...... }
To display the localized bean validation message, you use <h:messages/>, <h:message for="inputId"/>, or Primefaces tags <p:messages/> and <p:message for="inputId"/>. This is standard JSF2.0 stuff, as illustrated here:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" template="/template/template1.xhtml"> <ui:define name="title">#{msgs.studentSearch}</ui:define> <ui:define name="content"> <h:form id="searchForm"> <p:panel header="#{msgs.studentSearch}" style="width:60%"> <h:panelGrid columns="4"> <h:outputLabel value="#{msgs.name}: "/> <h:inputText id="nf" value="#{ss.nameFilter}"/> <p:message for="nf"/> <h:commandButton type="submit" value="#{msgs.search}" action="#{ss.findByName}"/> </h:panelGrid> </p:panel> </h:form> ...... </ui:define> </ui:composition>
Here's the screen shot for the student search screen, where it requires the name filter to be at 3 characters long:
We can see that bean validation framework and JSF2 can work together pretty well.
One last thing to mention is how to localize a FacesMessage in my program? That is, how to access the i18n messages in backing beans? Since the i18n messages are handled as resource bundles, you need to reference it by the ResourceBundle class:
FacesContext cntxt = FacesContext.getCurrentInstance(); Locale locale = cntxt.getViewRoot().getLocale(); // load the i18n messages as resource bundle to access // note: the base name must match the file path, ie, "/com/jxee/messages.properties" ResourceBundle messages = ResourceBundle.getBundle("com.jxee.messages", locale); String msg = messages.getString("nameFilterRequired"); log.debug("error message: " + msg); cntxt.addMessage(null, new FacesMessage(msg));
This might be useful when it comes to cross-field validations. For single field vaidation, bean validation is handy and clean, thus much easier to use.