I'm successfully using Hibernate Validator for validating request POJOs. The annotations on getters are somewhat self-documenting, so someone can peruse the generated javadoc to see what the constraints are.
I feel this isn't good enough for effective documentation. I'm attempting to write a Javadoc custom Taglet that introspects the current containing class and builds an HTML table of constraints on properties in the class.
I've actually been able to get the ConstraintDescriptor for each constrained property. What I want to be able to do is interpolate the "messageTemplate" to produce the readable string.
For instance, if a property has a @Size annotation, I'm able to find the information about this ConstraintDescriptor, and the "messageTemplate" property has a value of "{javax.validation.constraints.Size.message}". What I need to get is the message like "must be between X and Y" (or something like that). It would be great if that string had the actual "min" and "max" values as specified, but if it said "must be between {min} and {max} ..." I would be fine with that.
The only thing that seems to come close is the "messageInterpolator" property of the ValidatorFactory, and the "interpolate()" method of that. The problem is, that method requires a "Context" object, in addition to the message template. This is certainly understandable, as it would have to get parameters like "min" and "max" from something like that, but I don't understand what I have to provide here.
My code so far looks somewhat like this:
ValidatorFactory validatorFactory =
Validation.
byProvider(HibernateValidator.class).
providerResolver(new HibernateValidatorProviderResolver()).
configure().
externalClassLoader(getClass().getClassLoader()).
messageInterpolator(new ResourceBundleMessageInterpolator(
new PlatformResourceBundleLocator(ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES),
true, buildExpressionFactory())).
buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
BeanDescriptor constraintsForClass = validator.getConstraintsForClass(clazz);
System.out.println("constraintsForClass[" + constraintsForClass +
"] hasConstraints[" + constraintsForClass.hasConstraints() + "]");
if (constraintsForClass.hasConstraints()) {
for (ConstraintDescriptor<?> constraintDescriptor : constraintsForClass.getConstraintDescriptors()) {
System.out.println("constraintDescriptor[" + constraintDescriptor + "]");
emitTableRow(sb, "", constraintDescriptor);
}
}
for (PropertyDescriptor propertyDescriptor : constraintsForClass.getConstrainedProperties()) {
System.out.println("propertyDescriptor[" + propertyDescriptor +
"] name[" + propertyDescriptor.getPropertyName() + "]");
for (ConstraintDescriptor<?> constraintDescriptor : propertyDescriptor.getConstraintDescriptors()) {
System.out.println("constraintDescriptor[" + constraintDescriptor +
"] mt[" + constraintDescriptor.getMessageTemplate() + "]");
String interpolatedStr = validatorFactory.getMessageInterpolator().interpolate(constraintDescriptor.getMessageTemplate(), null);
System.out.println("interpolatedStr[" + interpolatedStr + "]");
As you can see, I'm currently passing "null" as the second parameter to "interpolate()", which results in an NPE in "org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver.getVariable(Context, String)
".
You could try and pass in a dummy implementation of MessageInterpolator.Context
which returns the descriptor you have in your hands and null for the validated value.