Last Updates | Login      

Using the Validatable interface

You can set the ValidatorFilter() as a global filter so that all your actions implementing the Validatable interface will be validated. Here is how you set this global filter inside your application manager.

Below is an example of how to validate your action using the Validatable interface. Note that you are not using annotation or xml. You are using programmatic configuration to setup the validation of your form fields.

To each form field is applied a set of validation rules. For example, for the field "age" we are applying two rules:

RequiredRule: enforces that a field is present, in other words, not blank or null
IntegerRule: enforces that a field is an Integer between a min and a max value

Mentawai validation rules are as flexible as Java code because they are Java code, not XML or Annotation. If you check the javadoc of the IntegerRule class you will find out that it has three different constructors:

IntegerRule(): meaning any integer will be accepted, even negative integers
IntegerRule(int min): meaning any integer >= min will be accepted
IntegerRule(int min, int max): meaning any integer >= min and <= max will be accepted

Mentawai comes with many read-to-use rules in the package org.mentawai.rule like StringRule, NumberRule, EmailRule, FileRule, etc. Plus it comes with a very handy rule called RegexRule which allows you to validate any field based on a regular expression. You will not be surprised to find out that many other Mentawai rules (like the EmailRule) inherit from RegexRule to do its job. As we will see later in this chapter, it is very, very easy to create your own rules for validation as well in case the provided ones do not suit your needs.

If a validation rule fails, a error message associated with it is displayed to the user. Because these messages are usually internationalized (translated into many languages) and more strongly because you don't want to mix content with source code, they should reside in a text file we call i18n file. (Soon you will see how to bypass this good practice and place your error messages together with your code if you prefer to)

The default location for your validation error messages are /validation/actionamehere_localehere.i18n. However if you prefer to keep all your messages in a single file (that we call master) you can invoke the following method inside your application manager:

If you do this, all your messages, not just for validation but all internationalized messages in your application will be stored in the file /i18n/master_localehere.i18n.

So for example if your are NOT using the master file, your action is MyAction (like our example above) and you have two languages pt and en, you would have the following files: /validation/MyAction_pt.i18n and /validation/MyAction_en.i18n.

But if you are using the master file, then all your messages from all your actions would be stored in the files: /i18n/master_pt.i18n and /i18n/master_en.i18n.

Some people will prefer to store all text messages in a single file what makes things easier when it comes the time to translate everything to a new language. Other people will prefer to have a separate file for each action. It is up to you what approach to take.

An example of the validation messages for the action above is listed below:

Note how we are using the tokens %min% and %max% to place some validation information inside our error messages. You should already be familiar with the i18n file syntax because it is the same of the Java Properties class.

Now to display these messages inside a JSP page, all you have to do is make use of some very useful mentawai tags. See our example below:

Note that this tags are conditional tags, in other words, nothing inside the tag will be shown if there is no error for the field. If you want to display all field errors in the top of your page instead of near each field in your form, you can use another menta tag, the mtw:outFieldErrors tag.

To conclude, an important tip that we will see later in the book: Use the Mentawai HTML form tags mtw:input, mtw:select, mtw:checkboxes, etc. so you don't have to worry about re-displaying the values that the user typed in case of validation error. We know you don't want to place a bunch of scriptlets in your JSPs to do this.

Placing messages in the code

Although not a good practice, you are free to place your validation error messages inside your source code instead of using an i18n file. If you application is a simple one that does not plan to use internationalization you may prefer this approach.

If Mentawai does not find the i18n file or the key inside the i18n file, it will just display the key as the error message. So just place your error message as the key and you are ready to go. See the example below:

Note that we are just using the error message as the key. Mentawai executes the following logic to find the error message.

Look for the i18n file. If you don't find the i18n file, use the key as the error message.
If you found the i18n file, look for the key. If you don't find the key, use the key as the error message.
If you found the key, print the associated text as the error message.

As you can see, it is very easy to place your error messages inside your source code instead of using an i18n file.

Validation is dynamic

It is important to realize that the validation code inside the prepareValidator() method is executed for each request. Therefore you can dynamically perform your validation based on the action parameters and characteristics. Check the example below:

In the example above, only a POST request will be validated. A GET will not have any validation. You can also perform validation based on the inner action, which is very common as each inner action will have its own validation. Check below:

In the code above, only the "addUser" innerAction will be validated. All others, including the default (no inner action) execute() method will not be validated. Recall that innerAction == null means the execute() method was called.

Because of the dynamic characteristic of the validation, you should avoid creating new rule objects on each request. That's why we have been using the getInstance() method instead of the new constructor for our rules. The getInstance() method returns the same instance every time, avoiding the creation of new objects on every request. Despite the fact that the Java garbage collector is very powerful nowadays, you should try to avoid creating too many Java objects unnecessarily.

