1
7 package org.hibernate.validator.internal.engine.constraintvalidation;
8
9 import java.lang.annotation.Annotation;
10 import java.lang.invoke.MethodHandles;
11 import java.lang.reflect.Type;
12 import java.util.Iterator;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.concurrent.ConcurrentHashMap;
16
17 import javax.validation.ConstraintValidator;
18 import javax.validation.ConstraintValidatorContext;
19 import javax.validation.ConstraintValidatorFactory;
20 import javax.validation.constraints.Null;
21
22 import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
23 import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
24 import org.hibernate.validator.internal.util.Contracts;
25 import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
26 import org.hibernate.validator.internal.util.logging.Log;
27 import org.hibernate.validator.internal.util.logging.LoggerFactory;
28
29
35 public class ConstraintValidatorManagerImpl extends AbstractConstraintValidatorManagerImpl {
36
37 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
38
39
43 private static final ConstraintValidator<?, ?> DUMMY_CONSTRAINT_VALIDATOR = new ConstraintValidator<Null, Object>() {
44
45 @Override
46 public boolean isValid(Object value, ConstraintValidatorContext context) {
47 return false;
48 }
49 };
50
51
54 private volatile ConstraintValidatorFactory mostRecentlyUsedNonDefaultConstraintValidatorFactory;
55
56
59 private volatile HibernateConstraintValidatorInitializationContext mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext;
60
61
65 private final Object mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex = new Object();
66
67
71 private final ConcurrentHashMap<CacheKey, ConstraintValidator<?, ?>> constraintValidatorCache;
72
73
79 public ConstraintValidatorManagerImpl(ConstraintValidatorFactory defaultConstraintValidatorFactory,
80 HibernateConstraintValidatorInitializationContext defaultConstraintValidatorInitializationContext) {
81 super( defaultConstraintValidatorFactory, defaultConstraintValidatorInitializationContext );
82 this.constraintValidatorCache = new ConcurrentHashMap<>();
83 }
84
85 @Override
86 public boolean isPredefinedScope() {
87 return false;
88 }
89
90
100 @Override
101 public <A extends Annotation> ConstraintValidator<A, ?> getInitializedValidator(
102 Type validatedValueType,
103 ConstraintDescriptorImpl<A> descriptor,
104 ConstraintValidatorFactory constraintValidatorFactory,
105 HibernateConstraintValidatorInitializationContext initializationContext) {
106 Contracts.assertNotNull( validatedValueType );
107 Contracts.assertNotNull( descriptor );
108 Contracts.assertNotNull( constraintValidatorFactory );
109 Contracts.assertNotNull( initializationContext );
110
111 CacheKey key = new CacheKey( descriptor.getAnnotationDescriptor(), validatedValueType, constraintValidatorFactory, initializationContext );
112
113 @SuppressWarnings("unchecked")
114 ConstraintValidator<A, ?> constraintValidator = (ConstraintValidator<A, ?>) constraintValidatorCache.get( key );
115
116 if ( constraintValidator == null ) {
117 constraintValidator = createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext );
118 constraintValidator = cacheValidator( key, constraintValidator );
119 }
120 else {
121 LOG.tracef( "Constraint validator %s found in cache.", constraintValidator );
122 }
123
124 return DUMMY_CONSTRAINT_VALIDATOR == constraintValidator ? null : constraintValidator;
125 }
126
127 private <A extends Annotation> ConstraintValidator<A, ?> cacheValidator(CacheKey key,
128 ConstraintValidator<A, ?> constraintValidator) {
129
130 if ( ( key.getConstraintValidatorFactory() != getDefaultConstraintValidatorFactory()
131 && key.getConstraintValidatorFactory() != mostRecentlyUsedNonDefaultConstraintValidatorFactory ) ||
132 ( key.getConstraintValidatorInitializationContext() != getDefaultConstraintValidatorInitializationContext()
133 && key.getConstraintValidatorInitializationContext() != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) ) {
134
135 synchronized ( mostRecentlyUsedNonDefaultConstraintValidatorFactoryAndInitializationContextMutex ) {
136 if ( key.constraintValidatorFactory != mostRecentlyUsedNonDefaultConstraintValidatorFactory ||
137 key.constraintValidatorInitializationContext != mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext ) {
138 clearEntries( mostRecentlyUsedNonDefaultConstraintValidatorFactory, mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext );
139 mostRecentlyUsedNonDefaultConstraintValidatorFactory = key.getConstraintValidatorFactory();
140 mostRecentlyUsedNonDefaultConstraintValidatorInitializationContext = key.getConstraintValidatorInitializationContext();
141 }
142 }
143 }
144
145 @SuppressWarnings("unchecked")
146 ConstraintValidator<A, ?> cached = (ConstraintValidator<A, ?>) constraintValidatorCache.putIfAbsent( key,
147 constraintValidator != null ? constraintValidator : DUMMY_CONSTRAINT_VALIDATOR );
148
149 return cached != null ? cached : constraintValidator;
150 }
151
152 private void clearEntries(ConstraintValidatorFactory constraintValidatorFactory, HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) {
153 Iterator<Entry<CacheKey, ConstraintValidator<?, ?>>> cacheEntries = constraintValidatorCache.entrySet().iterator();
154
155 while ( cacheEntries.hasNext() ) {
156 Entry<CacheKey, ConstraintValidator<?, ?>> cacheEntry = cacheEntries.next();
157 if ( cacheEntry.getKey().getConstraintValidatorFactory() == constraintValidatorFactory &&
158 cacheEntry.getKey().getConstraintValidatorInitializationContext() == constraintValidatorInitializationContext ) {
159 constraintValidatorFactory.releaseInstance( cacheEntry.getValue() );
160 cacheEntries.remove();
161 }
162 }
163 }
164
165 @Override
166 public void clear() {
167 for ( Map.Entry<CacheKey, ConstraintValidator<?, ?>> entry : constraintValidatorCache.entrySet() ) {
168 entry.getKey().getConstraintValidatorFactory().releaseInstance( entry.getValue() );
169 }
170 constraintValidatorCache.clear();
171 }
172
173 public int numberOfCachedConstraintValidatorInstances() {
174 return constraintValidatorCache.size();
175 }
176
177 private static final class CacheKey {
178
179 private ConstraintAnnotationDescriptor<?> annotationDescriptor;
180 private Type validatedType;
181 private ConstraintValidatorFactory constraintValidatorFactory;
182 private HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext;
183 private int hashCode;
184
185 private CacheKey(ConstraintAnnotationDescriptor<?> annotationDescriptor, Type validatorType, ConstraintValidatorFactory constraintValidatorFactory,
186 HibernateConstraintValidatorInitializationContext constraintValidatorInitializationContext) {
187 this.annotationDescriptor = annotationDescriptor;
188 this.validatedType = validatorType;
189 this.constraintValidatorFactory = constraintValidatorFactory;
190 this.constraintValidatorInitializationContext = constraintValidatorInitializationContext;
191 this.hashCode = createHashCode();
192 }
193
194 public ConstraintValidatorFactory getConstraintValidatorFactory() {
195 return constraintValidatorFactory;
196 }
197
198 public HibernateConstraintValidatorInitializationContext getConstraintValidatorInitializationContext() {
199 return constraintValidatorInitializationContext;
200 }
201
202 @Override
203 public boolean equals(Object o) {
204 if ( this == o ) {
205 return true;
206 }
207
208 if ( o == null ) {
209 return false;
210 }
211
212 CacheKey other = (CacheKey) o;
213
214 if ( !annotationDescriptor.equals( other.annotationDescriptor ) ) {
215 return false;
216 }
217 if ( !validatedType.equals( other.validatedType ) ) {
218 return false;
219 }
220 if ( !constraintValidatorFactory.equals( other.constraintValidatorFactory ) ) {
221 return false;
222 }
223 if ( !constraintValidatorInitializationContext.equals( other.constraintValidatorInitializationContext ) ) {
224 return false;
225 }
226
227 return true;
228 }
229
230 @Override
231 public int hashCode() {
232 return hashCode;
233 }
234
235 private int createHashCode() {
236 int result = annotationDescriptor.hashCode();
237 result = 31 * result + validatedType.hashCode();
238 result = 31 * result + constraintValidatorFactory.hashCode();
239 result = 31 * result + constraintValidatorInitializationContext.hashCode();
240 return result;
241 }
242 }
243 }
244