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.metadata;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
10 import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.Option.IDENTITY_COMPARISONS;
11 import static org.hibernate.validator.internal.util.ConcurrentReferenceHashMap.ReferenceType.SOFT;
12 import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
13
14 import java.util.ArrayList;
15 import java.util.EnumSet;
16 import java.util.List;
17
18 import org.hibernate.validator.internal.engine.ConstraintCreationContext;
19 import org.hibernate.validator.internal.engine.MethodValidationConfiguration;
20 import org.hibernate.validator.internal.engine.groups.ValidationOrderGenerator;
21 import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
22 import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder;
23 import org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl;
24 import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptions;
25 import org.hibernate.validator.internal.metadata.core.AnnotationProcessingOptionsImpl;
26 import org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider;
27 import org.hibernate.validator.internal.metadata.provider.MetaDataProvider;
28 import org.hibernate.validator.internal.metadata.raw.BeanConfiguration;
29 import org.hibernate.validator.internal.properties.javabean.JavaBeanHelper;
30 import org.hibernate.validator.internal.util.CollectionHelper;
31 import org.hibernate.validator.internal.util.ConcurrentReferenceHashMap;
32 import org.hibernate.validator.internal.util.Contracts;
33 import org.hibernate.validator.internal.util.ExecutableHelper;
34 import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
35 import org.hibernate.validator.internal.util.classhierarchy.ClassHierarchyHelper;
36 import org.hibernate.validator.internal.util.stereotypes.Immutable;
37 import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
38
39 /**
40  * This manager is in charge of providing all constraint related meta data
41  * required by the validation engine.
42  * <p>
43  * Actual retrieval of meta data is delegated to {@link MetaDataProvider}
44  * implementations which load meta-data based e.g. based on annotations or XML.
45  * <p>
46  * For performance reasons a cache is used which stores all meta data once
47  * loaded for repeated retrieval. Upon initialization this cache is populated
48  * with meta data provided by the given <i>eager</i> providers. If the cache
49  * doesn't contain the meta data for a requested type it will be retrieved on
50  * demand using the annotation based provider.
51  *
52  * @author Gunnar Morling
53  * @author Chris Beckey &lt;cbeckey@paypal.com&gt;
54  * @author Guillaume Smet
55 */

