Chapter 4. Validation

This chapter explains validation.

4.1. After population

The first pass in the population process is the conversion of request parameters etc. to Java objects and populating the provided form bean with those objects. If this is successful, you know that inputs for integers, dates, etc. conform to a valid form (e.g. 'foo' is not a valid integer, but '12' is).

Next, you might want to check if, for instance, a provided parameter not only is a valid integer, but is also greater than say 10. Or that a parameter not only is a valid date, but that this date also is after today. If we talk about validation, we mean this kind of checks.

Validation can be performed on two levels: field and form level. Field level validation is related to one input field (e.g. a request parameter) at a time. Form level validation is executed regardles the provided parameters.

Furthermore, whether a validation action is executed can be made dependend of a certain state (like the value a parameter) using nl.openedge.baritus.validation.ValidationActivationRules.

4.2. An example

Let us start with an example that displays some usages of the validation mechanism of Baritus. In the following example, we want to validate that not only myInteger is a valid integer, but also that the integer allways has a value that is greater than ten.

public class FooForm
{
	private Integer myInteger;
	
	public Integer getMyInteger() { return myInteger; }
	public void setMyInteger(Integer i) { myInteger = i; }
}

The validator:

public class BarValidator extends AbstractFieldValidator
{
	public boolean isValid(
		ControllerContext cctx, 
		FormBeanContext formBeanContext, 
		String fieldName, 
		Object value)
	{
		// note that type conversion is allready done by the population process
		Integer val = (Integer)value; 
		// the value can still be null though
		boolean valid = (val != null && val.intValue() > 10); 
		
		if(!valid)
		{
			String errorMessage = "input " + value + 
				" is not valid; please provide a number larger than 10";
			formBeanContext.setError(fieldName, errorMessage);
		}
		
		return valid;
	}

}

Now, register it with your control:

public class MyCtrl extends FormBeanCtrl
{
	...
	
	public void init(Element controllerNode) throws ConfigException
	{
		addValidator("myInteger", new BarValidator());
		...
	}
	
	...
}

And a crude example of how you can use it with a velocity template:

<input type="text" name="myInteger"
	#if( $model.errors.get('myInteger') ) class="fielderror" #else class="field" #end
	value="$!{model.displayProperty('myInteger')}">

<br>
#if( $model.errors.get('myInteger')  )  
	<span class="error"> ${model.errors.get('myInteger') } </span>
#end

4.3. Mapped and indexed properties

When using mapped or indexed properties, you have two options of registering field validators, on target name and/ or on flat name. Let's illustratie this by example:

public class FormWithMap
{
	private Map foo = new HashMap();
	
	public Integer getfoo() { return foo; }
	public void setfoo(Map m) { foo = m; }
}

Not only can we register validators with the whole target name, including the keys or indexes like this:

...
	addValidator("foo['bar']", myValidator);
	addValidator("foo['someOtherKey']", myOtherValidator);
...

But in case the validator should be executed for all keys (or indexes), the 'flat name' (name without markup for map/ index navigation) as well like:

...
	addValidator("foo", myValidatorForAllKeys);
...

4.4. ValidationActivationRules

Whether validators are actually used a given situation can be directed through the use of ValidationActivationRules (nl.openedge.baritus.validation.ValidationActivationRule).

For example:

public class MyValidationRule implements ValidationActivationRule
{
	public boolean allowValidation(
		ControllerContext cctx,
		FormBeanContext formBeanContext)
	{
		return "true".equals(cctx.getRequest().getParameter("validate"));
	}
}
FieldValidator validator = new RequiredFieldValidator();
validator.setValidationRule(new MyValidationRule());
addValidator("myField", validator);

In the above example the validator will only be called when request parameter 'validate' is provided and has value 'true'.

You can register only one validation activation rule with a validator at a time. It is not difficult though, to stack validator activation rules, for example by using nl.openedge.baritus.validation.impl.NestedValidationActivationRule.

4.5. Escaping validation

By default, if population failed and/ or if you registered validators that failed, the 'peform' method of your controller will not be called. Instead, the errors and override fields are saved, and the error page is shown.

Earlier, you have seen that the actual firing of Validators can be made conditional by registering ValidatorActivationRules with your validators. This works well for higher level validation that is dependent of e.g. the value of one of the other request parameters.

There are two ways of escaping the default mechanism of not performing the controller command method. The first way is to skip population and validation all together. In order to achieve this, you can set property 'populateAndValidate' of the ExecutionParams to false. The second way is to set property 'doPerformIfPopulationFailed' to true (false by default). In this case, population and validation is performed as usually, but now the perform method is allways executed, regardless of the population/ validation outcome. This option should be used with care.

4.6. Validator implementations

Package nl.openedge.baritus.validation.impl is reserved for implementations of Validators.

  • MaximumFieldLengthValidator. This validator checks on maximum length. If the type of the value is a String, the string length is checked. If the type of the value is a Number, the actual number is used. E.g. if property maxLength is 4, "hello" will fail, but "hi" will pass, and number 5 will fail, but 2 will pass.

  • MinimumFieldLengthValidator. This validator checks on minimum length. If the type of the value is a String, the string length is checked. If the type of the value is a Number, the actual number is used. E.g. if property maxLength is 4, "hello" will pass, but "hi" will fail, and number 5 will pass, but 2 will fail.

  • PropertyNotNullFormValidator. Checks whether the form contains a non null property with the name of property propertyName.

  • RegexValidator. Tests for a match against a regex pattern. if property 'mode' is MODE_VALID_IF_MATCHES (which is the default), isValid returns true if the input matches the pattern. If property mode is MODE_INVALID_IF_MATCHES (i.e. else), isValid returns false if the input matches the pattern.

  • RequiredFieldValidator. Checks for a non-EMPTY input. Use this for fields that should have a not null (empty string) input. Note that as this validator is a field validator, and thus is registered for a single field, it is only fired if a field (e.g. a request parameter) is actually provided. In other words: if an instance of a required field validator was registered for field name 'myprop', but 'myprop' is not part of the request parameters, this validator never fires. Hence, if you want to be REALLY sure that a property of the form is not null, use a form validator (PropertyNotNullValidator). RequiredFieldValidator works fine for most cases where you have a HTML form with a field that should have a non empty value, but that - if a user fools around - does not seriousely break anything when a value is not provided (e.g. you probably have not null constraint in you database as well).

  • RequiredSessionAttributeValidator. Checks whether a session attribute exists with the key that was set for property sessionAttributeKey.