Skip to content

Commit 4f0c69d

Browse files
gsmetgunnarmorling
authored andcommitted
HV-1587 Reduce significantly the tracking of already validated beans and constraints
1 parent 51ae929 commit 4f0c69d

File tree

6 files changed

+131
-11
lines changed

6 files changed

+131
-11
lines changed

engine/src/main/java/org/hibernate/validator/internal/engine/ValidationContext.java

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Iterator;
1717
import java.util.List;
1818
import java.util.Map;
19+
import java.util.Optional;
1920
import java.util.Set;
2021
import java.util.stream.Collectors;
2122

@@ -38,6 +39,7 @@
3839
import org.hibernate.validator.internal.engine.path.PathImpl;
3940
import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
4041
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
42+
import org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData;
4143
import org.hibernate.validator.internal.metadata.core.MetaConstraint;
4244
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
4345
import org.hibernate.validator.internal.util.logging.Log;
@@ -99,6 +101,11 @@ public class ValidationContext<T> {
99101
*/
100102
private final Object executableReturnValue;
101103

104+
/**
105+
* The metadata of the Executable. Will be non empty if we are in the case of method validation and the method is constrained.
106+
*/
107+
private final Optional<ExecutableMetaData> executableMetaData;
108+
102109
/**
103110
* The set of already processed meta constraints per bean - path ({@link BeanPathMetaConstraintProcessedUnit}).
104111
*/
@@ -139,6 +146,11 @@ public class ValidationContext<T> {
139146
*/
140147
private final HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext;
141148

149+
/**
150+
* Indicates if the tracking of already validated bean should be disabled.
151+
*/
152+
private final boolean disableAlreadyValidatedBeanTracking;
153+
142154
/**
143155
* The name of the validated (leaf) property in case of a validateProperty()/validateValue() call.
144156
*/
@@ -155,7 +167,8 @@ private ValidationContext(ValidationOperation validationOperation,
155167
BeanMetaData<T> rootBeanMetaData,
156168
Executable executable,
157169
Object[] executableParameters,
158-
Object executableReturnValue) {
170+
Object executableReturnValue,
171+
Optional<ExecutableMetaData> executableMetaData) {
159172
this.validationOperation = validationOperation;
160173

161174
this.constraintValidatorManager = constraintValidatorManager;
@@ -175,6 +188,10 @@ private ValidationContext(ValidationOperation validationOperation,
175188
this.processedPathUnits = new HashSet<>();
176189
this.processedPathsPerBean = new IdentityHashMap<>();
177190
this.failingConstraintViolations = newHashSet();
191+
192+
this.executableMetaData = executableMetaData;
193+
194+
this.disableAlreadyValidatedBeanTracking = buildDisableAlreadyValidatedBeanTracking( validationOperation, rootBeanMetaData, executableMetaData );
178195
}
179196

180197
public static ValidationContextBuilder getValidationContextBuilder(
@@ -211,6 +228,10 @@ public Executable getExecutable() {
211228
return executable;
212229
}
213230

231+
public Optional<ExecutableMetaData> getExecutableMetaData() {
232+
return executableMetaData;
233+
}
234+
214235
public TraversableResolver getTraversableResolver() {
215236
return traversableResolver;
216237
}
@@ -259,6 +280,10 @@ public ConstraintValidatorFactory getConstraintValidatorFactory() {
259280
}
260281

261282
public boolean isBeanAlreadyValidated(Object value, Class<?> group, PathImpl path) {
283+
if ( disableAlreadyValidatedBeanTracking ) {
284+
return false;
285+
}
286+
262287
boolean alreadyValidated;
263288
alreadyValidated = isAlreadyValidatedForCurrentGroup( value, group );
264289

@@ -270,6 +295,10 @@ public boolean isBeanAlreadyValidated(Object value, Class<?> group, PathImpl pat
270295
}
271296

272297
public void markCurrentBeanAsProcessed(ValueContext<?, ?> valueContext) {
298+
if ( disableAlreadyValidatedBeanTracking ) {
299+
return;
300+
}
301+
273302
markCurrentBeanAsProcessedForCurrentGroup( valueContext.getCurrentBean(), valueContext.getCurrentGroup() );
274303
markCurrentBeanAsProcessedForCurrentPath( valueContext.getCurrentBean(), valueContext.getPropertyPath() );
275304
}
@@ -348,10 +377,22 @@ public ConstraintViolation<T> createConstraintViolation(ValueContext<?, ?> local
348377
}
349378

350379
public boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstraint<?> metaConstraint) {
380+
// this is only useful if the constraint is defined for more than 1 group as in the case it's only
381+
// defined for one group, there is no chance it's going to be called twice.
382+
if ( metaConstraint.isDefinedForOneGroupOnly() ) {
383+
return false;
384+
}
385+
351386
return processedPathUnits.contains( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
352387
}
353388

354389
public void markConstraintProcessed(Object bean, Path path, MetaConstraint<?> metaConstraint) {
390+
// this is only useful if the constraint is defined for more than 1 group as in the case it's only
391+
// defined for one group, there is no chance it's going to be called twice.
392+
if ( metaConstraint.isDefinedForOneGroupOnly() ) {
393+
return;
394+
}
395+
355396
processedPathUnits.add( new BeanPathMetaConstraintProcessedUnit( bean, path, metaConstraint ) );
356397
}
357398

@@ -372,6 +413,33 @@ public String toString() {
372413
return sb.toString();
373414
}
374415

416+
private static boolean buildDisableAlreadyValidatedBeanTracking(ValidationOperation validationOperation, BeanMetaData<?> rootBeanMetaData,
417+
Optional<ExecutableMetaData> executableMetaData) {
418+
switch ( validationOperation ) {
419+
case BEAN_VALIDATION:
420+
case PROPERTY_VALIDATION:
421+
case VALUE_VALIDATION:
422+
// note that in the case of property and value validation, we are considering the root bean, whereas we
423+
// could consider the bean of the property or the value. We don't really have the info here though so it
424+
// will do for now.
425+
return !rootBeanMetaData.hasCascadingProperties();
426+
case PARAMETER_VALIDATION:
427+
if ( !executableMetaData.isPresent() ) {
428+
// the method is unconstrained so there's no need to worry about the tracking
429+
return false;
430+
}
431+
return !executableMetaData.get().hasCascadingParameters();
432+
case RETURN_VALUE_VALIDATION:
433+
if ( !executableMetaData.isPresent() ) {
434+
// the method is unconstrained so there's no need to worry about the tracking
435+
return false;
436+
}
437+
return !executableMetaData.get().getReturnValueMetaData().isCascading();
438+
default:
439+
return false;
440+
}
441+
}
442+
375443
private String interpolate(String messageTemplate,
376444
Object validatedValue,
377445
ConstraintDescriptor<?> descriptor,
@@ -487,7 +555,8 @@ public <T> ValidationContext<T> forValidate(T rootBean) {
487555
beanMetaDataManager.getBeanMetaData( rootBeanClass ),
488556
null, //executable
489557
null, //executable parameters
490-
null //executable return value
558+
null, //executable return value
559+
null //executable metadata
491560
);
492561
}
493562

@@ -506,7 +575,8 @@ public <T> ValidationContext<T> forValidateProperty(T rootBean) {
506575
beanMetaDataManager.getBeanMetaData( rootBeanClass ),
507576
null, //executable
508577
null, //executable parameters
509-
null //executable return value
578+
null, //executable return value
579+
null //executable metadata
510580
);
511581
}
512582

@@ -523,7 +593,8 @@ public <T> ValidationContext<T> forValidateValue(Class<T> rootBeanClass) {
523593
beanMetaDataManager.getBeanMetaData( rootBeanClass ),
524594
null, //executable
525595
null, //executable parameters
526-
null //executable return value
596+
null, //executable return value
597+
null //executable metadata
527598
);
528599
}
529600

@@ -534,6 +605,8 @@ public <T> ValidationContext<T> forValidateParameters(
534605
Object[] executableParameters) {
535606
@SuppressWarnings("unchecked")
536607
Class<T> rootBeanClass = rootBean != null ? (Class<T>) rootBean.getClass() : (Class<T>) executable.getDeclaringClass();
608+
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
609+
537610
return new ValidationContext<>(
538611
ValidationOperation.PARAMETER_VALIDATION,
539612
constraintValidatorManager,
@@ -543,10 +616,11 @@ public <T> ValidationContext<T> forValidateParameters(
543616
constraintValidatorInitializationContext,
544617
rootBean,
545618
rootBeanClass,
546-
beanMetaDataManager.getBeanMetaData( rootBeanClass ),
619+
rootBeanMetaData,
547620
executable,
548621
executableParameters,
549-
null //executable return value
622+
null, //executable return value
623+
rootBeanMetaData.getMetaDataFor( executable )
550624
);
551625
}
552626

@@ -556,6 +630,7 @@ public <T> ValidationContext<T> forValidateReturnValue(
556630
Object executableReturnValue) {
557631
@SuppressWarnings("unchecked")
558632
Class<T> rootBeanClass = rootBean != null ? (Class<T>) rootBean.getClass() : (Class<T>) executable.getDeclaringClass();
633+
BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
559634
return new ValidationContext<>(
560635
ValidationOperation.RETURN_VALUE_VALIDATION,
561636
constraintValidatorManager,
@@ -568,7 +643,8 @@ public <T> ValidationContext<T> forValidateReturnValue(
568643
beanMetaDataManager.getBeanMetaData( rootBeanClass ),
569644
executable,
570645
null, //executable parameters
571-
executableReturnValue
646+
executableReturnValue,
647+
rootBeanMetaData.getMetaDataFor( executable )
572648
);
573649
}
574650
}

engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ private <T> void validateParametersInContext(ValidationContext<T> validationCont
814814
ValidationOrder validationOrder) {
815815
BeanMetaData<T> beanMetaData = validationContext.getRootBeanMetaData();
816816

817-
Optional<ExecutableMetaData> executableMetaDataOptional = beanMetaData.getMetaDataFor( validationContext.getExecutable() );
817+
Optional<ExecutableMetaData> executableMetaDataOptional = validationContext.getExecutableMetaData();
818818

819819
if ( !executableMetaDataOptional.isPresent() ) {
820820
// the method is unconstrained
@@ -1011,7 +1011,7 @@ private <T> ValueContext<T, Object> getExecutableValueContext(T object, Executab
10111011
private <V, T> void validateReturnValueInContext(ValidationContext<T> validationContext, T bean, V value, ValidationOrder validationOrder) {
10121012
BeanMetaData<T> beanMetaData = validationContext.getRootBeanMetaData();
10131013

1014-
Optional<ExecutableMetaData> executableMetaDataOptional = beanMetaData.getMetaDataFor( validationContext.getExecutable() );
1014+
Optional<ExecutableMetaData> executableMetaDataOptional = validationContext.getExecutableMetaData();
10151015

10161016
if ( !executableMetaDataOptional.isPresent() ) {
10171017
// the method is unconstrained

engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaData.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ public interface BeanMetaData<T> extends Validatable {
113113
* element itself and goes up the hierarchy chain. Interfaces are not included.
114114
*/
115115
List<Class<? super T>> getClassHierarchy();
116+
117+
/**
118+
* @return {@code true} if there is at least one cascading property in the bean metadata, {@code false} otherwise.
119+
*/
120+
boolean hasCascadingProperties();
116121
}

engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public final class BeanMetaDataImpl<T> implements BeanMetaData<T> {
134134
@Immutable
135135
private final Set<Cascadable> cascadedProperties;
136136

137+
/**
138+
* Indicates if the bean has at least one cascading property.
139+
*/
140+
private final boolean hasCascadingProperties;
141+
137142
/**
138143
* The bean descriptor for this bean.
139144
*/
@@ -216,6 +221,7 @@ public BeanMetaDataImpl(Class<T> beanClass,
216221

217222
this.hasConstraints = hasConstraints;
218223
this.cascadedProperties = CollectionHelper.toImmutableSet( cascadedProperties );
224+
this.hasCascadingProperties = cascadedProperties.size() > 0;
219225
this.allMetaConstraints = CollectionHelper.toImmutableSet( allMetaConstraints );
220226

221227
this.classHierarchyWithoutInterfaces = CollectionHelper.toImmutableList( ClassHierarchyHelper.getHierarchy(
@@ -285,6 +291,11 @@ public Set<Cascadable> getCascadables() {
285291
return cascadedProperties;
286292
}
287293

294+
@Override
295+
public boolean hasCascadingProperties() {
296+
return hasCascadingProperties;
297+
}
298+
288299
@Override
289300
public PropertyMetaData getMetaDataFor(String propertyName) {
290301
PropertyMetaData propertyMetaData = propertyMetaDataMap.get( propertyName );

engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/ExecutableMetaData.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ public class ExecutableMetaData extends AbstractConstraintMetaData {
7676
private final ReturnValueMetaData returnValueMetaData;
7777
private final ElementKind kind;
7878

79+
private final boolean hasCascadingParameters;
80+
7981
private ExecutableMetaData(
8082
String name,
8183
Type returnType,
@@ -84,7 +86,7 @@ private ExecutableMetaData(
8486
Set<String> signatures,
8587
Set<MetaConstraint<?>> returnValueConstraints,
8688
Set<MetaConstraint<?>> returnValueContainerElementConstraints,
87-
List<ParameterMetaData> parameterMetaData,
89+
List<ParameterMetaData> parameterMetaDataList,
8890
Set<MetaConstraint<?>> crossParameterConstraints,
8991
CascadingMetaData cascadingMetaData,
9092
boolean isConstrained,
@@ -99,7 +101,7 @@ private ExecutableMetaData(
99101
);
100102

101103
this.parameterTypes = parameterTypes;
102-
this.parameterMetaDataList = CollectionHelper.toImmutableList( parameterMetaData );
104+
this.parameterMetaDataList = CollectionHelper.toImmutableList( parameterMetaDataList );
103105
this.crossParameterConstraints = CollectionHelper.toImmutableSet( crossParameterConstraints );
104106
this.signatures = signatures;
105107
this.returnValueMetaData = new ReturnValueMetaData(
@@ -110,6 +112,8 @@ private ExecutableMetaData(
110112
);
111113
this.isGetter = isGetter;
112114
this.kind = kind;
115+
116+
this.hasCascadingParameters = buildHasCascadingParameters( parameterMetaDataList );
113117
}
114118

115119
/**
@@ -240,6 +244,19 @@ public boolean equals(Object obj) {
240244
return true;
241245
}
242246

247+
public boolean hasCascadingParameters() {
248+
return hasCascadingParameters;
249+
}
250+
251+
private boolean buildHasCascadingParameters(List<ParameterMetaData> parameterMetaDataList) {
252+
for ( ParameterMetaData parameterMetaData : parameterMetaDataList ) {
253+
if ( parameterMetaData.isCascading() ) {
254+
return true;
255+
}
256+
}
257+
return false;
258+
}
259+
243260
/**
244261
* Creates new {@link ExecutableMetaData} instances.
245262
*

engine/src/main/java/org/hibernate/validator/internal/metadata/core/MetaConstraint.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ public class MetaConstraint<A extends Annotation> {
5555

5656
private final int hashCode;
5757

58+
/**
59+
* Indicates if the constraint is defined for one group only: used to optimize already validated constraints
60+
* tracking.
61+
*/
62+
private final boolean isDefinedForOneGroupOnly;
63+
5864
/**
5965
* @param constraintDescriptor The constraint descriptor for this constraint
6066
* @param location meta data about constraint placement
@@ -67,6 +73,7 @@ public class MetaConstraint<A extends Annotation> {
6773
this.location = location;
6874
this.valueExtractionPath = getValueExtractionPath( valueExtractionPath );
6975
this.hashCode = buildHashCode( constraintDescriptor, location );
76+
this.isDefinedForOneGroupOnly = constraintDescriptor.getGroups().size() <= 1;
7077
}
7178

7279
private static ValueExtractionPathNode getValueExtractionPath(List<ContainerClassTypeParameterAndExtractor> valueExtractionPath) {
@@ -85,6 +92,10 @@ public final Set<Class<?>> getGroupList() {
8592
return constraintTree.getDescriptor().getGroups();
8693
}
8794

95+
public final boolean isDefinedForOneGroupOnly() {
96+
return isDefinedForOneGroupOnly;
97+
}
98+
8899
public final ConstraintDescriptorImpl<A> getDescriptor() {
89100
return constraintTree.getDescriptor();
90101
}

0 commit comments

Comments
 (0)