1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package org.hibernate.validator.internal.engine;
8
9 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues;
10 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint;
11 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints;
12 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
13 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings;
14 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload;
15 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
16 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineFailFast;
17 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineScriptEvaluatorFactory;
18 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTemporalValidationTolerance;
19 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled;
20 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration;
21 import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.registerCustomConstraintValidators;
22 import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
23
24 import java.lang.invoke.MethodHandles;
25 import java.time.Duration;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32
33 import javax.validation.ClockProvider;
34 import javax.validation.ConstraintValidatorFactory;
35 import javax.validation.MessageInterpolator;
36 import javax.validation.ParameterNameProvider;
37 import javax.validation.TraversableResolver;
38 import javax.validation.Validator;
39 import javax.validation.ValidatorFactory;
40 import javax.validation.spi.ConfigurationState;
41
42 import org.hibernate.validator.HibernateValidatorContext;
43 import org.hibernate.validator.HibernateValidatorFactory;
44 import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
45 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
46 import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManagerImpl;
47 import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
48 import org.hibernate.validator.internal.engine.valueextraction.ValueExtractorManager;
49 import org.hibernate.validator.internal.metadata.BeanMetaDataManager;
50 import org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl;
51 import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
52 import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
53 import org.hibernate.validator.internal.metadata.provider.ProgrammaticMetaDataProvider;
54 import org.hibernate.validator.internal.metadata.provider.XmlMetaDataProvider;
55 import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
56 import org.hibernate.validator.internal.util.ExecutableHelper;
57 import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
58 import org.hibernate.validator.internal.util.TypeResolutionHelper;
59 import org.hibernate.validator.internal.util.logging.Log;
60 import org.hibernate.validator.internal.util.logging.LoggerFactory;
61 import org.hibernate.validator.internal.util.stereotypes.Immutable;
62 import org.hibernate.validator.internal.util.stereotypes.ThreadSafe;
63 import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
64 import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
65 import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
66
67 /**
68  * Factory returning initialized {@code Validator} instances. This is the Hibernate Validator default
69  * implementation of the {@code ValidatorFactory} interface.
70  *
71  * @author Emmanuel Bernard
72  * @author Hardy Ferentschik
73  * @author Gunnar Morling
74  * @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
75  * @author Chris Beckey &lt;cbeckey@paypal.com&gt;
76  * @author Guillaume Smet
77  * @author Marko Bekhta
78  */

