Skip to content

Commit

Permalink
HV-1487 Allow to disable the TraversableResolver result cache
Browse files Browse the repository at this point in the history
  • Loading branch information
gsmet authored and gunnarmorling committed Oct 18, 2017
1 parent 8b2f0d6 commit b301909
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 29 deletions.
Expand Up @@ -9,9 +9,11 @@
import java.util.Set;

import javax.validation.Configuration;
import javax.validation.TraversableResolver;
import javax.validation.valueextraction.ValueExtractor;

import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

/**
Expand Down Expand Up @@ -61,6 +63,15 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
*/
String CONSTRAINT_MAPPING_CONTRIBUTORS = "hibernate.validator.constraint_mapping_contributors";

/**
* Property corresponding to the {@link #enableTraversableResolverResultCache(boolean)}.
* Accepts {@code true} or {@code false}.
* Defaults to {@code true}.
*
* @since 6.0.3
*/
String ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE = "hibernate.validator.enable_traversable_resolver_result_cache";

/**
* <p>
* Returns the {@link ResourceBundleLocator} used by the
Expand Down Expand Up @@ -210,4 +221,20 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
* @since 5.3
*/
HibernateValidatorConfiguration allowParallelMethodsDefineParameterConstraints(boolean allow);

/**
* Define whether the per validation call caching of {@link TraversableResolver} results is enabled. The default
* value is {@code true}, i.e. the caching is enabled.
* <p>
* This behavior was initially introduced to cache the {@link JPATraversableResolver} results but the map lookups it
* introduces can be counterproductive when the {@code TraversableResolver} calls are very fast.
*
* @param enabled flag determining whether per validation call caching is enabled for {@code TraversableResolver}
* results.
*
* @return {@code this} following the chaining method pattern
*
* @since 6.0.3
*/
HibernateValidatorConfiguration enableTraversableResolverResultCache(boolean enabled);
}
Expand Up @@ -15,6 +15,8 @@
import javax.validation.ValidatorContext;
import javax.validation.valueextraction.ValueExtractor;

import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver;

/**
* Represents a Hibernate Validator specific context that is used to create
* {@link javax.validation.Validator} instances. Adds additional configuration options to those
Expand Down Expand Up @@ -122,4 +124,20 @@ public interface HibernateValidatorContext extends ValidatorContext {
* @since 5.3
*/
HibernateValidatorContext allowParallelMethodsDefineParameterConstraints(boolean allow);

/**
* Define whether the per validation call caching of {@link TraversableResolver} results is enabled. The default
* value is {@code true}, i.e. the caching is enabled.
* <p>
* This behavior was initially introduced to cache the {@link JPATraversableResolver} results but the map lookups it
* introduces can be counterproductive when the {@code TraversableResolver} calls are very fast.
*
* @param enabled flag determining whether per validation call caching is enabled for {@code TraversableResolver}
* results.
*
* @return {@code this} following the chaining method pattern
*
* @since 6.0.3
*/
HibernateValidatorContext enableTraversableResolverResultCache(boolean enabled);
}
Expand Up @@ -95,6 +95,7 @@ public class ConfigurationImpl implements HibernateValidatorConfiguration, Confi
private ClassLoader externalClassLoader;
private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder();
private final Map<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> valueExtractorDescriptors = new HashMap<>();
private boolean traversableResolverResultCacheEnabled = true;

public ConfigurationImpl(BootstrapState state) {
this();
Expand Down Expand Up @@ -155,6 +156,16 @@ public final ConfigurationImpl traversableResolver(TraversableResolver resolver)
return this;
}

@Override
public final ConfigurationImpl enableTraversableResolverResultCache(boolean enabled) {
this.traversableResolverResultCacheEnabled = enabled;
return this;
}

public final boolean isTraversableResolverResultCacheEnabled() {
return traversableResolverResultCacheEnabled;
}

