Validation is one of the first things you test out when evaluating a web framework and its often one of the most time consuming to wrap your head around. In this post I’ll talk about three types of validation mechanisms present in JBoss’ Seam which will get you on your way towards validating web forms without too much pain:
I’m going to cover three cases:
- Validating required fields without using annotations
- Validating manually and displaying custom messages
- Validating fields using Hibernate annotations in Seam beans
We’ll also display messages using a simple resource bundle (java.util.ResourceBundle) which will allow us to internationalize our forms and messages. Let’s dive into the examples:
Validating required fields without using annotations
This is the most general case, you want to force the user to input something in your text fields and if they don’t, you want to display a message telling them just that.
Here’s a JSF form which asks for a username and password:
<h:messages/>
<h:form id="LoginForm">
<table>
<tr>
<td><h:outputText value="#{messages['login.username']}"/></td>
<td>
<h:inputText required="true" value="#{user.username}" label="#{messages['login.username']}"/>
</td>
</tr>
<tr>
<td><h:outputText value="#{messages['login.password']}"/></td>
<td>
<h:inputSecret required="true" value="#{user.password}" label="#{messages['login.password']}"/>
</td>
</tr>
<tr>
<td></td>
<td>
<h:commandButton action="#{loginAction.login}"
value="#{messages['button.login']}"/>
</td>
</tr>
</table>
</h:form>
Specifying required=”true” is all you need to validate required fields. The matter of displaying messages is left to the <h:messages/> tag which iterates through the list of messages generated by the validation phase and displays them sequentially.
By default validation messages are displayed in a format that is not very intuitive, in order to change the format you must override a property in your messages.properties file located at the root of your classpath:
javax.faces.component.UIInput.REQUIRED={0} is a required field.
The placeholder is {0} is automatically filled by the label attribute of the <h:inputText> tag. So in the above case if login.username is defined to be “Username” in the resource bundle, the messages that will be printed will be: Username is a required field.
If validation passes, then the login() method on the bean referenced by loginAction will be called (as specified by the <h:commandButton> tag.
Validating manually and displaying custom messages
This is probably the most powerful way of validating user input – programmatically. Let’s face it, the built-in validators can only do so much, at some point you end up writing java code to validate input by checking it against a database or queue etc. Going back to the login example, say we need to check the values supplied by the user against a database and if they match, let them into the system and if they don’t, kick them back to the login page with a message. The login page does not change, let’s get to the LoginAction class:
@Name( "loginAction" )
public class LoginAction {
@In
private User user;
@In(create=true)
private FacesMessages facesMessages;
public String login() {
if (user.getUsername().equals( "Arsenalist" ) && user.getPassword().equals( "Raptors" )) {
return "success";
} else {
facesMessages.addFromResourceBundle("login.failed");
return "failure";
}
}
}
It’s very obvious what’s happening here. The injected instance of FacesMessages allows you to use its addFromResourceBundle() method to specify a message that will be displayed in the view resulting from returning “failure”. Just for completeness sakes, here’s the faces-config.xml navigation rule:
<navigation-rule>
<from-view-id>/pages/login.xhtml</from-view-id>
<navigation-case>
<from-action>#{loginAction.login}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/pages/home.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{loginAction.login}</from-action>
<from-outcome>failure</from-outcome>
<to-view-id>/pages/login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Validating fields using Hibernate annotations in Seam beans
The next step is to apply more complex validation without resorting to writing Java code and for that Seam relies on Hibernate’s validation framework. As an aside, this dependency is probably the most annoying thing about Seam at this point but it works well enough to look past.
Let’s say we need to force the value of username to be atleast 5 characters. In order to do this we need to annotate two things:
- The injected user variable in
LoginActionso Seam knows to validate it - The
Userobjects properties to specify what to validate. - @Length(min=, max=) Checks whether the string length matches the range
- @Max(value=) Checks that the value is less than or equal to the max
- @Min(value=) Checks that the value is greater than or equal to the min
- @NotNull Checks that the value is not null
- @Past For a date object, checks that the date is in the past
- @Future For a date object, checks that the date is in the future
- @Pattern(regex=”regexp”, flag=) For a string, checks that the string matches this pattern
- @Range(min=, max=) Checks whether the value is between the min and the max
- @Size(min=, max=) For collections, checks that the size is between the two
- @AssertFalse Asserts that the evaluation of the method is false
- @AssertTrue Asserts that the evaluation of the method is true
- @Valid For a collection or a map, checks that all the objects they contain are valid
- @Email Checks whether the string conforms to the email address specification
Here’s the relevant piece in LoginAction.java:
@Name( "loginAction" )
public class LoginAction {
@In @Valid
private User user;
public String login() {
. . . .
}
Here’s the relevant piece in User.java:
@Name( "user" )
public class User {
private String username;
private String password;
@Length(min=5, message= "#{messages['login.username.length']}" )
public String getUsername() {
return username;
}
. . . .
Notice how we’re using a resource key inside an annotation to print the message. This will look for a resource bundle called messages and inside it for the login.username.length property to retrieve the message. You could alternately just write the message in plain old English inside the annotation but why would you want to when you can internationalize.
The last thing we need to do is tell the JSF page that it will need validation (I think this is a somewhat redundant step since the bean is already annotated but nonetheless it is required). You must wrap the fields of the login form around the <s:validateAll> tag like so:
<h:messages/>
<h:form id="LoginForm">
<s:validateAll>
. . . .
<h:outputText value="#{messages['login.username']}"/></td>
. . . .
</s:validateAll>
</h:form>
Note: Be sure to specify required=”true” for the fields you want to apply annotation validations to. Otherwise, you’ll see some very weird results such as only non-blank fields getting the annotation validations applied to while the blank fields get considered valid.
Here’s a list of annotations that you can use to validate java beans:
That’s all there is to it.
13 Comments
Thank you for nice article.
excelent!!
it’s very usefull
If you owned a gun, do you think you would kill have somebody from a quick reaction yet?
http://www.chirpthis.com/2008/08/17/poll-if-you-own-a-gun-would-you-kill/
thank you.
Thanks..
“The placeholder is {0} is automatically filled by the label attribute of the tag…”
why the label,if I add {1},{2},… how will they be filled?
Im agree.
Good article. Thanks
can you talk more about internationalization?Or can you direct me to some god tutorials?I’m a beginer.
Thanks!
Masini, you can use resource keys which reference .properties files in your HTML controls, e.g:
#{messages['login.username']}
This is already in the example.
Very good article. Concise, right to the point. This is exactly what I was looking for. Thank you.
Thanks for such a nice and useful article
Dear Sir,
I am trying to use validator of JSF from managed bean with Jboss Seam , it is throwing error that method not found exception
Method not found: bean.MYbean@2e3cb1.dcno(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
Dear Sir,
How can I use a method validation, for example If I wanna to check the user enter name with database, and then let it throw an error if name mismatch , how…
One Trackback/Pingback
[...] From Depressed Programmer. [...]