I have to collect extra information from my Constraint<A ext Ann> implementations.
Why?
Because, I want to deal with complex objects and be able to report validation parameters using those objects.
Code:
class Measurement {
...
Context context; // duration, distance, so on
Unit unit;
double value;
}
Now, suppose, I have a validation annotation that wants to validate a Measurement for a particular Measurement context.
Provided that ContextKeys and UnitKeys are enums.
Code:
@ContextRange(context = ContextKeys.DISTANCE, unit = UnitKeys.MILE, min = 1 , max = 100)
Measurement measurement;
Moving on to the constraint implementation. This is where I translate the annotation and do some useful work.
Code:
class ContextRangeConstraint<ContextRange> impl Constraint<ContextRange
Measurement min;
Measurement max;
public void initialize(ContextRange ctxRange) { // translate into the min and max }
public boolean isValid(Object o) {
... // check the range using Measurement convenience methods
... // o.lessThan(min) and o.greaterThan(max)
}
}
My messages are personalized based on user preferences. In other words, my business constraints might be
specified in 'miles' but that's not very useful to the end user who's from Germany. I need to personalize
my measurement objects at the presentation tier (web services). That's the only place, I will actually know
who that user is and the adequate locale. Hence, I want to pass along with my ValidationException not just what
failed the validation (InvalidConstraint<T> which has its own problems, since Throwable can't be extended by anything
generic), but also the reasons for failure such as the constraints of a particular validation instance.
Code:
@ContextRange(context = ContextKeys.DISTANCE, unit = UnitKeys.MILE, min = 1 , max = 100)
Measurement measurement;
But I'm a user who enters his measurements in kilometers. Hence, I shouldn't get miles in error messages.
Quote:
"Distance measurement range exceeded. Should be between 1.6 and 160.0 km."
Never-mind the silly rounding.
Since, I need to form the final message away from the validation framework, in the presentation tier. I need the
converted Measurement min, max. So that my Personalizer can convert and display them properly.
Clearly, I need some kind of hook back to the ContextRangeConstraint that contains those objects.
Currently, I use the property name from InvalidConstraint to go back and look through all the Constraint implementations
inside which I declare what should be considered a part of the ValidationContext using a custom @MesssageParameter
annotation and reflection.
Code:
class ContextRangeConstraint<ContextRange> impl Constraint<ContextRange
@MessageParameter(key = "min")
Measurement min;
@MessageParameter(key = "max")
Measurement max;
// but these keys get fully qualified "org.p7k.example.ContextRangeConstraint.min"
}
In the end I have a Map<String,Object> with which I can do work now.
If I have a message that has "{org.p7k.example.ContextRangeConstraint.min}" I can replace
that placeholder with a personalized Measurement.
------------
This works but is very cumbersome (the degree of this post factum ValidationContext collection cumbersomeness depends on
the implementation). For example I can't do something like:
Code:
@ContextRanges({@ContextRange(context = ContextKeys.DISTANCE, ...),
@ContextRange(context = ContextKeys.DISTANCE, ...)})
Since, my Map<String, Object> will contain the 'min' and 'max' for the last ContextRange checked not the one that actually
failed.
However, I feel that the spec creates some fundamental limitations.
InvalidConstraint despite its name only describes the property that failed. The reason for failure is only conveyed
through the message. I would like to see a hook to get the Constraint implementation from which I could ask for more
contextual information (similar to how StatdardConstraintDescriptor works).
Perhaps, the internal reporting using a boolean instead of some validation report:
Code:
public ValidationReport validate(Object o){}
instead of
Code:
public boolean isValid(Object o){}
could help.
Please, let me know your thoughts and ask follow-up questions.
BTW, I've been working with the agimatec implementation (
Thank you!).