@Override
public final ConfigurationImpl constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
if ( log.isDebugEnabled() ) {
Expand Down
Expand Up @@ -43,6 +43,7 @@ public class ValidatorContextImpl implements HibernateValidatorContext {
private ExecutableParameterNameProvider parameterNameProvider;
private ClockProvider clockProvider;
private boolean failFast;
private boolean traversableResolverResultCacheEnabled;
private final ValueExtractorManager valueExtractorManager;
private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder;
private final Map<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> valueExtractorDescriptors;
Expand All @@ -55,6 +56,7 @@ public ValidatorContextImpl(ValidatorFactoryImpl validatorFactory) {
this.parameterNameProvider = validatorFactory.getExecutableParameterNameProvider();
this.clockProvider = validatorFactory.getClockProvider();
this.failFast = validatorFactory.isFailFast();
this.traversableResolverResultCacheEnabled = validatorFactory.isTraversableResolverResultCacheEnabled();
this.methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder( validatorFactory.getMethodValidationConfiguration() );
this.valueExtractorManager = validatorFactory.getValueExtractorManager();
this.valueExtractorDescriptors = new HashMap<>();
Expand Down Expand Up @@ -151,6 +153,12 @@ public HibernateValidatorContext allowParallelMethodsDefineParameterConstraints(
return this;
}

@Override
public HibernateValidatorContext enableTraversableResolverResultCache(boolean enabled) {
this.traversableResolverResultCacheEnabled = enabled;
return this;
}

@Override
public Validator getValidator() {
return validatorFactory.createValidator(
Expand All @@ -161,7 +169,8 @@ public Validator getValidator() {
clockProvider,
failFast,
valueExtractorDescriptors.isEmpty() ? valueExtractorManager : new ValueExtractorManager( valueExtractorManager, valueExtractorDescriptors ),
methodValidationConfigurationBuilder.build()
methodValidationConfigurationBuilder.build(),
traversableResolverResultCacheEnabled
);
}
}
Expand Up @@ -122,10 +122,15 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory {
private final boolean failFast;

/**
* Hibernate validator specific flags to relax constraints on parameters.
* Hibernate Validator specific flags to relax constraints on parameters.
*/
private final MethodValidationConfiguration methodValidationConfiguration;

/**
* Hibernate Validator specific flag to disable the {@code TraversableResolver} result cache.
*/
private final boolean traversableResolverResultCacheEnabled;

/**
* Metadata provider for XML configuration.
*/
Expand Down Expand Up @@ -157,6 +162,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
this.executableHelper = new ExecutableHelper( typeResolutionHelper );

boolean tmpFailFast = false;
boolean tmpTraversableResolverResultCacheEnabled = true;
boolean tmpAllowOverridingMethodAlterParameterConstraint = false;
boolean tmpAllowMultipleCascadedValidationOnReturnValues = false;
boolean tmpAllowParallelMethodsDefineParameterConstraints = false;
Expand All @@ -176,6 +182,8 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
tmpAllowParallelMethodsDefineParameterConstraints =
hibernateSpecificConfig.getMethodValidationConfiguration()
.isAllowParallelMethodsDefineParameterConstraints();

tmpTraversableResolverResultCacheEnabled = hibernateSpecificConfig.isTraversableResolverResultCacheEnabled();
}

// HV-302; don't load XmlMappingParser if not necessary
Expand All @@ -200,7 +208,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {

Map<String, String> properties = configurationState.getProperties();

tmpFailFast = checkPropertiesForBoolean( properties, HibernateValidatorConfiguration.FAIL_FAST, tmpFailFast );
tmpFailFast = checkPropertiesForFailFast( properties, tmpFailFast );
this.failFast = tmpFailFast;

Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder();
Expand Down Expand Up @@ -233,6 +241,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
);
this.methodValidationConfiguration = methodValidationConfigurationBuilder.build();

tmpTraversableResolverResultCacheEnabled = checkPropertiesForBoolean( properties,
HibernateValidatorConfiguration.ENABLE_TRAVERSABLE_RESOLVER_RESULT_CACHE, tmpTraversableResolverResultCacheEnabled );
this.traversableResolverResultCacheEnabled = tmpTraversableResolverResultCacheEnabled;

this.constraintValidatorManager = new ConstraintValidatorManager( configurationState.getConstraintValidatorFactory() );
}

Expand Down Expand Up @@ -283,7 +295,8 @@ public Validator getValidator() {
clockProvider,
failFast,
valueExtractorManager,
methodValidationConfiguration
methodValidationConfiguration,
traversableResolverResultCacheEnabled
);
}

Expand Down Expand Up @@ -324,6 +337,10 @@ MethodValidationConfiguration getMethodValidationConfiguration() {
return methodValidationConfiguration;
}

public boolean isTraversableResolverResultCacheEnabled() {
return traversableResolverResultCacheEnabled;
}

ValueExtractorManager getValueExtractorManager() {
return valueExtractorManager;
}
Expand Down Expand Up @@ -358,7 +375,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
ClockProvider clockProvider,
boolean failFast,
ValueExtractorManager valueExtractorManager,
MethodValidationConfiguration methodValidationConfiguration) {
MethodValidationConfiguration methodValidationConfiguration,
boolean traversableResolverResultCacheEnabled) {

ValidationOrderGenerator validationOrderGenerator = new ValidationOrderGenerator();

Expand Down Expand Up @@ -386,7 +404,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
valueExtractorManager,
constraintValidatorManager,
validationOrderGenerator,
failFast
failFast,
traversableResolverResultCacheEnabled
);
}

