Skip to content

Commit d8f3193

Browse files
committed
HV-2004 Create observer to track basic lifecycle factory events
Signed-off-by: marko-bekhta <[email protected]>
1 parent 13233a8 commit d8f3193

File tree

8 files changed

+247
-9
lines changed

8 files changed

+247
-9
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hibernate.validator.cfg.ConstraintMapping;
2424
import org.hibernate.validator.constraints.ParameterScriptAssert;
2525
import org.hibernate.validator.constraints.ScriptAssert;
26+
import org.hibernate.validator.constraintvalidation.HibernateValidatorFactoryObserver;
2627
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
2728
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
2829
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
@@ -40,7 +41,6 @@
4041
* {@link PredefinedScopeHibernateValidatorConfiguration}.
4142
*
4243
* @param <S> The actual type of the configuration.
43-
*
4444
* @author Emmanuel Bernard
4545
* @author Gunnar Morling
4646
* @author Kevin Pollet &lt;[email protected]&gt; (C) 2011 SERLI
@@ -179,6 +179,15 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
179179
@Incubating
180180
String FAIL_FAST_ON_PROPERTY_VIOLATION = "hibernate.validator.fail_fast_on_property_violation";
181181

182+
/**
183+
* Property for configuring the validator factory observers.
184+
* A comma separated list of fully qualified names of classes implementing {@link HibernateValidatorFactoryObserver} is expected as a value.
185+
*
186+
* @since 9.1
187+
*/
188+
@Incubating
189+
String HIBERNATE_VALIDATOR_FACTORY_OBSERVER = "hibernate.validator.factory_observer";
190+
182191
/**
183192
* <p>
184193
* Returns the {@link ResourceBundleLocator} used by the
@@ -522,4 +531,15 @@ default S locales(Locale... locales) {
522531
*/
523532
@Incubating
524533
S processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeanTrackingVoter);
534+
535+
/**
536+
* Allows adding validator factory observers tracking basic lifecycle factory events.
537+
*
538+
* @param observer An observer to register.
539+
* @return {@code this} following the chaining method pattern
540+
*
541+
* @since 9.1
542+
*/
543+
@Incubating
544+
S addHibernateValidatorFactoryObserver(HibernateValidatorFactoryObserver observer);
525545
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.validator.constraintvalidation;
6+
7+
import org.hibernate.validator.HibernateValidatorFactory;
8+
import org.hibernate.validator.Incubating;
9+
10+
/**
11+
* Allows observing the basic validator factory lifecycle events.
12+
* <p>
13+
* Observers are required to handle exceptions internally and must <b>not</b> propagate them.
14+
*
15+
* @see org.hibernate.validator.HibernateValidatorConfiguration#addHibernateValidatorFactoryObserver(HibernateValidatorFactoryObserver)
16+
* @since 9.1.0
17+
*/
18+
@Incubating
19+
public interface HibernateValidatorFactoryObserver {
20+
/**
21+
* A callback invoked upon the successful creation of a factory.
22+
*
23+
* @param factory The fully initialized factory.
24+
*/
25+
default void factoryCreated(HibernateValidatorFactory factory) {
26+
}
27+
28+
/**
29+
* A callback invoked just before the factory is shut down.
30+
*
31+
* @param factory The factory that is about to be closed.
32+
*/
33+
default void factoryClosing(HibernateValidatorFactory factory) {
34+
}
35+
36+
/**
37+
* A callback invoked after the factory has been successfully shut down.
38+
*
39+
* @param factory The closed factory.
40+
*/
41+
default void factoryClosed(HibernateValidatorFactory factory) {
42+
}
43+
}

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.io.InputStream;
1313
import java.lang.invoke.MethodHandles;
1414
import java.time.Duration;
15+
import java.util.ArrayList;
1516
import java.util.Collections;
1617
import java.util.HashMap;
1718
import java.util.List;
@@ -35,6 +36,7 @@
3536

