martin mucha (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=70121%3...
) *commented* on HV-1949 (
https://hibernate.atlassian.net/browse/HV-1949?atlOrigin=eyJpIjoiOGFhZTg3...
)
Re: Nondeterministic behavior of HibernateValidator (in springboot?) when both annotation
and xml-based definition are used (
https://hibernate.atlassian.net/browse/HV-1949?atlOrigin=eyJpIjoiOGFhZTg3...
)
So far I have no luck with this.
* When I attempt quick fix with touching ConstraintValidator during startup to be
initialized on working path it works, but ONLY for that specific validator. So in order
for this to work I’d need to correctly know all available validators across whole module
tree discoverable via ServiceLoader, make fake class to init them all, and hope everyone
who will potentially adds new validator in future anywhere will know about this, and will
also update my workaround. Maybe for me and my <20 validators and theoretically full
control over everything it’s doable, but generally not viable soluion.
2. Then I tried to used programmatic validators definition(validators on fields/properties
instead of xml declaration) while still using service loader to declare custom
ConstraintValidator. Won’t work (new to programmatic approach) as ServiceLoader
declaration does not apply for programmatic ConstraintValidator definition. So this is
also dead end.
3. Then I tried both programmatic validators definition(instead of xml) and programmatic
validators declaration. And this one does not work either. Since we do have the validators
implemented in library which already uses ServiceLoader, which is used by several apps. So
again, maybe it’s somehow doable, but generally no, we cannot make the ServiceLoader file
go away, it is used already in public library, I cannot easily replace it. So what I did
here, that I read ServiceLoader file myself and fed it into programmatic declaration of
pairing, to make sure it’s there when needed. Which is this beauty:
@Beanpublic static LocalValidatorFactoryBean defaultValidator(ApplicationContext
applicationContext) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean() {
@Override protected void
postProcessConfiguration(javax.validation.Configuration<?> configuration) {
super.postProcessConfiguration(configuration);
if (configuration instanceof HibernateValidatorConfiguration hvc) {
ConstraintMapping constraintMapping = hvc.createConstraintMapping();
ServiceLoader.load(ConstraintValidator.class).forEach(cv->{
Arrays.stream(cv.getClass().getGenericInterfaces())
.filter(e -> e instanceof ParameterizedType)
.map(e -> (ParameterizedType) e)
.filter(e ->
e.getRawType().equals(ConstraintValidator.class))
.map(ParameterizedType::getActualTypeArguments)
.forEach(aa -> {
Type annotationType = aa[0];
Class annotationClass = (Class<?>) annotationType;
Class constraintValidatorClass = cv.getClass();
System.out.println(String.format("registering %s",
constraintValidatorClass));
registerConstraint(constraintMapping, annotationClass,
constraintValidatorClass);
System.out.println(String.format("%s registered",
constraintValidatorClass));
});
});
//registering instead of xml
constraintMapping.type( mwe.validatorfail.dto.SampleDTO.class )
.getter( "uuid" )
.constraint( new
GenericConstraintDef<>(AnyUuid.class) )
.constraint( new NotNullDef() );
hvc.addMapping(constraintMapping);
} else {
throw new IllegalStateException("Should not happen, we explicitly
depend on HibernateValidator ValidationProvider");
}
}
};
factoryBean.setProviderClass(HibernateValidator.class);
MessageInterpolatorFactory interpolatorFactory = new
MessageInterpolatorFactory(applicationContext);
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
public static <A extends Annotation> void registerConstraint(ConstraintMapping
constraintMapping, Class<A> annotation,
Class<? extends ConstraintValidator<A, ?>> validator) {
constraintMapping.constraintDefinition(annotation)
.includeExistingValidators(false)
.validatedBy(validator);
}
which will produce this error on startup:
Caused by: javax.validation.ValidationException: HV000193:
mwe.validatorfail.validation.AnyUuid is configured more than once via the programmatic
constraint definition API.
Which is kinda funny. This code registers AnyUuid ONLY ONCE. So if this code does not run,
validator for AnyUuid does not exist, if it does run it creates duplicity. Which means,
that HibernateValidator after initialization is in ambivalent state, where it does have
and does not have validator declared at the same time.
So unless I’m doing something wrong, this is not a viable workaround either, since
ServiceLoader and manual registration of validators cannot coexist. For me it might be
doable, since I theoretically have full control over all apps using given library and can
rewrite everything to avoid wrong parts of HibernateValidator, but generally it’s not
viable workaround.
Is there a another way, how we can keep ServiceLoader in place (we might not be able to
rewrite public libraries depending on it) and somehow add non-annotation-based
validations, which will work with validators defined in ServiceLoader? What is motivation
behind it: we want to use annotation where possible, but some sources, like generated
stubs, does not allow us (without nasty tricks which even might not be available
everytime) to add annotation.
(
https://hibernate.atlassian.net/browse/HV-1949#add-comment?atlOrigin=eyJp...
) Add Comment (
https://hibernate.atlassian.net/browse/HV-1949#add-comment?atlOrigin=eyJp...
)
Get Jira notifications on your phone! Download the Jira Cloud app for Android (
https://play.google.com/store/apps/details?id=com.atlassian.android.jira....
) or iOS (
https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=Em...
) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:2b972a1 )