79 public class ValidatorFactoryImpl implements HibernateValidatorFactory {
80
81     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
82
83     /**
84      * Context containing all {@link ValidatorFactory} level helpers and configuration properties.
85      */

86     private final ValidatorFactoryScopedContext validatorFactoryScopedContext;
87
88     /**
89      * Programmatic constraints passed via the Hibernate Validator specific API. Empty if there are
90      * no programmatic constraints
91      */

92     @Immutable
93     private final Set<DefaultConstraintMapping> constraintMappings;
94
95     /**
96      * The constraint creation context containing all the helpers necessary to the constraint creation.
97      */

98     private final ConstraintCreationContext constraintCreationContext;
99
100     /**
101      * Used for discovering overridden methods. Thread-safe.
102      */

103     private final ExecutableHelper executableHelper;
104
105     /**
106      * Hibernate Validator specific flags to relax constraints on parameters.
107      */

108     private final MethodValidationConfiguration methodValidationConfiguration;
109
110     /**
111      * Metadata provider for XML configuration.
112      */

113     private final XmlMetaDataProvider xmlMetaDataProvider;
114
115     /**
116      * Prior to the introduction of {@code ParameterNameProvider} all the bean meta data was static and could be
117      * cached for all created {@code Validator}s. {@code ParameterNameProvider} makes parts of the meta data and
118      * Bean Validation element descriptors dynamic, since depending of the used provider different parameter names
119      * could be used. To still have the metadata static we create a {@code BeanMetaDataManager} per parameter name
120      * provider. See also HV-659.
121      */

122     @ThreadSafe
123     private final ConcurrentMap<BeanMetaDataManagerKey, BeanMetaDataManager> beanMetaDataManagers = new ConcurrentHashMap<>();
124
125     private final JavaBeanHelper javaBeanHelper;
126
127     private final BeanMetaDataClassNormalizer beanMetadataClassNormalizer;
128
129     private final ValidationOrderGenerator validationOrderGenerator;
130
131     public ValidatorFactoryImpl(ConfigurationState configurationState) {
132         ClassLoader externalClassLoader = determineExternalClassLoader( configurationState );
133
134         ConfigurationImpl hibernateSpecificConfig = null;
135         if ( configurationState instanceof ConfigurationImpl ) {
136             hibernateSpecificConfig = (ConfigurationImpl) configurationState;
137         }
138
139         Map<String, String> properties = configurationState.getProperties();
140
141         this.methodValidationConfiguration = new MethodValidationConfiguration.Builder()
142                 .allowOverridingMethodAlterParameterConstraint(
143                         determineAllowOverridingMethodAlterParameterConstraint( hibernateSpecificConfig, properties )
144                 ).allowMultipleCascadedValidationOnReturnValues(
145                         determineAllowMultipleCascadedValidationOnReturnValues( hibernateSpecificConfig, properties )
146                 ).allowParallelMethodsDefineParameterConstraints(
147                         determineAllowParallelMethodsDefineParameterConstraints( hibernateSpecificConfig, properties )
148                 ).build();
149
150         this.validatorFactoryScopedContext = new ValidatorFactoryScopedContext(
151                 configurationState.getMessageInterpolator(),
152                 configurationState.getTraversableResolver(),
153                 new ExecutableParameterNameProvider( configurationState.getParameterNameProvider() ),
154                 configurationState.getClockProvider(),
155                 determineTemporalValidationTolerance( configurationState, properties ),
156                 determineScriptEvaluatorFactory( configurationState, properties, externalClassLoader ),
157                 determineFailFast( hibernateSpecificConfig, properties ),
158                 determineTraversableResolverResultCacheEnabled( hibernateSpecificConfig, properties ),
159                 determineConstraintValidatorPayload( hibernateSpecificConfig )
160         );
161
162         ConstraintValidatorManager constraintValidatorManager = new ConstraintValidatorManagerImpl(
163                 configurationState.getConstraintValidatorFactory(),
164                 this.validatorFactoryScopedContext.getConstraintValidatorInitializationContext()
165         );
166
167         this.validationOrderGenerator = new ValidationOrderGenerator();
168
169         ValueExtractorManager valueExtractorManager = new ValueExtractorManager( configurationState.getValueExtractors() );
170         ConstraintHelper constraintHelper = ConstraintHelper.forAllBuiltinConstraints();
171         TypeResolutionHelper typeResolutionHelper = new TypeResolutionHelper();
172
173         this.constraintCreationContext = new ConstraintCreationContext( constraintHelper, constraintValidatorManager, typeResolutionHelper, valueExtractorManager );
174
175         this.executableHelper = new ExecutableHelper( typeResolutionHelper );
176         this.javaBeanHelper = new JavaBeanHelper( ValidatorFactoryConfigurationHelper.determineGetterPropertySelectionStrategy( hibernateSpecificConfig, properties, externalClassLoader ),
177                 ValidatorFactoryConfigurationHelper.determinePropertyNodeNameProvider( hibernateSpecificConfig, properties, externalClassLoader ) );
178         this.beanMetadataClassNormalizer = determineBeanMetaDataClassNormalizer( hibernateSpecificConfig );
179
180         // HV-302; don't load XmlMappingParser if not necessary
181         if ( configurationState.getMappingStreams().isEmpty() ) {
182             this.xmlMetaDataProvider = null;
183         }
184         else {
185             this.xmlMetaDataProvider = new XmlMetaDataProvider( constraintCreationContext, javaBeanHelper, configurationState.getMappingStreams(), externalClassLoader );
186         }
187
188         this.constraintMappings = Collections.unmodifiableSet(
189                 determineConstraintMappings(
190                         typeResolutionHelper,
191                         configurationState,
192                         javaBeanHelper,
193                         externalClassLoader
194                 )
195         );
196
197         registerCustomConstraintValidators( constraintMappings, constraintHelper );
198
199         if ( LOG.isDebugEnabled() ) {
200             logValidatorFactoryScopedConfiguration( validatorFactoryScopedContext );
201         }
202     }
203
204     @Override
205     public Validator getValidator() {
206         return createValidator(
207                 constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory(),
208                 constraintCreationContext,
209                 validatorFactoryScopedContext,
210                 methodValidationConfiguration
211         );
212     }
213
214     @Override
215     public MessageInterpolator getMessageInterpolator() {
216         return validatorFactoryScopedContext.getMessageInterpolator();
217     }
218
219     @Override
220     public TraversableResolver getTraversableResolver() {
221         return validatorFactoryScopedContext.getTraversableResolver();
222     }
223
224     @Override
225     public ConstraintValidatorFactory getConstraintValidatorFactory() {
226         return constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory();
227     }
228
229     @Override
230     public ParameterNameProvider getParameterNameProvider() {
231         return validatorFactoryScopedContext.getParameterNameProvider().getDelegate();
232     }
233
234     public ExecutableParameterNameProvider getExecutableParameterNameProvider() {
235         return validatorFactoryScopedContext.getParameterNameProvider();
236     }
237
238     @Override
239     public ClockProvider getClockProvider() {
240         return validatorFactoryScopedContext.getClockProvider();
241     }
242
243     @Override
244     public ScriptEvaluatorFactory getScriptEvaluatorFactory() {
245         return validatorFactoryScopedContext.getScriptEvaluatorFactory();
246     }
247
248     @Override
249     public Duration getTemporalValidationTolerance() {
250         return validatorFactoryScopedContext.getTemporalValidationTolerance();
251     }
252
253     @Override
254     public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
255         return javaBeanHelper.getGetterPropertySelectionStrategy();
256     }
257
258     public boolean isFailFast() {
259         return validatorFactoryScopedContext.isFailFast();
260     }
261
262     MethodValidationConfiguration getMethodValidationConfiguration() {
263         return methodValidationConfiguration;
264     }
265
266     public boolean isTraversableResolverResultCacheEnabled() {
267         return validatorFactoryScopedContext.isTraversableResolverResultCacheEnabled();
268     }
269
270     ConstraintCreationContext getConstraintCreationContext() {
271         return constraintCreationContext;
272     }
273
274     @Override
275     public <T> T unwrap(Class<T> type) {
276         //allow unwrapping into public super types
277         if ( type.isAssignableFrom( HibernateValidatorFactory.class ) ) {
278             return type.cast( this );
279         }
280         throw LOG.getTypeNotSupportedForUnwrappingException( type );
281     }
282
283     @Override
284     public HibernateValidatorContext usingContext() {
285         return new ValidatorContextImpl( this );
286     }
287
288     @Override
289     public void close() {
290         constraintCreationContext.getConstraintValidatorManager().clear();
291         constraintCreationContext.getConstraintHelper().clear();
292         for ( BeanMetaDataManager beanMetaDataManager : beanMetaDataManagers.values() ) {
293             beanMetaDataManager.clear();
294         }
295         validatorFactoryScopedContext.getScriptEvaluatorFactory().clear();
296         constraintCreationContext.getValueExtractorManager().clear();
297     }
298
299     public ValidatorFactoryScopedContext getValidatorFactoryScopedContext() {
300         return this.validatorFactoryScopedContext;
301     }
302
303     Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory,
304             ConstraintCreationContext constraintCreationContext,
305             ValidatorFactoryScopedContext validatorFactoryScopedContext,
306             MethodValidationConfiguration methodValidationConfiguration) {
307         BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent(
308                 new BeanMetaDataManagerKey( validatorFactoryScopedContext.getParameterNameProvider(), constraintCreationContext.getValueExtractorManager(), methodValidationConfiguration ),
309                 key -> new BeanMetaDataManagerImpl(
310                         constraintCreationContext,
311                         executableHelper,
312                         validatorFactoryScopedContext.getParameterNameProvider(),
313                         javaBeanHelper,
314                         beanMetadataClassNormalizer,
315                         validationOrderGenerator,
316                         buildMetaDataProviders(),
317                         methodValidationConfiguration
318                 )
319         );
320
321         return new ValidatorImpl(
322                 constraintValidatorFactory,
323                 beanMetaDataManager,
324                 constraintCreationContext.getValueExtractorManager(),
325                 constraintCreationContext.getConstraintValidatorManager(),
326                 validationOrderGenerator,
327                 validatorFactoryScopedContext
328         );
329     }
330
331     private List<MetaDataProvider> buildMetaDataProviders() {
332         List<MetaDataProvider> metaDataProviders = newArrayList();
333         if ( xmlMetaDataProvider != null ) {
334             metaDataProviders.add( xmlMetaDataProvider );
335         }
336
337         if ( !constraintMappings.isEmpty() ) {
338             metaDataProviders.add(
339                     new ProgrammaticMetaDataProvider(
340                             constraintCreationContext,
341                             constraintMappings
342                     )
343             );
344         }
345         return metaDataProviders;
346     }
347
348     private static class BeanMetaDataManagerKey {
349         private final ExecutableParameterNameProvider parameterNameProvider;
350         private final ValueExtractorManager valueExtractorManager;
351         private final MethodValidationConfiguration methodValidationConfiguration;
352         private final int hashCode;
353
354         public BeanMetaDataManagerKey(ExecutableParameterNameProvider parameterNameProvider, ValueExtractorManager valueExtractorManager, MethodValidationConfiguration methodValidationConfiguration) {
355             this.parameterNameProvider = parameterNameProvider;
356             this.valueExtractorManager = valueExtractorManager;
357             this.methodValidationConfiguration = methodValidationConfiguration;
358             this.hashCode = buildHashCode( parameterNameProvider, valueExtractorManager, methodValidationConfiguration );
359         }
360
361         private static int buildHashCode(ExecutableParameterNameProvider parameterNameProvider, ValueExtractorManager valueExtractorManager, MethodValidationConfiguration methodValidationConfiguration) {
362             final int prime = 31;
363             int result = 1;
364             result = prime * result + ( ( methodValidationConfiguration == null ) ? 0 : methodValidationConfiguration.hashCode() );
365             result = prime * result + ( ( parameterNameProvider == null ) ? 0 : parameterNameProvider.hashCode() );
366             result = prime * result + ( ( valueExtractorManager == null ) ? 0 : valueExtractorManager.hashCode() );
367             return result;
368         }
369
370         @Override
371         public int hashCode() {
372             return hashCode;
373         }
374
375         @Override
376         public boolean equals(Object obj) {
377             if ( this == obj ) {
378                 return true;
379             }
380             if ( obj == null ) {
381                 return false;
382             }
383             if ( getClass() != obj.getClass() ) {
384                 return false;
385             }
386             BeanMetaDataManagerKey other = (BeanMetaDataManagerKey) obj;
387
388             return methodValidationConfiguration.equals( other.methodValidationConfiguration ) &&
389                     parameterNameProvider.equals( other.parameterNameProvider ) &&
390                     valueExtractorManager.equals( other.valueExtractorManager );
391         }
392
393         @Override
394         public String toString() {
395             return "BeanMetaDataManagerKey [parameterNameProvider=" + parameterNameProvider + ", valueExtractorManager=" + valueExtractorManager
396                     + ", methodValidationConfiguration=" + methodValidationConfiguration + "]";
397         }
398     }
399 }
400