8000 HV-1831 Optimize cascading validation for large lists by marko-bekhta · Pull Request #1331 · hibernate/hibernate-validator · GitHub
[go: up one dir, main page]

Skip to content

HV-1831 Optimize cascading validation for large lists #1331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose 8000 a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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; 8000

/**
* Base interface for Hibernate Validator specific configurations.
Expand Down Expand Up @@ -486,4 +487,7 @@ default S locales(Locale... locales) {
*/
@Incubating
S failFastOnPropertyViolation(boolean failFastOnPropertyViolation);

@Incubating
S processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeanTrackingVoter);
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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.
Expand Down Expand Up @@ -129,6 +130,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel;
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;
private ProcessedBeansTrackingVoter processedBeansTrackingVoter;
private boolean showValidatedValuesInTraceLogs;

protected AbstractConfigurationImpl(BootstrapState state) {
Expand Down Expand Up @@ -673,6 +675,22 @@ public final boolean getShowValidatedValuesInTraceLogs() {
return this.showValidatedValuesInTraceLogs;
}

@Override
public T processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeansTrackingVoter) {
if ( LOG.isDebugEnabled() ) {
if ( processedBeansTrackingVoter != null ) {
LOG.debug( "Setting custom ProcessedBeansTrackingVoter of type " + processedBeansTrackingVoter.getClass()
.getName() );
}
}
this.processedBeansTrackingVoter = processedBeansTrackingVoter;
return thisAsT();
}

public ProcessedBeansTrackingVoter getProcessedBeansTrackingVoter() {
return processedBeansTrackingVoter;
}

public final Set<DefaultConstraintMapping> getProgrammaticMappings() {
return programmaticMappings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,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.DefaultProcessedBeansTrackingVoter;
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
Expand Down Expand Up @@ -118,6 +120,14 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties )
).build();

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.validatorFactoryScopedContext = new ValidatorFactoryScopedContext(
configurationState.getMessageInterpolator(),
configurationState.getTraversableResolver(),
Expand All @@ -128,15 +138,16 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
determineFailFast( hibernateSpecificConfig, properties ),
determineFailFastOnPropertyViolation( hibernateSpecificConfig, properties ),
determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties ),
determineConstraintValidatorPayload( hibernateSpecificConfig ),
determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties )
constraintValidatorInitializationContext
);

this.constraintValidatorManager = new PredefinedScopeConstraintValidatorManagerImpl(
configurationState.getConstraintValidatorFactory(),
this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext()
constraintValidatorInitializationContext
);

this.validationOrderGenerator = new ValidationOrderGenerator();
Expand All @@ -147,11 +158,14 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() );
ConstraintHelper constraintHelper = ConstraintHelper.forBuiltinConstraints(
hibernateSpecificConfig.getBuiltinConstraints(),
hibernateSpecificConfig.isIncludeBeansAndConstraintsDefinedOnlyInXml() );
hibernateSpecificConfig.isIncludeBeansAndConstraintsDefinedOnlyInXml()
);
TypeResolutionHelper typeResolutionHelper = new TypeResolutionHelper();

ConstraintCreationContext constraintCreationContext = new ConstraintCreationContext( constraintHelper,
constraintValidatorManager, typeResolutionHelper, valueExtractorManager );
ConstraintCreationContext constraintCreationContext = new ConstraintCreationContext(
constraintHelper,
constraintValidatorManager, typeResolutionHelper, valueExtractorManager
);

ExecutableHelper executableHelper = new ExecutableHelper( typeResolutionHelper );
JavaBeanHelper javaBeanHelper = new JavaBeanHelper( getterPropertySelectionStrategy, propertyNodeNameProvider );
Expand All @@ -164,15 +178,18 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
javaBeanHelper,
externalClassLoader
),
constraintHelper );
constraintHelper
);

// we parse all XML mappings but only register constraint validators and delay constraint mappings building till
// we collect all the constraint validators.
// HV-302; don't load XmlMappingParser if not necessary
MappingXmlParser mappingParser = null;
if ( !configurationState.getMappingStreams().isEmpty() ) {
mappingParser = new MappingXmlParser( constraintCreationContext,
javaBeanHelper, externalClassLoader );
mappingParser = new MappingXmlParser(
constraintCreationContext,
javaBeanHelper, externalClassLoader
);
mappingParser.parse( configurationState.getMappingStreams() );
}

Expand Down Expand Up @@ -202,15 +219,32 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
xmlMetaDataProvider = null;
}

// 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,
validatorFactoryScopedContext.getParameterNameProvider(),
parameterNameProvider,
javaBeanHelper,
validationOrderGenerator,
buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ),
methodValidationConfiguration,
determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ),
( hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null )
? hibernateSpecificConfig.getProcessedBeansTrackingVoter()
: new DefaultProcessedBeansTrackingVoter(),
beanClassesToInitialize
);

