Validation explained

Stuts2 uses XWork's validation framework internally. 

It allows you to validate Models and Actions using a set of specialised (field)validators, grouped together in an xml file named YourModel-validation.xml or ActionName-validation (and, in the case of the alias, ActionName-alias-validation.xml).

Since we only want to validate the crud action, we create a file EmployeeAction-crud-validation.xml and place it in our classpath (mostly next to our compiled Action class).


<validators>
  <field name="employee.firstName">
     <field-validator type="requiredstring">
        <message key="errors.required.firstname"/>
      </field-validator>
  </field>
  <field name="employee.lastName">
     <field-validator type="requiredstring">
        <message key="errors.required.lastname"/>
      </field-validator>
  </field>
  <field name="employee.age">
     <field-validator type="required" short-circuit="true">
        <message key="errors.required.age"/>
      </field-validator>
      <field-validator type="int">
        <param name="min">18</param>
        <param name="max">65</param>
        <message key="errors.required.age.limit"/>
      </field-validator>
  </field>
</validators>
A very important reminder: validation is once again done by an interceptor, so it should be in your stack. Even more important, validators 'query your Action/Model' and NOT your request! Keep this in mind at all times.

With that out of the way, let's analyze this snippet: there are two types of validators: field validators and general validators. We start by analyzing the field employee.firstName - so this means: we will analyze the result from the invocation of getEmployee().getFirstName() on our Action, not the request parameter named employee.firstName!

Field validators will not validate input fields - they are named field validators because they will automatically mark the input field in your form with the validation error, whereas normal validators would create actionErrors.

First we apply a requiredstring validator to the getEmployee().getFirstName() return value. There are quite a few validators, ranging from requiredstring, required, intrange, emailaddress, url, .. etc. Writing your own validator is not hard, and it can be reused easily. Here we use the requiredstring, which does 2 checks:

  • check the availabilty of the string (!= null)
  • check the length of the string to be > 0
Why the stringlength greater than 0? HTML forms will submit an empty string (not a null!) for an empty text input, so simply testing for null would fail if we required at least one character to be submitted.
The employee.lastName validation is exactly the same as the firstName. The only difference is in the validation of the age property.

We use in fact 2 validators: a 'required' one, which will make sure that our age is in fact set, and not null, and another one that will check the range to make sure our employee is between 18 and 65 years old. If course, if the first validator fails, we shouldn't continue processing, so that's why we can specify the short-circuit attribute. If a validator short-cicuits the validation, validation is failed and skips the current (field) validator.

The "int" validator takes 2 optional parameters: min and max, who can be set by providing two simple param tags (most items in S2 can be configured that way). This way, we ensure validation to be loosely coupled with our code and do not require re-compilation.

Of course, you should also be able to i18n'ize your error messages, so that's why we providing a <message key="my_key"/> - if you prefer otherwise, you can always add a hardcoded text as a child element of the message tags.

It does not stop there. The text you pass, be it hardcoded or a looked-up value, can in fact contain OGNL expressions as well !

Take a look at the value we get back from the 'errors.required.age.limit'-key:

errors.required.age.limit=Please provide an age between ${min} and ${max}.
And guess what: min and max are indeed the two parameters we just set before ! Using OGNL expressions you can retrieve whatever you want from your validator/action/context, giving you really nice error messages (we like nice error messages). 

In fact, you can not only use these expressions in your error messages, but you can even set the min and max parameters dynamically. Different types of employees could have different age requirements - OGNL and polymorphy to the rescue !