Zest™
Introduction
Tutorials
Javadoc
Samples
Core
Libraries
Extensions
Tools
Glossary 

Create a Constraint

Constraints are defined in Constraint.

If you want to reproduce what’s explained in this tutorial, remember to depend on the Core Bootstrap artifact:

Table 9. Artifact

Group IDArtifact IDVersion

org.qi4j.core

org.qi4j.core.bootstrap

2.1


At runtime you will need the Core Runtime artifact too. See the Depend on Zest™ in your build tutorial for details.

Method Constraint

Method Constraints are declared with annotations on the method argument. The annotation itself is custom, and it is possible to make your own.

public interface Dialer
{
    void callPhoneNumber(@PhoneNumber String phoneNo);

}

In the code above we say that we want the argument to the callPhoneNumber() method to be a valid phone number. This annotation is not built-in, so we need to declare it.

@ConstraintDeclaration
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.METHOD } )
public @interface PhoneNumber
{
}

We then need to provide the Constraint implementation.

public class PhoneNumberConstraint
        implements Constraint<PhoneNumber, String>
{
    public boolean isValid( PhoneNumber annotation, String number )
    {
        boolean validPhoneNumber = true; // check phone number format...
        return validPhoneNumber;  // return true if valid phone number.
    }
}

We also need to include the Constraint on the Composites we want to have them present.

@Constraints( PhoneNumberConstraint.class )
public interface DialerComposite extends ServiceComposite, Dialer
{
}

If a Constraint is violated, then a ConstraintViolationException is thrown. The Exception contains ALL violations found in the method invocation. Concerns can be used to catch and report these violations.

public class ParameterViolationConcern extends ConcernOf<InvocationHandler>
    implements InvocationHandler
{
    public Object invoke( Object proxy, Method method, Object[] args )
        throws Throwable
    {
        try
        {
            return next.invoke( proxy, method, args );
        }
        catch( ConstraintViolationException e )
        {
            for( ConstraintViolation violation : e.constraintViolations() )
            {
                String name = violation.name();
                Object value = violation.value();
                Annotation constraint = violation.constraint();
                report( name, value, constraint );
            }
            throw new IllegalArgumentException("Invalid argument(s)", e);
        }
    }

      [...snip...]

    private void report( String name, Object value, Annotation constraint )
    {
    }
}

Property Constraint

Property Constraints are declared on the Property method.

public interface HasPhoneNumber
{
    @PhoneNumber
    Property<String> phoneNumber();
}

In this case, the Constraint associated with the phoneNumber() method, will be called before the set() method on that Property is called. If there is a constraint violation, the Exception thrown will be part of the caller, and not the composite containing the Property, so a reporting constraint on the containing Composite will not see it. If you want the containing Composite to handle the Constraint Violation, then you need to add a Concern on the Property itself, which can be done like this;

public abstract class PhoneNumberParameterViolationConcern extends ConcernOf<HasPhoneNumber>
    implements HasPhoneNumber
{
    @Concerns( CheckViolation.class )
    public abstract Property<String> phoneNumber();

    private abstract class CheckViolation extends ConcernOf<Property<String>>
        implements Property<String>
    {
        public void set( String number )
        {
            try
            {
                next.set( number );
            }
            catch( ConstraintViolationException e )
            {
                Collection<ConstraintViolation> violations = e.constraintViolations();
                report( violations );
            }
        }

          [...snip...]

        private void report( Collection<ConstraintViolation> violations )
        {
        }
    }
}