56 public class BeanMetaDataManagerImpl implements BeanMetaDataManager {
57     /**
58      * The default initial capacity for this cache.
59      */

60     private static final int DEFAULT_INITIAL_CAPACITY = 16;
61
62     /**
63      * The default load factor for this cache.
64      */

65     private static final float DEFAULT_LOAD_FACTOR = 0.75f;
66
67     /**
68      * The default concurrency level for this cache.
69      */

70     private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
71
72     /**
73      * Additional metadata providers used for meta data retrieval if
74      * the XML and/or programmatic configuration is used.
75      */

76     @Immutable
77     private final List<MetaDataProvider> metaDataProviders;
78
79     /**
80      * The constraint creation context containing all the helpers necessary to the constraint creation.
81      */

82     private final ConstraintCreationContext constraintCreationContext;
83
84     private final ExecutableParameterNameProvider parameterNameProvider;
85
86     /**
87      * Used to cache the constraint meta data for validated entities
88      */

89     private final ConcurrentReferenceHashMap<Class<?>, BeanMetaData<?>> beanMetaDataCache;
90
91     /**
92      * Used for resolving type parameters. Thread-safe.
93      */

94     private final ExecutableHelper executableHelper;
95
96     private final BeanMetaDataClassNormalizer beanMetaDataClassNormalizer;
97
98     private final ValidationOrderGenerator validationOrderGenerator;
99
100     /**
101      * the three properties in this field affect the invocation of rules associated to section 4.5.5
102      * of the specification.  By default they are all falseif true they allow
103      * for relaxation of the Liskov Substitution Principal.
104      */

105     private final MethodValidationConfiguration methodValidationConfiguration;
106
107     public BeanMetaDataManagerImpl(ConstraintCreationContext constraintCreationContext,
108             ExecutableHelper executableHelper,
109             ExecutableParameterNameProvider parameterNameProvider,
110             JavaBeanHelper javaBeanHelper,
111             BeanMetaDataClassNormalizer beanMetaDataClassNormalizer,
112             ValidationOrderGenerator validationOrderGenerator,
113             List<MetaDataProvider> optionalMetaDataProviders,
114             MethodValidationConfiguration methodValidationConfiguration) {
115         this.constraintCreationContext = constraintCreationContext;
116         this.executableHelper = executableHelper;
117         this.parameterNameProvider = parameterNameProvider;
118         this.beanMetaDataClassNormalizer = beanMetaDataClassNormalizer;
119         this.validationOrderGenerator = validationOrderGenerator;
120
121         this.methodValidationConfiguration = methodValidationConfiguration;
122
123         this.beanMetaDataCache = new ConcurrentReferenceHashMap<>(
124                 DEFAULT_INITIAL_CAPACITY,
125                 DEFAULT_LOAD_FACTOR,
126                 DEFAULT_CONCURRENCY_LEVEL,
127                 SOFT,
128                 SOFT,
129                 EnumSet.of( IDENTITY_COMPARISONS )
130         );
131
132         AnnotationProcessingOptions annotationProcessingOptions = getAnnotationProcessingOptionsFromNonDefaultProviders( optionalMetaDataProviders );
133         AnnotationMetaDataProvider defaultProvider = new AnnotationMetaDataProvider(
134                 constraintCreationContext,
135                 javaBeanHelper,
136                 annotationProcessingOptions
137         );
138         List<MetaDataProvider> tmpMetaDataProviders = new ArrayList<>( optionalMetaDataProviders.size() + 1 );
139         // We add the annotation based metadata provider at the first position so that the entire metadata model is assembled
140         // first.
141         // The other optional metadata providers will then contribute their additional metadata to the preexisting model.
142         // This helps to mitigate issues like HV-1450.
143         tmpMetaDataProviders.add( defaultProvider );
144         tmpMetaDataProviders.addAll( optionalMetaDataProviders );
145
146         this.metaDataProviders = CollectionHelper.toImmutableList( tmpMetaDataProviders );
147     }
148
149     @Override
150     // TODO Some of these casts from BeanMetadata<? super T> to BeanMetadata<T> may not be safe.
151     //  Maybe we should return a wrapper around the BeanMetadata if the normalized class is different from beanClass?
152     @SuppressWarnings("unchecked")
153     public <T> BeanMetaData<T> getBeanMetaData(Class<T> beanClass) {
154         Contracts.assertNotNull( beanClass, MESSAGES.beanTypeCannotBeNull() );
155
156         Class<? super T> normalizedBeanClass = beanMetaDataClassNormalizer.normalize( beanClass );
157
158         // First, let's do a simple lookup as it's the default case
159         BeanMetaData<? super T> beanMetaData = (BeanMetaData<? super T>) beanMetaDataCache.get( normalizedBeanClass );
160
161         if ( beanMetaData != null ) {
162             return (BeanMetaData<T>) beanMetaData;
163         }
164
165         beanMetaData = createBeanMetaData( normalizedBeanClass );
166         BeanMetaData<? super T> previousBeanMetaData =
167                 (BeanMetaData<? super T>) beanMetaDataCache.putIfAbsent( normalizedBeanClass, beanMetaData );
168
169         // we return the previous value if not null
170         if ( previousBeanMetaData != null ) {
171             return (BeanMetaData<T>) previousBeanMetaData;
172         }
173
174         return (BeanMetaData<T>) beanMetaData;
175     }
176
177     @Override
178     public void clear() {
179         beanMetaDataCache.clear();
180     }
181
182     public int numberOfCachedBeanMetaDataInstances() {
183         return beanMetaDataCache.size();
184     }
185
186     /**
187      * Creates a {@link org.hibernate.validator.internal.metadata.aggregated.BeanMetaData} containing the meta data from all meta
188      * data providers for the given type and its hierarchy.
189      *
190      * @param <T> The type of interest.
191      * @param clazz The type's class.
192      *
193      * @return A bean meta data object for the given type.
194      */

195     private <T> BeanMetaDataImpl<T> createBeanMetaData(Class<T> clazz) {
196         BeanMetaDataBuilder<T> builder = BeanMetaDataBuilder.getInstance(
197                 constraintCreationContext, executableHelper, parameterNameProvider,
198                 validationOrderGenerator, clazz, methodValidationConfiguration );
199
200         for ( MetaDataProvider provider : metaDataProviders ) {
201             for ( BeanConfiguration<? super T> beanConfiguration : getBeanConfigurationForHierarchy( provider, clazz ) ) {
202                 builder.add( beanConfiguration );
203             }
204         }
205
206         return builder.build();
207     }
208
209     /**
210      * @return returns the annotation ignores from the non annotation based meta data providers
211      */

212     private AnnotationProcessingOptions getAnnotationProcessingOptionsFromNonDefaultProviders(List<MetaDataProvider> optionalMetaDataProviders) {
213         AnnotationProcessingOptions options = new AnnotationProcessingOptionsImpl();
214         for ( MetaDataProvider metaDataProvider : optionalMetaDataProviders ) {
215             options.merge( metaDataProvider.getAnnotationProcessingOptions() );
216         }
217
218         return options;
219     }
220
221     /**
222      * Returns a list with the configurations for all types contained in the given type's hierarchy (including
223      * implemented interfaces) starting at the specified type.
224      *
225      * @param beanClass The type of interest.
226      * @param <T> The type of the class to get the configurations for.
227      * @return A set with the configurations for the complete hierarchy of the given type. May be empty, but never
228      * {@code null}.
229      */

230     private <T> List<BeanConfiguration<? super T>> getBeanConfigurationForHierarchy(MetaDataProvider provider, Class<T> beanClass) {
231         List<BeanConfiguration<? super T>> configurations = newArrayList();
232
233         for ( Class<? super T> clazz : ClassHierarchyHelper.getHierarchy( beanClass ) ) {
234             BeanConfiguration<? super T> configuration = provider.getBeanConfiguration( clazz );
235             if ( configuration != null ) {
236                 configurations.add( configuration );
237             }
238         }
239
240         return configurations;
241     }
242 }
243