JSF Seam Validation + Custom Messages + Annotations + Internationalization

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:

  1. Validating required fields without using annotations
  2. Validating manually and displaying custom messages
  3. 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:

  1. The injected user variable in LoginAction so Seam knows to validate it
  2. The User objects properties to specify what to validate.
    1. 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:

      • @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

      That’s all there is to it.

13 Comments

  1. bhavani
    Posted July 16, 2008 at 4:26 pm | Permalink

    Thank you for nice article.

  2. luis
    Posted August 13, 2008 at 8:44 pm | Permalink

    excelent!!
    it’s very usefull

  3. Posted August 19, 2008 at 4:35 am | Permalink

    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/

  4. Posted September 16, 2008 at 3:07 am | Permalink

    thank you.

  5. Sefai
    Posted September 21, 2008 at 2:40 am | Permalink

    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?

  6. Posted September 26, 2008 at 10:14 am | Permalink

    Im agree.

  7. Rajkumar
    Posted September 28, 2008 at 10:47 pm | Permalink

    Good article. Thanks

  8. Posted November 27, 2008 at 12:46 pm | Permalink

    can you talk more about internationalization?Or can you direct me to some god tutorials?I’m a beginer.
    Thanks!

  9. Posted November 27, 2008 at 6:28 pm | Permalink

    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.

  10. ozzy
    Posted February 23, 2009 at 2:35 pm | Permalink

    Very good article. Concise, right to the point. This is exactly what I was looking for. Thank you.

  11. Chandan Khandelwal
    Posted March 9, 2009 at 6:41 am | Permalink

    Thanks for such a nice and useful article

  12. Anjaya Pillai
    Posted May 24, 2009 at 12:51 pm | Permalink

    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)

  13. Posted May 27, 2009 at 3:02 pm | Permalink

    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

  1. [...] From Depressed Programmer. [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*