Skip to content

Commit 13233a8

Browse files
committed
HV-2004 Add constraint initialization payload
and use it to cache patterns in the predefined factory Signed-off-by: marko-bekhta <[email protected]>
1 parent eb85d34 commit 13233a8

13 files changed

+241
-23
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,30 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
364364
@Incubating
365365
S constraintValidatorPayload(Object constraintValidatorPayload);
366366

367+
/**
368+
* Allows adding a payload which will be available during the constraint validators initialization.
369+
* If the method is called multiple times passing different instances of the same class,
370+
* only the payload passed last will be available for that type.
371+
*
372+
* @param constraintValidatorInitializationSharedService the payload to retrieve from the constraint validator initializers
373+
* @return {@code this} following the chaining method pattern
374+
* @since 9.1.0
375+
*/
376+
@Incubating
377+
S addConstraintValidatorInitializationSharedService(Object constraintValidatorInitializationSharedService);
378+
379+
/**
380+
* Allows adding a payload which will be available during the constraint validators initialization.
381+
* If the method is called multiple times passing different instances of the same class,
382+
* only the payload passed last will be available for that type.
383+
*
384+
* @param constraintValidatorInitializationSharedService the payload to retrieve from the constraint validator initializers
385+
* @return {@code this} following the chaining method pattern
386+
* @since 9.1.0
387+
*/
388+
@Incubating
389+
<T, V extends T> S addConstraintValidatorInitializationSharedService(Class<T> serviceClass, V constraintValidatorInitializationSharedService);
390+
367391
/**
368392
* Allows to set a getter property selection strategy defining the rules determining if a method is a getter
369393
* or not.

engine/src/main/java/org/hibernate/validator/constraintvalidation/HibernateConstraintValidatorInitializationContext.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,20 @@ public interface HibernateConstraintValidatorInitializationContext {
5656
*/
5757
@Incubating
5858
Duration getTemporalValidationTolerance();
59+
60+
/**
61+
* Returns an instance of the specified service type or {@code null} if the current context does not
62+
* contain such service.
63+
* The requested service type must match the one with which it was originally added with
64+
* {@link org.hibernate.validator.HibernateValidatorConfiguration#addConstraintValidatorInitializationSharedService(Object)}.
65+
*
66+
* @param type the type of service to retrieve
67+
* @return an instance of the specified type or {@code null} if the current constraint initialization context does not
68+
* contain an instance of such type
69+
*
70+
* @since 9.1.0
71+
* @see org.hibernate.validator.HibernateValidatorConfiguration#addConstraintValidatorInitializationSharedService(Object)
72+
*/
73+
@Incubating
74+
<C> C getConstraintValidatorInitializationSharedService(Class<C> type);
5975
}

engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/PatternValidator.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,40 @@
88
import java.util.regex.Matcher;
99
import java.util.regex.PatternSyntaxException;
1010

11-
import jakarta.validation.ConstraintValidator;
1211
import jakarta.validation.ConstraintValidatorContext;
1312
import jakarta.validation.constraints.Pattern;
13+
import jakarta.validation.metadata.ConstraintDescriptor;
1414

15+
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator;
1516
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
17+
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
18+
import org.hibernate.validator.internal.engine.constraintvalidation.PatternConstraintInitializer;
1619
import org.hibernate.validator.internal.engine.messageinterpolation.util.InterpolationHelper;
1720
import org.hibernate.validator.internal.util.logging.Log;
1821
import org.hibernate.validator.internal.util.logging.LoggerFactory;
1922

