I think that the current constraint interface
Code:
public interface Constraint<A extends Annotation> {
/**
* Initialize the constraint validator.
* <p/>
* This method is guaranteed to be called once right after the constraint is retrieved
* from the <code>ConstraintFactory</code> and before the Bean Validation provider
* starts using it.
*
* @param constraintAnnotation The constraint declaration
*/
void initialize(A constraintAnnotation);
/**
* Evaluates the constraint against a value. This method
* must be thread safe.
*
* @param value The object to validate
* @return false if the value is not valid, true otherwise
* @throws IllegalArgumentException The value's type isn't understood
* by the constraint validator
*/
boolean isValid(Object value);
}
sets unacceptable limits to constraint objects :
1. Wasting memory instantiating the constraint class each time it is used
Solved in :
http://forum.hibernate.org/viewtopic.php?p=2394200for non-initializable constraints which need the annotation metadata: ctx.getConstraintDescriptor()
All others can be solved via introducing ValidationContext,ValidationListener and changing the Contraint interface to:
Code:
public interface Constraint {
public void validate(ValidationContext ctx);
}
Code:
public interface ValidationContext<T> {
public Class<T> getBeanClass();
public Object getBean();
public T getRootBean();
public void setBean(Object bean);
public void setBeanClass(Class<T> beanClass);
public String getPropertyName();
public void setPropertyName(String propertyName);
public Object getPropertyValue();
public void setPropertyValue(Object propertyValue);
public Annotation getConstraintDescriptor();
public void setConstraintDescriptor(Annotation constraintDescriptor);
public ValidationListener getValidationListener();
public ValidatorFactory getValidatorFactory();
/**
* Cascade validation to the current property if available.
*/
public void cascade();
/**
* Cascade validation to the specified bean.
*
* @param bean - a new validation root bean
*/
public void cascade(Object bean);
public Stack<String> getPath();
public boolean isValidated(Object bean);
/**
*
* @param bean
* @return false if the object was already validated, true - the object is added
*/
public boolean addValidated(Object bean);
}
Code:
public interface ValidationListener {
/**
* Error notification sent by a Constraint.
*
* @param reason a constant describing the reason. This is normally the key of the
* feature that was violated in the object 'owner' for property 'propertyName'
* @param context - contains
* bean = the object that contains the error (owner)
* propertyName = the Name of the attribute that caused the error
*/
void addError(InvalidConstraint reason, ValidationContext context);
}
2. Type constraints error messages :
http://forum.hibernate.org/viewtopic.php?t=986340I think there is a problem with the error messages a type (class level) constraint can generate.
The field/property constraint usually has a single error message template, but a type constraint can validate more than one thing (multiple sub-constraints), so it will need to generate more then one error message.
With the current API this seems to be impossible. solved via:
ctx.getValidationListener().addError(new InvalidConstraint(...), ValidationContext ctx);
3. Stop validation routine on first validation failure
solved via: throwing exception on the first call of the ctx.getValidationListener().addError(...)
4. Unable to delegate validation to a known validator
solved via: ctx.getValidatorFactory().getValidator(...)
Note that we need this, because each context can use differently configured validator instances!
5. Unable to cascade validation to the current property
solved via: ctx.cascade();
6. Unable to cascade validation to another object graph
solved via: ctx.cascade(bean);
7. Unable to access and manage already validated beans to prevent cycles
solved via: ctx.addValidated(bean)
8. Unable to intercept each indirect bean validation
This can be solved adding method in ValidationListener (or in a separate interface)
Code:
/**
* Called before every constraint.validate(...) invocation.
*
* @param ctx
* @return true - do validate the current property/bean, false - skips the current validation
*/
boolean preValidate(ValidationContext<?> ctx);