Skip to content

Commit c5b8c43

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 09c7ebb commit c5b8c43

File tree

13 files changed

+171
-22
lines changed

13 files changed

+171
-22
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,18 @@ 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 constraintValidatorInitializationPayload 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 addConstraintValidatorInitializationPayload(Object constraintValidatorInitializationPayload);
378+
367379
/**
368380
* Allows to set a getter property selection strategy defining the rules determining if a method is a getter
369381
* or not.

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,18 @@ public interface HibernateConstraintValidatorInitializationContext {
5656
*/
5757
@Incubating
5858
Duration getTemporalValidationTolerance();
59+
60+
/**
61+
* Returns an instance of the specified type or {@code null} if the current constraint initialization context does not
62+
* contain an instance of such type.
63+
*
64+
* @param type the type of payload to retrieve
65+
* @return an instance of the specified type or {@code null} if the current constraint initialization context does not
66+
* contain an instance of such type
67+
*
68+
* @since 9.1.0
69+
* @see org.hibernate.validator.HibernateValidatorConfiguration#addConstraintValidatorInitializationPayload(Object)
70+
*/
71+
@Incubating
72+
<C> C getConstraintValidatorInitializationPayload(Class<C> type);
5973
}

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.getConstraintValidatorInitializationPayload( 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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.validator.internal.engine;
66

7+
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
78
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
89
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
910

@@ -123,6 +124,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
123124
private ScriptEvaluatorFactory scriptEvaluatorFactory;
124125
private Duration temporalValidationTolerance;
125126
private Object constraintValidatorPayload;
127+
private final Map<Class<?>, Object> constraintValidatorInitializationPayload = newHashMap();
126128
private GetterPropertySelectionStrategy getterPropertySelectionStrategy;
127129
private Set<Locale> locales = Collections.emptySet();
128130
private Locale defaultLocale = Locale.getDefault();
@@ -352,6 +354,14 @@ public T constraintValidatorPayload(Object constraintValidatorPayload) {
352354
return thisAsT();
353355
}
354356

357+
@Override
358+
public T addConstraintValidatorInitializationPayload(Object constraintValidatorInitializationPayload) {
359+
Contracts.assertNotNull( constraintValidatorInitializationPayload, MESSAGES.parameterMustNotBeNull( "constraintValidatorInitializationPayload" ) );
360+
361+
this.constraintValidatorInitializationPayload.put( constraintValidatorInitializationPayload.getClass(), constraintValidatorInitializationPayload );
362+
return thisAsT();
363+
}
364+
355365
@Override
356366
public T getterPropertySelectionStrategy(GetterPropertySelectionStrategy getterPropertySelectionStrategy) {
357367
Contracts.assertNotNull( getterPropertySelectionStrategy, MESSAGES.parameterMustNotBeNull( "getterPropertySelectionStrategy" ) );
@@ -548,6 +558,10 @@ public Object getConstraintValidatorPayload() {
548558
return constraintValidatorPayload;
549559
}
550560

561+
public Map<Class<?>, Object> getConstraintValidatorInitializationPayload() {
562+
return constraintValidatorInitializationPayload;
563+
}
564+
551565
public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
552566
return getterPropertySelectionStrategy;
553567
}

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.determineConstraintValidatorInitializationPayload;
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+
determineConstraintValidatorInitializationPayload( 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
@@ -11,6 +11,7 @@
1111
import java.lang.invoke.MethodHandles;
1212
import java.time.Duration;
1313
import java.util.Collections;
14+
import java.util.HashMap;
1415
import java.util.List;
1516
import java.util.Map;
1617
import java.util.Set;
@@ -21,6 +22,7 @@
2122
import org.hibernate.validator.cfg.ConstraintMapping;
2223
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
2324
import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution;
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 Map<Class<?>, Object> determineConstraintValidatorInitializationPayload(ConfigurationState configurationState, PatternConstraintInitializer patternConstraintInitializer) {
268+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
269+
if ( hibernateSpecificConfig.getConstraintValidatorPayload() != null ) {
270+
Map<Class<?>, Object> configured = hibernateSpecificConfig.getConstraintValidatorInitializationPayload();
271+
Map<Class<?>, Object> payload = new HashMap<>();
272+
payload.put( PatternConstraintInitializer.class, patternConstraintInitializer );
273+
if ( configured != null ) {
274+
payload.putAll( configured );
275+
}
276+
LOG.logConstraintValidatorInitializationPayload( payload );
277+
return Collections.unmodifiableMap( payload );
278+
}
279+
}
280+
281+
return Map.of( PatternConstraintInitializer.class, patternConstraintInitializer );
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.determineConstraintValidatorInitializationPayload;
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+
determineConstraintValidatorInitializationPayload( 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
@@ -5,6 +5,7 @@
55
package org.hibernate.validator.internal.engine;
66

77
import java.time.Duration;
8+
import java.util.Map;
89

910
import jakarta.validation.ClockProvider;
1011
import jakarta.validation.MessageInterpolator;
@@ -103,13 +104,15 @@ public class ValidatorFactoryScopedContext {
103104
boolean traversableResolverResultCacheEnabled,
104105
boolean showValidatedValuesInTraceLogs,
105106
Object constraintValidatorPayload,
107+
Map<Class<?>, Object> constraintValidatorInitializationPayload,
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, constraintValidatorInitializationPayload
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.getConstraintValidatorInitializationPayload()
352356
)
353357
);
354358
}

engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/HibernateConstraintValidatorInitializationContextImpl.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.validator.internal.engine.constraintvalidation;
66

77
import java.time.Duration;
8+
import java.util.Map;
89

910
import jakarta.validation.ClockProvider;
1011

@@ -23,25 +24,29 @@ public class HibernateConstraintValidatorInitializationContextImpl implements Hi
2324

2425
private final Duration temporalValidationTolerance;
2526

27+
private final Map<Class<?>, Object> constraintValidatorInitializationPayload;
28+
2629
private final int hashCode;
2730

2831
public HibernateConstraintValidatorInitializationContextImpl(ScriptEvaluatorFactory scriptEvaluatorFactory, ClockProvider clockProvider,
29-
Duration temporalValidationTolerance) {
32+
Duration temporalValidationTolerance, Map<Class<?>, Object> constraintValidatorInitializationPayload
33+
) {
3034
this.scriptEvaluatorFactory = scriptEvaluatorFactory;
3135
this.clockProvider = clockProvider;
3236
this.temporalValidationTolerance = temporalValidationTolerance;
37+
this.constraintValidatorInitializationPayload = constraintValidatorInitializationPayload;
3338
this.hashCode = createHashCode();
3439
}
3540

3641
public static HibernateConstraintValidatorInitializationContextImpl of(HibernateConstraintValidatorInitializationContextImpl defaultContext,
37-
ScriptEvaluatorFactory scriptEvaluatorFactory, ClockProvider clockProvider, Duration temporalValidationTolerance) {
42+
ScriptEvaluatorFactory scriptEvaluatorFactory, ClockProvider clockProvider, Duration temporalValidationTolerance, Map<Class<?>, Object> constraintValidatorInitializationPayload) {
3843
if ( scriptEvaluatorFactory == defaultContext.scriptEvaluatorFactory
3944
&& clockProvider == defaultContext.clockProvider
4045
&& temporalValidationTolerance.equals( defaultContext.temporalValidationTolerance ) ) {
4146
return defaultContext;
4247
}
4348

44-
return new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, temporalValidationTolerance );
49+
return new HibernateConstraintValidatorInitializationContextImpl( scriptEvaluatorFactory, clockProvider, temporalValidationTolerance, constraintValidatorInitializationPayload );
4550
}
4651

