Skip to content

Commit 33dc3e4

Browse files
gsmetmarko-bekhta
authored andcommitted
HV-1831 Enhance ExecutableMetaData with tracking information
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. 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. HV-1831 : Wrap a `BeanMetaData` in a `NonTrackedBeanMetaDataImpl` if tracking is not required HV-1831 Add some guidance about next step HV-1831 Specific benchmark infrastructure for predefined scope HV-1831 : Update Cascade tests to use PredefinedScopeHibernateValidator with -p=predefined=true HV-1831 : Experiment detecting cycles in bean classes Add test for Map HV-1831 : Experiment detecting cycles in bean classes Add support for containers; add tests for List w/ and w/o duplicated values HV-1831 : Experiment detecting cycles in bean classes HV-1831 Copy nodes when changing the nature of the leaf HV-1831 Add the same bean to List twice HV-1831 Clean up another experiment that shouldn't have been committed HV-1831 Add a couple of examples illustrating various cases HV-1831 Unfinished experiments
1 parent 3064bba commit 33dc3e4

File tree

46 files changed

+2692
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2692
-76
lines changed

engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
3232
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
3333
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
34+
import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter;
3435

3536
/**
3637
* Base interface for Hibernate Validator specific configurations.
@@ -486,4 +487,7 @@ default S locales(Locale... locales) {
486487
*/
487488
@Incubating
488489
S failFastOnPropertyViolation(boolean failFastOnPropertyViolation);
490+
491+
@Incubating
492+
S processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeanTrackingVoter);
489493
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
6363
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
6464
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
65+
import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter;
6566

