From 0eb0e178f2e689ecc7b5d37c636054f7a658576f Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 24 Feb 2021 19:10:09 +0100 Subject: [PATCH 01/16] HV-1831 Unfinished experiments --- .../BaseHibernateValidatorConfiguration.java | 4 + .../engine/AbstractConfigurationImpl.java | 18 +++++ .../PredefinedScopeValidatorFactoryImpl.java | 46 +++++++---- .../internal/engine/ValidatorFactoryImpl.java | 7 +- .../engine/ValidatorFactoryScopedContext.java | 28 ++++++- ...adablesProcessedBeansTrackingStrategy.java | 31 +++++++ ...edScopeProcessedBeansTrackingStrategy.java | 65 +++++++++++++++ .../ProcessedBeansTrackingStrategy.java | 20 +++++ .../AbstractValidationContext.java | 63 +++++++++++---- .../BeanValidationContext.java | 9 ++- .../ParameterExecutableValidationContext.java | 11 ++- ...eturnValueExecutableValidationContext.java | 10 ++- .../ValidatorScopedContext.java | 11 +++ .../PredefinedScopeBeanMetaDataManager.java | 5 ++ performance/pom.xml | 80 ++++++++++++++----- 15 files changed, 342 insertions(+), 66 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java diff --git a/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java index 7ce04ef270..97e2f5fe15 100644 --- a/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java @@ -25,6 +25,7 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; @@ -480,4 +481,7 @@ default S locales(Locale... locales) { */ @Incubating S customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel); + + @Incubating + S processedBeansTrackingStrategy(ProcessedBeansTrackingStrategy processedBeanTrackingStrategy); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java index f858c61d9e..088805e0d7 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java @@ -42,6 +42,7 @@ import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; @@ -132,6 +133,7 @@ public abstract class AbstractConfigurationImpl getProgrammaticMappings() { return programmaticMappings; } 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 ca59ece651..5b96537c63 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 @@ -9,11 +9,11 @@ import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints; -import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel; -import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineCustomViolationExpressionLanguageFeatureLevel; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineCustomViolationExpressionLanguageFeatureLevel; 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; @@ -44,8 +44,10 @@ import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; +import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.PredefinedScopeProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -112,23 +114,16 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties ) ).build(); - this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext( - configurationState.getMessageInterpolator(), - configurationState.getTraversableResolver(), - new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() ), - configurationState.getClockProvider(), - determineTemporalValidationTolerance( configurationState, properties ), - determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader ), - determineFailFast( hibernateSpecificConfig, properties ), - determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), - determineConstraintValidatorPayload( hibernateSpecificConfig ), - determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ) - ); + ExecutableParameterNameProvider parameterNameProvider = new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() ); + ScriptEvaluatorFactory scriptEvaluatorFactory = determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader ); + Duration temporalValidationTolerance = determineTemporalValidationTolerance( configurationState, properties ); + + HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext = new HibernateConstraintValidatorInitializationContextImpl( + scriptEvaluatorFactory, configurationState.getClockProvider(), temporalValidationTolerance ); this.constraintValidatorManager = new PredefinedScopeConstraintValidatorManagerImpl( configurationState.getConstraintValidatorFactory(), - this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext() + constraintValidatorInitializationContext ); this.validationOrderGenerator = new ValidationOrderGenerator(); @@ -171,7 +166,7 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState this.beanMetaDataManager = new PredefinedScopeBeanMetaDataManager( constraintCreationContext, executableHelper, - validatorFactoryScopedContext.getParameterNameProvider(), + parameterNameProvider, javaBeanHelper, validationOrderGenerator, buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ), @@ -180,6 +175,23 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState hibernateSpecificConfig.getBeanClassesToInitialize() ); + this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext( + configurationState.getMessageInterpolator(), + configurationState.getTraversableResolver(), + parameterNameProvider, + configurationState.getClockProvider(), + temporalValidationTolerance, + scriptEvaluatorFactory, + determineFailFast( hibernateSpecificConfig, properties ), + determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), + determineConstraintValidatorPayload( hibernateSpecificConfig ), + determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), + determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), + ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) + ? hibernateSpecificConfig.getProcessedBeansTrackingStrategy() + : new PredefinedScopeProcessedBeansTrackingStrategy( beanMetaDataManager ), + constraintValidatorInitializationContext ); + if ( LOG.isDebugEnabled() ) { logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); } 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 e7ae23617d..95a86a6d03 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 @@ -47,6 +47,7 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.HasCascadablesProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; @@ -160,8 +161,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), determineConstraintValidatorPayload( hibernateSpecificConfig ), determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ) - ); + determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), + ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) + ? hibernateSpecificConfig.getProcessedBeansTrackingStrategy() + : new HasCascadablesProcessedBeansTrackingStrategy() ); ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl( configurationState.getConstraintValidatorFactory(), diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java index 85b9599471..dc53b4a35e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java @@ -15,6 +15,7 @@ import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; @@ -78,6 +79,11 @@ public class ValidatorFactoryScopedContext { */ private final ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; + /** + * Strategy used to enable or not processed beans tracking. + */ + private final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; + /** * The constraint validator initialization context. */ @@ -93,15 +99,16 @@ public class ValidatorFactoryScopedContext { boolean traversableResolverResultCacheEnabled, Object constraintValidatorPayload, ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel, - ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel) { + ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel, + ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast, traversableResolverResultCacheEnabled, constraintValidatorPayload, constraintExpressionLanguageFeatureLevel, - customViolationExpressionLanguageFeatureLevel, + customViolationExpressionLanguageFeatureLevel, processedBeansTrackingStrategy, new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, temporalValidationTolerance ) ); } - private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, + ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, TraversableResolver traversableResolver, ExecutableParameterNameProvider parameterNameProvider, ClockProvider clockProvider, @@ -112,6 +119,7 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, Object constraintValidatorPayload, ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel, ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel, + ProcessedBeansTrackingStrategy processedBeanTrackingStrategy, HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) { this.messageInterpolator = messageInterpolator; this.traversableResolver = traversableResolver; @@ -124,6 +132,7 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator, this.constraintValidatorPayload = constraintValidatorPayload; this.constraintExpressionLanguageFeatureLevel = constraintExpressionLanguageFeatureLevel; this.customViolationExpressionLanguageFeatureLevel = customViolationExpressionLanguageFeatureLevel; + this.processedBeansTrackingStrategy = processedBeanTrackingStrategy; this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; } @@ -175,6 +184,10 @@ public ExpressionLanguageFeatureLevel getCustomViolationExpressionLanguageFeatur return this.customViolationExpressionLanguageFeatureLevel; } + public ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return processedBeansTrackingStrategy; + } + static class Builder { private final ValidatorFactoryScopedContext defaultContext; @@ -189,6 +202,7 @@ static class Builder { private Object constraintValidatorPayload; private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel; private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; + private ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; Builder(ValidatorFactoryScopedContext defaultContext) { @@ -206,6 +220,7 @@ static class Builder { this.constraintValidatorPayload = defaultContext.constraintValidatorPayload; this.constraintExpressionLanguageFeatureLevel = defaultContext.constraintExpressionLanguageFeatureLevel; this.customViolationExpressionLanguageFeatureLevel = defaultContext.customViolationExpressionLanguageFeatureLevel; + this.processedBeansTrackingStrategy = defaultContext.processedBeansTrackingStrategy; this.constraintValidatorInitializationContext = defaultContext.constraintValidatorInitializationContext; } @@ -292,6 +307,12 @@ public ValidatorFactoryScopedContext.Builder setCustomViolationExpressionLanguag return this; } + public ValidatorFactoryScopedContext.Builder setProcessedBeansTrackingStrategy( + ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + this.processedBeansTrackingStrategy = processedBeansTrackingStrategy; + return this; + } + public ValidatorFactoryScopedContext build() { return new ValidatorFactoryScopedContext( messageInterpolator, @@ -305,6 +326,7 @@ public ValidatorFactoryScopedContext build() { constraintValidatorPayload, constraintExpressionLanguageFeatureLevel, customViolationExpressionLanguageFeatureLevel, + processedBeansTrackingStrategy, HibernateConstraintValidatorInitializationContextImpl.of( constraintValidatorInitializationContext, scriptEvaluatorFactory, diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java new file mode 100644 index 0000000000..7a7a33083f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java @@ -0,0 +1,31 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine.tracking; + +import java.lang.reflect.Executable; + +public class HasCascadablesProcessedBeansTrackingStrategy implements ProcessedBeansTrackingStrategy { + + @Override + public boolean isEnabledForBean(Class beanClass, boolean hasCascadables) { + return hasCascadables; + } + + @Override + public boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables) { + return hasCascadables; + } + + @Override + public boolean isEnabledForParameters(Executable executable, boolean hasCascadables) { + return hasCascadables; + } + + @Override + public void clear() { + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java new file mode 100644 index 0000000000..9e3a155b25 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -0,0 +1,65 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine.tracking; + +import java.lang.reflect.Executable; +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; +import org.hibernate.validator.internal.util.CollectionHelper; + +public class PredefinedScopeProcessedBeansTrackingStrategy implements ProcessedBeansTrackingStrategy { + + private final Map, Boolean> trackingEnabledForBeans; + + private final Map trackingEnabledForReturnValues; + + private final Map trackingEnabledForParameters; + + public PredefinedScopeProcessedBeansTrackingStrategy(PredefinedScopeBeanMetaDataManager beanMetaDataManager) { + // TODO: build the maps from the information inside the beanMetaDataManager + + this.trackingEnabledForBeans = CollectionHelper.toImmutableMap( new HashMap<>() ); + this.trackingEnabledForReturnValues = CollectionHelper.toImmutableMap( new HashMap<>() ); + this.trackingEnabledForParameters = CollectionHelper.toImmutableMap( new HashMap<>() ); + } + + @Override + public boolean isEnabledForBean(Class rootBeanClass, boolean hasCascadables) { + if ( !hasCascadables ) { + return false; + } + + return trackingEnabledForBeans.getOrDefault( rootBeanClass, true ); + } + + @Override + public boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables) { + if ( !hasCascadables ) { + return false; + } + + return trackingEnabledForReturnValues.getOrDefault( executable, true ); + } + + @Override + public boolean isEnabledForParameters(Executable executable, boolean hasCascadables) { + if ( !hasCascadables ) { + return false; + } + + return trackingEnabledForParameters.getOrDefault( executable, true ); + } + + @Override + public void clear() { + trackingEnabledForBeans.clear(); + trackingEnabledForReturnValues.clear(); + trackingEnabledForParameters.clear(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java new file mode 100644 index 0000000000..b5db188e3b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java @@ -0,0 +1,20 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine.tracking; + +import java.lang.reflect.Executable; + +public interface ProcessedBeansTrackingStrategy { + + boolean isEnabledForBean(Class beanClass, boolean hasCascadables); + + boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables); + + boolean isEnabledForParameters(Executable executable, boolean hasCascadables); + + void clear(); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java index 361d2bf65f..d8d0b1e390 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java @@ -7,9 +7,10 @@ package org.hibernate.validator.internal.engine.validationcontext; import java.lang.invoke.MethodHandles; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -96,25 +97,25 @@ abstract class AbstractValidationContext implements BaseBeanValidationContext /** * Indicates if the tracking of already validated bean should be disabled. */ - private final boolean disableAlreadyValidatedBeanTracking; + private final boolean processedBeanTrackingEnabled; /** * The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}). */ @Lazy - private Set processedPathUnits; + private HashSet processedPathUnits; /** * The set of already processed groups per bean ({@link BeanGroupProcessedUnit}). */ @Lazy - private Set processedGroupUnits; + private HashSet processedGroupUnits; /** * Maps an object to a list of paths in which it has been validated. The objects are the bean instances. */ @Lazy - private Map> processedPathsPerBean; + private HashMap> processedPathsPerBean; /** * Contains all failing constraints so far. @@ -131,7 +132,7 @@ protected AbstractValidationContext( T rootBean, Class rootBeanClass, BeanMetaData rootBeanMetaData, - boolean disableAlreadyValidatedBeanTracking + boolean processedBeanTrackingEnabled ) { this.constraintValidatorManager = constraintValidatorManager; this.validatorScopedContext = validatorScopedContext; @@ -143,7 +144,7 @@ protected AbstractValidationContext( this.rootBeanClass = rootBeanClass; this.rootBeanMetaData = rootBeanMetaData; - this.disableAlreadyValidatedBeanTracking = disableAlreadyValidatedBeanTracking; + this.processedBeanTrackingEnabled = processedBeanTrackingEnabled; } @Override @@ -188,7 +189,7 @@ public ConstraintValidatorFactory getConstraintValidatorFactory() { @Override public boolean isBeanAlreadyValidated(Object value, Class group, PathImpl path) { - if ( disableAlreadyValidatedBeanTracking ) { + if ( !processedBeanTrackingEnabled ) { return false; } @@ -204,7 +205,7 @@ public boolean isBeanAlreadyValidated(Object value, Class group, PathImpl pat @Override public void markCurrentBeanAsProcessed(ValueContext valueContext) { - if ( disableAlreadyValidatedBeanTracking ) { + if ( !processedBeanTrackingEnabled ) { return; } @@ -333,7 +334,7 @@ private String interpolate( } private boolean isAlreadyValidatedForPath(Object value, PathImpl path) { - Set pathSet = getInitializedProcessedPathsPerBean().get( value ); + ArrayList pathSet = getInitializedProcessedPathsPerBean().get( new ProcessedBean( value ) ); if ( pathSet == null ) { return false; } @@ -369,12 +370,13 @@ private boolean isAlreadyValidatedForCurrentGroup(Object value, Class group) private void markCurrentBeanAsProcessedForCurrentPath(Object bean, PathImpl path) { // HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it - Map> processedPathsPerBean = getInitializedProcessedPathsPerBean(); + HashMap> processedPathsPerBean = getInitializedProcessedPathsPerBean(); - Set processedPaths = processedPathsPerBean.get( bean ); + ProcessedBean processedBean = new ProcessedBean( bean ); + ArrayList processedPaths = processedPathsPerBean.get( processedBean ); if ( processedPaths == null ) { - processedPaths = new HashSet<>(); - processedPathsPerBean.put( bean, processedPaths ); + processedPaths = new ArrayList<>(); + processedPathsPerBean.put( processedBean, processedPaths ); } processedPaths.add( PathImpl.createCopy( path ) ); @@ -391,16 +393,16 @@ private Set getInitializedProcessedPathUnit return processedPathUnits; } - private Set getInitializedProcessedGroupUnits() { + private HashSet getInitializedProcessedGroupUnits() { if ( processedGroupUnits == null ) { processedGroupUnits = new HashSet<>(); } return processedGroupUnits; } - private Map> getInitializedProcessedPathsPerBean() { + private HashMap> getInitializedProcessedPathsPerBean() { if ( processedPathsPerBean == null ) { - processedPathsPerBean = new IdentityHashMap<>(); + processedPathsPerBean = new HashMap<>(); } return processedPathsPerBean; } @@ -505,4 +507,31 @@ private int createHashCode() { return result; } } + + private static final class ProcessedBean { + + private Object bean; + private int hashCode = -1; + + ProcessedBean(Object bean) { + this.bean = bean; + } + + @Override + public boolean equals(Object o) { + return this.bean == ((ProcessedBean) o).bean; + } + + @Override + public int hashCode() { + if ( hashCode == -1 ) { + hashCode = createHashCode(); + } + return hashCode; + } + + private int createHashCode() { + return System.identityHashCode( bean ); + } + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java index ab7aa153d8..de2789ac4b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java @@ -16,6 +16,7 @@ import org.hibernate.validator.internal.engine.ConstraintViolationImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; @@ -37,12 +38,14 @@ class BeanValidationContext extends AbstractValidationContext { BeanMetaData rootBeanMetaData ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, - rootBean, rootBeanClass, rootBeanMetaData, buildDisableAlreadyValidatedBeanTracking( rootBeanMetaData ) + rootBean, rootBeanClass, rootBeanMetaData, buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), + rootBeanClass, rootBeanMetaData ) ); } - private static boolean buildDisableAlreadyValidatedBeanTracking(BeanMetaData rootBeanMetaData) { - return !rootBeanMetaData.hasCascadables(); + private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, Class rootBeanClass, + BeanMetaData rootBeanMetaData) { + return processedBeansTrackingStrategy.isEnabledForBean( rootBeanClass, rootBeanMetaData.hasCascadables() ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java index fc08e920b4..a41641859f 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java @@ -24,6 +24,7 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; import org.hibernate.validator.internal.engine.constraintvalidation.CrossParameterConstraintValidatorContextImpl; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; @@ -68,7 +69,8 @@ public class ParameterExecutableValidationContext extends AbstractValidationC ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, - buildDisableAlreadyValidatedBeanTracking( executableMetaData ) + buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), executable, + executableMetaData ) ); this.executable = executable; this.executableMetaData = executableMetaData; @@ -85,13 +87,16 @@ public Optional getExecutableMetaData() { return executableMetaData; } - private static boolean buildDisableAlreadyValidatedBeanTracking(Optional executableMetaData) { + private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, + Executable executable, + Optional executableMetaData) { if ( !executableMetaData.isPresent() ) { // the method is unconstrained so there's no need to worry about the tracking return false; } - return !executableMetaData.get().getValidatableParametersMetaData().hasCascadables(); + return processedBeansTrackingStrategy.isEnabledForReturnValue( executable, + executableMetaData.get().getValidatableParametersMetaData().hasCascadables() ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java index 0c7715db79..af975c9679 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java @@ -19,6 +19,7 @@ import org.hibernate.validator.internal.engine.ConstraintViolationImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; @@ -61,7 +62,8 @@ public class ReturnValueExecutableValidationContext extends AbstractValidatio ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, - buildDisableAlreadyValidatedBeanTracking( executableMetaData ) + buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), executable, + executableMetaData ) ); this.executable = executable; this.executableMetaData = executableMetaData; @@ -78,13 +80,15 @@ public Optional getExecutableMetaData() { return executableMetaData; } - private static boolean buildDisableAlreadyValidatedBeanTracking(Optional executableMetaData) { + private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, + Executable executable, + Optional executableMetaData) { if ( !executableMetaData.isPresent() ) { // the method is unconstrained so there's no need to worry about the tracking return false; } - return !executableMetaData.get().getReturnValueMetaData().hasCascadables(); + return processedBeansTrackingStrategy.isEnabledForReturnValue( executable, executableMetaData.get().getReturnValueMetaData().hasCascadables() ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java index 0e78c05025..5b35742e85 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java @@ -13,6 +13,7 @@ import javax.validation.Validator; import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -76,6 +77,11 @@ public class ValidatorScopedContext { */ private final ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; + /** + * Strategy used to enable or not processed beans tracking. + */ + private final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; + public ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScopedContext) { this.messageInterpolator = validatorFactoryScopedContext.getMessageInterpolator(); this.parameterNameProvider = validatorFactoryScopedContext.getParameterNameProvider(); @@ -87,6 +93,7 @@ public ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScop this.constraintValidatorPayload = validatorFactoryScopedContext.getConstraintValidatorPayload(); this.constraintExpressionLanguageFeatureLevel = validatorFactoryScopedContext.getConstraintExpressionLanguageFeatureLevel(); this.customViolationExpressionLanguageFeatureLevel = validatorFactoryScopedContext.getCustomViolationExpressionLanguageFeatureLevel(); + this.processedBeansTrackingStrategy = validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); } public MessageInterpolator getMessageInterpolator() { @@ -128,4 +135,8 @@ public ExpressionLanguageFeatureLevel getConstraintExpressionLanguageFeatureLeve public ExpressionLanguageFeatureLevel getCustomViolationExpressionLanguageFeatureLevel() { return customViolationExpressionLanguageFeatureLevel; } + + public ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return processedBeansTrackingStrategy; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java index b7215b0637..0da27bf01c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java @@ -11,6 +11,7 @@ import java.lang.annotation.ElementType; import java.lang.reflect.Executable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -119,6 +120,10 @@ public BeanMetaData getBeanMetaData(Class beanClass) { return beanMetaData; } + public Collection> getBeanMetaData() { + return beanMetaDataMap.values(); + } + @Override public void clear() { beanMetaDataMap.clear(); diff --git a/performance/pom.xml b/performance/pom.xml index 393d7c8068..e54f562629 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -182,8 +182,52 @@ jakarta.el - log4j - log4j + org.apache.logging.log4j + log4j-core + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + + hv-6.2 + + + validator + hv-6.2 + + + + 2.0.1.Final + Hibernate Validator + 6.2.0.Final + + + + javax.validation + validation-api + ${validation-api.version} + + + ${project.groupId} + hibernate-validator + ${beanvalidation-impl.version} + + + org.glassfish + javax.el + 3.0.1-b11 + + + org.apache.logging.log4j + log4j-core @@ -223,8 +267,8 @@ jakarta.el - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -267,8 +311,8 @@ 3.0.1-b11 - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -311,8 +355,8 @@ 3.0.1-b11 - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -352,8 +396,8 @@ ${javax-el.version} - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -393,8 +437,8 @@ ${javax-el.version} - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -434,8 +478,8 @@ ${javax-el.version} - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -475,8 +519,8 @@ ${javax-el.version} - log4j - log4j + org.apache.logging.log4j + log4j-core @@ -505,8 +549,8 @@ ${beanvalidation-impl.version} - log4j - log4j + org.apache.logging.log4j + log4j-core From d4acd34c2b8d0c1440d2c776fe316cd639f6a0e3 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 25 Feb 2021 11:25:48 +0100 Subject: [PATCH 02/16] HV-1831 Add a couple of examples illustrating various cases --- ...edScopeProcessedBeansTrackingStrategy.java | 5 ++ .../ProcessedBeansTrackingCycles1Test.java | 49 ++++++++++++++++ .../ProcessedBeansTrackingCycles2Test.java | 50 ++++++++++++++++ .../ProcessedBeansTrackingCycles3Test.java | 51 ++++++++++++++++ .../ProcessedBeansTrackingCycles4Test.java | 51 ++++++++++++++++ .../ProcessedBeansTrackingCycles5Test.java | 58 +++++++++++++++++++ .../ProcessedBeansTrackingNoCycles1Test.java | 44 ++++++++++++++ .../ProcessedBeansTrackingNoCycles2Test.java | 45 ++++++++++++++ .../ProcessedBeansTrackingNoCycles3Test.java | 48 +++++++++++++++ 9 files changed, 401 insertions(+) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java index 9e3a155b25..a78c7ee43c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -23,6 +23,11 @@ public class PredefinedScopeProcessedBeansTrackingStrategy implements ProcessedB public PredefinedScopeProcessedBeansTrackingStrategy(PredefinedScopeBeanMetaDataManager beanMetaDataManager) { // TODO: build the maps from the information inside the beanMetaDataManager + // There is a good chance we will need a structure with the whole hierarchy of constraint classes. + // That's something we could add to PredefinedScopeBeanMetaDataManager, as we are already doing similar things + // there (see the ClassHierarchyHelper.getHierarchy call). + // In the predefined scope case, we will have the whole hierarchy of constrained classes passed to + // PredefinedScopeBeanMetaDataManager. this.trackingEnabledForBeans = CollectionHelper.toImmutableMap( new HashMap<>() ); this.trackingEnabledForReturnValues = CollectionHelper.toImmutableMap( new HashMap<>() ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java new file mode 100644 index 0000000000..9da4aba204 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java @@ -0,0 +1,49 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * This is the most simple example. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles1Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + @Valid + private Child child; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java new file mode 100644 index 0000000000..5fa8226919 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles2Test.java @@ -0,0 +1,50 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is in the container element. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles2Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid Child> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java new file mode 100644 index 0000000000..2d136405e5 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles3Test.java @@ -0,0 +1,51 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; +import java.util.Map; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is deep in a container element. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles3Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private Map> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java new file mode 100644 index 0000000000..75cfbf50b6 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles4Test.java @@ -0,0 +1,51 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; +import java.util.Map; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * Simple enough but this time the cascading annotation is deep in a container element with a bound. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles4Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private Map> children; + } + + private static class Child { + + @NotNull + private String property; + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java new file mode 100644 index 0000000000..9b5457638a --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles5Test.java @@ -0,0 +1,58 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * This one is a bit more tricky: during the validation, when cascading, we take into account the runtime type to get + * the metadata, not the declared type. + *

+ * So even if you couldn't have a cycle with the declared type, when trying to find the cycles, we need to take into + * consideration all the subclasses too. The good news is that we are in a closed world so we have them all passed + * to our PredefinedScopedValidatorFactoryImpl! + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingCycles5Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid ChildWithNoCycles> children; + } + + private static class ChildWithNoCycles { + + @NotNull + private String property; + } + + private static class Child extends ChildWithNoCycles { + + @Valid + private Parent parent; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java new file mode 100644 index 0000000000..8bb1d30b74 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles1Test.java @@ -0,0 +1,44 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles1Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + @Valid + private Child child; + } + + private static class Child { + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java new file mode 100644 index 0000000000..b80a032b35 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java @@ -0,0 +1,45 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles2Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid Child> children; + } + + private static class Child { + + @NotNull + private String property; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java new file mode 100644 index 0000000000..10d4c6b239 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles3Test.java @@ -0,0 +1,48 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.testutils.ValidatorUtil; +import org.testng.annotations.Test; + +/** + * This is not a real test, just an illustration. + *

+ * In this case, given we will have all the subclasses of Child in the metadata, we should be able to know there are no + * cycles. + * + * @author Guillaume Smet + */ +public class ProcessedBeansTrackingNoCycles3Test { + + @Test + public void testSerializeHibernateEmail() throws Exception { + Validator validator = ValidatorUtil.getValidator(); + + validator.validate( new Parent() ); + } + + private static class Parent { + + @NotNull + private String property; + + private List<@Valid ? extends Child> children; + } + + private static class Child { + + @NotNull + private String property; + } +} From aea064236988170511c109fd5464021385550bf8 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 8 Mar 2021 19:41:22 +0100 Subject: [PATCH 03/16] HV-1831 Clean up another experiment that shouldn't have been committed --- .../AbstractValidationContext.java | 53 +++++-------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java index d8d0b1e390..235cac7af4 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java @@ -7,10 +7,9 @@ package org.hibernate.validator.internal.engine.validationcontext; import java.lang.invoke.MethodHandles; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -103,19 +102,19 @@ abstract class AbstractValidationContext implements BaseBeanValidationContext * The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}). */ @Lazy - private HashSet processedPathUnits; + private Set processedPathUnits; /** * The set of already processed groups per bean ({@link BeanGroupProcessedUnit}). */ @Lazy - private HashSet processedGroupUnits; + private Set processedGroupUnits; /** * Maps an object to a list of paths in which it has been validated. The objects are the bean instances. */ @Lazy - private HashMap> processedPathsPerBean; + private Map> processedPathsPerBean; /** * Contains all failing constraints so far. @@ -334,7 +333,7 @@ private String interpolate( } private boolean isAlreadyValidatedForPath(Object value, PathImpl path) { - ArrayList pathSet = getInitializedProcessedPathsPerBean().get( new ProcessedBean( value ) ); + Set pathSet = getInitializedProcessedPathsPerBean().get( value ); if ( pathSet == null ) { return false; } @@ -370,13 +369,12 @@ private boolean isAlreadyValidatedForCurrentGroup(Object value, Class group) private void markCurrentBeanAsProcessedForCurrentPath(Object bean, PathImpl path) { // HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it - HashMap> processedPathsPerBean = getInitializedProcessedPathsPerBean(); + Map> processedPathsPerBean = getInitializedProcessedPathsPerBean(); - ProcessedBean processedBean = new ProcessedBean( bean ); - ArrayList processedPaths = processedPathsPerBean.get( processedBean ); + Set processedPaths = processedPathsPerBean.get( bean ); if ( processedPaths == null ) { - processedPaths = new ArrayList<>(); - processedPathsPerBean.put( processedBean, processedPaths ); + processedPaths = new HashSet<>(); + processedPathsPerBean.put( bean, processedPaths ); } processedPaths.add( PathImpl.createCopy( path ) ); @@ -393,16 +391,16 @@ private Set getInitializedProcessedPathUnit return processedPathUnits; } - private HashSet getInitializedProcessedGroupUnits() { + private Set getInitializedProcessedGroupUnits() { if ( processedGroupUnits == null ) { processedGroupUnits = new HashSet<>(); } return processedGroupUnits; } - private HashMap> getInitializedProcessedPathsPerBean() { + private Map> getInitializedProcessedPathsPerBean() { if ( processedPathsPerBean == null ) { - processedPathsPerBean = new HashMap<>(); + processedPathsPerBean = new IdentityHashMap<>(); } return processedPathsPerBean; } @@ -507,31 +505,4 @@ private int createHashCode() { return result; } } - - private static final class ProcessedBean { - - private Object bean; - private int hashCode = -1; - - ProcessedBean(Object bean) { - this.bean = bean; - } - - @Override - public boolean equals(Object o) { - return this.bean == ((ProcessedBean) o).bean; - } - - @Override - public int hashCode() { - if ( hashCode == -1 ) { - hashCode = createHashCode(); - } - return hashCode; - } - - private int createHashCode() { - return System.identityHashCode( bean ); - } - } } From cea9b8cb40609394cba9731809ab72021e31b6fd Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 10 Mar 2021 10:02:52 -0800 Subject: [PATCH 04/16] Add the same bean to List twice --- .../tracking/ProcessedBeansTrackingNoCycles2Test.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java index b80a032b35..90a3e0e7f6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingNoCycles2Test.java @@ -6,6 +6,7 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import java.util.ArrayList; import java.util.List; import javax.validation.Valid; @@ -26,7 +27,14 @@ public class ProcessedBeansTrackingNoCycles2Test { public void testSerializeHibernateEmail() throws Exception { Validator validator = ValidatorUtil.getValidator(); - validator.validate( new Parent() ); + final Parent parent = new Parent(); + parent.property = "parent property"; + final Child child = new Child(); + child.property = "child property"; + parent.children = new ArrayList<>(); + parent.children.add( child ); + parent.children.add( child ); + validator.validate( parent ); } private static class Parent { From f4d765617643dc751e1b9a75700b1457d9d41c21 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 11 Mar 2021 14:00:12 +0100 Subject: [PATCH 05/16] Copy nodes when changing the nature of the leaf --- .../validator/internal/engine/path/PathImpl.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java index d83a9e8f7e..017615db26 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java @@ -195,7 +195,7 @@ private NodeImpl addMethodNode(String name, Class[] parameterTypes) { } public NodeImpl makeLeafNodeIterable() { - requiresWriteableNodeList(); + copyNodeList(); currentLeafNode = NodeImpl.makeIterable( currentLeafNode ); @@ -205,7 +205,7 @@ public NodeImpl makeLeafNodeIterable() { } public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) { - requiresWriteableNodeList(); + copyNodeList(); currentLeafNode = NodeImpl.makeIterableAndSetIndex( currentLeafNode, index ); @@ -215,7 +215,7 @@ public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) { } public NodeImpl makeLeafNodeIterableAndSetMapKey(Object key) { - requiresWriteableNodeList(); + copyNodeList(); currentLeafNode = NodeImpl.makeIterableAndSetMapKey( currentLeafNode, key ); @@ -300,6 +300,10 @@ private void requiresWriteableNodeList() { return; } + copyNodeList(); + } + + private void copyNodeList() { // Usually, the write operation is about adding one more node, so let's make the list one element larger. List newNodeList = new ArrayList<>( nodeList.size() + 1 ); newNodeList.addAll( nodeList ); From e859ea76a92cd7aa89cbdbe2442f7aa0516330ae Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 8 Apr 2021 18:20:24 -0700 Subject: [PATCH 06/16] HV-1831 : Experiment detecting cycles in bean classes --- ...edScopeProcessedBeansTrackingStrategy.java | 155 ++++++++++++- .../ParameterExecutableValidationContext.java | 2 +- .../ProcessedBeansTrackingCycles1Test.java | 76 ++++++- ...cessedBeansTrackingCyclesNoCyclesTest.java | 207 ++++++++++++++++++ 4 files changed, 434 insertions(+), 6 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java index a78c7ee43c..7c1c8503d3 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -7,10 +7,16 @@ package org.hibernate.validator.internal.engine.tracking; import java.lang.reflect.Executable; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; +import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; +import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.util.CollectionHelper; public class PredefinedScopeProcessedBeansTrackingStrategy implements ProcessedBeansTrackingStrategy { @@ -29,18 +35,163 @@ public PredefinedScopeProcessedBeansTrackingStrategy(PredefinedScopeBeanMetaData // In the predefined scope case, we will have the whole hierarchy of constrained classes passed to // PredefinedScopeBeanMetaDataManager. - this.trackingEnabledForBeans = CollectionHelper.toImmutableMap( new HashMap<>() ); + this.trackingEnabledForBeans = CollectionHelper.toImmutableMap( + new TrackingEnabledStrategyBuilder( beanMetaDataManager ).build() + ); this.trackingEnabledForReturnValues = CollectionHelper.toImmutableMap( new HashMap<>() ); this.trackingEnabledForParameters = CollectionHelper.toImmutableMap( new HashMap<>() ); } + private static class TrackingEnabledStrategyBuilder { + private final PredefinedScopeBeanMetaDataManager beanMetaDataManager; + private final Map, Boolean> classToBeanTrackingEnabled; + + TrackingEnabledStrategyBuilder(PredefinedScopeBeanMetaDataManager beanMetaDataManager) { + this.beanMetaDataManager = beanMetaDataManager; + this.classToBeanTrackingEnabled = new HashMap<>( beanMetaDataManager.getBeanMetaData().size() ); + } + + public Map, Boolean> build() { + final Set> beanClassesInPath = new HashSet<>(); + for (BeanMetaData beanMetadata : beanMetaDataManager.getBeanMetaData() ) { + determineTrackingRequired( beanMetadata.getBeanClass(), beanClassesInPath ); + if ( !beanClassesInPath.isEmpty() ) { + throw new IllegalStateException( "beanClassesInPath not empty" ); + } + } + return classToBeanTrackingEnabled; + } + + // Do a depth-first search for cycles along paths of cascaded bean classes. + // The algorithm stops due to one of the following: + // 1) The bean class was previously put in classToBeanTrackingEnabled + // (i.e., the bean class was already determined to either have a cycle, + // or not have a cycle). + // 2) A cycle is found. In this case, all bean classes in the particular path, + // starting from beanClass up to first bean class that causes a cycle, will + // be registered in classToBeanTrackingEnabled with a value of true. + // Once a cycle is found, no further bean classes are examined. Those bean + // classes that were examined in the process that are found to not have a + // cycle are registered in classToBeanTrackingEnabled with a value of false. + // 3) No cycle is found. In this case, all bean classes in the tree will be + // registered in classToBeanTrackingEnabled with a value of false. + // + // Examples: An arrow, ->, indicates a cascading constraint from a bean class. + // + // 1) A -> B + // | ^ + // | | + // ---- + // A, B have no cycles. A has 2 paths to B, but there are no cycles, because there is no path from B to A. + // + // 2) A <- + // | | + // --- + // A has a cycle to itself. + // + // 3) A -> B -> C -> D + // ^ | + // | | + // ----- + // A, B, C have cycles; D does not have a cycle. + // + private boolean determineTrackingRequired(Class beanClass, Set> beanClassesInPath) { + + final Boolean isBeanTrackingEnabled = classToBeanTrackingEnabled.get( beanClass ); + if ( isBeanTrackingEnabled != null ) { + // It was already determined for beanClass. + return isBeanTrackingEnabled; + } + + // Add beanClass to the path. + // We don't care about the order of the bean classes in + // beanClassesInPath. We only care about detecting a duplicate, + // which indicates a cycle. If no cycle is found in beanClass, + // it will be removed below. + if ( !beanClassesInPath.add( beanClass ) ) { + // The bean was already present in the path being examined. + // That means that there is cycle involving beanClass. + // Enable tracking for all elements in beanClassesInPath + for ( Class dependency : beanClassesInPath ) { + register( dependency, true ); + } + beanClassesInPath.clear(); + return true; + } + + // Now check the cascaded bean classes. + for ( Class directCascadedBeanClass : getDirectCascadedBeanClasses( beanClass ) ) { + // Check to see if tracking has already been determined for directCascadedBeanClass + Boolean isSubBeanTrackingEnabled = classToBeanTrackingEnabled.get( directCascadedBeanClass ); + if ( isSubBeanTrackingEnabled != null ) { + if ( isSubBeanTrackingEnabled ) { + // We already know that directCascadedBeanClass has a cycle. + // That means that all elements in beanClassesInPath + // will have a cycle. + for ( Class dependency : beanClassesInPath ) { + register( dependency, true ); + } + // No point in checking any others in this loop. + beanClassesInPath.clear(); + return true; + } + else { + // We already know that directCascadedBeanClass is not involved in + // any cycles, so move on to the next iteration. + continue; + } + } + if ( determineTrackingRequired( directCascadedBeanClass, beanClassesInPath ) ) { + // A cycle was found. No point in checking any others in this loop. + // beanClassesInPath should have already been cleared. + assert beanClassesInPath.isEmpty(); + return true; + } + // directCascadedBeanClass does not have a cycle. + // directCascadedBeanClass would have already been removed by the + // call to #determineTrackingRequired above + } + beanClassesInPath.remove( beanClass ); + return register( beanClass, false ); + } + + // TODO: is there a more concise way to do this? + private Set> getDirectCascadedBeanClasses(Class beanClass ) { + final BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( beanClass ); + if ( beanMetaData.hasCascadables() ) { + final Set> directCascadedBeanClasses = new HashSet<>(); + for ( Cascadable cascadable : beanMetaData.getCascadables() ) { + final CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData(); + if ( cascadingMetaData.isContainer() ) { + throw new UnsupportedOperationException( "Containers are not supported yet." ); + } + else { + // TODO: For now, assume non-container Cascadables are always beans. Truee??? + directCascadedBeanClasses.add( (Class) cascadable.getCascadableType() ); + } + } + return directCascadedBeanClasses; + } + else { + return Collections.emptySet(); + } + } + + private boolean register(Class beanClass, boolean isBeanTrackingEnabled) { + if ( classToBeanTrackingEnabled.put( beanClass, isBeanTrackingEnabled ) != null ) { + throw new IllegalStateException( beanClass.getName() + " registered more than once." ); + } + return isBeanTrackingEnabled; + } + } + @Override public boolean isEnabledForBean(Class rootBeanClass, boolean hasCascadables) { if ( !hasCascadables ) { return false; } - return trackingEnabledForBeans.getOrDefault( rootBeanClass, true ); + return trackingEnabledForBeans.get( rootBeanClass ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java index a41641859f..45a2efd102 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java @@ -95,7 +95,7 @@ private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTracking return false; } - return processedBeansTrackingStrategy.isEnabledForReturnValue( executable, + return processedBeansTrackingStrategy.isEnabledForParameters( executable, executableMetaData.get().getValidatableParametersMetaData().hasCascadables() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java index 9da4aba204..1d55accef0 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java @@ -6,13 +6,25 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import javax.validation.ConstraintViolation; import javax.validation.Valid; +import javax.validation.Validation; import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.Email; import javax.validation.constraints.NotNull; +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.test.predefinedscope.LocaleResolverTest; import org.hibernate.validator.testutils.ValidatorUtil; import org.testng.annotations.Test; +import static org.testng.Assert.assertTrue; + /** * This is not a real test, just an illustration. *

@@ -23,14 +35,56 @@ public class ProcessedBeansTrackingCycles1Test { @Test - public void testSerializeHibernateEmail() throws Exception { - Validator validator = ValidatorUtil.getValidator(); + public void testValidNull() throws Exception { + final Parent parent = new Parent( "parent property" ); + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNull() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + + Set> violations = getValidator().validate( parent ); + //Set> violations = ValidatorUtil.getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNullNonCyclic() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + parent.child.parent = new Parent( "other parent property" ); + + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); + } + + @Test + public void testValidNotNullCyclic() throws Exception { + final Parent parent = new Parent( "parent property" ); + parent.child = new Child( "child property" ); + parent.child.parent = parent; - validator.validate( new Parent() ); + Set> violations = getValidator().validate( parent ); + assertTrue( violations.isEmpty() ); } + private Validator getValidator() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Parent.class, Child.class, Other.class) ) ) + .buildValidatorFactory() + .getValidator(); + } private static class Parent { + Parent(String property) { + this.property = property; + } + @NotNull private String property; @@ -40,10 +94,26 @@ private static class Parent { private static class Child { + Child(String property) { + this.property = property; + } + @NotNull private String property; @Valid private Parent parent; + + @Valid + private Other other; + } + + private static class Other { + Other(String property) { + this.property = property; + } + + @NotNull + private String property; } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java new file mode 100644 index 0000000000..2387dce609 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java @@ -0,0 +1,207 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * An example of beans with cascading constraints, some cycle and others do not. + * + * A -> B ---> C ------> F -> G <- + * | ^ | ^ ^ | + * | | | | | | + * | -- D <-- | | | + * --------------------> E ------- + * + * A, B, C, D, E, F, and G are beans that get validated. + * + * An arrow, ->, indicates a cascading constraint. + * + * The following are the properties with cascading constraints: + * A.b + * .e + * B.c + * C.d + * .f + * D.b + * E.f + * .g + * F.g + * + * @author Gail Badner + * + */ + +public class ProcessedBeansTrackingCyclesNoCyclesTest { + + @Test + public void testTrackingEnabled() { + + final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = + validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + A.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + B.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + C.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + D.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + E.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + F.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + G.class, + false + ) ); + } + + @Test + public void testValidate() { + final A a = new A(); + final B b = new B(); + final C c = new C(); + final D d = new D(); + final E e = new E(); + final F f = new F(); + final G g = new G(); + + a.b = b; + a.e = e; + b.c = c; + c.d = d; + d.b = b; + e.f = f; + e.g = g; + e.gAnother = g; + f.g = g; + + final Validator validator = getValidator(); + final Set> violationsA = validator.validate( a ); + final Set> violationsB = validator.validate( b ); + final Set> violationsC = validator.validate( c ); + final Set> violationsD = validator.validate( d ); + final Set> violationsE = validator.validate( e ); + final Set> violationsF = validator.validate( f ); + final Set> violationsG = validator.validate( g ); + } + + private Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( + A.class, B.class, C.class, D.class, E.class, F.class, G.class + ) ) ) + .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); + } + + private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + } + + private static class A { + + private String description; + + @Valid + private B b; + + @Valid + private E e; + } + + private static class B { + @Valid + private String description; + + @Valid + private C c; + } + + private static class C { + + private String description; + + @Valid + private D d; + + @Valid + private F f; + } + + private static class D { + + private String description; + + @Valid + private B b; + } + + private static class E { + + private String description; + + @Valid + private F f; + + @Valid + private G g; + + @Valid + private G gAnother; + } + + private static class F { + + private String description; + + @Valid + private G g; + } + + private static class G { + + private String description; + } +} From 21d7d7bd2d51512bc243dacd4b4c49e7f6a3c940 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 15 Apr 2021 14:42:31 -0700 Subject: [PATCH 07/16] Correct checkstyle failures --- .../PredefinedScopeProcessedBeansTrackingStrategy.java | 2 +- .../engine/tracking/ProcessedBeansTrackingCycles1Test.java | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java index 7c1c8503d3..c83d7175ce 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -53,7 +53,7 @@ private static class TrackingEnabledStrategyBuilder { public Map, Boolean> build() { final Set> beanClassesInPath = new HashSet<>(); - for (BeanMetaData beanMetadata : beanMetaDataManager.getBeanMetaData() ) { + for ( BeanMetaData beanMetadata : beanMetaDataManager.getBeanMetaData() ) { determineTrackingRequired( beanMetadata.getBeanClass(), beanClassesInPath ); if ( !beanClassesInPath.isEmpty() ) { throw new IllegalStateException( "beanClassesInPath not empty" ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java index 1d55accef0..8a6e512bb3 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCycles1Test.java @@ -7,20 +7,15 @@ package org.hibernate.validator.test.internal.engine.tracking; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validation; import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.constraints.Email; import javax.validation.constraints.NotNull; import org.hibernate.validator.PredefinedScopeHibernateValidator; -import org.hibernate.validator.test.predefinedscope.LocaleResolverTest; -import org.hibernate.validator.testutils.ValidatorUtil; import org.testng.annotations.Test; import static org.testng.Assert.assertTrue; @@ -75,7 +70,7 @@ private Validator getValidator() { return Validation.byProvider( PredefinedScopeHibernateValidator.class ) .configure() .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) - .initializeBeanMetaData( new HashSet<>( Arrays.asList( Parent.class, Child.class, Other.class) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Parent.class, Child.class, Other.class ) ) ) .buildValidatorFactory() .getValidator(); } From 5fbe22f865c625e7f56eafeb59249c7a24ed17dc Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 22 Apr 2021 20:30:21 -0700 Subject: [PATCH 08/16] HV-1831 : Experiment detecting cycles in bean classes Add support for containers; add tests for List w/ and w/o duplicated values --- ...edScopeProcessedBeansTrackingStrategy.java | 19 +- ...clesNoCyclesListDuplicateElementsTest.java | 209 ++++++++++++++++++ ...edBeansTrackingCyclesNoCyclesListTest.java | 200 +++++++++++++++++ 3 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java index c83d7175ce..167f0856e5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -7,6 +7,8 @@ package org.hibernate.validator.internal.engine.tracking; import java.lang.reflect.Executable; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -16,6 +18,7 @@ import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; +import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.facets.Cascadable; import org.hibernate.validator.internal.util.CollectionHelper; @@ -163,7 +166,21 @@ private Set> getDirectCascadedBeanClasses(Class beanClass ) { for ( Cascadable cascadable : beanMetaData.getCascadables() ) { final CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData(); if ( cascadingMetaData.isContainer() ) { - throw new UnsupportedOperationException( "Containers are not supported yet." ); + final ContainerCascadingMetaData containerCascadingMetaData = (ContainerCascadingMetaData) cascadingMetaData; + if ( containerCascadingMetaData.getEnclosingType() instanceof ParameterizedType ) { + ParameterizedType parameterizedType = (ParameterizedType) containerCascadingMetaData.getEnclosingType(); + for ( Type typeArgument : parameterizedType.getActualTypeArguments() ) { + if ( typeArgument instanceof Class ) { + directCascadedBeanClasses.add( (Class) typeArgument ); + } + else { + throw new UnsupportedOperationException( "Only ParameterizedType values of type Class are supported" ); + } + } + } + else { + throw new UnsupportedOperationException( "Non-parameterized containers are not supported yet." ); + } } else { // TODO: For now, assume non-container Cascadables are always beans. Truee??? diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java new file mode 100644 index 0000000000..5be2a6fe6f --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java @@ -0,0 +1,209 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * An example of beans with cascading constraints, some cycle and others do not. + * + * A -> B ---> C ------> F -> G <- + * | ^ | ^ ^ | + * | | | | | | + * | -- D <-- | | | + * --------------------> E ------- + * + * A, B, C, D, E, F, and G are beans that get validated. + * + * An arrow, ->, indicates a cascading constraint. + * + * The following are the properties with cascading List constraints: + * A.bValues + * .eValues + * B.cValues + * C.dValues + * .fValues + * D.bValues + * E.fValues + * .gValues + * .gAnotherValues + * F.gValues + * + * @author Gail Badner + * + */ + +public class ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest { + + @Test + public void testTrackingEnabled() { + + final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = + validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + A.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + B.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + C.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + D.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + E.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + F.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + G.class, + false + ) ); + } + + @Test + public void testValidate() { + final A a = new A(); + final B b = new B(); + final C c = new C(); + final D d = new D(); + final E e = new E(); + final F f = new F(); + final G g = new G(); + + a.bValues.add( b ); + a.bValues.add( b ); + a.eValues.add( e ); + a.eValues.add( e ); + b.cValues.add( c ); + b.cValues.add( c ); + c.dValues.add( d ); + c.dValues.add( d ); + d.bValues.add( b ); + d.bValues.add( b ); + e.fValues.add( f ); + e.fValues.add( f ); + e.gValues.add( g ); + e.gValues.add( g ); + e.gAnotherValues.add( g ); + e.gAnotherValues.add( g ); + f.gValues.add( g ); + f.gValues.add( g ); + + final Validator validator = getValidator(); + final Set> violationsA = validator.validate( a ); + final Set> violationsB = validator.validate( b ); + final Set> violationsC = validator.validate( c ); + final Set> violationsD = validator.validate( d ); + final Set> violationsE = validator.validate( e ); + final Set> violationsF = validator.validate( f ); + final Set> violationsG = validator.validate( g ); + } + + private Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( + A.class, B.class, C.class, D.class, E.class, F.class, G.class + ) ) ) + .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); + } + + private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + } + + private static class A { + + private String description; + + private List<@Valid B> bValues = new ArrayList<>(); + + private List<@Valid E> eValues = new ArrayList<>(); + } + + private static class B { + @Valid + private String description; + + private List<@Valid C> cValues = new ArrayList<>(); + } + + private static class C { + + private String description; + + private List<@Valid D> dValues = new ArrayList<>(); + + private List<@Valid F> fValues = new ArrayList<>(); + } + + private static class D { + + private String description; + + private List<@Valid B> bValues = new ArrayList<>(); + } + + private static class E { + + private String description; + + private List<@Valid F> fValues = new ArrayList<>(); + + private List<@Valid G> gValues = new ArrayList<>(); + + private List<@Valid G> gAnotherValues = new ArrayList<>(); + } + + private static class F { + + private String description; + + private List<@Valid G> gValues = new ArrayList<>(); + } + + private static class G { + + private String description; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java new file mode 100644 index 0000000000..b32982237f --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java @@ -0,0 +1,200 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * An example of beans with cascading constraints, some cycle and others do not. + * + * A -> B ---> C ------> F -> G <- + * | ^ | ^ ^ | + * | | | | | | + * | -- D <-- | | | + * --------------------> E ------- + * + * A, B, C, D, E, F, and G are beans that get validated. + * + * An arrow, ->, indicates a cascading constraint. + * + * The following are the properties with cascading List constraints: + * A.bValues + * .eValues + * B.cValues + * C.dValues + * .fValues + * D.bValues + * E.fValues + * .gValues + * .gAnotherValues + * F.gValues + * + * @author Gail Badner + * + */ + +public class ProcessedBeansTrackingCyclesNoCyclesListTest { + + @Test + public void testTrackingEnabled() { + + final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = + validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + A.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + B.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + C.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + D.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + E.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + F.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + G.class, + false + ) ); + } + + @Test + public void testValidate() { + final A a = new A(); + final B b = new B(); + final C c = new C(); + final D d = new D(); + final E e = new E(); + final F f = new F(); + final G g = new G(); + + a.bValues.add( b ); + a.eValues.add( e ); + b.cValues.add( c ); + c.dValues.add( d ); + d.bValues.add( b ); + e.fValues.add( f ); + e.gValues.add( g ); + e.gAnotherValues.add( g ); + f.gValues.add( g ); + + final Validator validator = getValidator(); + final Set> violationsA = validator.validate( a ); + final Set> violationsB = validator.validate( b ); + final Set> violationsC = validator.validate( c ); + final Set> violationsD = validator.validate( d ); + final Set> violationsE = validator.validate( e ); + final Set> violationsF = validator.validate( f ); + final Set> violationsG = validator.validate( g ); + } + + private Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( + A.class, B.class, C.class, D.class, E.class, F.class, G.class + ) ) ) + .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); + } + + private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + } + + private static class A { + + private String description; + + private List<@Valid B> bValues = new ArrayList<>(); + + private List<@Valid E> eValues = new ArrayList<>(); + } + + private static class B { + @Valid + private String description; + + private List<@Valid C> cValues = new ArrayList<>(); + } + + private static class C { + + private String description; + + private List<@Valid D> dValues = new ArrayList<>(); + + private List<@Valid F> fValues = new ArrayList<>(); + } + + private static class D { + + private String description; + + private List<@Valid B> bValues = new ArrayList<>(); + } + + private static class E { + + private String description; + + private List<@Valid F> fValues = new ArrayList<>(); + + private List<@Valid G> gValues = new ArrayList<>(); + + private List<@Valid G> gAnotherValues = new ArrayList<>(); + } + + private static class F { + + private String description; + + private List<@Valid G> gValues = new ArrayList<>(); + } + + private static class G { + + private String description; + } +} From d4a4f4ca3da10e5cb15f9604f342921187bf1786 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 29 Apr 2021 19:42:52 -0700 Subject: [PATCH 09/16] HV-1831 : Experiment detecting cycles in bean classes Add test for Map --- ...sedBeansTrackingCyclesNoCyclesMapTest.java | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java new file mode 100644 index 0000000000..82c8b7188a --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java @@ -0,0 +1,189 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.tracking; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; +import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; +import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * An example of beans with cascading constraints, some cycle and others do not. + * + * A -> B ---> C ------> F -> G <- + * | ^ | ^ ^ | + * | | | | | | + * | -- D <-- | | | + * --------------------> E ------- + * + * A, B, C, D, E, F, and G are beans that get validated. + * + * An arrow, ->, indicates a cascading constraint.ProcessedBeansTrackingCyclesNoCyclesMapTest + * + * The following are the properties with cascading Map constraints: + * A.bToEs + * B.cToCs + * C.dToFs + * D.bToBs + * E.fToGs + * .gToGs + * F.gToGs + * + * @author Gail Badner + * + */ + +public class ProcessedBeansTrackingCyclesNoCyclesMapTest { + + @Test + public void testTrackingEnabled() { + + final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = + validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + A.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + B.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + C.class, + true + ) ); + assertTrue( processedBeansTrackingStrategy.isEnabledForBean( + D.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + E.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + F.class, + true + ) ); + assertFalse( processedBeansTrackingStrategy.isEnabledForBean( + G.class, + false + ) ); + } + + @Test + public void testValidate() { + final A a = new A(); + final B b = new B(); + final C c = new C(); + final D d = new D(); + final E e = new E(); + final F f = new F(); + final G g = new G(); + + a.bToEs.put( b, e ); + b.cToCs.put( c, c ); + c.dToFs.put( d, f ); + d.bToBs.put( b, b ); + e.fToGs.put( f, g ); + e.gToGs.put( g, g ); + + final Validator validator = getValidator(); + final Set> violationsA = validator.validate( a ); + final Set> violationsB = validator.validate( b ); + final Set> violationsC = validator.validate( c ); + final Set> violationsD = validator.validate( d ); + final Set> violationsE = validator.validate( e ); + final Set> violationsF = validator.validate( f ); + final Set> violationsG = validator.validate( g ); + } + + private Validator getValidator() { + return getValidatorFactory().getValidator(); + } + + private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { + return Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( + A.class, B.class, C.class, D.class, E.class, F.class, G.class + ) ) ) + .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); + } + + private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + } + + private static class A { + + private String description; + + private Map<@Valid B, @Valid E> bToEs = new HashMap<>(); + + } + + private static class B { + @Valid + private String description; + + private Map<@Valid C, @Valid C> cToCs = new HashMap<>(); + } + + private static class C { + + private String description; + + private Map<@Valid D, @Valid F> dToFs = new HashMap<>(); + } + + private static class D { + + private String description; + + private Map<@Valid B, @Valid B> bToBs = new HashMap<>(); + } + + private static class E { + + private String description; + + private Map<@Valid F, G> fToGs = new HashMap<>(); + + private Map<@Valid G, @Valid G> gToGs = new HashMap<>(); + } + + private static class F { + + private String description; + + private Map<@Valid G, @Valid G> gToGs = new HashMap<>(); + } + + private static class G { + + private String description; + } +} From 2bf5eec9099ad436b4a092ec4e9be4001a32d259 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 29 Apr 2021 19:44:28 -0700 Subject: [PATCH 10/16] HV-1831 : Update Cascade tests to use PredefinedScopeHibernateValidator with -p=predefined=true --- performance/pom.xml | 2 +- .../performance/BenchmarkRunner.java | 42 +++++++++++++++++++ .../cascaded/CascadedValidation.java | 8 +++- ...tsOfItemsAndMoreConstraintsValidation.java | 10 ++++- .../CascadedWithLotsOfItemsValidation.java | 10 ++++- 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/performance/pom.xml b/performance/pom.xml index e54f562629..79bcd1421b 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -207,7 +207,7 @@ 2.0.1.Final Hibernate Validator - 6.2.0.Final + 6.2.1-SNAPSHOT diff --git a/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java b/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java index 728e66cc81..73ea3386cb 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java +++ b/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java @@ -6,9 +6,21 @@ */ package org.hibernate.validator.performance; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; +import javax.validation.spi.ValidationProvider; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.performance.cascaded.CascadedValidation; import org.hibernate.validator.performance.cascaded.CascadedWithLotsOfItemsValidation; import org.hibernate.validator.performance.simple.SimpleValidation; @@ -22,6 +34,7 @@ import org.openjdk.jmh.runner.options.CommandLineOptions; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.util.Optional; /** * Class containing main method to run all performance tests. @@ -31,6 +44,10 @@ */ public final class BenchmarkRunner { + private static String PREDEFINED_PARAMETER = "predefined"; + private static boolean IS_PREDEFINED = false; + private static ValidationProvider validationProvider; + private static final Stream> DEFAULT_TEST_CLASSES = Stream.of( SimpleValidation.class.getName(), CascadedValidation.class.getName(), @@ -58,10 +75,35 @@ public static void main(String[] args) throws RunnerException, CommandLineOption DEFAULT_TEST_CLASSES.forEach( testClass -> builder.include( testClass.getName() ) ); } + IS_PREDEFINED = isPredefined( commandLineOptions ); + Options opt = builder.build(); new Runner( opt ).run(); } + public static ValidatorFactory buildValidatorFactory(Set constraintNames, Set> beanClasses) { + return IS_PREDEFINED + ? Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( constraintNames ) + .initializeBeanMetaData( beanClasses ) + .buildValidatorFactory() + : Validation.buildDefaultValidatorFactory(); + } + + private static boolean isPredefined(Options commandLineOptions) throws CommandLineOptionException { + Optional> isPredefinedValues = commandLineOptions.getParameter( PREDEFINED_PARAMETER ); + if ( isPredefinedValues.hasValue() ) { + if ( isPredefinedValues.get().size() == 1 ) { + return Boolean.parseBoolean( isPredefinedValues.get().iterator().next() ); + } + else { + throw new CommandLineOptionException( "More than one value provided for parameter: " + PREDEFINED_PARAMETER ); + } + } + return false; + } + private static Class classForName(String qualifiedName) { try { return Class.forName( qualifiedName ); diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java index 39007dcfa3..4c21a38ba4 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java @@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -19,6 +20,8 @@ import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; +import org.hibernate.validator.performance.BenchmarkRunner; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -42,7 +45,10 @@ public static class CascadedValidationState { public volatile Person person; public CascadedValidationState() { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( + Collections.singleton( NotNull.class.getName() ), + Collections.singleton( Person.class ) + ); validator = factory.getValidator(); // TODO graphs needs to be generated and deeper diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java index b1110997bd..eefae8e153 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java @@ -9,18 +9,21 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.validation.ConstraintViolation; import javax.validation.Valid; -import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.hibernate.validator.performance.BenchmarkRunner; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -47,7 +50,10 @@ public static class CascadedWithLotsOfItemsValidationState { public volatile Shop shop; public CascadedWithLotsOfItemsValidationState() { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( + new HashSet<>( Arrays.asList( NotNull.class.getName(), Size.class.getName() ) ), + new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) + ); validator = factory.getValidator(); shop = createShop(); diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java index 08c015471e..eca3e4452c 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java @@ -9,6 +9,9 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -20,6 +23,8 @@ import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; +import org.hibernate.validator.performance.BenchmarkRunner; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -46,7 +51,10 @@ public static class CascadedWithLotsOfItemsValidationState { public volatile Shop shop; public CascadedWithLotsOfItemsValidationState() { - ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( + Collections.singleton( NotNull.class.getName() ), + new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) + ); validator = factory.getValidator(); shop = createShop(); From eec033f744a400477cfcc26cccc6c9a110a2c2b8 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Apr 2021 10:22:00 +0200 Subject: [PATCH 11/16] HV-1831 Specific benchmark infrastructure for predefined scope --- performance/README.md | 7 +- performance/pom.xml | 69 +++++++++- .../PredefinedScopeCascadedValidation.java | 100 ++++++++++++++ ...tsOfItemsAndMoreConstraintsValidation.java | 124 ++++++++++++++++++ ...copeCascadedWithLotsOfItemsValidation.java | 116 ++++++++++++++++ .../performance/BenchmarkRunner.java | 43 ------ .../cascaded/CascadedValidation.java | 8 +- ...tsOfItemsAndMoreConstraintsValidation.java | 10 +- .../CascadedWithLotsOfItemsValidation.java | 10 +- 9 files changed, 412 insertions(+), 75 deletions(-) create mode 100644 performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedValidation.java create mode 100644 performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsAndMoreConstraintsValidation.java create mode 100644 performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsValidation.java diff --git a/performance/README.md b/performance/README.md index 01e63dc38b..a2261f4e21 100644 --- a/performance/README.md +++ b/performance/README.md @@ -11,8 +11,9 @@ To allow performance testing of different Hibernate Validator versions there are Choosing a profile executes the tests against the specified Hibernate Validator or BVal version, respectively. The defined profiles are: -* hv-current (Hibernate Validator 6.1.0-SNAPSHOT) -* hv-6.0 (Hibernate Validator 6.0.15.Final) +* hv-current (Hibernate Validator 6.2.1-SNAPSHOT) +* hv-6.1 (Hibernate Validator 6.1.7.Final) +* hv-6.0 (Hibernate Validator 6.0.22.Final) * hv-5.4 (Hibernate Validator 5.4.3.Final) * hv-5.3 (Hibernate Validator 5.3.4.Final) * hv-5.2 (Hibernate Validator 5.2.4.Final) @@ -64,7 +65,7 @@ If you want to run one of those profilers - pass it as parameter when running a To run a specific benchmark: - java -jar target/hibernate-validator-performance.jar CascadedValidation + java -jar target/hibernate-validator-performance-hv-current.jar CascadedValidation #### Creating reports for all major Hibernate Validator versions diff --git a/performance/pom.xml b/performance/pom.xml index 79bcd1421b..d51bbf2f1a 100644 --- a/performance/pom.xml +++ b/performance/pom.xml @@ -186,12 +186,27 @@ log4j-core - + org.codehaus.mojo build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/src/main/java-bv2 + ${project.basedir}/src/main/java-predefined-scope + + + + @@ -230,12 +245,27 @@ log4j-core - + org.codehaus.mojo build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/src/main/java-bv2 + ${project.basedir}/src/main/java-predefined-scope + + + + @@ -250,7 +280,7 @@ Hibernate Validator - 6.1.2.Final + 6.1.7.Final @@ -271,12 +301,27 @@ log4j-core - + org.codehaus.mojo build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/src/main/java-bv2 + ${project.basedir}/src/main/java-predefined-scope + + + + @@ -292,7 +337,7 @@ 2.0.1.Final Hibernate Validator - 6.0.19.Final + 6.0.22.Final @@ -321,6 +366,20 @@ org.codehaus.mojo build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/src/main/java-bv2 + + + + diff --git a/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedValidation.java b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedValidation.java new file mode 100644 index 0000000000..46944777b3 --- /dev/null +++ b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedValidation.java @@ -0,0 +1,100 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.performance.cascaded; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Hardy Ferentschik + * @author Guillaume Smet + */ +public class PredefinedScopeCascadedValidation { + + @State(Scope.Benchmark) + public static class PredefinedScopeCascadedValidationState { + + public volatile Validator validator; + public volatile Person person; + + public PredefinedScopeCascadedValidationState() { + ValidatorFactory factory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( Collections.singleton( NotNull.class.getName() ) ) + .initializeBeanMetaData( Collections.singleton( Person.class ) ) + .buildValidatorFactory(); + validator = factory.getValidator(); + + // TODO graphs needs to be generated and deeper + Person kermit = new Person( "kermit" ); + Person piggy = new Person( "miss piggy" ); + Person gonzo = new Person( "gonzo" ); + + kermit.addFriend( piggy ).addFriend( gonzo ); + piggy.addFriend( kermit ).addFriend( gonzo ); + gonzo.addFriend( kermit ).addFriend( piggy ); + + person = kermit; + } + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + @Fork(value = 1) + @Threads(50) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testPredefinedScopeCascadedValidation(PredefinedScopeCascadedValidationState state, Blackhole bh) { + Set> violations = state.validator.validate( state.person ); + assertThat( violations ).hasSize( 0 ); + + bh.consume( violations ); + } + + public static class Person { + + @NotNull + String name; + + @Valid + Set friends = new HashSet<>(); + + public Person(String name) { + this.name = name; + } + + public Person addFriend(Person friend) { + friends.add( friend ); + return this; + } + } +} diff --git a/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsAndMoreConstraintsValidation.java b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsAndMoreConstraintsValidation.java new file mode 100644 index 0000000000..e55523daa1 --- /dev/null +++ b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsAndMoreConstraintsValidation.java @@ -0,0 +1,124 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.performance.cascaded; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Guillaume Smet + */ +public class PredefinedScopeCascadedWithLotsOfItemsAndMoreConstraintsValidation { + + private static final int NUMBER_OF_ARTICLES_PER_SHOP = 2000; + + @State(Scope.Benchmark) + public static class PredefinedScopeCascadedWithLotsOfItemsValidationState { + + public volatile Validator validator; + + public volatile Shop shop; + + public PredefinedScopeCascadedWithLotsOfItemsValidationState() { + ValidatorFactory factory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( new HashSet<>( Arrays.asList( NotNull.class.getName(), Size.class.getName() ) ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) ) + .buildValidatorFactory(); + validator = factory.getValidator(); + + shop = createShop(); + } + + private Shop createShop() { + Shop shop = new Shop( 1, "Shop" ); + + for ( int i = 0; i < NUMBER_OF_ARTICLES_PER_SHOP; i++ ) { + shop.addArticle( new Article( i, "Article " + i ) ); + } + + return shop; + } + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + @Fork(value = 1) + @Threads(20) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testPredefinedScopeCascadedValidationWithLotsOfItems(PredefinedScopeCascadedWithLotsOfItemsValidationState state, Blackhole bh) { + Set> violations = state.validator.validate( state.shop ); + assertThat( violations ).hasSize( 0 ); + + bh.consume( violations ); + } + + public static class Shop { + + @NotNull + private Integer id; + + @Size(min = 1) + private String name; + + @NotNull + @Valid + private List

articles = new ArrayList<>(); + + public Shop(Integer id, String name) { + this.id = id; + this.name = name; + } + + public void addArticle(Article article) { + articles.add( article ); + } + } + + public static class Article { + + @NotNull + private Integer id; + + @Size(min = 1) + private String name; + + public Article(Integer id, String name) { + this.id = id; + this.name = name; + } + } +} diff --git a/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsValidation.java b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsValidation.java new file mode 100644 index 0000000000..c5b8a6e418 --- /dev/null +++ b/performance/src/main/java-predefined-scope/org/hibernate/validator/performance/cascaded/PredefinedScopeCascadedWithLotsOfItemsValidation.java @@ -0,0 +1,116 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.performance.cascaded; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.validation.ConstraintViolation; +import javax.validation.Valid; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotNull; + +import org.hibernate.validator.PredefinedScopeHibernateValidator; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/** + * @author Guillaume Smet + */ +public class PredefinedScopeCascadedWithLotsOfItemsValidation { + + private static final int NUMBER_OF_ARTICLES_PER_SHOP = 2000; + + @State(Scope.Benchmark) + public static class PredefinedScopeCascadedWithLotsOfItemsValidationState { + + public volatile Validator validator; + + public volatile Shop shop; + + public PredefinedScopeCascadedWithLotsOfItemsValidationState() { + ValidatorFactory factory = Validation.byProvider( PredefinedScopeHibernateValidator.class ) + .configure() + .builtinConstraints( Collections.singleton( NotNull.class.getName() ) ) + .initializeBeanMetaData( new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) ) + .buildValidatorFactory(); + validator = factory.getValidator(); + + shop = createShop(); + } + + private Shop createShop() { + Shop shop = new Shop( 1 ); + + for ( int i = 0; i < NUMBER_OF_ARTICLES_PER_SHOP; i++ ) { + shop.addArticle( new Article( i ) ); + } + + return shop; + } + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + @Fork(value = 1) + @Threads(20) + @Warmup(iterations = 10) + @Measurement(iterations = 20) + public void testPredefinedScopeCascadedValidationWithLotsOfItems(PredefinedScopeCascadedWithLotsOfItemsValidationState state, Blackhole bh) { + Set> violations = state.validator.validate( state.shop ); + assertThat( violations ).hasSize( 0 ); + + bh.consume( violations ); + } + + public static class Shop { + + @NotNull + private Integer id; + + @NotNull + @Valid + private List
articles = new ArrayList<>(); + + public Shop(Integer id) { + this.id = id; + } + + public void addArticle(Article article) { + articles.add( article ); + } + } + + public static class Article { + + @NotNull + private Integer id; + + public Article(Integer id) { + this.id = id; + } + } +} diff --git a/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java b/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java index 73ea3386cb..a887c92fe5 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java +++ b/performance/src/main/java/org/hibernate/validator/performance/BenchmarkRunner.java @@ -6,26 +6,13 @@ */ package org.hibernate.validator.performance; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Locale; import java.util.Objects; -import java.util.Set; import java.util.stream.Stream; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.constraints.NotNull; -import javax.validation.spi.ValidationProvider; - -import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.performance.cascaded.CascadedValidation; import org.hibernate.validator.performance.cascaded.CascadedWithLotsOfItemsValidation; import org.hibernate.validator.performance.simple.SimpleValidation; import org.hibernate.validator.performance.statistical.StatisticalValidation; - import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; @@ -34,7 +21,6 @@ import org.openjdk.jmh.runner.options.CommandLineOptions; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; -import org.openjdk.jmh.util.Optional; /** * Class containing main method to run all performance tests. @@ -44,10 +30,6 @@ */ public final class BenchmarkRunner { - private static String PREDEFINED_PARAMETER = "predefined"; - private static boolean IS_PREDEFINED = false; - private static ValidationProvider validationProvider; - private static final Stream> DEFAULT_TEST_CLASSES = Stream.of( SimpleValidation.class.getName(), CascadedValidation.class.getName(), @@ -75,35 +57,10 @@ public static void main(String[] args) throws RunnerException, CommandLineOption DEFAULT_TEST_CLASSES.forEach( testClass -> builder.include( testClass.getName() ) ); } - IS_PREDEFINED = isPredefined( commandLineOptions ); - Options opt = builder.build(); new Runner( opt ).run(); } - public static ValidatorFactory buildValidatorFactory(Set constraintNames, Set> beanClasses) { - return IS_PREDEFINED - ? Validation.byProvider( PredefinedScopeHibernateValidator.class ) - .configure() - .builtinConstraints( constraintNames ) - .initializeBeanMetaData( beanClasses ) - .buildValidatorFactory() - : Validation.buildDefaultValidatorFactory(); - } - - private static boolean isPredefined(Options commandLineOptions) throws CommandLineOptionException { - Optional> isPredefinedValues = commandLineOptions.getParameter( PREDEFINED_PARAMETER ); - if ( isPredefinedValues.hasValue() ) { - if ( isPredefinedValues.get().size() == 1 ) { - return Boolean.parseBoolean( isPredefinedValues.get().iterator().next() ); - } - else { - throw new CommandLineOptionException( "More than one value provided for parameter: " + PREDEFINED_PARAMETER ); - } - } - return false; - } - private static Class classForName(String qualifiedName) { try { return Class.forName( qualifiedName ); diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java index 4c21a38ba4..39007dcfa3 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedValidation.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -20,8 +19,6 @@ import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; -import org.hibernate.validator.performance.BenchmarkRunner; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -45,10 +42,7 @@ public static class CascadedValidationState { public volatile Person person; public CascadedValidationState() { - ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( - Collections.singleton( NotNull.class.getName() ), - Collections.singleton( Person.class ) - ); + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); // TODO graphs needs to be generated and deeper diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java index eefae8e153..b1110997bd 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsAndMoreConstraintsValidation.java @@ -9,21 +9,18 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.validation.ConstraintViolation; import javax.validation.Valid; +import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.hibernate.validator.performance.BenchmarkRunner; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -50,10 +47,7 @@ public static class CascadedWithLotsOfItemsValidationState { public volatile Shop shop; public CascadedWithLotsOfItemsValidationState() { - ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( - new HashSet<>( Arrays.asList( NotNull.class.getName(), Size.class.getName() ) ), - new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) - ); + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); shop = createShop(); diff --git a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java index eca3e4452c..08c015471e 100644 --- a/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java +++ b/performance/src/main/java/org/hibernate/validator/performance/cascaded/CascadedWithLotsOfItemsValidation.java @@ -9,9 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -23,8 +20,6 @@ import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; -import org.hibernate.validator.performance.BenchmarkRunner; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -51,10 +46,7 @@ public static class CascadedWithLotsOfItemsValidationState { public volatile Shop shop; public CascadedWithLotsOfItemsValidationState() { - ValidatorFactory factory = BenchmarkRunner.buildValidatorFactory( - Collections.singleton( NotNull.class.getName() ), - new HashSet<>( Arrays.asList( Shop.class, Article.class ) ) - ); + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); shop = createShop(); From dc1cdb480f8e81578c2feeb3504bbfb615994ad8 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 20 May 2021 19:30:59 +0200 Subject: [PATCH 12/16] HV-1831 Add some guidance about next step --- .../PredefinedScopeValidatorFactoryImpl.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 5b96537c63..d9cf0ffa8d 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 @@ -163,6 +163,20 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState registerCustomConstraintValidators( constraintMappings, constraintHelper ); + // collect all metadata, I don't think we need this work to be in BeanMetaDataManager contract, it can be a specific class (or private method if simple enough) + // it's basically the content of PredefinedScopeBeanMetaDataManager constructor + // the metadata wouldn't be complete because we want to inject the tracking information + + // then you build the tracking information from these incomplete metadata + + // finally you create a PredefinedScopeBeanMetaDataManager with the augmented metadata pushed to it + // you will need to augment both BeanMetaData and ExecutableMetaData + // I would prototype BeanMetaData first then discuss it before going further + + // Note: we want classes to be immutable + // Might be a good idea to push a default method to BeanMetaData as enabling tracking is the default behavior we want + // Maybe first try composition and benchmark it and if good enough, we keep it + this.beanMetaDataManager = new PredefinedScopeBeanMetaDataManager( constraintCreationContext, executableHelper, From ee2233173bde50d8a90bf075c29e62ee73e4a5b6 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 19 Jul 2021 21:17:11 -0700 Subject: [PATCH 13/16] HV-1831 : Wrap a `BeanMetaData` in a `NonTrackedBeanMetaDataImpl` if tracking is not required --- .../PredefinedScopeValidatorFactoryImpl.java | 7 +- .../BeanValidationContext.java | 9 +- .../PredefinedScopeBeanMetaDataManager.java | 46 +++++++-- .../metadata/aggregated/BeanMetaData.java | 8 ++ .../NonTrackedBeanMetaDataImpl.java | 96 +++++++++++++++++++ 5 files changed, 144 insertions(+), 22 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java 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 d9cf0ffa8d..2e60f556a1 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 @@ -47,7 +47,6 @@ import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; -import org.hibernate.validator.internal.engine.tracking.PredefinedScopeProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -186,7 +185,7 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ), methodValidationConfiguration, determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ), - hibernateSpecificConfig.getBeanClassesToInitialize() + hibernateSpecificConfig ); this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext( @@ -201,9 +200,7 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState determineConstraintValidatorPayload( hibernateSpecificConfig ), determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) - ? hibernateSpecificConfig.getProcessedBeansTrackingStrategy() - : new PredefinedScopeProcessedBeansTrackingStrategy( beanMetaDataManager ), + beanMetaDataManager.getProcessedBeansTrackingStrategy(), constraintValidatorInitializationContext ); if ( LOG.isDebugEnabled() ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java index de2789ac4b..fd22e4880b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java @@ -16,7 +16,6 @@ import org.hibernate.validator.internal.engine.ConstraintViolationImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; @@ -38,16 +37,10 @@ class BeanValidationContext extends AbstractValidationContext { BeanMetaData rootBeanMetaData ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, - rootBean, rootBeanClass, rootBeanMetaData, buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), - rootBeanClass, rootBeanMetaData ) + rootBean, rootBeanClass, rootBeanMetaData, rootBeanMetaData.isTrackingRequired() ); } - private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, Class rootBeanClass, - BeanMetaData rootBeanMetaData) { - return processedBeansTrackingStrategy.isEnabledForBean( rootBeanClass, rootBeanMetaData.hasCascadables() ); - } - @Override protected ConstraintViolation createConstraintViolation( String messageTemplate, String interpolatedMessage, Path propertyPath, diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java index 0da27bf01c..3ebb526625 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java @@ -15,6 +15,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -31,12 +32,16 @@ import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.PredefinedScopeConfigurationImpl; import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.PredefinedScopeProcessedBeansTrackingStrategy; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; +import org.hibernate.validator.internal.metadata.aggregated.NonTrackedBeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; @@ -61,15 +66,17 @@ public class PredefinedScopeBeanMetaDataManager implements BeanMetaDataManager { */ private final ConcurrentMap, BeanMetaData> beanMetaDataMap = new ConcurrentHashMap<>(); + private final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; + public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCreationContext, - ExecutableHelper executableHelper, - ExecutableParameterNameProvider parameterNameProvider, - JavaBeanHelper javaBeanHelper, - ValidationOrderGenerator validationOrderGenerator, - List optionalMetaDataProviders, - MethodValidationConfiguration methodValidationConfiguration, - BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, - Set> beanClassesToInitialize) { + ExecutableHelper executableHelper, + ExecutableParameterNameProvider parameterNameProvider, + JavaBeanHelper javaBeanHelper, + ValidationOrderGenerator validationOrderGenerator, + List optionalMetaDataProviders, + MethodValidationConfiguration methodValidationConfiguration, + BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, + PredefinedScopeConfigurationImpl hibernateSpecificConfig) { AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( constraintCreationContext, @@ -85,7 +92,7 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr metaDataProviders.add( defaultProvider ); metaDataProviders.addAll( optionalMetaDataProviders ); - for ( Class validatedClass : beanClassesToInitialize ) { + for ( Class validatedClass : hibernateSpecificConfig.getBeanClassesToInitialize() ) { Class normalizedValidatedClass = beanMetaDataClassNormalizer.normalize( validatedClass ); @SuppressWarnings("unchecked") @@ -105,6 +112,23 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr } this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; + + if ( hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) { + this.processedBeansTrackingStrategy = hibernateSpecificConfig.getProcessedBeansTrackingStrategy(); + } + else { + this.processedBeansTrackingStrategy = new PredefinedScopeProcessedBeansTrackingStrategy( + this + ); + } + // Wrap the BeanMetaData objects with NonTrackedBeanMetaDataImpl if tracking is not required. + for ( Map.Entry, BeanMetaData> entry : beanMetaDataMap.entrySet() ) { + final Class beanClass = entry.getKey(); + final BeanMetaData beanMetaData = entry.getValue(); + if ( ! processedBeansTrackingStrategy.isEnabledForBean( beanClass, beanMetaData.hasCascadables() ) ) { + beanMetaDataMap.put( beanClass, new NonTrackedBeanMetaDataImpl<>( beanMetaData ) ); + } + } } @SuppressWarnings("unchecked") @@ -124,6 +148,10 @@ public Collection> getBeanMetaData() { return beanMetaDataMap.values(); } + public ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return processedBeansTrackingStrategy; + } + @Override public void clear() { beanMetaDataMap.clear(); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java index fdeac0ffa5..6313155c9a 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java @@ -113,4 +113,12 @@ public interface BeanMetaData extends Validatable { * element itself and goes up the hierarchy chain. Interfaces are not included. */ List> getClassHierarchy(); + + /** + * @return {@code true} if the bean class is required to be tracked; {@code false} otherwise. + */ + default boolean isTrackingRequired() { + // TODO: is this the correct return value for UninitializedBeanMetaData??? + return true; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java new file mode 100644 index 0000000000..fea1418812 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java @@ -0,0 +1,96 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.metadata.aggregated; + +import java.lang.reflect.Executable; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import javax.validation.metadata.BeanDescriptor; + +import org.hibernate.validator.internal.engine.groups.Sequence; +import org.hibernate.validator.internal.metadata.core.MetaConstraint; +import org.hibernate.validator.internal.metadata.facets.Cascadable; + +public class NonTrackedBeanMetaDataImpl implements BeanMetaData { + private final BeanMetaData beanMetaData; + + public NonTrackedBeanMetaDataImpl(BeanMetaData beanMetaData) { + this.beanMetaData = beanMetaData; + } + + @Override + public Class getBeanClass() { + return beanMetaData.getBeanClass(); + } + + @Override + public boolean hasConstraints() { + return beanMetaData.hasConstraints(); + } + + @Override + public BeanDescriptor getBeanDescriptor() { + return beanMetaData.getBeanDescriptor(); + } + + @Override + public PropertyMetaData getMetaDataFor(String propertyName) { + return beanMetaData.getMetaDataFor( propertyName ); + } + + @Override + public List> getDefaultGroupSequence(T beanState) { + return beanMetaData.getDefaultGroupSequence( beanState ); + } + + @Override + public Iterator getDefaultValidationSequence(T beanState) { + return beanMetaData.getDefaultValidationSequence( beanState ); + } + + @Override + public boolean isDefaultGroupSequenceRedefined() { + return beanMetaData.isDefaultGroupSequenceRedefined(); + } + + @Override + public Set> getMetaConstraints() { + return beanMetaData.getMetaConstraints(); + } + + @Override + public Set> getDirectMetaConstraints() { + return beanMetaData.getDirectMetaConstraints(); + } + + @Override + public Optional getMetaDataFor(Executable executable) throws IllegalArgumentException { + return beanMetaData.getMetaDataFor( executable ); + } + + @Override + public List> getClassHierarchy() { + return beanMetaData.getClassHierarchy(); + } + + @Override + public Iterable getCascadables() { + return beanMetaData.getCascadables(); + } + + @Override + public boolean hasCascadables() { + return beanMetaData.hasCascadables(); + } + + @Override + public boolean isTrackingRequired() { + return false; + } +} From b5262e5470186617ed00575772d497064669e63e Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Jul 2021 18:55:05 +0200 Subject: [PATCH 14/16] HV-1831 New zero cost approach to processed bean tracking strategy I removed it from the traditional VF for now as I would like us to focus on the case where it is useful first. We will reintroduce it later once we have validated the approach where it is the most useful. I'm a bit unclear right now if we should use the same contract for traditional and predefined scope VF as we are dealing with different things and they won't be evaluated at the same moment. I'm thinking that maybe this needs to be a different contract. --- .../PredefinedScopeValidatorFactoryImpl.java | 5 +- .../internal/engine/ValidatorFactoryImpl.java | 8 +- .../engine/ValidatorFactoryScopedContext.java | 26 +---- ...adablesProcessedBeansTrackingStrategy.java | 31 ------ ...edScopeProcessedBeansTrackingStrategy.java | 79 ++++++++------- .../ProcessedBeansTrackingStrategy.java | 6 +- .../BeanValidationContext.java | 2 +- .../ParameterExecutableValidationContext.java | 11 +-- ...eturnValueExecutableValidationContext.java | 10 +- .../ValidatorScopedContext.java | 11 --- .../PredefinedScopeBeanMetaDataManager.java | 30 +++--- .../AbstractConstraintMetaData.java | 10 ++ .../metadata/aggregated/BeanMetaData.java | 5 +- .../metadata/aggregated/BeanMetaDataImpl.java | 31 ++++++ .../aggregated/ExecutableMetaData.java | 42 ++++++++ .../NonTrackedBeanMetaDataImpl.java | 96 ------------------- ...clesNoCyclesListDuplicateElementsTest.java | 17 ++-- ...edBeansTrackingCyclesNoCyclesListTest.java | 17 ++-- ...sedBeansTrackingCyclesNoCyclesMapTest.java | 17 ++-- ...cessedBeansTrackingCyclesNoCyclesTest.java | 17 ++-- 20 files changed, 188 insertions(+), 283 deletions(-) delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java 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 2e60f556a1..2509ce3ff8 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 @@ -200,7 +200,6 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState determineConstraintValidatorPayload( hibernateSpecificConfig ), determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - beanMetaDataManager.getProcessedBeansTrackingStrategy(), constraintValidatorInitializationContext ); if ( LOG.isDebugEnabled() ) { @@ -265,6 +264,10 @@ public boolean isTraversableResolverResultCacheEnabled() { return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled(); } + public PredefinedScopeBeanMetaDataManager getBeanMetaDataManager() { + return beanMetaDataManager; + } + @Override public T unwrap(Class type) { // allow unwrapping into public super types 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 95a86a6d03..28156aa341 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 @@ -10,9 +10,9 @@ import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer; +import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload; -import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineCustomViolationExpressionLanguageFeatureLevel; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader; import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast; @@ -47,7 +47,6 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; -import org.hibernate.validator.internal.engine.tracking.HasCascadablesProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; @@ -161,10 +160,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ), determineConstraintValidatorPayload( hibernateSpecificConfig ), determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ), - ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) - ? hibernateSpecificConfig.getProcessedBeansTrackingStrategy() - : new HasCascadablesProcessedBeansTrackingStrategy() ); + determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ) ); ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl( configurationState.getConstraintValidatorFactory(), diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java index dc53b4a35e..9aa989f492 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryScopedContext.java @@ -15,7 +15,6 @@ import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; @@ -79,11 +78,6 @@ public class ValidatorFactoryScopedContext { */ private final ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; - /** - * Strategy used to enable or not processed beans tracking. - */ - private final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; - /** * The constraint validator initialization context. */ @@ -99,11 +93,10 @@ public class ValidatorFactoryScopedContext { boolean traversableResolverResultCacheEnabled, Object constraintValidatorPayload, ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel, - ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel, - ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel) { this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast, traversableResolverResultCacheEnabled, constraintValidatorPayload, constraintExpressionLanguageFeatureLevel, - customViolationExpressionLanguageFeatureLevel, processedBeansTrackingStrategy, + customViolationExpressionLanguageFeatureLevel, new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, temporalValidationTolerance ) ); } @@ -119,7 +112,6 @@ public class ValidatorFactoryScopedContext { Object constraintValidatorPayload, ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel, ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel, - ProcessedBeansTrackingStrategy processedBeanTrackingStrategy, HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) { this.messageInterpolator = messageInterpolator; this.traversableResolver = traversableResolver; @@ -132,7 +124,6 @@ public class ValidatorFactoryScopedContext { this.constraintValidatorPayload = constraintValidatorPayload; this.constraintExpressionLanguageFeatureLevel = constraintExpressionLanguageFeatureLevel; this.customViolationExpressionLanguageFeatureLevel = customViolationExpressionLanguageFeatureLevel; - this.processedBeansTrackingStrategy = processedBeanTrackingStrategy; this.constraintValidatorInitializationContext = constraintValidatorInitializationContext; } @@ -184,10 +175,6 @@ public ExpressionLanguageFeatureLevel getCustomViolationExpressionLanguageFeatur return this.customViolationExpressionLanguageFeatureLevel; } - public ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { - return processedBeansTrackingStrategy; - } - static class Builder { private final ValidatorFactoryScopedContext defaultContext; @@ -202,7 +189,6 @@ static class Builder { private Object constraintValidatorPayload; private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel; private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; - private ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext; Builder(ValidatorFactoryScopedContext defaultContext) { @@ -220,7 +206,6 @@ static class Builder { this.constraintValidatorPayload = defaultContext.constraintValidatorPayload; this.constraintExpressionLanguageFeatureLevel = defaultContext.constraintExpressionLanguageFeatureLevel; this.customViolationExpressionLanguageFeatureLevel = defaultContext.customViolationExpressionLanguageFeatureLevel; - this.processedBeansTrackingStrategy = defaultContext.processedBeansTrackingStrategy; this.constraintValidatorInitializationContext = defaultContext.constraintValidatorInitializationContext; } @@ -307,12 +292,6 @@ public ValidatorFactoryScopedContext.Builder setCustomViolationExpressionLanguag return this; } - public ValidatorFactoryScopedContext.Builder setProcessedBeansTrackingStrategy( - ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { - this.processedBeansTrackingStrategy = processedBeansTrackingStrategy; - return this; - } - public ValidatorFactoryScopedContext build() { return new ValidatorFactoryScopedContext( messageInterpolator, @@ -326,7 +305,6 @@ public ValidatorFactoryScopedContext build() { constraintValidatorPayload, constraintExpressionLanguageFeatureLevel, customViolationExpressionLanguageFeatureLevel, - processedBeansTrackingStrategy, HibernateConstraintValidatorInitializationContextImpl.of( constraintValidatorInitializationContext, scriptEvaluatorFactory, diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java deleted file mode 100644 index 7a7a33083f..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/HasCascadablesProcessedBeansTrackingStrategy.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.engine.tracking; - -import java.lang.reflect.Executable; - -public class HasCascadablesProcessedBeansTrackingStrategy implements ProcessedBeansTrackingStrategy { - - @Override - public boolean isEnabledForBean(Class beanClass, boolean hasCascadables) { - return hasCascadables; - } - - @Override - public boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables) { - return hasCascadables; - } - - @Override - public boolean isEnabledForParameters(Executable executable, boolean hasCascadables) { - return hasCascadables; - } - - @Override - public void clear() { - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java index 167f0856e5..f570ebc9ea 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/PredefinedScopeProcessedBeansTrackingStrategy.java @@ -6,7 +6,6 @@ */ package org.hibernate.validator.internal.engine.tracking; -import java.lang.reflect.Executable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collections; @@ -15,22 +14,22 @@ import java.util.Map; import java.util.Set; -import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.CascadingMetaData; import org.hibernate.validator.internal.metadata.aggregated.ContainerCascadingMetaData; import org.hibernate.validator.internal.metadata.facets.Cascadable; +import org.hibernate.validator.internal.properties.Signature; import org.hibernate.validator.internal.util.CollectionHelper; public class PredefinedScopeProcessedBeansTrackingStrategy implements ProcessedBeansTrackingStrategy { private final Map, Boolean> trackingEnabledForBeans; - private final Map trackingEnabledForReturnValues; + private final Map trackingEnabledForReturnValues; - private final Map trackingEnabledForParameters; + private final Map trackingEnabledForParameters; - public PredefinedScopeProcessedBeansTrackingStrategy(PredefinedScopeBeanMetaDataManager beanMetaDataManager) { + public PredefinedScopeProcessedBeansTrackingStrategy(Map, BeanMetaData> rawBeanMetaDataMap) { // TODO: build the maps from the information inside the beanMetaDataManager // There is a good chance we will need a structure with the whole hierarchy of constraint classes. // That's something we could add to PredefinedScopeBeanMetaDataManager, as we are already doing similar things @@ -39,24 +38,24 @@ public PredefinedScopeProcessedBeansTrackingStrategy(PredefinedScopeBeanMetaData // PredefinedScopeBeanMetaDataManager. this.trackingEnabledForBeans = CollectionHelper.toImmutableMap( - new TrackingEnabledStrategyBuilder( beanMetaDataManager ).build() + new TrackingEnabledStrategyBuilder( rawBeanMetaDataMap ).build() ); this.trackingEnabledForReturnValues = CollectionHelper.toImmutableMap( new HashMap<>() ); this.trackingEnabledForParameters = CollectionHelper.toImmutableMap( new HashMap<>() ); } private static class TrackingEnabledStrategyBuilder { - private final PredefinedScopeBeanMetaDataManager beanMetaDataManager; + private final Map, BeanMetaData> rawBeanMetaDataMap; private final Map, Boolean> classToBeanTrackingEnabled; - TrackingEnabledStrategyBuilder(PredefinedScopeBeanMetaDataManager beanMetaDataManager) { - this.beanMetaDataManager = beanMetaDataManager; - this.classToBeanTrackingEnabled = new HashMap<>( beanMetaDataManager.getBeanMetaData().size() ); + TrackingEnabledStrategyBuilder(Map, BeanMetaData> rawBeanMetaDataMap) { + this.rawBeanMetaDataMap = rawBeanMetaDataMap; + this.classToBeanTrackingEnabled = CollectionHelper.newHashMap( rawBeanMetaDataMap.size() ); } public Map, Boolean> build() { final Set> beanClassesInPath = new HashSet<>(); - for ( BeanMetaData beanMetadata : beanMetaDataManager.getBeanMetaData() ) { + for ( BeanMetaData beanMetadata : rawBeanMetaDataMap.values() ) { determineTrackingRequired( beanMetadata.getBeanClass(), beanClassesInPath ); if ( !beanClassesInPath.isEmpty() ) { throw new IllegalStateException( "beanClassesInPath not empty" ); @@ -160,38 +159,38 @@ private boolean determineTrackingRequired(Class beanClass, Set> bean // TODO: is there a more concise way to do this? private Set> getDirectCascadedBeanClasses(Class beanClass ) { - final BeanMetaData beanMetaData = beanMetaDataManager.getBeanMetaData( beanClass ); - if ( beanMetaData.hasCascadables() ) { - final Set> directCascadedBeanClasses = new HashSet<>(); - for ( Cascadable cascadable : beanMetaData.getCascadables() ) { - final CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData(); - if ( cascadingMetaData.isContainer() ) { - final ContainerCascadingMetaData containerCascadingMetaData = (ContainerCascadingMetaData) cascadingMetaData; - if ( containerCascadingMetaData.getEnclosingType() instanceof ParameterizedType ) { - ParameterizedType parameterizedType = (ParameterizedType) containerCascadingMetaData.getEnclosingType(); - for ( Type typeArgument : parameterizedType.getActualTypeArguments() ) { - if ( typeArgument instanceof Class ) { - directCascadedBeanClasses.add( (Class) typeArgument ); - } - else { - throw new UnsupportedOperationException( "Only ParameterizedType values of type Class are supported" ); - } + final BeanMetaData beanMetaData = rawBeanMetaDataMap.get( beanClass ); + + if ( beanMetaData == null || !beanMetaData.hasCascadables() ) { + return Collections.emptySet(); + } + + final Set> directCascadedBeanClasses = new HashSet<>(); + for ( Cascadable cascadable : beanMetaData.getCascadables() ) { + final CascadingMetaData cascadingMetaData = cascadable.getCascadingMetaData(); + if ( cascadingMetaData.isContainer() ) { + final ContainerCascadingMetaData containerCascadingMetaData = (ContainerCascadingMetaData) cascadingMetaData; + if ( containerCascadingMetaData.getEnclosingType() instanceof ParameterizedType ) { + ParameterizedType parameterizedType = (ParameterizedType) containerCascadingMetaData.getEnclosingType(); + for ( Type typeArgument : parameterizedType.getActualTypeArguments() ) { + if ( typeArgument instanceof Class ) { + directCascadedBeanClasses.add( (Class) typeArgument ); + } + else { + throw new UnsupportedOperationException( "Only ParameterizedType values of type Class are supported" ); } - } - else { - throw new UnsupportedOperationException( "Non-parameterized containers are not supported yet." ); } } else { - // TODO: For now, assume non-container Cascadables are always beans. Truee??? - directCascadedBeanClasses.add( (Class) cascadable.getCascadableType() ); + throw new UnsupportedOperationException( "Non-parameterized containers are not supported yet." ); } } - return directCascadedBeanClasses; - } - else { - return Collections.emptySet(); + else { + // TODO: For now, assume non-container Cascadables are always beans. Truee??? + directCascadedBeanClasses.add( (Class) cascadable.getCascadableType() ); + } } + return directCascadedBeanClasses; } private boolean register(Class beanClass, boolean isBeanTrackingEnabled) { @@ -212,21 +211,21 @@ public boolean isEnabledForBean(Class rootBeanClass, boolean hasCascadables) } @Override - public boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables) { + public boolean isEnabledForReturnValue(Signature signature, boolean hasCascadables) { if ( !hasCascadables ) { return false; } - return trackingEnabledForReturnValues.getOrDefault( executable, true ); + return trackingEnabledForReturnValues.getOrDefault( signature, true ); } @Override - public boolean isEnabledForParameters(Executable executable, boolean hasCascadables) { + public boolean isEnabledForParameters(Signature signature, boolean hasCascadables) { if ( !hasCascadables ) { return false; } - return trackingEnabledForParameters.getOrDefault( executable, true ); + return trackingEnabledForParameters.getOrDefault( signature, true ); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java index b5db188e3b..7945ab7f46 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/ProcessedBeansTrackingStrategy.java @@ -6,15 +6,15 @@ */ package org.hibernate.validator.internal.engine.tracking; -import java.lang.reflect.Executable; +import org.hibernate.validator.internal.properties.Signature; public interface ProcessedBeansTrackingStrategy { boolean isEnabledForBean(Class beanClass, boolean hasCascadables); - boolean isEnabledForReturnValue(Executable executable, boolean hasCascadables); + boolean isEnabledForReturnValue(Signature signature, boolean hasCascadables); - boolean isEnabledForParameters(Executable executable, boolean hasCascadables); + boolean isEnabledForParameters(Signature signature, boolean hasCascadables); void clear(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java index fd22e4880b..044d463c60 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BeanValidationContext.java @@ -37,7 +37,7 @@ class BeanValidationContext extends AbstractValidationContext { BeanMetaData rootBeanMetaData ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, - rootBean, rootBeanClass, rootBeanMetaData, rootBeanMetaData.isTrackingRequired() + rootBean, rootBeanClass, rootBeanMetaData, rootBeanMetaData.isTrackingEnabled() ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java index 45a2efd102..9d3688bbc1 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ParameterExecutableValidationContext.java @@ -24,7 +24,6 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; import org.hibernate.validator.internal.engine.constraintvalidation.CrossParameterConstraintValidatorContextImpl; import org.hibernate.validator.internal.engine.path.PathImpl; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; @@ -69,8 +68,7 @@ public class ParameterExecutableValidationContext extends AbstractValidationC ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, - buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), executable, - executableMetaData ) + isProcessedBeansTrackingEnabled( executableMetaData ) ); this.executable = executable; this.executableMetaData = executableMetaData; @@ -87,16 +85,13 @@ public Optional getExecutableMetaData() { return executableMetaData; } - private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, - Executable executable, - Optional executableMetaData) { + private static boolean isProcessedBeansTrackingEnabled(Optional executableMetaData) { if ( !executableMetaData.isPresent() ) { // the method is unconstrained so there's no need to worry about the tracking return false; } - return processedBeansTrackingStrategy.isEnabledForParameters( executable, - executableMetaData.get().getValidatableParametersMetaData().hasCascadables() ); + return executableMetaData.get().isTrackingEnabledForParameters(); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java index af975c9679..3e383bc6e0 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ReturnValueExecutableValidationContext.java @@ -19,7 +19,6 @@ import org.hibernate.validator.internal.engine.ConstraintViolationImpl; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valuecontext.ValueContext; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; @@ -62,8 +61,7 @@ public class ReturnValueExecutableValidationContext extends AbstractValidatio ) { super( constraintValidatorManager, constraintValidatorFactory, validatorScopedContext, traversableResolver, constraintValidatorInitializationContext, rootBean, rootBeanClass, rootBeanMetaData, - buildProcessedBeansTrackingEnabled( validatorScopedContext.getProcessedBeansTrackingStrategy(), executable, - executableMetaData ) + isTrackingEnabled( executableMetaData ) ); this.executable = executable; this.executableMetaData = executableMetaData; @@ -80,15 +78,13 @@ public Optional getExecutableMetaData() { return executableMetaData; } - private static boolean buildProcessedBeansTrackingEnabled(ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, - Executable executable, - Optional executableMetaData) { + private static boolean isTrackingEnabled(Optional executableMetaData) { if ( !executableMetaData.isPresent() ) { // the method is unconstrained so there's no need to worry about the tracking return false; } - return processedBeansTrackingStrategy.isEnabledForReturnValue( executable, executableMetaData.get().getReturnValueMetaData().hasCascadables() ); + return executableMetaData.get().isTrackingEnabledForReturnValue(); } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java index 5b35742e85..0e78c05025 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/ValidatorScopedContext.java @@ -13,7 +13,6 @@ import javax.validation.Validator; import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; @@ -77,11 +76,6 @@ public class ValidatorScopedContext { */ private final ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel; - /** - * Strategy used to enable or not processed beans tracking. - */ - private final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy; - public ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScopedContext) { this.messageInterpolator = validatorFactoryScopedContext.getMessageInterpolator(); this.parameterNameProvider = validatorFactoryScopedContext.getParameterNameProvider(); @@ -93,7 +87,6 @@ public ValidatorScopedContext(ValidatorFactoryScopedContext validatorFactoryScop this.constraintValidatorPayload = validatorFactoryScopedContext.getConstraintValidatorPayload(); this.constraintExpressionLanguageFeatureLevel = validatorFactoryScopedContext.getConstraintExpressionLanguageFeatureLevel(); this.customViolationExpressionLanguageFeatureLevel = validatorFactoryScopedContext.getCustomViolationExpressionLanguageFeatureLevel(); - this.processedBeansTrackingStrategy = validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); } public MessageInterpolator getMessageInterpolator() { @@ -135,8 +128,4 @@ public ExpressionLanguageFeatureLevel getConstraintExpressionLanguageFeatureLeve public ExpressionLanguageFeatureLevel getCustomViolationExpressionLanguageFeatureLevel() { return customViolationExpressionLanguageFeatureLevel; } - - public ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { - return processedBeansTrackingStrategy; - } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java index 3ebb526625..9e09278599 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -41,7 +42,6 @@ import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData; -import org.hibernate.validator.internal.metadata.aggregated.NonTrackedBeanMetaDataImpl; import org.hibernate.validator.internal.metadata.aggregated.PropertyMetaData; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions; import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl; @@ -92,6 +92,7 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr metaDataProviders.add( defaultProvider ); metaDataProviders.addAll( optionalMetaDataProviders ); + Map, BeanMetaData> rawBeanMetaDataMap = new HashMap<>(); for ( Class validatedClass : hibernateSpecificConfig.getBeanClassesToInitialize() ) { Class normalizedValidatedClass = beanMetaDataClassNormalizer.normalize( validatedClass ); @@ -100,11 +101,11 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr // note that the hierarchy also contains the initial class for ( Class hierarchyElement : classHierarchy ) { - if ( this.beanMetaDataMap.containsKey( hierarchyElement ) ) { + if ( rawBeanMetaDataMap.containsKey( hierarchyElement ) ) { continue; } - this.beanMetaDataMap.put( hierarchyElement, + rawBeanMetaDataMap.put( hierarchyElement, createBeanMetaData( constraintCreationContext, executableHelper, parameterNameProvider, javaBeanHelper, validationOrderGenerator, optionalMetaDataProviders, methodValidationConfiguration, metaDataProviders, hierarchyElement ) ); @@ -118,16 +119,13 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr } else { this.processedBeansTrackingStrategy = new PredefinedScopeProcessedBeansTrackingStrategy( - this + rawBeanMetaDataMap ); } - // Wrap the BeanMetaData objects with NonTrackedBeanMetaDataImpl if tracking is not required. - for ( Map.Entry, BeanMetaData> entry : beanMetaDataMap.entrySet() ) { - final Class beanClass = entry.getKey(); - final BeanMetaData beanMetaData = entry.getValue(); - if ( ! processedBeansTrackingStrategy.isEnabledForBean( beanClass, beanMetaData.hasCascadables() ) ) { - beanMetaDataMap.put( beanClass, new NonTrackedBeanMetaDataImpl<>( beanMetaData ) ); - } + + // Inject the processed beans tracking information into the BeanMetaData objects + for ( Map.Entry, BeanMetaData> rawBeanMetaDataEntry : rawBeanMetaDataMap.entrySet() ) { + beanMetaDataMap.put( rawBeanMetaDataEntry.getKey(), injectTrackingInformation( rawBeanMetaDataEntry.getValue(), processedBeansTrackingStrategy ) ); } } @@ -222,6 +220,11 @@ private static List> getBeanConfigurationForHie return configurations; } + private static BeanMetaData injectTrackingInformation(BeanMetaData rawBeanMetaData, + ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + return new BeanMetaDataImpl( (BeanMetaDataImpl) rawBeanMetaData, processedBeansTrackingStrategy ); + } + private static class UninitializedBeanMetaData implements BeanMetaData { private final Class beanClass; @@ -301,6 +304,11 @@ public Optional getMetaDataFor(Executable executable) throws public List> getClassHierarchy() { return classHierarchy; } + + @Override + public boolean isTrackingEnabled() { + return true; + } } private static class UninitializedBeanDescriptor implements BeanDescriptor { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java index d0760f45ea..2534a1fc67 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/AbstractConstraintMetaData.java @@ -70,6 +70,16 @@ public AbstractConstraintMetaData(String name, this.isConstrained = isConstrained; } + protected AbstractConstraintMetaData(AbstractConstraintMetaData originalAbstractConstraintMetaData) { + this.name = originalAbstractConstraintMetaData.name; + this.type = originalAbstractConstraintMetaData.type; + this.directConstraints = originalAbstractConstraintMetaData.directConstraints; + this.containerElementsConstraints = originalAbstractConstraintMetaData.containerElementsConstraints; + this.allConstraints = originalAbstractConstraintMetaData.allConstraints; + this.isCascading = originalAbstractConstraintMetaData.isCascading; + this.isConstrained = originalAbstractConstraintMetaData.isConstrained; + } + @Override public String getName() { return name; diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java index 6313155c9a..dea8e01f8c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java @@ -117,8 +117,5 @@ public interface BeanMetaData extends Validatable { /** * @return {@code true} if the bean class is required to be tracked; {@code false} otherwise. */ - default boolean isTrackingRequired() { - // TODO: is this the correct return value for UninitializedBeanMetaData??? - return true; - } + boolean isTrackingEnabled(); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 509e05b549..0abbd57426 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -30,6 +30,7 @@ import org.hibernate.validator.internal.engine.groups.Sequence; import org.hibernate.validator.internal.engine.groups.ValidationOrder; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.BeanDescriptorImpl; import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl; @@ -161,6 +162,11 @@ public final class BeanMetaDataImpl implements BeanMetaData { */ private volatile BeanDescriptor beanDescriptor; + /** + * Whether tracking of processed beans should be enabled for objects of this type. + */ + private final boolean trackingEnabled; + /** * Creates a new {@link BeanMetaDataImpl} * @@ -238,6 +244,26 @@ else if ( constraintMetaData.getKind() == ElementKind.BEAN ) { // We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); + this.trackingEnabled = hasCascadables(); + } + + public BeanMetaDataImpl(BeanMetaDataImpl originalBeanMetaData, ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + this.validationOrderGenerator = originalBeanMetaData.validationOrderGenerator; + this.beanClass = originalBeanMetaData.beanClass; + this.propertyMetaDataMap = originalBeanMetaData.propertyMetaDataMap; + this.hasConstraints = originalBeanMetaData.hasConstraints; + this.cascadedProperties = originalBeanMetaData.cascadedProperties; + this.allMetaConstraints = originalBeanMetaData.allMetaConstraints; + this.classHierarchyWithoutInterfaces = originalBeanMetaData.classHierarchyWithoutInterfaces; + this.defaultGroupSequenceProvider = originalBeanMetaData.defaultGroupSequenceProvider; + this.defaultGroupSequence = originalBeanMetaData.defaultGroupSequence; + this.validationOrder = originalBeanMetaData.validationOrder; + this.directMetaConstraints = originalBeanMetaData.directMetaConstraints; + this.executableMetaDataMap = originalBeanMetaData.executableMetaDataMap; + this.unconstrainedExecutables = originalBeanMetaData.unconstrainedExecutables; + this.defaultGroupSequenceRedefined = originalBeanMetaData.defaultGroupSequenceRedefined; + this.resolvedDefaultGroupSequence = originalBeanMetaData.resolvedDefaultGroupSequence; + this.trackingEnabled = processedBeansTrackingStrategy.isEnabledForBean( this.beanClass, hasCascadables() ); } @Override @@ -357,6 +383,11 @@ public List> getClassHierarchy() { return classHierarchyWithoutInterfaces; } + @Override + public boolean isTrackingEnabled() { + return trackingEnabled; + } + private static BeanDescriptor createBeanDescriptor(Class beanClass, Set> allMetaConstraints, Map propertyMetaDataMap, Map executableMetaDataMap, boolean defaultGroupSequenceRedefined, diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index d91977c5bf..9dd54eb43e 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -23,6 +23,7 @@ import org.hibernate.validator.internal.engine.ConstraintCreationContext; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; +import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.metadata.aggregated.rule.MethodConfigurationRule; import org.hibernate.validator.internal.metadata.core.MetaConstraint; import org.hibernate.validator.internal.metadata.descriptor.ExecutableDescriptorImpl; @@ -78,6 +79,9 @@ public class ExecutableMetaData extends AbstractConstraintMetaData { private final ReturnValueMetaData returnValueMetaData; private final ElementKind kind; + private final boolean trackingEnabledForParameters; + private final boolean trackingEnabledForReturnValue; + private ExecutableMetaData( String name, Type returnType, @@ -113,6 +117,35 @@ private ExecutableMetaData( ); this.isGetter = isGetter; this.kind = kind; + this.trackingEnabledForParameters = validatableParametersMetaData.hasCascadables(); + this.trackingEnabledForReturnValue = returnValueMetaData.hasCascadables(); + } + + public ExecutableMetaData(ExecutableMetaData originalExecutableMetaData, ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + super( originalExecutableMetaData ); + + this.parameterTypes = originalExecutableMetaData.parameterTypes; + this.parameterMetaDataList = originalExecutableMetaData.parameterMetaDataList; + this.validatableParametersMetaData = originalExecutableMetaData.validatableParametersMetaData; + this.crossParameterConstraints = originalExecutableMetaData.crossParameterConstraints; + this.signatures = originalExecutableMetaData.signatures; + this.returnValueMetaData = originalExecutableMetaData.returnValueMetaData; + this.isGetter = originalExecutableMetaData.isGetter; + this.kind = originalExecutableMetaData.kind; + + boolean trackingEnabledForParameters = false; + for ( Signature signature : originalExecutableMetaData.getSignatures() ) { + trackingEnabledForParameters = trackingEnabledForParameters || processedBeansTrackingStrategy.isEnabledForParameters( signature, + originalExecutableMetaData.getValidatableParametersMetaData().hasCascadables() ); + } + this.trackingEnabledForParameters = trackingEnabledForParameters; + + boolean trackingEnabledForReturnValue = false; + for ( Signature signature : originalExecutableMetaData.getSignatures() ) { + trackingEnabledForReturnValue = trackingEnabledForReturnValue || processedBeansTrackingStrategy.isEnabledForReturnValue( signature, + originalExecutableMetaData.getReturnValueMetaData().hasCascadables() ); + } + this.trackingEnabledForReturnValue = trackingEnabledForReturnValue; } /** @@ -199,6 +232,15 @@ public ElementKind getKind() { return kind; } + + public boolean isTrackingEnabledForParameters() { + return trackingEnabledForParameters; + } + + public boolean isTrackingEnabledForReturnValue() { + return trackingEnabledForReturnValue; + } + @Override public String toString() { StringBuilder parameterBuilder = new StringBuilder(); diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java deleted file mode 100644 index fea1418812..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/NonTrackedBeanMetaDataImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.metadata.aggregated; - -import java.lang.reflect.Executable; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import javax.validation.metadata.BeanDescriptor; - -import org.hibernate.validator.internal.engine.groups.Sequence; -import org.hibernate.validator.internal.metadata.core.MetaConstraint; -import org.hibernate.validator.internal.metadata.facets.Cascadable; - -public class NonTrackedBeanMetaDataImpl implements BeanMetaData { - private final BeanMetaData beanMetaData; - - public NonTrackedBeanMetaDataImpl(BeanMetaData beanMetaData) { - this.beanMetaData = beanMetaData; - } - - @Override - public Class getBeanClass() { - return beanMetaData.getBeanClass(); - } - - @Override - public boolean hasConstraints() { - return beanMetaData.hasConstraints(); - } - - @Override - public BeanDescriptor getBeanDescriptor() { - return beanMetaData.getBeanDescriptor(); - } - - @Override - public PropertyMetaData getMetaDataFor(String propertyName) { - return beanMetaData.getMetaDataFor( propertyName ); - } - - @Override - public List> getDefaultGroupSequence(T beanState) { - return beanMetaData.getDefaultGroupSequence( beanState ); - } - - @Override - public Iterator getDefaultValidationSequence(T beanState) { - return beanMetaData.getDefaultValidationSequence( beanState ); - } - - @Override - public boolean isDefaultGroupSequenceRedefined() { - return beanMetaData.isDefaultGroupSequenceRedefined(); - } - - @Override - public Set> getMetaConstraints() { - return beanMetaData.getMetaConstraints(); - } - - @Override - public Set> getDirectMetaConstraints() { - return beanMetaData.getDirectMetaConstraints(); - } - - @Override - public Optional getMetaDataFor(Executable executable) throws IllegalArgumentException { - return beanMetaData.getMetaDataFor( executable ); - } - - @Override - public List> getClassHierarchy() { - return beanMetaData.getClassHierarchy(); - } - - @Override - public Iterable getCascadables() { - return beanMetaData.getCascadables(); - } - - @Override - public boolean hasCascadables() { - return beanMetaData.hasCascadables(); - } - - @Override - public boolean isTrackingRequired() { - return false; - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java index 5be2a6fe6f..d521755e72 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest.java @@ -6,11 +6,15 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validation; @@ -20,14 +24,9 @@ import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; - import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - /** * An example of beans with cascading constraints, some cycle and others do not. * @@ -61,10 +60,8 @@ public class ProcessedBeansTrackingCyclesNoCyclesListDuplicateElementsTest { @Test public void testTrackingEnabled() { + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = getProcessedBeansTrackingStrategy(); - final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); - final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = - validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); assertTrue( processedBeansTrackingStrategy.isEnabledForBean( A.class, true @@ -148,8 +145,8 @@ private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); } - private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { - return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + private ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getBeanMetaDataManager().getProcessedBeansTrackingStrategy(); } private static class A { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java index b32982237f..e148387a2b 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesListTest.java @@ -6,11 +6,15 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validation; @@ -20,14 +24,9 @@ import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; - import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - /** * An example of beans with cascading constraints, some cycle and others do not. * @@ -61,10 +60,8 @@ public class ProcessedBeansTrackingCyclesNoCyclesListTest { @Test public void testTrackingEnabled() { + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = getProcessedBeansTrackingStrategy(); - final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); - final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = - validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); assertTrue( processedBeansTrackingStrategy.isEnabledForBean( A.class, true @@ -139,8 +136,8 @@ private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); } - private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { - return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + private ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getBeanMetaDataManager().getProcessedBeansTrackingStrategy(); } private static class A { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java index 82c8b7188a..70fe1efe61 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesMapTest.java @@ -6,11 +6,15 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validation; @@ -20,14 +24,9 @@ import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; - import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - /** * An example of beans with cascading constraints, some cycle and others do not. * @@ -58,10 +57,8 @@ public class ProcessedBeansTrackingCyclesNoCyclesMapTest { @Test public void testTrackingEnabled() { + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = getProcessedBeansTrackingStrategy(); - final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); - final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = - validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); assertTrue( processedBeansTrackingStrategy.isEnabledForBean( A.class, true @@ -133,8 +130,8 @@ private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); } - private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { - return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + private ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getBeanMetaDataManager().getProcessedBeansTrackingStrategy(); } private static class A { diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java index 2387dce609..26e521e4aa 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/tracking/ProcessedBeansTrackingCyclesNoCyclesTest.java @@ -6,9 +6,13 @@ */ package org.hibernate.validator.test.internal.engine.tracking; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + import java.util.Arrays; import java.util.HashSet; import java.util.Set; + import javax.validation.ConstraintViolation; import javax.validation.Valid; import javax.validation.Validation; @@ -18,14 +22,9 @@ import org.hibernate.validator.PredefinedScopeHibernateValidator; import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory; import org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl; -import org.hibernate.validator.internal.engine.ValidatorFactoryScopedContext; import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; - import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - /** * An example of beans with cascading constraints, some cycle and others do not. * @@ -58,10 +57,8 @@ public class ProcessedBeansTrackingCyclesNoCyclesTest { @Test public void testTrackingEnabled() { + final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = getProcessedBeansTrackingStrategy(); - final ValidatorFactoryScopedContext validatorFactoryScopedContext = getValidatorFactoryScopedContext(); - final ProcessedBeansTrackingStrategy processedBeansTrackingStrategy = - validatorFactoryScopedContext.getProcessedBeansTrackingStrategy(); assertTrue( processedBeansTrackingStrategy.isEnabledForBean( A.class, true @@ -136,8 +133,8 @@ private PredefinedScopeHibernateValidatorFactory getValidatorFactory() { .buildValidatorFactory().unwrap( PredefinedScopeHibernateValidatorFactory.class ); } - private ValidatorFactoryScopedContext getValidatorFactoryScopedContext() { - return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getValidatorFactoryScopedContext(); + private ProcessedBeansTrackingStrategy getProcessedBeansTrackingStrategy() { + return ( (PredefinedScopeValidatorFactoryImpl) getValidatorFactory() ).getBeanMetaDataManager().getProcessedBeansTrackingStrategy(); } private static class A { From 9d250a031d24df80228735704dff9adc47831253 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 26 Aug 2021 14:57:15 +0200 Subject: [PATCH 15/16] HV-1831 Create ProcessedBeansTrackingVoter contract This contract allows to override the default bean process tracking behavior without exposing our internal structures. It needs a bit more love on the config side so that we can define it via XML too and some documentation. --- .../BaseHibernateValidatorConfiguration.java | 6 +- .../engine/AbstractConfigurationImpl.java | 16 +-- .../PredefinedScopeValidatorFactoryImpl.java | 4 + .../internal/engine/ValidatorFactoryImpl.java | 11 ++- .../DefaultProcessedBeansTrackingVoter.java | 27 ++++++ .../metadata/BeanMetaDataManagerImpl.java | 11 ++- .../PredefinedScopeBeanMetaDataManager.java | 30 +++--- .../aggregated/BeanMetaDataBuilder.java | 29 ++++-- .../metadata/aggregated/BeanMetaDataImpl.java | 36 ++++++- .../aggregated/ExecutableMetaData.java | 97 ++++++++++++++++--- .../tracking/ProcessedBeansTrackingVoter.java | 24 +++++ .../internal/engine/path/PathImplTest.java | 4 +- .../metadata/BeanMetaDataManagerTest.java | 4 +- .../aggregated/ExecutableMetaDataTest.java | 4 +- .../aggregated/ParameterMetaDataTest.java | 7 +- .../aggregated/PropertyMetaDataTest.java | 4 +- 16 files changed, 250 insertions(+), 64 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/engine/tracking/DefaultProcessedBeansTrackingVoter.java create mode 100644 engine/src/main/java/org/hibernate/validator/spi/tracking/ProcessedBeansTrackingVoter.java diff --git a/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java index 97e2f5fe15..dae0fcc02b 100644 --- a/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java +++ b/engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java @@ -25,15 +25,15 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.hibernate.validator.constraints.ParameterScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel; -import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.spi.messageinterpolation.LocaleResolver; import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; /** * Base interface for Hibernate Validator specific configurations. @@ -483,5 +483,5 @@ default S locales(Locale... locales) { S customViolationExpressionLanguageFeatureLevel(ExpressionLanguageFeatureLevel expressionLanguageFeatureLevel); @Incubating - S processedBeansTrackingStrategy(ProcessedBeansTrackingStrategy processedBeanTrackingStrategy); + S processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeanTrackingVoter); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java index 088805e0d7..4b78e0d8a6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java @@ -42,7 +42,6 @@ import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl; import org.hibernate.validator.internal.engine.resolver.TraversableResolvers; -import org.hibernate.validator.internal.engine.tracking.ProcessedBeansTrackingStrategy; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.properties.DefaultGetterPropertySelectionStrategy; @@ -67,6 +66,7 @@ import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; /** * Hibernate specific {@code Configuration} implementation. @@ -133,7 +133,7 @@ public abstract class AbstractConfigurationImpl getProgrammaticMappings() { 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 2509ce3ff8..5cfae0682e 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 @@ -47,6 +47,7 @@ import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl; import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager; import org.hibernate.validator.internal.metadata.core.ConstraintHelper; @@ -185,6 +186,9 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ), methodValidationConfiguration, determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ), + ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null ) + ? hibernateSpecificConfig.getProcessedBeansTrackingVoter() + : new DefaultProcessedBeansTrackingVoter(), hibernateSpecificConfig ); 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 28156aa341..5493dc3829 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 @@ -47,6 +47,7 @@ import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; @@ -65,6 +66,7 @@ import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy; import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; /** * Factory returning initialized {@code Validator} instances. This is the Hibernate Validator default @@ -130,6 +132,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory { private final ValidationOrderGenerator validationOrderGenerator; + private final ProcessedBeansTrackingVoter processedBeansTrackingVoter; + public ValidatorFactoryImpl(ConfigurationState configurationState) { ClassLoader externalClassLoader = determineExternalClassLoader( configurationState ); @@ -199,6 +203,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) { registerCustomConstraintValidators( constraintMappings, constraintHelper ); + this.processedBeansTrackingVoter = ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null ) + ? hibernateSpecificConfig.getProcessedBeansTrackingVoter() + : new DefaultProcessedBeansTrackingVoter(); + if ( LOG.isDebugEnabled() ) { logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext ); } @@ -317,7 +325,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, beanMetadataClassNormalizer, validationOrderGenerator, buildMetaDataProviders(), - methodValidationConfiguration + methodValidationConfiguration, + processedBeansTrackingVoter ) ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/DefaultProcessedBeansTrackingVoter.java b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/DefaultProcessedBeansTrackingVoter.java new file mode 100644 index 0000000000..91ae5140fe --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/engine/tracking/DefaultProcessedBeansTrackingVoter.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.engine.tracking; + +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; + +public class DefaultProcessedBeansTrackingVoter implements ProcessedBeansTrackingVoter { + + @Override + public Vote isEnabledForBean(Class beanClass, boolean hasCascadables) { + return Vote.DEFAULT; + } + + @Override + public Vote isEnabledForReturnValue(Class beanClass, String name, Class[] parameterTypes, boolean hasCascadables) { + return Vote.DEFAULT; + } + + @Override + public Vote isEnabledForParameters(Class beanClass, String name, Class[] parameterTypes, boolean hasCascadables) { + return Vote.DEFAULT; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java index b9b3e6f8ae..3ee817b920 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/BeanMetaDataManagerImpl.java @@ -35,6 +35,7 @@ import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; /** * This manager is in charge of providing all constraint related meta data @@ -97,6 +98,8 @@ public class BeanMetaDataManagerImpl implements BeanMetaDataManager { private final ValidationOrderGenerator validationOrderGenerator; + private final ProcessedBeansTrackingVoter processedBeansTrackingVoter; + /** * the three properties in this field affect the invocation of rules associated to section 4.5.5 * of the specification. By default they are all false, if true they allow @@ -111,14 +114,15 @@ public BeanMetaDataManagerImpl(ConstraintCreationContext constraintCreationConte BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, ValidationOrderGenerator validationOrderGenerator, List optionalMetaDataProviders, - MethodValidationConfiguration methodValidationConfiguration) { + MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { this.constraintCreationContext = constraintCreationContext; this.executableHelper = executableHelper; this.parameterNameProvider = parameterNameProvider; this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; this.validationOrderGenerator = validationOrderGenerator; - this.methodValidationConfiguration = methodValidationConfiguration; + this.processedBeansTrackingVoter = processedBeansTrackingVoter; this.beanMetaDataCache = new ConcurrentReferenceHashMap<>( DEFAULT_INITIAL_CAPACITY, @@ -195,7 +199,8 @@ public int numberOfCachedBeanMetaDataInstances() { private BeanMetaDataImpl createBeanMetaData(Class clazz) { BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( constraintCreationContext, executableHelper, parameterNameProvider, - validationOrderGenerator, clazz, methodValidationConfiguration ); + validationOrderGenerator, clazz, methodValidationConfiguration, + processedBeansTrackingVoter ); for ( MetaDataProvider provider : metaDataProviders ) { for ( BeanConfiguration beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java index 9e09278599..34807e71d5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/PredefinedScopeBeanMetaDataManager.java @@ -56,6 +56,7 @@ import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper; import org.hibernate.validator.internal.util.classhierarchy.Filters; import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; public class PredefinedScopeBeanMetaDataManager implements BeanMetaDataManager { @@ -76,6 +77,7 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr List optionalMetaDataProviders, MethodValidationConfiguration methodValidationConfiguration, BeanMetaDataClassNormalizer beanMetaDataClassNormalizer, + ProcessedBeansTrackingVoter processedBeansTrackingVoter, PredefinedScopeConfigurationImpl hibernateSpecificConfig) { AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders ); AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider( @@ -108,24 +110,19 @@ public PredefinedScopeBeanMetaDataManager(ConstraintCreationContext constraintCr rawBeanMetaDataMap.put( hierarchyElement, createBeanMetaData( constraintCreationContext, executableHelper, parameterNameProvider, javaBeanHelper, validationOrderGenerator, optionalMetaDataProviders, methodValidationConfiguration, - metaDataProviders, hierarchyElement ) ); + processedBeansTrackingVoter, metaDataProviders, hierarchyElement ) ); } } this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer; - - if ( hibernateSpecificConfig.getProcessedBeansTrackingStrategy() != null ) { - this.processedBeansTrackingStrategy = hibernateSpecificConfig.getProcessedBeansTrackingStrategy(); - } - else { - this.processedBeansTrackingStrategy = new PredefinedScopeProcessedBeansTrackingStrategy( - rawBeanMetaDataMap - ); - } + this.processedBeansTrackingStrategy = new PredefinedScopeProcessedBeansTrackingStrategy( + rawBeanMetaDataMap + ); // Inject the processed beans tracking information into the BeanMetaData objects - for ( Map.Entry, BeanMetaData> rawBeanMetaDataEntry : rawBeanMetaDataMap.entrySet() ) { - beanMetaDataMap.put( rawBeanMetaDataEntry.getKey(), injectTrackingInformation( rawBeanMetaDataEntry.getValue(), processedBeansTrackingStrategy ) ); + for ( Map.Entry, BeanMetaData> rawBeanMetaDataEntry : rawBeanMetaDataMap.entrySet() ) { + beanMetaDataMap.put( rawBeanMetaDataEntry.getKey(), + injectTrackingInformation( rawBeanMetaDataEntry.getValue(), processedBeansTrackingStrategy, processedBeansTrackingVoter ) ); } } @@ -171,11 +168,13 @@ private static BeanMetaDataImpl createBeanMetaData(ConstraintCreationCont ValidationOrderGenerator validationOrderGenerator, List optionalMetaDataProviders, MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter, List metaDataProviders, Class clazz) { BeanMetaDataBuilder builder = BeanMetaDataBuilder.getInstance( constraintCreationContext, executableHelper, parameterNameProvider, - validationOrderGenerator, clazz, methodValidationConfiguration ); + validationOrderGenerator, clazz, methodValidationConfiguration, + processedBeansTrackingVoter ); for ( MetaDataProvider provider : metaDataProviders ) { for ( BeanConfiguration beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) { @@ -221,8 +220,9 @@ private static List> getBeanConfigurationForHie } private static BeanMetaData injectTrackingInformation(BeanMetaData rawBeanMetaData, - ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { - return new BeanMetaDataImpl( (BeanMetaDataImpl) rawBeanMetaData, processedBeansTrackingStrategy ); + ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { + return new BeanMetaDataImpl( (BeanMetaDataImpl) rawBeanMetaData, processedBeansTrackingStrategy, processedBeansTrackingVoter ); } private static class UninitializedBeanMetaData implements BeanMetaData { diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java index 4f266dfa27..dc799e29e2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataBuilder.java @@ -25,6 +25,7 @@ import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.StringHelper; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; /** * @author Hardy Ferentschik @@ -43,6 +44,7 @@ public class BeanMetaDataBuilder { private final ExecutableHelper executableHelper; private final ExecutableParameterNameProvider parameterNameProvider; private final MethodValidationConfiguration methodValidationConfiguration; + private ProcessedBeansTrackingVoter processedBeansTrackingVoter; private ConfigurationSource sequenceSource; private ConfigurationSource providerSource; @@ -56,13 +58,15 @@ private BeanMetaDataBuilder( ExecutableParameterNameProvider parameterNameProvider, ValidationOrderGenerator validationOrderGenerator, Class beanClass, - MethodValidationConfiguration methodValidationConfiguration) { + MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { this.beanClass = beanClass; this.constraintCreationContext = constraintCreationContext; this.validationOrderGenerator = validationOrderGenerator; this.executableHelper = executableHelper; this.parameterNameProvider = parameterNameProvider; this.methodValidationConfiguration = methodValidationConfiguration; + this.processedBeansTrackingVoter = processedBeansTrackingVoter; } public static BeanMetaDataBuilder getInstance( @@ -71,14 +75,16 @@ public static BeanMetaDataBuilder getInstance( ExecutableParameterNameProvider parameterNameProvider, ValidationOrderGenerator validationOrderGenerator, Class beanClass, - MethodValidationConfiguration methodValidationConfiguration) { + MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { return new BeanMetaDataBuilder<>( constraintCreationContext, executableHelper, parameterNameProvider, validationOrderGenerator, beanClass, - methodValidationConfiguration ); + methodValidationConfiguration, + processedBeansTrackingVoter ); } public void add(BeanConfiguration configuration) { @@ -121,7 +127,8 @@ private void addMetaDataToBuilder(ConstrainedElement constrainableElement, Set build() { defaultGroupSequence, defaultGroupSequenceProvider, aggregatedElements, - validationOrderGenerator + validationOrderGenerator, + processedBeansTrackingVoter ); } @@ -151,6 +159,7 @@ private static class BuilderDelegate { private MetaDataBuilder metaDataBuilder; private ExecutableMetaData.Builder methodBuilder; private final MethodValidationConfiguration methodValidationConfiguration; + private final ProcessedBeansTrackingVoter processedBeansTrackingVoter; private final int hashCode; public BuilderDelegate( @@ -159,7 +168,8 @@ public BuilderDelegate( ConstraintCreationContext constraintCreationContext, ExecutableHelper executableHelper, ExecutableParameterNameProvider parameterNameProvider, - MethodValidationConfiguration methodValidationConfiguration + MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter ) { this.beanClass = beanClass; this.constrainedElement = constrainedElement; @@ -167,6 +177,7 @@ public BuilderDelegate( this.executableHelper = executableHelper; this.parameterNameProvider = parameterNameProvider; this.methodValidationConfiguration = methodValidationConfiguration; + this.processedBeansTrackingVoter = processedBeansTrackingVoter; switch ( constrainedElement.getKind() ) { case FIELD: @@ -192,7 +203,8 @@ public BuilderDelegate( constraintCreationContext, executableHelper, parameterNameProvider, - methodValidationConfiguration + methodValidationConfiguration, + processedBeansTrackingVoter ); } @@ -239,7 +251,8 @@ public boolean add(ConstrainedElement constrainedElement) { constraintCreationContext, executableHelper, parameterNameProvider, - methodValidationConfiguration + methodValidationConfiguration, + processedBeansTrackingVoter ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 0abbd57426..52ee7f06a6 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -46,6 +46,8 @@ import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.stereotypes.Immutable; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter.Vote; /** * This class encapsulates all meta data needed for validation. Implementations of {@code Validator} interface can @@ -179,7 +181,8 @@ public BeanMetaDataImpl(Class beanClass, List> defaultGroupSequence, DefaultGroupSequenceProvider defaultGroupSequenceProvider, Set constraintMetaDataSet, - ValidationOrderGenerator validationOrderGenerator) { + ValidationOrderGenerator validationOrderGenerator, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { this.validationOrderGenerator = validationOrderGenerator; this.beanClass = beanClass; @@ -244,10 +247,23 @@ else if ( constraintMetaData.getKind() == ElementKind.BEAN ) { // We initialize those elements eagerly so that any eventual error is thrown when bootstrapping the bean metadata this.defaultGroupSequenceRedefined = this.defaultGroupSequence.size() > 1 || hasDefaultGroupSequenceProvider(); this.resolvedDefaultGroupSequence = getDefaultGroupSequence( null ); - this.trackingEnabled = hasCascadables(); + + Vote processedBeansTrackingVote = processedBeansTrackingVoter.isEnabledForBean( beanClass, hasCascadables() ); + switch ( processedBeansTrackingVote ) { + case NON_TRACKING: + this.trackingEnabled = false; + break; + case TRACKING: + this.trackingEnabled = true; + break; + default: + this.trackingEnabled = hasCascadables(); + break; + } } - public BeanMetaDataImpl(BeanMetaDataImpl originalBeanMetaData, ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + public BeanMetaDataImpl(BeanMetaDataImpl originalBeanMetaData, ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { this.validationOrderGenerator = originalBeanMetaData.validationOrderGenerator; this.beanClass = originalBeanMetaData.beanClass; this.propertyMetaDataMap = originalBeanMetaData.propertyMetaDataMap; @@ -263,7 +279,19 @@ public BeanMetaDataImpl(BeanMetaDataImpl originalBeanMetaData, ProcessedBeans this.unconstrainedExecutables = originalBeanMetaData.unconstrainedExecutables; this.defaultGroupSequenceRedefined = originalBeanMetaData.defaultGroupSequenceRedefined; this.resolvedDefaultGroupSequence = originalBeanMetaData.resolvedDefaultGroupSequence; - this.trackingEnabled = processedBeansTrackingStrategy.isEnabledForBean( this.beanClass, hasCascadables() ); + + Vote processedBeansTrackingVote = processedBeansTrackingVoter.isEnabledForBean( beanClass, hasCascadables() ); + switch ( processedBeansTrackingVote ) { + case NON_TRACKING: + this.trackingEnabled = false; + break; + case TRACKING: + this.trackingEnabled = true; + break; + default: + this.trackingEnabled = processedBeansTrackingStrategy.isEnabledForBean( this.beanClass, hasCascadables() ); + break; + } } @Override diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java index 9dd54eb43e..baec8e5516 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java @@ -37,6 +37,8 @@ import org.hibernate.validator.internal.util.ExecutableHelper; import org.hibernate.validator.internal.util.ExecutableParameterNameProvider; import org.hibernate.validator.internal.util.stereotypes.Immutable; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter; +import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter.Vote; /** * An aggregated view of the constraint related meta data for a given method or @@ -57,6 +59,8 @@ */ public class ExecutableMetaData extends AbstractConstraintMetaData { + private final Class beanClass; + private final Class[] parameterTypes; @Immutable @@ -83,6 +87,7 @@ public class ExecutableMetaData extends AbstractConstraintMetaData { private final boolean trackingEnabledForReturnValue; private ExecutableMetaData( + Class beanClass, String name, Type returnType, Class[] parameterTypes, @@ -94,7 +99,8 @@ private ExecutableMetaData( Set> crossParameterConstraints, CascadingMetaData cascadingMetaData, boolean isConstrained, - boolean isGetter) { + boolean isGetter, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { super( name, returnType, @@ -104,6 +110,7 @@ private ExecutableMetaData( isConstrained ); + this.beanClass = beanClass; this.parameterTypes = parameterTypes; this.parameterMetaDataList = CollectionHelper.toImmutableList( parameterMetaDataList ); this.validatableParametersMetaData = new ValidatableParametersMetaData( parameterMetaDataList ); @@ -117,13 +124,42 @@ private ExecutableMetaData( ); this.isGetter = isGetter; this.kind = kind; - this.trackingEnabledForParameters = validatableParametersMetaData.hasCascadables(); - this.trackingEnabledForReturnValue = returnValueMetaData.hasCascadables(); + + Vote processedBeansTrackingVoteForParameters = processedBeansTrackingVoter.isEnabledForParameters( beanClass, name, parameterTypes, + validatableParametersMetaData.hasCascadables() ); + switch ( processedBeansTrackingVoteForParameters ) { + case NON_TRACKING: + this.trackingEnabledForParameters = false; + break; + case TRACKING: + this.trackingEnabledForParameters = true; + break; + default: + this.trackingEnabledForParameters = validatableParametersMetaData.hasCascadables(); + break; + } + + Vote processedBeansTrackingVoteForReturnValue = processedBeansTrackingVoter.isEnabledForReturnValue( beanClass, name, parameterTypes, + returnValueMetaData.hasCascadables() ); + switch ( processedBeansTrackingVoteForReturnValue ) { + case NON_TRACKING: + this.trackingEnabledForReturnValue = false; + break; + case TRACKING: + this.trackingEnabledForReturnValue = true; + break; + default: + this.trackingEnabledForReturnValue = returnValueMetaData.hasCascadables(); + break; + } } - public ExecutableMetaData(ExecutableMetaData originalExecutableMetaData, ProcessedBeansTrackingStrategy processedBeansTrackingStrategy) { + public ExecutableMetaData(ExecutableMetaData originalExecutableMetaData, + ProcessedBeansTrackingStrategy processedBeansTrackingStrategy, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { super( originalExecutableMetaData ); + this.beanClass = originalExecutableMetaData.beanClass; this.parameterTypes = originalExecutableMetaData.parameterTypes; this.parameterMetaDataList = originalExecutableMetaData.parameterMetaDataList; this.validatableParametersMetaData = originalExecutableMetaData.validatableParametersMetaData; @@ -133,19 +169,43 @@ public ExecutableMetaData(ExecutableMetaData originalExecutableMetaData, Process this.isGetter = originalExecutableMetaData.isGetter; this.kind = originalExecutableMetaData.kind; - boolean trackingEnabledForParameters = false; - for ( Signature signature : originalExecutableMetaData.getSignatures() ) { - trackingEnabledForParameters = trackingEnabledForParameters || processedBeansTrackingStrategy.isEnabledForParameters( signature, - originalExecutableMetaData.getValidatableParametersMetaData().hasCascadables() ); + Vote processedBeansTrackingVoteForParameters = processedBeansTrackingVoter.isEnabledForParameters( originalExecutableMetaData.beanClass, + originalExecutableMetaData.getName(), parameterTypes, originalExecutableMetaData.getValidatableParametersMetaData().hasCascadables() ); + switch ( processedBeansTrackingVoteForParameters ) { + case NON_TRACKING: + this.trackingEnabledForParameters = false; + break; + case TRACKING: + this.trackingEnabledForParameters = true; + break; + default: + boolean trackingEnabledForParameters = false; + for ( Signature signature : originalExecutableMetaData.getSignatures() ) { + trackingEnabledForParameters = trackingEnabledForParameters || processedBeansTrackingStrategy.isEnabledForParameters( signature, + originalExecutableMetaData.getValidatableParametersMetaData().hasCascadables() ); + } + this.trackingEnabledForParameters = trackingEnabledForParameters; + break; } - this.trackingEnabledForParameters = trackingEnabledForParameters; - boolean trackingEnabledForReturnValue = false; - for ( Signature signature : originalExecutableMetaData.getSignatures() ) { - trackingEnabledForReturnValue = trackingEnabledForReturnValue || processedBeansTrackingStrategy.isEnabledForReturnValue( signature, - originalExecutableMetaData.getReturnValueMetaData().hasCascadables() ); + Vote processedBeansTrackingVoteForReturnValue = processedBeansTrackingVoter.isEnabledForReturnValue( originalExecutableMetaData.beanClass, + originalExecutableMetaData.getName(), parameterTypes, originalExecutableMetaData.getReturnValueMetaData().hasCascadables() ); + switch ( processedBeansTrackingVoteForReturnValue ) { + case NON_TRACKING: + this.trackingEnabledForReturnValue = false; + break; + case TRACKING: + this.trackingEnabledForReturnValue = true; + break; + default: + boolean trackingEnabledForReturnValue = false; + for ( Signature signature : originalExecutableMetaData.getSignatures() ) { + trackingEnabledForReturnValue = trackingEnabledForReturnValue || processedBeansTrackingStrategy.isEnabledForReturnValue( signature, + originalExecutableMetaData.getReturnValueMetaData().hasCascadables() ); + } + this.trackingEnabledForReturnValue = trackingEnabledForReturnValue; + break; } - this.trackingEnabledForReturnValue = trackingEnabledForReturnValue; } /** @@ -304,6 +364,7 @@ public static class Builder extends MetaDataBuilder { private final Set rules; private boolean isConstrained = false; private CascadingMetaDataBuilder cascadingMetaDataBuilder; + private ProcessedBeansTrackingVoter processedBeansTrackingVoter; /** * Holds a merged representation of the configurations for one method @@ -323,11 +384,13 @@ public Builder( ConstraintCreationContext constraintCreationContext, ExecutableHelper executableHelper, ExecutableParameterNameProvider parameterNameProvider, - MethodValidationConfiguration methodValidationConfiguration) { + MethodValidationConfiguration methodValidationConfiguration, + ProcessedBeansTrackingVoter processedBeansTrackingVoter) { super( beanClass, constraintCreationContext ); this.executableHelper = executableHelper; this.parameterNameProvider = parameterNameProvider; + this.processedBeansTrackingVoter = processedBeansTrackingVoter; this.kind = constrainedExecutable.getKind(); this.callable = constrainedExecutable.getCallable(); this.rules = methodValidationConfiguration.getConfiguredRuleSet(); @@ -422,6 +485,7 @@ public ExecutableMetaData build() { assertCorrectnessOfConfiguration(); return new ExecutableMetaData( + callable.getDeclaringClass(), callable.getName(), callable.getType(), callable.getParameterTypes(), @@ -434,7 +498,8 @@ public ExecutableMetaData build() { adaptOriginsAndImplicitGroups( crossParameterConstraints ), cascadingMetaDataBuilder.build( constraintCreationContext.getValueExtractorManager(), callable ), isConstrained, - kind == ConstrainedElementKind.GETTER + kind == ConstrainedElementKind.GETTER, + processedBeansTrackingVoter ); } diff --git a/engine/src/main/java/org/hibernate/validator/spi/tracking/ProcessedBeansTrackingVoter.java b/engine/src/main/java/org/hibernate/validator/spi/tracking/ProcessedBeansTrackingVoter.java new file mode 100644 index 0000000000..e3c113a9b1 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/spi/tracking/ProcessedBeansTrackingVoter.java @@ -0,0 +1,24 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.spi.tracking; + +import org.hibernate.validator.Incubating; + +@Incubating +public interface ProcessedBeansTrackingVoter { + + Vote isEnabledForBean(Class beanClass, boolean hasCascadables); + + Vote isEnabledForReturnValue(Class beanClass, String name, Class[] parameterTypes, boolean hasCascadables); + + Vote isEnabledForParameters(Class beanClass, String name, Class[] parameterTypes, boolean hasCascadables); + + enum Vote { + + DEFAULT, NON_TRACKING, TRACKING; + } +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java index 074645b79f..0a4a40b552 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/path/PathImplTest.java @@ -33,6 +33,7 @@ import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; import org.hibernate.validator.internal.engine.path.PathImpl; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; @@ -202,7 +203,8 @@ public void testCreationOfExecutablePath() throws Exception { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); ExecutableMetaData executableMetaData = beanMetaDataManager.getBeanMetaData( Container.class ) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java index d83b3ce7fe..e71adffce8 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/BeanMetaDataManagerTest.java @@ -25,6 +25,7 @@ import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData; @@ -61,7 +62,8 @@ public void setUpBeanMetaDataManager() { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java index caf2eb5849..10f2239c29 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ExecutableMetaDataTest.java @@ -27,6 +27,7 @@ import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; @@ -68,7 +69,8 @@ public void setupBeanMetaData() { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); beanMetaData = beanMetaDataManager.getBeanMetaData( CustomerRepositoryExt.class ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java index e9a97a095e..66bd8f546c 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/ParameterMetaDataTest.java @@ -28,6 +28,7 @@ import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; @@ -67,7 +68,8 @@ public void setupBeanMetaData() { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); beanMetaData = beanMetaDataManager.getBeanMetaData( CustomerRepository.class ); @@ -137,7 +139,8 @@ public void parameterNameInInheritanceHierarchy() throws Exception { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); BeanMetaData localBeanMetaData = beanMetaDataManager.getBeanMetaData( ServiceImpl.class ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java index f73cb77d7d..5e9d6e4a33 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/metadata/aggregated/PropertyMetaDataTest.java @@ -21,6 +21,7 @@ import org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider; import org.hibernate.validator.internal.engine.MethodValidationConfiguration; import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator; +import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter; import org.hibernate.validator.internal.metadata.BeanMetaDataManager; import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl; import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer; @@ -51,7 +52,8 @@ public void setupBeanMetaDataManager() { new DefaultBeanMetaDataClassNormalizer(), new ValidationOrderGenerator(), Collections.emptyList(), - new MethodValidationConfiguration.Builder().build() + new MethodValidationConfiguration.Builder().build(), + new DefaultProcessedBeansTrackingVoter() ); } From 19786dfeab9d355899f93c7478b4e8d87e036a09 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 26 Aug 2021 15:06:13 +0200 Subject: [PATCH 16/16] HV-1831 Enhance ExecutableMetaData with tracking information --- .../internal/metadata/aggregated/BeanMetaDataImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index 52ee7f06a6..70c0f7e0fd 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -275,7 +275,12 @@ public BeanMetaDataImpl(BeanMetaDataImpl originalBeanMetaData, ProcessedBeans this.defaultGroupSequence = originalBeanMetaData.defaultGroupSequence; this.validationOrder = originalBeanMetaData.validationOrder; this.directMetaConstraints = originalBeanMetaData.directMetaConstraints; - this.executableMetaDataMap = originalBeanMetaData.executableMetaDataMap; + Map tempExecutableMetaDataMap = newHashMap(); + for ( Entry executableMetaDataEntry : originalBeanMetaData.executableMetaDataMap.entrySet() ) { + tempExecutableMetaDataMap.put( executableMetaDataEntry.getKey(), + new ExecutableMetaData( executableMetaDataEntry.getValue(), processedBeansTrackingStrategy, processedBeansTrackingVoter ) ); + } + this.executableMetaDataMap = CollectionHelper.toImmutableMap( tempExecutableMetaDataMap ); this.unconstrainedExecutables = originalBeanMetaData.unconstrainedExecutables; this.defaultGroupSequenceRedefined = originalBeanMetaData.defaultGroupSequenceRedefined; this.resolvedDefaultGroupSequence = originalBeanMetaData.resolvedDefaultGroupSequence;