Expand Down Expand Up @@ -281,6 +315,10 @@ public boolean isTraversableResolverResultCacheEnabled() {
return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled();
}

public PredefinedScopeBeanMetaDataManager getBeanMetaDataManager() {
return beanMetaDataManager;
}

@Override
public <T> T unwrap(Class<T> type) {
// allow unwrapping into public super types
Expand Down Expand Up @@ -322,7 +360,8 @@ Validator createValidator(ValidatorFactoryScopedContext validatorFactoryScopedCo
private static List<MetaDataProvider> buildMetaDataProviders(
ConstraintCreationContext constraintCreationContext,
XmlMetaDataProvider xmlMetaDataProvider,
Set<DefaultConstraintMapping> constraintMappings) {
Set<DefaultConstraintMapping> constraintMappings
) {
List<MetaDataProvider> metaDataProviders = newArrayList();
if ( xmlMetaDataProvider != null ) {
metaDataProviders.add( xmlMetaDataProvider );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,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;
Expand All @@ -68,6 +69,7 @@
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
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
Expand Down Expand Up @@ -133,6 +135,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory {

private final ValidationOrderGenerator validationOrderGenerator;

private final ProcessedBeansTrackingVoter processedBeansTrackingVoter;

public ValidatorFactoryImpl(ConfigurationState configurationState) {
ClassLoader externalClassLoader = determineExternalClassLoader( configurationState );

Expand Down Expand Up @@ -162,10 +166,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
determineFailFast( hibernateSpecificConfig, properties ),
determineFailFastOnPropertyViolation( hibernateSpecificConfig, properties ),
determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties ),
determineConstraintValidatorPayload( hibernateSpecificConfig ),
determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties )
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ) F987
);

ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl(
Expand Down Expand Up @@ -226,6 +230,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
this.xmlMetaDataProvider = null;
}

this.processedBeansTrackingVoter = ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null )
? hibernateSpecificConfig.getProcessedBeansTrackingVoter()
: new DefaultProcessedBeansTrackingVoter();

if ( LOG.isDebugEnabled() ) {
logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
}
Expand Down Expand Up @@ -349,7 +357,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
beanMetadataClassNormalizer,
validationOrderGenerator,
buildMetaDataProviders(),
methodValidationConfiguration
methodValidationConfiguration,
processedBeansTrackingVoter
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,18 @@ public class ValidatorFactoryScopedContext {
boolean failFast,
boolean failFastOnPropertyViolation,
boolean traversableResolverResultCacheEnabled,
boolean showValidatedValuesInTraceLogs,
Object constraintValidatorPayload,
ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel,
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel,
boolean showValidatedValuesInTraceLogs) {
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel) {
this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast,
failFastOnPropertyViolation, traversableResolverResultCacheEnabled, showValidatedValuesInTraceLogs, constraintValidatorPayload, constraintExpressionLanguageFeatureLevel,
customViolationExpressionLanguageFeatureLevel,
new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider,
temporalValidationTolerance ) );
}

private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
TraversableResolver traversableResolver,
ExecutableParameterNameProvider parameterNameProvider,
ClockProvider clockProvider,
Expand All @@ -121,7 +121,8 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
boolean failFast,
boolean failFastOnPropertyViolation,
boolean traversableResolverResultCacheEnabled,
boolean showValidatedValuesInTraceLogs, Object constraintValidatorPayload,
boolean showValidatedValuesInTraceLogs,
Object constraintValidatorPayload,
ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel,
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel,
HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) {
Expand Down Expand Up @@ -212,7 +213,6 @@ static class Builder {
private Object constraintValidatorPayload;
private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel;
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;

private boolean showValidatedValuesInTraceLogs;
private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private NodeImpl addMethodNode(String name, Class<?>[] parameterTypes) {
}

public NodeImpl makeLeafNodeIterable() {
requiresWriteableNodeList();
copyNodeList();

currentLeafNode = NodeImpl.makeIterable( currentLeafNode );

Expand All @@ -214,7 +214,7 @@ public NodeImpl makeLeafNodeIterable() {
}

public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) {
requiresWriteableNodeList();
copyNodeList();

currentLeafNode = NodeImpl.makeIterableAndSetIndex( currentLeafNode, index );

Expand All @@ -224,7 +224,7 @@ public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) {
}

public NodeImpl makeLeafNodeIterableAndSetMapKey(Object key) {
requiresWriteableNodeList();
copyNodeList();

currentLeafNode = NodeImpl.makeIterableAndSetMapKey( currentLeafNode, key );

Expand Down Expand Up @@ -309,6 +309,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<Node> newNodeList = new ArrayList<>( nodeList.size() + 1 );
newNodeList.addAll( nodeList );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
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;
}
}
Loading
0