Expand All @@ -409,9 +428,9 @@ private List<MetaDataProvider> buildDataProviders() {
return metaDataProviders;
}

private boolean checkPropertiesForBoolean(Map<String, String> properties, String propertyKey, boolean programmaticValue) {
private boolean checkPropertiesForFailFast(Map<String, String> properties, boolean programmaticValue) {
boolean value = programmaticValue;
String propertyStringValue = properties.get( propertyKey );
String propertyStringValue = properties.get( HibernateValidatorConfiguration.FAIL_FAST );
if ( propertyStringValue != null ) {
boolean configurationValue = Boolean.valueOf( propertyStringValue );
// throw an exception if the programmatic value is true and it overrides a false configured value
Expand All @@ -423,6 +442,15 @@ private boolean checkPropertiesForBoolean(Map<String, String> properties, String
return value;
}

private boolean checkPropertiesForBoolean(Map<String, String> properties, String propertyKey, boolean programmaticValue) {
boolean value = programmaticValue;
String propertyStringValue = properties.get( propertyKey );
if ( propertyStringValue != null ) {
value = Boolean.valueOf( propertyStringValue );
}
return value;
}

/**
* Returns a list with {@link ConstraintMappingContributor}s configured via the
* {@link HibernateValidatorConfiguration#CONSTRAINT_MAPPING_CONTRIBUTORS} property.
Expand Down
Expand Up @@ -138,6 +138,11 @@ public class ValidatorImpl implements Validator, ExecutableValidator {
*/
private final boolean failFast;

/**
* Indicates if the {@code TraversableResolver} result cache is enabled.
*/
private final boolean traversableResolverResultCacheEnabled;

private final ValueExtractorManager valueExtractorManager;

public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory,
Expand All @@ -149,7 +154,8 @@ public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory,
ValueExtractorManager valueExtractorManager,
ConstraintValidatorManager constraintValidatorManager,
ValidationOrderGenerator validationOrderGenerator,
boolean failFast) {
boolean failFast,
boolean traversableResolverResultCacheEnabled) {
this.constraintValidatorFactory = constraintValidatorFactory;
this.messageInterpolator = messageInterpolator;
this.traversableResolver = traversableResolver;
Expand All @@ -160,6 +166,7 @@ public ValidatorImpl(ConstraintValidatorFactory constraintValidatorFactory,
this.constraintValidatorManager = constraintValidatorManager;
this.validationOrderGenerator = validationOrderGenerator;
this.failFast = failFast;
this.traversableResolverResultCacheEnabled = traversableResolverResultCacheEnabled;
}

@Override
Expand Down Expand Up @@ -332,7 +339,7 @@ private ValidationContextBuilder getValidationContextBuilder() {
constraintValidatorManager,
messageInterpolator,
constraintValidatorFactory,
wrapTraversableResolverForCachingIfRequired( traversableResolver ),
wrapWithCaching( traversableResolver, traversableResolverResultCacheEnabled ),
clockProvider,
failFast
);
Expand Down Expand Up @@ -1287,15 +1294,16 @@ private <V> ValueContext<?, V> getValueContextForValueValidation(ValidationConte
* Potentially wrap the {@link TraversableResolver} into a caching one.
* <p>
* If {@code traversableResolver} is {@code TraverseAllTraversableResolver.INSTANCE}, we don't wrap it and it is
* returned directly.
* returned directly. Same if the caching is explicitly disabled.
* <p>
* If it is not, we wrap the resolver for caching. In this case, a new instance is returned each time and it should
* If not, we wrap the resolver for caching. In this case, a new instance is returned each time and it should
* be used only for the duration of a validation call.
*
* @return The resolver for the duration of a full validation.
* @return The resolver for the duration of a validation call.
*/
private static TraversableResolver wrapTraversableResolverForCachingIfRequired(TraversableResolver traversableResolver) {
if ( traversableResolver.getClass() == TraverseAllTraversableResolver.class ) {
private static TraversableResolver wrapWithCaching(TraversableResolver traversableResolver,
boolean traversableResolverResultCacheEnabled) {
if ( TraverseAllTraversableResolver.class.equals( traversableResolver.getClass() ) || !traversableResolverResultCacheEnabled ) {
return traversableResolver;
}
return new CachingTraversableResolverForSingleValidation( traversableResolver );
Expand Down

0 comments on commit b301909

Please sign in to comment.