6667
/**
6768
* Hibernate specific {@code Configuration} implementation.
@@ -129,6 +130,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
129130
private BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
130131
private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel;
131132
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;
133+
private ProcessedBeansTrackingVoter processedBeansTrackingVoter;
132134
private boolean showValidatedValuesInTraceLogs;
133135

134136
protected AbstractConfigurationImpl(BootstrapState state) {
@@ -673,6 +675,22 @@ public final boolean getShowValidatedValuesInTraceLogs() {
673675
return this.showValidatedValuesInTraceLogs;
674676
}
675677

678+
@Override
679+
public T processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeansTrackingVoter) {
680+
if ( LOG.isDebugEnabled() ) {
681+
if ( processedBeansTrackingVoter != null ) {
682+
LOG.debug( "Setting custom ProcessedBeansTrackingVoter of type " + processedBeansTrackingVoter.getClass()
683+
.getName() );
684+
}
685+
}
686+
this.processedBeansTrackingVoter = processedBeansTrackingVoter;
687+
return thisAsT();
688+
}
689+
690+
public ProcessedBeansTrackingVoter getProcessedBeansTrackingVoter() {
691+
return processedBeansTrackingVoter;
692+
}
693+
676694
public final Set<DefaultConstraintMapping> getProgrammaticMappings() {
677695
return programmaticMappings;
678696
}

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

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory;
4747
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
4848
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
49+
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl;
4950
import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl;
5051
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
52+
import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter;
5153
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
5254
import org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager;
5355
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
@@ -118,6 +120,14 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
118120
determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties )
119121
).build();
120122

123+
ExecutableParameterNameProvider parameterNameProvider = new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() );
124+
ScriptEvaluatorFactory scriptEvaluatorFactory = determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader );
125+
Duration temporalValidationTolerance = determineTemporalValidationTolerance( configurationState, properties );
126+
127+
HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext = new HibernateConstraintValidatorInitializationContextImpl(
128+
scriptEvaluatorFactory, configurationState.getClockProvider(), temporalValidationTolerance );
129+
130+
121131
this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext(
122132
configurationState.getMessageInterpolator(),
123133
configurationState.getTraversableResolver(),
@@ -128,15 +138,16 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
128138
determineFailFast( hibernateSpecificConfig, properties ),
129139
determineFailFastOnPropertyViolation( hibernateSpecificConfig, properties ),
130140
determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
141+
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties ),
131142
determineConstraintValidatorPayload( hibernateSpecificConfig ),
132143
determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
133144
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
134-
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties )
145+
constraintValidatorInitializationContext
135146
);
136147

137148
this.constraintValidatorManager = new PredefinedScopeConstraintValidatorManagerImpl(
138149
configurationState.getConstraintValidatorFactory(),
139-
this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext()
150+
constraintValidatorInitializationContext
140151
);
141152

142153
this.validationOrderGenerator = new ValidationOrderGenerator();
@@ -147,11 +158,14 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
147158
this.valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() );
148159
ConstraintHelper constraintHelper = ConstraintHelper.forBuiltinConstraints(
149160
hibernateSpecificConfig.getBuiltinConstraints(),
150-
hibernateSpecificConfig.isIncludeBeansAndConstraintsDefinedOnlyInXml() );
161+
hibernateSpecificConfig.isIncludeBeansAndConstraintsDefinedOnlyInXml()
162+
);
151163
TypeResolutionHelper typeResolutionHelper = new TypeResolutionHelper();
152164

153-
ConstraintCreationContext constraintCreationContext = new ConstraintCreationContext( constraintHelper,
154-
constraintValidatorManager, typeResolutionHelper, valueExtractorManager );
165+
ConstraintCreationContext constraintCreationContext = new ConstraintCreationContext(
166+
constraintHelper,
167+
constraintValidatorManager, typeResolutionHelper, valueExtractorManager
168+
);
155169

156170
ExecutableHelper executableHelper = new ExecutableHelper( typeResolutionHelper );
157171
JavaBeanHelper javaBeanHelper = new JavaBeanHelper( getterPropertySelectionStrategy, propertyNodeNameProvider );
@@ -164,15 +178,18 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
164178
javaBeanHelper,
165179
externalClassLoader
166180
),
167-
constraintHelper );
181+
constraintHelper
182+
);
168183

169184
// we parse all XML mappings but only register constraint validators and delay constraint mappings building till
170185
// we collect all the constraint validators.
171186
// HV-302; don't load XmlMappingParser if not necessary
172187
MappingXmlParser mappingParser = null;
173188
if ( !configurationState.getMappingStreams().isEmpty() ) {
174-
mappingParser = new MappingXmlParser( constraintCreationContext,
175-
javaBeanHelper, externalClassLoader );
189+
mappingParser = new MappingXmlParser(
190+
constraintCreationContext,
191+
javaBeanHelper, externalClassLoader
192+
);
176193
mappingParser.parse( configurationState.getMappingStreams() );
177194
}
178195

@@ -202,15 +219,32 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
202219
xmlMetaDataProvider = null;
203220
}
204221

222+
// 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)
223+
// it's basically the content of PredefinedScopeBeanMetaDataManager constructor
224+
// the metadata wouldn't be complete because we want to inject the tracking information
225+
226+
// then you build the tracking information from these incomplete metadata
227+
228+
// finally you create a PredefinedScopeBeanMetaDataManager with the augmented metadata pushed to it
229+
// you will need to augment both BeanMetaData and ExecutableMetaData
230+
// I would prototype BeanMetaData first then discuss it before going further
231+
232+
// Note: we want classes to be immutable
233+
// Might be a good idea to push a default method to BeanMetaData as enabling tracking is the default behavior we want
234+
// Maybe first try composition and benchmark it and if good enough, we keep it
235+
205236
this.beanMetaDataManager = new PredefinedScopeBeanMetaDataManager(
206237
constraintCreationContext,
207238
executableHelper,
208-
validatorFactoryScopedContext.getParameterNameProvider(),
239+
parameterNameProvider,
209240
javaBeanHelper,
210241
validationOrderGenerator,
211242
buildMetaDataProviders( constraintCreationContext, xmlMetaDataProvider, constraintMappings ),
212243
methodValidationConfiguration,
213244
determineBeanMetaDataClassNormalizer( hibernateSpecificConfig ),
245+
( hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null )
246+
? hibernateSpecificConfig.getProcessedBeansTrackingVoter()
247+
: new DefaultProcessedBeansTrackingVoter(),
214248
beanClassesToInitialize
215249
);
216250

@@ -281,6 +315,10 @@ public boolean isTraversableResolverResultCacheEnabled() {
281315
return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled();
282316
}
283317

318+
public PredefinedScopeBeanMetaDataManager getBeanMetaDataManager() {
319+
return beanMetaDataManager;
320+
}
321+
284322
@Override
285323
public <T> T unwrap(Class<T> type) {
286324
// allow unwrapping into public super types
@@ -322,7 +360,8 @@ Validator createValidator(ValidatorFactoryScopedContext validatorFactoryScopedCo
322360
private static List<MetaDataProvider> buildMetaDataProviders(
323361
ConstraintCreationContext constraintCreationContext,
324362
XmlMetaDataProvider xmlMetaDataProvider,
325-
Set<DefaultConstraintMapping> constraintMappings) {
363+
Set<DefaultConstraintMapping> constraintMappings
364+
) {
326365
List<MetaDataProvider> metaDataProviders = newArrayList();
327366
if ( xmlMetaDataProvider != null ) {
328367
metaDataProviders.add( xmlMetaDataProvider );

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
4949
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl;
5050
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
51+
import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter;
5152
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
5253
import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
5354
import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl;
@@ -68,6 +69,7 @@
6869
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
6970
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
7071
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
72+
import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter;
7173

7274
/**
7375
* Factory returning initialized {@code Validator} instances. This is the Hibernate Validator default
@@ -133,6 +135,8 @@ public class ValidatorFactoryImpl implements HibernateValidatorFactory {
133135

134136
private final ValidationOrderGenerator validationOrderGenerator;
135137

138+
private final ProcessedBeansTrackingVoter processedBeansTrackingVoter;
139+
136140
public ValidatorFactoryImpl(ConfigurationState configurationState) {
137141
ClassLoader externalClassLoader = determineExternalClassLoader( configurationState );
138142

@@ -162,10 +166,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
162166
determineFailFast( hibernateSpecificConfig, properties ),
163167
determineFailFastOnPropertyViolation( hibernateSpecificConfig, properties ),
164168
determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
169+
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties ),
165170
determineConstraintValidatorPayload( hibernateSpecificConfig ),
166171
determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
167-
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
168-
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties )
172+
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties )
169173
);
170174

171175
ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl(
@@ -226,6 +230,10 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
226230
this.xmlMetaDataProvider = null;
227231
}
228232

233+
this.processedBeansTrackingVoter = ( hibernateSpecificConfig != null && hibernateSpecificConfig.getProcessedBeansTrackingVoter() != null )
234+
? hibernateSpecificConfig.getProcessedBeansTrackingVoter()
235+
: new DefaultProcessedBeansTrackingVoter();
236+
229237
if ( LOG.isDebugEnabled() ) {
230238
logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
231239
}
@@ -349,7 +357,8 @@ Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
349357
beanMetadataClassNormalizer,
350358
validationOrderGenerator,
351359
buildMetaDataProviders(),
352-
methodValidationConfiguration
360+
methodValidationConfiguration,
361+
processedBeansTrackingVoter
353362
)
354363
);
355364

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,18 @@ public class ValidatorFactoryScopedContext {
101101
boolean failFast,
102102
boolean failFastOnPropertyViolation,
103103
boolean traversableResolverResultCacheEnabled,
104+
boolean showValidatedValuesInTraceLogs,
104105
Object constraintValidatorPayload,
105106
ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel,
106-
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel,
107-
boolean showValidatedValuesInTraceLogs) {
107+
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel) {
108108
this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast,
109109
failFastOnPropertyViolation, traversableResolverResultCacheEnabled, showValidatedValuesInTraceLogs, constraintValidatorPayload, constraintExpressionLanguageFeatureLevel,
110110
customViolationExpressionLanguageFeatureLevel,
111111
new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider,
112112
temporalValidationTolerance ) );
113113
}
114114

115-
private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
115+
ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
116116
TraversableResolver traversableResolver,
117117
ExecutableParameterNameProvider parameterNameProvider,
118118
ClockProvider clockProvider,
@@ -121,7 +121,8 @@ private ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
121121
boolean failFast,
122122
boolean failFastOnPropertyViolation,
123123
boolean traversableResolverResultCacheEnabled,
124-
boolean showValidatedValuesInTraceLogs, Object constraintValidatorPayload,
124+
boolean showValidatedValuesInTraceLogs,
125+
Object constraintValidatorPayload,
125126
ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel,
126127
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel,
127128
HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext) {
@@ -212,7 +213,6 @@ static class Builder {
212213
private Object constraintValidatorPayload;
213214
private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel;
214215
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;
215-
216216
private boolean showValidatedValuesInTraceLogs;
217217
private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext;
218218

engine/src/main/java/org/hibernate/validator/internal/engine/path/PathImpl.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private NodeImpl addMethodNode(String name, Class<?>[] parameterTypes) {
204204
}
205205

206206
public NodeImpl makeLeafNodeIterable() {
207-
requiresWriteableNodeList();
207+
copyNodeList();
208208

209209
currentLeafNode = NodeImpl.makeIterable( currentLeafNode );
210210

@@ -214,7 +214,7 @@ public NodeImpl makeLeafNodeIterable() {
214214
}
215215

216216
public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) {
217-
requiresWriteableNodeList();
217+
copyNodeList();
218218

219219
currentLeafNode = NodeImpl.makeIterableAndSetIndex( currentLeafNode, index );
220220

@@ -224,7 +224,7 @@ public NodeImpl makeLeafNodeIterableAndSetIndex(Integer index) {
224224
}
225225

226226
public NodeImpl makeLeafNodeIterableAndSetMapKey(Object key) {
227-
requiresWriteableNodeList();
227+
copyNodeList();
228228

229229
currentLeafNode = NodeImpl.makeIterableAndSetMapKey( currentLeafNode, key );
230230

@@ -309,6 +309,10 @@ private void requiresWriteableNodeList() {
309309
return;
310310
}
311311

312+
copyNodeList();
313+
}
314+
315+
private void copyNodeList() {
312316
// Usually, the write operation is about adding one more node, so let's make the list one element larger.
313317
List<Node> newNodeList = new ArrayList<>( nodeList.size() + 1 );
314318
newNodeList.addAll( nodeList );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.internal.engine.tracking;
6+
7+
import org.hibernate.validator.spi.tracking.ProcessedBeansTrackingVoter;
8+
9+
public class DefaultProcessedBeansTrackingVoter implements ProcessedBeansTrackingVoter {
10+
11+
@Override
12+
public Vote isEnabledForBean(Class<?> beanClass, boolean hasCascadables) {
13+
return Vote.DEFAULT;
14+
}
15+
16+
@Override
17+
public Vote isEnabledForReturnValue(Class<?> beanClass, String name, Class<?>[] parameterTypes, boolean hasCascadables) {
18+
return Vote.DEFAULT;
19+
}
20+
21+
@Override
22+
public Vote isEnabledForParameters(Class<?> beanClass, String name, Class<?>[] parameterTypes, boolean hasCascadables) {
23+
return Vote.DEFAULT;
24+
}
25+
}

0 commit comments

Comments
 (0)