Most of the Mentawai built-in rules already provide the getInstance() method. If possible, and as a good practice, you should provide this method with any new rule you code. We will see how to create new validation rules in a bit.

Validation through a separate filter

Instead of using the Validatable interface, which has to be implemented by the action, you can code a separate filter that will be applied to the action in the application manager. You may choose that approach if you want your action to be clean from any validation code or if you want to apply the same validation to different actions that may depend on the same form data.

To code your validation filter, you should inherit from org.mentawai.filter.ValidationFilter and implement the abstract method prepareValidator(). Check the example below:

The method prepareValidator() is very similar to the same method of the Validatable interface, the only difference being that it also passes as a parameter the action object that the filter is being applied to. The rest is the same. You can use an i18n file or you can place the error messages in the filter code, pretty much like you can do when using the Validatable interface. One thing to note is that if you use an i18n file, the name of the file will be /validation/filtername_localehere.i18n. So for the filter above it would be /validation/HelloValidator_pt.i18n, /validation/HelloValidator_en.i18n, etc.

Then you can apply the filter to one or more actions inside the application manager. Below is an example:

Creating your own rules

A rule validates whether an action input value is valid or not to be passed along to the action. To learn about rules let's code a simple NegativeRule class that validates only negative numbers.

When you extend BasicRule you should implement two methods: check() and getTokens().

check(String value): In this method you receive the value you are suppose to validate and just do it, however you want to, returning true or false.

getTokens(): In this method you return a map with tokens and their values to be used by error messages. The default implementation in BasicRule is to return null, so you should only implement this method if you have tokens to return.

A token is used inside error messages so that it can be substituted for values associated with the rule. For example, in our simple rule above, the negative number has no minimum boundary. Let's change it to include a possible minimum:

Note that now we have the possibility to validate the input value based on a minimum number and we are returning the token "min" with whatever value is given to that minimum number. Therefore you may have an error message like the one below:

The %min% token is substituted with the value returned in the getTokens() method. The IntegerRule that comes with Mentawai also returns tokens for its min and max values. The tokens of a rule are nothing more than its properties that you may want to display along with the error messages.

There are other types of validation that are not solely based in the value itself. For example you may want to perform validation based on the user locale and you may want to perform validation of one field based on another field.

The most common example of a validation that depends on the locale is date validation. Depending on the user locale the date 12/30/2008 may be valid or not. Below is the source code of a simple rule to validate dates. Note that we are now extending LocaleRule instead of BasicRule.

Note that now the check() method receives not just the value but also the locale of the user performing the request. You should use the locale in any way you want to perform the validation. Mentawai already comes with a complete DateRule so you should not need to code a rule for dates. This was just an example for you to understand how the LocaleRule works in case you want to write your own.

The last form of common validation is when you need to compare two fields to determine whether one field is valid or not. If you thought about password and password confirmation you were right. For that we can extend the CrossRule class. Check the source below for a simple EqualRule that validates whether two fields have the same value:

When inheriting from CrossRule you should implement two abstract methods:

getFieldsToValidate() should return an array of the field names that will be compared.

check(String[] values) receives the values of the fields indicated by the getFieldsToValidate() method, so that you can perform any comparison you want to validate them.

Note that Mentawai gets the field values for you from the action input and pass them along with the check method so that you can compare them and return the validation result. Mentawai already comes with a complete EqualRule in the org.mentawai.rule package.

Another very useful built-in rule that you can use for your advantage is the RegexRule. Check below how a SocialSecurityNumberRule could be implemented by inheriting from RegexRule:

You could have also used the RegexRule with the Social Security Number pattern without the need to create a new rule for it. See below:

As you can see, by inheriting from BasicRule, LocaleRule, CrossRule and RegexRule you can perform any kind of validation for your action input values.

Returning the same rule instance

Because validation is dynamic, in other words, the validation code is executed per each request, you should avoid creating new rule objects inside your validation code. Check the code below:

The code above is perfectly correct and legal, but we are creating too many new objects on each request without a real need for it. A rule is thread-safe by design and you should re-use the same instance across different requests. A possible fix for this problem is illustrated in the code below:

However note that it is not very easy to read the validation code when the rules are defined outside the prepareValidator() method. A better approach is to make the rule constructor private and create a static getInstance() method that will return instances of the rule and it will be smart enough to return the same instance when the same object is requested. Check below how we would accomplish this with the NegativeRule introduced in the previous chapter.

Check how we are using a cache to return the same instance for each "min" parameter passed to the getInstance() method. This will avoid creating unnecessary duplicates of the same object.

Most of the provided rules inside the org.mentawai.rule package implement the getInstance() method. So when using them you should make use of this method to obtain instances of the rule. Check the example below:

When coding your own rules, you should provide a getInstance() method whenever possible. But this is not really a necessity. It is just a good practice that it is worth mentioning for the competent programmer.