Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
java
java
titleUsage of cross validation
public class Person {
{
    @NotEquals("lastName")
    private String firstName;

    private String lastName;
...
    // Getters and setters omitted for brevity
}

The rest is equivalent to the simple validation (just bind the properties to your input components). Note how we refer to the lastName property by simply using the name of the property.

Available annotations

Simple validation

With the property validation module, there are two ways to define validation rules. The first is to (re)use JPA annotations, see #JPA based validation. The second is to use ExtVal's simple validation annotations, see #Simple validation and #Cross-Validation.

JPA based validation

JPA annotations are used automatically for UI validation as soon as you:

  • Add myfaces-extval-core-.jar and myfaces-extval-property-validation-.jar to the classpath
  • Use JPA annotations within the model (e.g.: @Column(nullable = false) -> the field is required)

That's it!

(A simple demo is available here: demo_000)

Simple validation

Annotation

Description

DoubleRange

Annotation

Description

DoubleRange

delegates to the implementation of javax.faces.validator.DoubleRangeValidator

JoinValidation

to reuse (point to) annotations of a different property (see re-use existing annotations)

Length

delegates to the implementation of javax.faces.validator.LengthValidator

LongRange

delegates to the implementation of javax.faces.validator.LongRangeValidatorDoubleRangeValidator

Pattern

use a regular expression for validation

Required

alternative to the required attribute

SkipValidation

allows to keep validation optional. (the annotations which are afterwards and support this mechanism)

Validator

generic validator to delegate validation to an existing jsf validator e.g.: @Validator(EmailValidator.class)

JPA based validation

JPA annotations are used automatically for UI validation as soon as you:

  • Add myfaces-extval-core-.jar and myfaces-extval-property-validation-.jar to the classpath
  • Use JPA annotations within the model (e.g.: @Column(nullable = false) -> the field is required)

That's it!

...

JoinValidation

to reuse (point to) annotations of a different property (see re-use existing annotations)

Length

delegates to the implementation of javax.faces.validator.LengthValidator

LongRange

delegates to the implementation of javax.faces.validator.LongRangeValidator

Pattern

use a regular expression for validation

Required

alternative to the required attribute

SkipValidation

allows to keep validation optional. (the annotations which are afterwards and support this mechanism)

Validator

generic validator to delegate validation to an existing jsf validator e.g.: @Validator(EmailValidator.class)

Cross-Validation

Annotation

Description

DateIs

validates if a date is equal, before or after a second date

Equals

validates if two values are equal

NotEquals

validates if two values are different

RequiredIf

validates if a value is required depending on a second value (if it is empty or not)

...

ExtVal has no special requirements for annotations. It's the responsibility of the validation strategy to know how to validate the annotation.
So you can use annotations within any layer without introducing an ExtVal dependency below the view layer.
If you would like to validate 3rd party annotations you can provide a mapping. With the same mechanism you can replace existing (ExtVal) validation strategies. Find detailed information below.

...

The simplest case is to create a custom annotation and to use a name convention for the validator (= validation strategy).
The validation strategy has to implement the ValidationStrategy interface. Or you can extend a class which implements this interface in-/directly. (A simple demo is available here: demo_002, demo_006)

...

Code Block
java
java
titleMinimal implementationcustom constraint
//package my.custom.package.CustomConstraint

@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface CustomConstraint
 {
}

Note that this is just a minimalistic annotation definition. There's nothing ExtVal-specific here.

Code Block
java
java
titleMinimal validation strategy implementation

package 
//my.custom.package.CustomConstraintValidationStrategy

public class CustomConstraintValidationStrategy implements ValidationStrategy {
{
    public void validate(FacesContext facesContext, UIComponent uiComponent,
                         MetaDataEntry metaDataEntry, Object convertedObject)
    {

        //custom validation logic
    }
}

    }
}

Note that only one method (validate()) has to be implemented. The naming convention here is that the validation strategy has the same name as the custom annotation, with ValidationStrategy appended. The annotation and the validation strategy must also be in the same package.

That's just the simplest case. You can also use one of the other available name conventions or you can provide a custom convention or a custom name mapper or ...

Tip
titleHint

It's recommended to use subclass AbstractValidatorAdapter or AbstractAnnotationValidationStrategy instead of ValidationStrategy rather than implementing the ValidationStrategy interface yourself.

Info

In ExtVal r3+ validation strategies don't have to be aware of empty/null values. So it's safe to delegate to legacy JSF validators.
If a validation strategy should validate such values, it's possible to annotate the validation strategy with @NullValueAwareValidationStrategy and/or @EmptyValueAwareValidationStrategy.

Cross validation annotations

The class below implements the validation strategy for the @Equals annotation. Note that the class can be relatively simple, since the hard work is done for us in the AbstractCompareStrategy.

Code Block
java
java
titleCustom cross-validation strategies
@SkipValidationSupport
public class EqualsStrategy extends AbstractCompareStrategy {
{
    public boolean useTargetComponentToDisplayErrorMsg(CrossValidationStorageEntry crossValidationStorageEntry)
    {
        return true;
    }

    protected String getValidationErrorMsgKey(Annotation annotation, boolean isTargetComponent)
    {
        return ((Equals) annotation).validationErrorMsgKey();
    }

    public boolean isViolation(Object object1, Object object2, Annotation annotation)
    {
        return object1 != null && !object1.equals(object2);
    }

    public String[] getValidationTargets(Annotation annotation)
    {
        return ((Equals) annotation).value();
    }
}
Tip
titleHint

Be aware that the isViolation() method should return true if the input values are not valid (not equal in this case). Although the name of the method is very clear, you might have expected the opposite.

In case of model aware cross-validation the validation error message is displayed at the source component instead of the target component.
Since the message is still meaningful, there's no need to provide a special reverse message. (This can be changed by returning false in the useTargetComponentToDisplayErrorMsg() method.

Cross validation details

As we've seen before, we can easily reference a property within the same bean by using the property name.

Code Block
java
java
titleReferencing a local property
public class Person
{
    @NotEquals("lastName")
    private String firstName;

    private String lastName;
...
}

We can also refer to properties of related beans, as is shown in the example below:

Code Block
java
java
titleReferencing a property of a local reference

{code:java|title=Referencing a property of a local reference}
public class RegistrationPage
{
    private Person person = new Person();

    @Equals("person.password")
    private String oldPassword;
...
}

It is even possible to use EL-like expressions to refer to properties of beans that will be available at runtime.

Code Block
java
java
titleReferencing a property of a bean
public class RegistrationPage
{
    @Equals("#{person.password}")
    private String oldPassword;
...
}
Note
titleBe Careful

You should realize that by referencing bean properties, you may be introducing a dependency of the View-layer.

Details of the examples

Within the page you just have to bind a property which has a cross-validation constraint.
If both, the source property as well as the referenced target, are bound to input components of the same form, the converted objects are used for validation. In case of a validation error, the validation error message(s) is/are displayed.

...

If the target of the cross-validation isn't bound to an input component of the same form, the model value of the target is used for validation. The property which triggers the cross-validation always "uses" the converted object (not the model value).

That means:

  • Normal cross-validation: converted object (constraint source) + converted object (constraint target)
  • Model aware cross-validation: converted object (constraint source) + model object (constraint target)

Reverse validation error messages

...