2023
/**
2124
* @author Hardy Ferentschik
2225
*/
23-
public class PatternValidator implements ConstraintValidator<Pattern, CharSequence> {
26+
public class PatternValidator implements HibernateConstraintValidator<Pattern, CharSequence> {
2427

2528
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
2629

2730
private java.util.regex.Pattern pattern;
2831
private String escapedRegexp;
2932

3033
@Override
31-
public void initialize(Pattern parameters) {
34+
public void initialize(ConstraintDescriptor<Pattern> constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) {
35+
Pattern parameters = constraintDescriptor.getAnnotation();
3236
Pattern.Flag[] flags = parameters.flags();
3337
int intFlag = 0;
3438
for ( Pattern.Flag flag : flags ) {
3539
intFlag = intFlag | flag.getValue();
3640
}
3741

3842
try {
39-
pattern = java.util.regex.Pattern.compile( parameters.regexp(), intFlag );
43+
pattern = initializationContext.getConstraintValidatorInitializationSharedService( PatternConstraintInitializer.class )
44+
.of( parameters.regexp(), intFlag );
4045
}
4146
catch (PatternSyntaxException e) {
4247
throw LOG.getInvalidRegularExpressionException( e );

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.hibernate.validator.cfg.ConstraintMapping;
3838
import org.hibernate.validator.constraintvalidation.spi.DefaultConstraintValidatorFactory;
3939
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
40+
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationSharedServiceManager;
4041
import org.hibernate.validator.internal.engine.resolver.TraversableResolvers;
4142
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorDescriptor;
4243
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
@@ -114,11 +115,12 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
114115
private final Map<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> valueExtractorDescriptors = new HashMap<>();
115116

116117
// HV-specific options
118+
private final HibernateConstraintValidatorInitializationSharedServiceManager constraintValidatorInitializationSharedServiceManager;
117119
private final Set<DefaultConstraintMapping> programmaticMappings = newHashSet();
120+
private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder();
118121
private boolean failFast;
119122
private boolean failFastOnPropertyViolation;
120123
private ClassLoader externalClassLoader;
121-
private final MethodValidationConfiguration.Builder methodValidationConfigurationBuilder = new MethodValidationConfiguration.Builder();
122124
private boolean traversableResolverResultCacheEnabled = true;
123125
private ScriptEvaluatorFactory scriptEvaluatorFactory;
124126
private Duration temporalValidationTolerance;
@@ -159,6 +161,7 @@ private AbstractConfigurationImpl() {
159161
this.defaultParameterNameProvider = new DefaultParameterNameProvider();
160162
this.defaultClockProvider = DefaultClockProvider.INSTANCE;
161163
this.defaultPropertyNodeNameProvider = new DefaultPropertyNodeNameProvider();
164+
this.constraintValidatorInitializationSharedServiceManager = new HibernateConstraintValidatorInitializationSharedServiceManager();
162165
}
163166

164167
@Override
@@ -352,6 +355,23 @@ public T constraintValidatorPayload(Object constraintValidatorPayload) {
352355
return thisAsT();
353356
}
354357

358+
@Override
359+
public T addConstraintValidatorInitializationSharedService(Object constraintValidatorInitializationSharedService) {
360+
Contracts.assertNotNull( constraintValidatorInitializationSharedService, MESSAGES.parameterMustNotBeNull( "constraintValidatorInitializationSharedService" ) );
361+
362+
this.constraintValidatorInitializationSharedServiceManager.register( constraintValidatorInitializationSharedService );
363+
return thisAsT();
364+
}
365+
366+
@Override
367+
public <V, S extends V> T addConstraintValidatorInitializationSharedService(Class<V> serviceClass, S constraintValidatorInitializationSharedService) {
368+
Contracts.assertNotNull( constraintValidatorInitializationSharedService, MESSAGES.parameterMustNotBeNull( "serviceClass" ) );
369+
Contracts.assertNotNull( constraintValidatorInitializationSharedService, MESSAGES.parameterMustNotBeNull( "constraintValidatorInitializationSharedService" ) );
370+
this.constraintValidatorInitializationSharedServiceManager.register( serviceClass, constraintValidatorInitializationSharedService );
371+
372+
return thisAsT();
373+
}
374+
355375
@Override
356376
public T getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterPropertySelectionStrategy) {
357377
Contracts.assertNotNull( getterPropertySelectionStrategy, MESSAGES.parameterMustNotBeNull( "getterPropertySelectionStrategy" ) );
@@ -548,6 +568,10 @@ public Object getConstraintValidatorPayload() {
548568
return constraintValidatorPayload;
549569
}
550570

571+
public HibernateConstraintValidatorInitializationSharedServiceManager getConstraintValidatorInitializationSharedServiceManager() {
572+
return constraintValidatorInitializationSharedServiceManager;
573+
}
574+
551575
public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
552576
return getterPropertySelectionStrategy;
553577
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
1111
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel;
1212
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings;
13+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorInitializationSharedServices;
1314
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload;
1415
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineCustomViolationExpressionLanguageFeatureLevel;
1516
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
@@ -47,6 +48,7 @@
4748
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
4849
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
4950
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl;
51+
import org.hibernate.validator.internal.engine.constraintvalidation.PatternConstraintInitializer;
5052
import org.hibernate.validator.internal.engine.constraintvalidation.PredefinedScopeConstraintValidatorManagerImpl;
5153
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
5254
import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter;
@@ -124,9 +126,10 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
124126
ScriptEvaluatorFactory scriptEvaluatorFactory = determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader );
125127
Duration temporalValidationTolerance = determineTemporalValidationTolerance( configurationState, properties );
126128

129+
PatternConstraintInitializer.CachingPatternConstraintInitializer patternConstraintInitializer = new PatternConstraintInitializer.CachingPatternConstraintInitializer();
127130
HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext = new HibernateConstraintValidatorInitializationContextImpl(
128-
scriptEvaluatorFactory, configurationState.getClockProvider(), temporalValidationTolerance );
129-
131+
scriptEvaluatorFactory, configurationState.getClockProvider(), temporalValidationTolerance,
132+
determineConstraintValidatorInitializationSharedServices( hibernateSpecificConfig, patternConstraintInitializer ) );
130133

131134
this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext(
132135
configurationState.getMessageInterpolator(),
@@ -248,6 +251,9 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
248251
beanClassesToInitialize
249252
);
250253

254+
// at this point all constraints had to be initialized, so we can clear up the pattern cache:
255+
patternConstraintInitializer.close();
256+
251257
if ( LOG.isDebugEnabled() ) {
252258
logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
253259
}

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.hibernate.validator.cfg.ConstraintMapping;
2222
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
2323
import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution;
24+
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationSharedServiceManager;
25+
import org.hibernate.validator.internal.engine.constraintvalidation.PatternConstraintInitializer;
2426
import org.hibernate.validator.internal.engine.messageinterpolation.DefaultLocaleResolver;
2527
import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory;
2628
import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer;
@@ -252,8 +254,7 @@ static Duration determineTemporalValidationTolerance(ConfigurationState configur
252254
}
253255

254256
static Object determineConstraintValidatorPayload(ConfigurationState configurationState) {
255-
if ( configurationState instanceof AbstractConfigurationImpl ) {
256-
AbstractConfigurationImpl<?> hibernateSpecificConfig = (AbstractConfigurationImpl<?>) configurationState;
257+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
257258
if ( hibernateSpecificConfig.getConstraintValidatorPayload() != null ) {
258259
LOG.logConstraintValidatorPayload( hibernateSpecificConfig.getConstraintValidatorPayload() );
259260
return hibernateSpecificConfig.getConstraintValidatorPayload();
@@ -263,6 +264,23 @@ static Object determineConstraintValidatorPayload(ConfigurationState configurati
263264
return null;
264265
}
265266

267+
static HibernateConstraintValidatorInitializationSharedServiceManager determineConstraintValidatorInitializationSharedServices(ConfigurationState configurationState,
268+
PatternConstraintInitializer patternConstraintInitializer) {
269+
HibernateConstraintValidatorInitializationSharedServiceManager configured = null;
270+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
271+
if ( hibernateSpecificConfig.getConstraintValidatorPayload() != null ) {
272+
configured = hibernateSpecificConfig.getConstraintValidatorInitializationSharedServiceManager();
273+
}
274+
}
275+
if ( configured == null ) {
276+
configured = new HibernateConstraintValidatorInitializationSharedServiceManager();
277+
}
278+
279+
return configured.immutableWithDefaultServices(
280+
Map.of( PatternConstraintInitializer.class, patternConstraintInitializer )
281+
);
282+
}
283+
266284
static ExpressionLanguageFeatureLevel determineConstraintExpressionLanguageFeatureLevel(AbstractConfigurationImpl<?> hibernateSpecificConfig,
267285
Map<String, String> properties) {
268286
if ( hibernateSpecificConfig != null && hibernateSpecificConfig.getConstraintExpressionLanguageFeatureLevel() != null ) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
1111
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintExpressionLanguageFeatureLevel;
1212
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings;
13+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorInitializationSharedServices;
1314
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload;
1415
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineCustomViolationExpressionLanguageFeatureLevel;
1516
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
@@ -47,6 +48,7 @@
4748
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
4849
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
4950
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl;
51+
import org.hibernate.validator.internal.engine.constraintvalidation.PatternConstraintInitializer;
5052
import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
5153
import org.hibernate.validator.internal.engine.tracking.DefaultProcessedBeansTrackingVoter;
5254
import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
@@ -168,6 +170,7 @@ public ValidatorFactoryImpl(ConfigurationState configurationState) {
168170
determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
169171
determineShowValidatedValuesInTraceLogs( hibernateSpecificConfig, properties ),
170172
determineConstraintValidatorPayload( hibernateSpecificConfig ),
173+
determineConstraintValidatorInitializationSharedServices( hibernateSpecificConfig, new PatternConstraintInitializer.SimplePatternConstraintInitializer() ),
171174
determineConstraintExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties ),
172175
determineCustomViolationExpressionLanguageFeatureLevel( hibernateSpecificConfig, properties )
173176
);

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
1515
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl;
16+
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationSharedServiceManager;
1617
import org.hibernate.validator.internal.util.Contracts;
1718
import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
1819
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
@@ -103,13 +104,15 @@ public class ValidatorFactoryScopedContext {
103104
boolean traversableResolverResultCacheEnabled,
104105
boolean showValidatedValuesInTraceLogs,
105106
Object constraintValidatorPayload,
107+
HibernateConstraintValidatorInitializationSharedServiceManager constraintValidatorInitializationSharedServiceManager,
106108
ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel,
107109
ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel) {
108110
this( messageInterpolator, traversableResolver, parameterNameProvider, clockProvider, temporalValidationTolerance, scriptEvaluatorFactory, failFast,
109111
failFastOnPropertyViolation, traversableResolverResultCacheEnabled, showValidatedValuesInTraceLogs, constraintValidatorPayload, constraintExpressionLanguageFeatureLevel,
110112
customViolationExpressionLanguageFeatureLevel,
111113
new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider,
112-
temporalValidationTolerance ) );
114+
temporalValidationTolerance, constraintValidatorInitializationSharedServiceManager
115+
) );
113116
}
114117

115118
ValidatorFactoryScopedContext(MessageInterpolator messageInterpolator,
@@ -214,7 +217,7 @@ static class Builder {
214217
private ExpressionLanguageFeatureLevel constraintExpressionLanguageFeatureLevel;
215218
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;
216219
private boolean showValidatedValuesInTraceLogs;
217-
private HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext;
220+
private final HibernateConstraintValidatorInitializationContextImpl constraintValidatorInitializationContext;
218221

219222
Builder(ValidatorFactoryScopedContext defaultContext) {
220223
Contracts.assertNotNull( defaultContext, "Default context cannot be null." );
@@ -348,7 +351,8 @@ public ValidatorFactoryScopedContext build() {
348351
constraintValidatorInitializationContext,
349352
scriptEvaluatorFactory,
350353
clockProvider,
351-
temporalValidationTolerance
354+
temporalValidationTolerance,
355+
constraintValidatorInitializationContext.getConstraintValidatorInitializationSharedServiceManager()
352356
)
353357
);
354358
}

0 commit comments

Comments
 (0)