3637
import org.hibernate.validator.BaseHibernateValidatorConfiguration;
3738
import org.hibernate.validator.cfg.ConstraintMapping;
39+
import org.hibernate.validator.constraintvalidation.HibernateValidatorFactoryObserver;
3840
import org.hibernate.validator.constraintvalidation.spi.DefaultConstraintValidatorFactory;
3941
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
4042
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationSharedServiceManager;
@@ -134,6 +136,7 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
134136
private ExpressionLanguageFeatureLevel customViolationExpressionLanguageFeatureLevel;
135137
private ProcessedBeansTrackingVoter processedBeansTrackingVoter;
136138
private boolean showValidatedValuesInTraceLogs;
139+
private List<HibernateValidatorFactoryObserver> hibernateValidatorFactoryObservers;
137140

138141
protected AbstractConfigurationImpl(BootstrapState state) {
139142
this();
@@ -711,14 +714,28 @@ public T processedBeansTrackingVoter(ProcessedBeansTrackingVoter processedBeansT
711714
return thisAsT();
712715
}
713716

714-
public ProcessedBeansTrackingVoter getProcessedBeansTrackingVoter() {
717+
@Override
718+
public T addHibernateValidatorFactoryObserver(HibernateValidatorFactoryObserver observer) {
719+
Contracts.assertNotNull( observer, MESSAGES.parameterMustNotBeNull( "observer" ) );
720+
if ( hibernateValidatorFactoryObservers == null ) {
721+
hibernateValidatorFactoryObservers = new ArrayList<>();
722+
}
723+
hibernateValidatorFactoryObservers.add( observer );
724+
return thisAsT();
725+
}
726+
727+
public final ProcessedBeansTrackingVoter getProcessedBeansTrackingVoter() {
715728
return processedBeansTrackingVoter;
716729
}
717730

718731
public final Set<DefaultConstraintMapping> getProgrammaticMappings() {
719732
return programmaticMappings;
720733
}
721734

735+
public final List<HibernateValidatorFactoryObserver> getHibernateValidatorFactoryObservers() {
736+
return hibernateValidatorFactoryObservers == null ? List.of() : hibernateValidatorFactoryObservers;
737+
}
738+
722739
private boolean isSpecificProvider() {
723740
return validationBootstrapParameters.getProvider() != null;
724741
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
*/
55
package org.hibernate.validator.internal.engine;
66

7+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.OBSERVE_FACTORY_CLOSED;
8+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.OBSERVE_FACTORY_CLOSING;
9+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.OBSERVE_FACTORY_CREATED;
710
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues;
811
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint;
912
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints;
@@ -16,13 +19,15 @@
1619
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
1720
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast;
1821
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFastOnPropertyViolation;
22+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineHibernateValidatorFactoryObservers;
1923
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory;
2024
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineServiceLoadedConstraintMappings;
2125
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineShowValidatedValuesInTraceLogs;
2226
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance;
2327
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled;
2428
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration;
2529
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.registerCustomConstraintValidators;
30+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.safeObserve;
2631
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
2732

2833
import java.lang.invoke.MethodHandles;
@@ -45,6 +50,7 @@
4550
import org.hibernate.validator.HibernateValidatorContext;
4651
import org.hibernate.validator.HibernateValidatorFactory;
4752
import org.hibernate.validator.PredefinedScopeHibernateValidatorFactory;
53+
import org.hibernate.validator.constraintvalidation.HibernateValidatorFactoryObserver;
4854
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
4955
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
5056
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationContextImpl;
@@ -104,6 +110,8 @@ public class PredefinedScopeValidatorFactoryImpl implements PredefinedScopeHiber
104110

105111
private final ValidationOrderGenerator validationOrderGenerator;
106112

113+
private final List<HibernateValidatorFactoryObserver> hibernateValidatorFactoryObservers;
114+
107115
public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState) {
108116
Contracts.assertTrue( configurationState instanceof PredefinedScopeConfigurationImpl, "Only PredefinedScopeConfigurationImpl is supported." );
109117

@@ -254,9 +262,13 @@ public PredefinedScopeValidatorFactoryImpl(ConfigurationState configurationState
254262
// at this point all constraints had to be initialized, so we can clear up the pattern cache:
255263
patternConstraintInitializer.close();
256264

265+
this.hibernateValidatorFactoryObservers = determineHibernateValidatorFactoryObservers( configurationState, properties, externalClassLoader );
266+
safeObserve( hibernateValidatorFactoryObservers, this, OBSERVE_FACTORY_CREATED );
267+
257268
if ( LOG.isDebugEnabled() ) {
258269
logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
259270
}
271+
260272
}
261273

262274
@Override
@@ -342,10 +354,14 @@ public HibernateValidatorContext usingContext() {
342354

343355
@Override
344356
public void close() {
357+
safeObserve( hibernateValidatorFactoryObservers, this, OBSERVE_FACTORY_CLOSING );
358+
345359
constraintValidatorManager.clear();
346360
beanMetaDataManager.clear();
347361
validatorFactoryScopedContext.getScriptEvaluatorFactory().clear();
348362
valueExtractorManager.clear();
363+
364+
safeObserve( hibernateValidatorFactoryObservers, this, OBSERVE_FACTORY_CLOSED );
349365
}
350366

351367
public ValidatorFactoryScopedContext getValidatorFactoryScopedContext() {

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

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
import java.util.List;
1515
import java.util.Map;
1616
import java.util.Set;
17+
import java.util.function.BiConsumer;
1718

1819
import jakarta.validation.spi.ConfigurationState;
1920

2021
import org.hibernate.validator.HibernateValidatorConfiguration;
22+
import org.hibernate.validator.HibernateValidatorFactory;
2123
import org.hibernate.validator.cfg.ConstraintMapping;
24+
import org.hibernate.validator.constraintvalidation.HibernateValidatorFactoryObserver;
2225
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
2326
import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution;
2427
import org.hibernate.validator.internal.engine.constraintvalidation.HibernateConstraintValidatorInitializationSharedServiceManager;
@@ -48,6 +51,10 @@ final class ValidatorFactoryConfigurationHelper {
4851

4952
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
5053

54+
static final BiConsumer<HibernateValidatorFactoryObserver, HibernateValidatorFactory> OBSERVE_FACTORY_CLOSING = HibernateValidatorFactoryObserver::factoryClosing;
55+
static final BiConsumer<HibernateValidatorFactoryObserver, HibernateValidatorFactory> OBSERVE_FACTORY_CLOSED = HibernateValidatorFactoryObserver::factoryClosed;
56+
static final BiConsumer<HibernateValidatorFactoryObserver, HibernateValidatorFactory> OBSERVE_FACTORY_CREATED = HibernateValidatorFactoryObserver::factoryCreated;
57+
5158
private ValidatorFactoryConfigurationHelper() {
5259
}
5360

@@ -61,9 +68,7 @@ static Set<DefaultConstraintMapping> determineConstraintMappings(TypeResolutionH
6168
ConfigurationState configurationState, JavaBeanHelper javaBeanHelper, ClassLoader externalClassLoader) {
6269
Set<DefaultConstraintMapping> constraintMappings = newHashSet();
6370

64-
if ( configurationState instanceof AbstractConfigurationImpl ) {
65-
AbstractConfigurationImpl<?> hibernateConfiguration = (AbstractConfigurationImpl<?>) configurationState;
66-
71+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateConfiguration ) {
6772
// programmatic config
6873
/* We add these first so that constraint mapping created through DefaultConstraintMappingBuilder will take
6974
* these programmatically defined mappings into account when checking for constraint definition uniqueness
@@ -204,8 +209,7 @@ static boolean determineFailFastOnPropertyViolation(AbstractConfigurationImpl<?>
204209

205210
static ScriptEvaluatorFactory determineScriptEvaluatorFactory(ConfigurationState configurationState, Map<String, String> properties,
206211
ClassLoader externalClassLoader) {
207-
if ( configurationState instanceof AbstractConfigurationImpl ) {
208-
AbstractConfigurationImpl<?> hibernateSpecificConfig = (AbstractConfigurationImpl<?>) configurationState;
212+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
209213
if ( hibernateSpecificConfig.getScriptEvaluatorFactory() != null ) {
210214
LOG.usingScriptEvaluatorFactory( hibernateSpecificConfig.getScriptEvaluatorFactory().getClass() );
211215
return hibernateSpecificConfig.getScriptEvaluatorFactory();
@@ -231,8 +235,7 @@ static ScriptEvaluatorFactory determineScriptEvaluatorFactory(ConfigurationState
231235
}
232236

233237
static Duration determineTemporalValidationTolerance(ConfigurationState configurationState, Map<String, String> properties) {
234-
if ( configurationState instanceof AbstractConfigurationImpl ) {
235-
AbstractConfigurationImpl<?> hibernateSpecificConfig = (AbstractConfigurationImpl<?>) configurationState;
238+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
236239
if ( hibernateSpecificConfig.getTemporalValidationTolerance() != null ) {
237240
LOG.logTemporalValidationTolerance( hibernateSpecificConfig.getTemporalValidationTolerance() );
238241
return hibernateSpecificConfig.getTemporalValidationTolerance();
@@ -453,6 +456,42 @@ static boolean determineShowValidatedValuesInTraceLogs(AbstractConfigurationImpl
453456
return tmpShowValidatedValuesInTraceLogging;
454457
}
455458

459+
static List<HibernateValidatorFactoryObserver> determineHibernateValidatorFactoryObservers(ConfigurationState configurationState, Map<String, String> properties, ClassLoader externalClassLoader) {
460+
List<HibernateValidatorFactoryObserver> observers = newArrayList();
461+
if ( configurationState instanceof AbstractConfigurationImpl<?> hibernateSpecificConfig ) {
462+
observers.addAll( hibernateSpecificConfig.getHibernateValidatorFactoryObservers() );
463+
}
464+
465+
String[] observerFqcn = properties.getOrDefault( HibernateValidatorConfiguration.HIBERNATE_VALIDATOR_FACTORY_OBSERVER, "" ).split( "," );
466+
for ( String fqcn : observerFqcn ) {
467+
if ( fqcn == null || fqcn.isBlank() ) {
468+
continue;
469+
}
470+
try {
471+
@SuppressWarnings("unchecked")
472+
Class<? extends HibernateValidatorFactoryObserver> clazz = (Class<? extends HibernateValidatorFactoryObserver>) LoadClass.action( fqcn, externalClassLoader );
473+
HibernateValidatorFactoryObserver observer = NewInstance.action( clazz, "Hibernate Validator factory observer class" );
474+
observers.add( observer );
475+
}
476+
catch (Exception e) {
477+
throw LOG.getUnableToInstantiateFactoryObserverClassException( fqcn, e );
478+
}
479+
}
480+
return observers;
481+
}
482+
483+
static void safeObserve(List<HibernateValidatorFactoryObserver> hibernateValidatorFactoryObservers, HibernateValidatorFactory factory,
484+
BiConsumer<HibernateValidatorFactoryObserver, HibernateValidatorFactory> action) {
485+
for ( HibernateValidatorFactoryObserver observer : hibernateValidatorFactoryObservers ) {
486+
try {
487+
action.accept( observer, factory );
488+
}
489+
catch (Exception e) {
490+
LOG.unexpectedObserverException( observer, e );
491+
}
492+
}
493+
}
494+
456495
static void logValidatorFactoryScopedConfiguration(ValidatorFactoryScopedContext context) {
457496
LOG.logValidatorFactoryScopedConfiguration( context.getMessageInterpolator().getClass(), "message interpolator" );
458497
LOG.logValidatorFactoryScopedConfiguration( context.getTraversableResolver().getClass(), "traversable resolver" );

0 commit comments

Comments
 (0)