4752
@Override
@@ -59,6 +64,16 @@ public Duration getTemporalValidationTolerance() {
5964
return temporalValidationTolerance;
6065
}
6166

67+
@SuppressWarnings("unchecked") // because of the way we populate that map
68+
@Override
69+
public <C> C getConstraintValidatorInitializationPayload(Class<C> type) {
70+
return ( (C) constraintValidatorInitializationPayload.get( type ) );
71+
}
72+
73+
public Map<Class<?>, Object> getConstraintValidatorInitializationPayload() {
74+
return constraintValidatorInitializationPayload;
75+
}
76+
6277
@Override
6378
public boolean equals(Object o) {
6479
if ( this == o ) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.internal.engine.constraintvalidation;
6+
7+
import java.util.Map;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.regex.Pattern;
10+
11+
public interface PatternConstraintInitializer extends AutoCloseable {
12+
13+
Pattern of(String pattern, int flags);
14+
15+
@Override
16+
default void close() {
17+
}
18+
19+
class SimplePatternConstraintInitializer implements PatternConstraintInitializer {
20+
21+
@Override
22+
public Pattern of(String pattern, int flags) {
23+
return Pattern.compile( pattern, flags );
24+
}
25+
}
26+
27+
class CachingPatternConstraintInitializer implements PatternConstraintInitializer {
28+
private final Map<PatternKey, Pattern> cache = new ConcurrentHashMap<PatternKey, Pattern>();
29+
30+
@Override
31+
public Pattern of(String pattern, int flags) {
32+
return cache.computeIfAbsent( new PatternKey( pattern, flags ), key -> Pattern.compile( pattern, flags ) );
33+
}
34+
35+
@Override
36+
public void close() {
37+
cache.clear();
38+
}
39+
40+
private record PatternKey(String pattern, int flags) {
41+
}
42+
}
43+
44+
}

0 commit comments

Comments
 (0)