Users make mistakes. They won’t admit it, but they do. As GUI developers, our task is to make sure that their mistakes don’t crash or cripple our system. Input validation can help a great deal with this. Valkyrie supports validators for various input. These are all subclasses of RichValidator.
Valkyrie has multiple validation strategies:
The built-in rules system
Using a JSR303 Validator
Your own validation framework
A combination of any of the above
Valkyrie has created their own validation framework, which is rule based. A change to a value it put through a number of checks, which are contained within a rule, and results in a success or a validation error.
The FormModelHelper can create formmodel with rule validation by providing a RulesSource in one of its factory methods.
A rule source contains different rules for a certain object type. If, for example, we want to validate that the field1 property of the TestObject class is never empty, we can create a RulesSource like this:
DefaultRulesSource source = new DefaultRulesSource(); Rules rules = new Rules(TestObject.class); Constraints c = new Constraints(); rules.add(c.required("field1")); source.addRules(rules);
Constraints contain the actual logic that checks the values.
The Constraints class contains a lot of predefined constraints. Among these you can find
Maximum length of string
Maximum value
Not null
Minimum value
Creating your own constraint is done by implementing the Constraint interface, which consists of one method
public interface Constraint { boolean test(Object argument); }
This constraint will test any object. Most of the time, we’ll want to split up functionality to check individual properties, so that we can reuse this logic elsewhere.
For this, you’ll need to subclass AbstractPropertyConstraint, which needs a property name. Also, in it’s test method, it provides an easy way to get values of individual properties.
Say we want to create a constraint that checks whether a String property’s value equals “RCP” (silly, but a good example). We’ll end up with something like this:
public class RcpConstraint extends AbstractPropertyConstraint { protected boolean test(final PropertyAccessStrategy domainObjectAccessStrategy) { Object prop = domainObjectAccessStrategy.getPropertyValue(getPropertyName()); return !(prop instanceof String) || ((String) prop).equals("RCP"); } }
This constraint you can then add to your RulesSource for a specific property.
Validation on a property is triggered when that property is changed in its valuemodel. Valkyrie will search for rules for that property and execute them.
Some rules that are registered for a certain property need to be triggered when another property is changed (for example two dates, for which the first needs to be before the last). Valkyrie supports this by overriding the isDependentOn(…) method. Out-of-the-box, this method returns true if the parameters equals the property name for which the rule is defined. However, you can add additional properties to this method. Every change in a property that returns true on this method will cause this rule to be checked.
An example of this type of rule is the RequiredIfTrue constraint. This constraint can accept a property and another PropertyConstraint. For example, you want the driver’s license number required if the age of the person is over 21. Creating a constraint for this would be something like this:
Rules rules = new Rules(Person.class); rules.add(new RequiredIfTrue("driverLicenseNumber", new PropertyValueConstraint("age", new Constraint() { public boolean test(final Object argument) { return ((Integer) argument).intValue() >= 21; } })));
This constraint will be fired if the value of driverLicenseNumber or age is fired. Mind you though, the validation error will only be reported on the ‘main’ property, which is driverLicenseNumber.
Getting the message for a custom rule is done by implementing TypeResolvable in your constraint class. For convenience, there’s already a constraint class with custom message support called TypeResolvableConstraint. Valkyrie will then use that message in its validation results.
When you provide TypeResolvable support in a property constraint, Valkyrie will add the message to the property message (which is also used for the label).
JSR303 Bean Validation is a well-known framework for validating JavaBeans, of which Hibernate Validator is the reference implementation
Simply put, if the object behind a form has JSR303 annotations, by setting the validator to a JSR303Validator, it will validate using those annotations. This validator will check all property-based validations (no @AssertTrue support at the moment yet). You can even turn off validation for certain properties (for example, a code is @NotNull, but in the GUI you’re allowed to leave it empty because you’ll generate one on the fly when that’s the case before sending it to the business layer).
Valkyrie also supports combining different validators. For example, AssertTrue validations are currently not checked when using the JSR303 integration. To be able to check those validations, you can transform these into rules in the Valkyrie’s rules system. By combining the two, you then have the complete validation for your object.
Building a composite validator can be done by the following snippet
JSR303Validator jsrValidator = ... RulesValidator rulesValidator = ... CompositeRichValidator validator = new CompositeRichValidator(new RichValidator[] { jsrValidator, rulesValidator });