From 20651080faf0cc980450e1dc3815b6518c4971c1 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 24 May 2023 13:17:48 +0200 Subject: [PATCH] HV-1949 Load constraint validators from a Service Loader first - since there's no control over them and we cannot override a validator coming from a service loader --- .../PredefinedScopeValidatorFactoryImpl.java | 15 ++++++++++++ .../ValidatorFactoryConfigurationHelper.java | 23 +++++++++++++------ .../internal/engine/ValidatorFactoryImpl.java | 17 +++++++++++++- ...ceLoaderAndProgrammaticDefinitionTest.java | 19 +++++++++++++++ 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java index f917766d51..9a290d2346 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java @@ -17,6 +17,7 @@ import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineServiceLoadedConstraintMappings; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration; @@ -152,6 +153,18 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState ExecutableHelper executableHelper = new ExecutableHelper( typeResolutionHelper ); JavaBeanHelper javaBeanHelper = new JavaBeanHelper( getterPropertySelectionStrategy, propertyNodeNameProvider ); + // first we want to register any validators coming from a service loader. Since they are just loaded and there's + // no control over them (include/exclude the ones that already exists from any other sources etc.) + registerCustomConstraintValidators( + determineServiceLoadedConstraintMappings( + typeResolutionHelper, + javaBeanHelper, + externalClassLoader + ), + constraintHelper ); + + // we parse all XML mappings but only register constraint validators and delay constraint mappings building till + // we collect all the constraint validators. // HV-302; don't load XmlMappingParser if not necessary MappingXmlParser mappingParser = null; if ( !configurationState.getMappingStreams().isEmpty() ) { @@ -169,6 +182,8 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState ) ); + // now the final step of registering any constraint validators that can come either from ConstraintMappingContributors + // or from programmatic mappings registerCustomConstraintValidators( constraintMappings, constraintHelper ); XmlMetaDataProvider xmlMetaDataProvider; diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java index 519955d93c..412d97c61a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java @@ -72,13 +72,6 @@ static Set determineConstraintMappings(TypeResolutionH * these programmatically defined mappings into account when checking for constraint definition uniqueness */ constraintMappings.addAll( hibernateConfiguration.getProgrammaticMappings() ); - - // service loader based config - ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor( - typeResolutionHelper, - externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) ); - DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( javaBeanHelper, constraintMappings ); - serviceLoaderBasedContributor.createConstraintMappings( builder ); } // XML-defined constraint mapping contributors @@ -93,6 +86,22 @@ static Set determineConstraintMappings(TypeResolutionH return constraintMappings; } + static Set determineServiceLoadedConstraintMappings( + TypeResolutionHelper typeResolutionHelper, + JavaBeanHelper javaBeanHelper, ClassLoader externalClassLoader) { + Set constraintMappings = newHashSet(); + + // service loader based config + ConstraintMappingContributor serviceLoaderBasedContributor = new ServiceLoaderBasedConstraintMappingContributor( + typeResolutionHelper, + externalClassLoader != null ? externalClassLoader : run( GetClassLoader.fromContext() ) + ); + DefaultConstraintMappingBuilder builder = new DefaultConstraintMappingBuilder( + javaBeanHelper, constraintMappings ); + serviceLoaderBasedContributor.createConstraintMappings( builder ); + return constraintMappings; + } + static boolean checkPropertiesForBoolean(Map properties, String propertyKey, boolean programmaticValue) { boolean value = programmaticValue; String propertyStringValue = properties.get( propertyKey ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java index 6ac74792c2..8d90a6976d 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryImpl.java @@ -17,6 +17,7 @@ import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineServiceLoadedConstraintMappings; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration; @@ -185,6 +186,19 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ValidatorFactoryConfigurationHelper.determinePropertyNodeNameProvider( hibernateSpecificConfig, properties, externalClassLoader ) ); this.beanMetadataClassNormalizer = determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ); + // first we want to register any validators coming from a service loader. Since they are just loaded and there's + // no control over them (include/exclude the ones that already exists from any other sources etc.) + registerCustomConstraintValidators( + determineServiceLoadedConstraintMappings( + typeResolutionHelper, + javaBeanHelper, + externalClassLoader + ), + constraintHelper ); + + // we parse all XML mappings but only register constraint validators and delay constraint mappings building till + // we collect all the constraint validators. + // HV-302; don't load XmlMappingParser if not necessary MappingXmlParser mappingParser = null; if ( !configurationState.getMappingStreams().isEmpty() ) { mappingParser = new MappingXmlParser( constraintCreationContext, @@ -201,9 +215,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { ) ); + // now the final step of registering any constraint validators that can come either from ConstraintMappingContributors + // or from programmatic mappings registerCustomConstraintValidators( constraintMappings, constraintHelper ); - // HV-302; don't load XmlMappingParser if not necessary if ( mappingParser != null && mappingParser.createConstrainedElements() ) { this.xmlMetaDataProvider = new XmlMetaDataProvider( mappingParser ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingMixedWithServiceLoaderAndProgrammaticDefinitionTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingMixedWithServiceLoaderAndProgrammaticDefinitionTest.java index 5745a1083f..b8d4a84bf2 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingMixedWithServiceLoaderAndProgrammaticDefinitionTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/xml/XmlMappingMixedWithServiceLoaderAndProgrammaticDefinitionTest.java @@ -123,6 +123,25 @@ public void constraintAppliedProgrammaticallyDefinitionIsAppliedThroughXml() { ); } + @Test + public void constraintValidatorLoadedByServiceLoaderOverriddenByProgrammaticDefinition() { + final HibernateValidatorConfiguration configuration = ValidatorUtil.getConfiguration(); + configuration.externalClassLoader( new ServiceLoaderTestingClassLoader() ); + + ConstraintMapping constraintMapping = configuration.createConstraintMapping(); + configuration.addMapping( constraintMapping ); + + constraintMapping.constraintDefinition( MyOtherConstraint.class ) + .includeExistingValidators( false ) + .validatedBy( MyOtherConstraint.MyOtherOtherConstraintValidator.class ); + + final ValidatorFactory validatorFactory = configuration.buildValidatorFactory(); + final Validator validator = validatorFactory.getValidator(); + + assertNoViolations( validator.validate( new Foo() ) ); + assertNoViolations( validator.validate( new Bar() ) ); + } + public static class Foo { public String string; }