1
16 package org.springframework.data.mapping.context;
17
18 import lombok.AccessLevel;
19 import lombok.NonNull;
20 import lombok.RequiredArgsConstructor;
21
22 import java.beans.PropertyDescriptor;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Map;
30 import java.util.Optional;
31 import java.util.Set;
32 import java.util.concurrent.locks.Lock;
33 import java.util.concurrent.locks.ReentrantReadWriteLock;
34 import java.util.function.Predicate;
35 import java.util.stream.Collectors;
36
37 import org.springframework.beans.BeanUtils;
38 import org.springframework.beans.BeansException;
39 import org.springframework.beans.factory.InitializingBean;
40 import org.springframework.context.ApplicationContext;
41 import org.springframework.context.ApplicationContextAware;
42 import org.springframework.context.ApplicationEventPublisher;
43 import org.springframework.context.ApplicationEventPublisherAware;
44 import org.springframework.core.KotlinDetector;
45 import org.springframework.data.mapping.MappingException;
46 import org.springframework.data.mapping.PersistentEntity;
47 import org.springframework.data.mapping.PersistentProperty;
48 import org.springframework.data.mapping.PersistentPropertyPath;
49 import org.springframework.data.mapping.PersistentPropertyPaths;
50 import org.springframework.data.mapping.PropertyPath;
51 import org.springframework.data.mapping.model.BeanWrapperPropertyAccessorFactory;
52 import org.springframework.data.mapping.model.ClassGeneratingPropertyAccessorFactory;
53 import org.springframework.data.mapping.model.EntityInstantiators;
54 import org.springframework.data.mapping.model.InstantiationAwarePropertyAccessorFactory;
55 import org.springframework.data.mapping.model.MutablePersistentEntity;
56 import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
57 import org.springframework.data.mapping.model.Property;
58 import org.springframework.data.mapping.model.SimpleTypeHolder;
59 import org.springframework.data.spel.EvaluationContextProvider;
60 import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider;
61 import org.springframework.data.util.ClassTypeInformation;
62 import org.springframework.data.util.KotlinReflectionUtils;
63 import org.springframework.data.util.Optionals;
64 import org.springframework.data.util.Streamable;
65 import org.springframework.data.util.TypeInformation;
66 import org.springframework.lang.Nullable;
67 import org.springframework.util.Assert;
68 import org.springframework.util.ReflectionUtils;
69 import org.springframework.util.ReflectionUtils.FieldCallback;
70 import org.springframework.util.ReflectionUtils.FieldFilter;
71
72
90 public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
91 implements MappingContext<E, P>, ApplicationEventPublisherAware, ApplicationContextAware, InitializingBean {
92
93 private static final boolean IN_NATIVE_IMAGE = System.getProperty("org.graalvm.nativeimage.imagecode") != null;
94
95 private final Optional<E> NONE = Optional.empty();
96 private final Map<TypeInformation<?>, Optional<E>> persistentEntities = new HashMap<>();
97 private final PersistentPropertyAccessorFactory persistentPropertyAccessorFactory;
98 private final PersistentPropertyPathFactory<E, P> persistentPropertyPathFactory;
99
100 private @Nullable ApplicationEventPublisher applicationEventPublisher;
101 private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;
102
103 private Set<? extends Class<?>> initialEntitySet = new HashSet<>();
104 private boolean strict = false;
105 private SimpleTypeHolder simpleTypeHolder = SimpleTypeHolder.DEFAULT;
106
107 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
108 private final Lock read = lock.readLock();
109 private final Lock write = lock.writeLock();
110
111 protected AbstractMappingContext() {
112
113 this.persistentPropertyPathFactory = new PersistentPropertyPathFactory<>(this);
114
115 EntityInstantiators instantiators = new EntityInstantiators();
116 PersistentPropertyAccessorFactory accessorFactory = IN_NATIVE_IMAGE ? BeanWrapperPropertyAccessorFactory.INSTANCE
117 : new ClassGeneratingPropertyAccessorFactory();
118
119 this.persistentPropertyAccessorFactory = new InstantiationAwarePropertyAccessorFactory(accessorFactory,
120 instantiators);
121 }
122
123
127 @Override
128 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
129 this.applicationEventPublisher = applicationEventPublisher;
130 }
131
132
136 @Override
137 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
138
139 this.evaluationContextProvider = new ExtensionAwareEvaluationContextProvider(applicationContext);
140
141 if (applicationEventPublisher == null) {
142 this.applicationEventPublisher = applicationContext;
143 }
144 }
145
146
151 public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
152 this.initialEntitySet = initialEntitySet;
153 }
154
155
163 public void setStrict(boolean strict) {
164 this.strict = strict;
165 }
166
167
173 public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) {
174
175 Assert.notNull(simpleTypes, "SimpleTypeHolder must not be null!");
176
177 this.simpleTypeHolder = simpleTypes;
178 }
179
180
184 @Override
185 public Collection<E> getPersistentEntities() {
186
187 try {
188
189 read.lock();
190
191 return persistentEntities.values().stream()
192 .flatMap(Optionals::toStream)
193 .collect(Collectors.toSet());
194
195 } finally {
196 read.unlock();
197 }
198 }
199
200
204 @Nullable
205 public E getPersistentEntity(Class<?> type) {
206 return getPersistentEntity(ClassTypeInformation.from(type));
207 }
208
209
213 @Override
214 public boolean hasPersistentEntityFor(Class<?> type) {
215
216 Assert.notNull(type, "Type must not be null!");
217
218 Optional<E> entity = persistentEntities.get(ClassTypeInformation.from(type));
219
220 return entity == null ? false : entity.isPresent();
221 }
222
223
227 @Nullable
228 @Override
229 public E getPersistentEntity(TypeInformation<?> type) {
230
231 Assert.notNull(type, "Type must not be null!");
232
233 try {
234
235 read.lock();
236
237 Optional<E> entity = persistentEntities.get(type);
238
239 if (entity != null) {
240 return entity.orElse(null);
241 }
242
243 } finally {
244 read.unlock();
245 }
246
247 if (!shouldCreatePersistentEntityFor(type)) {
248
249 try {
250 write.lock();
251 persistentEntities.put(type, NONE);
252 } finally {
253 write.unlock();
254 }
255
256 return null;
257 }
258
259 if (strict) {
260 throw new MappingException("Unknown persistent entity " + type);
261 }
262
263 return addPersistentEntity(type).orElse(null);
264 }
265
266
270 @Nullable
271 @Override
272 public E getPersistentEntity(P persistentProperty) {
273
274 Assert.notNull(persistentProperty, "PersistentProperty must not be null!");
275
276 if (!persistentProperty.isEntity()) {
277 return null;
278 }
279
280 TypeInformation<?> typeInfo = persistentProperty.getTypeInformation();
281 return getPersistentEntity(typeInfo.getRequiredActualType());
282 }
283
284
288 @Override
289 public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
290 return persistentPropertyPathFactory.from(propertyPath);
291 }
292
293
297 @Override
298 public PersistentPropertyPath<P> getPersistentPropertyPath(String propertyPath, Class<?> type) {
299 return persistentPropertyPathFactory.from(type, propertyPath);
300 }
301
302
306 @Override
307 public <T> PersistentPropertyPaths<T, P> findPersistentPropertyPaths(Class<T> type, Predicate<? super P> predicate) {
308
309 Assert.notNull(type, "Type must not be null!");
310 Assert.notNull(predicate, "Selection predicate must not be null!");
311
312 return doFindPersistentPropertyPaths(type, predicate, it -> !it.isAssociation());
313 }
314
315
326 protected final <T> PersistentPropertyPaths<T, P> doFindPersistentPropertyPaths(Class<T> type,
327 Predicate<? super P> predicate, Predicate<P> traversalGuard) {
328 return persistentPropertyPathFactory.from(ClassTypeInformation.from(type), predicate, traversalGuard);
329 }
330
331
337 protected Optional<E> addPersistentEntity(Class<?> type) {
338 return addPersistentEntity(ClassTypeInformation.from(type));
339 }
340
341
347 protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
348
349 Assert.notNull(typeInformation, "TypeInformation must not be null!");
350
351 try {
352
353 read.lock();
354
355 Optional<E> persistentEntity = persistentEntities.get(typeInformation);
356
357 if (persistentEntity != null) {
358 return persistentEntity;
359 }
360
361 } finally {
362 read.unlock();
363 }
364
365 Class<?> type = typeInformation.getType();
366 E entity = null;
367
368 try {
369
370 write.lock();
371
372 entity = createPersistentEntity(typeInformation);
373
374 entity.setEvaluationContextProvider(evaluationContextProvider);
375
376
377 persistentEntities.put(typeInformation, Optional.of(entity));
378
379 PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(type);
380
381 final Map<String, PropertyDescriptor> descriptors = new HashMap<>();
382 for (PropertyDescriptor descriptor : pds) {
383 descriptors.put(descriptor.getName(), descriptor);
384 }
385
386 try {
387
388 PersistentPropertyCreator persistentPropertyCreator = new PersistentPropertyCreator(entity, descriptors);
389 ReflectionUtils.doWithFields(type, persistentPropertyCreator, PersistentPropertyFilter.INSTANCE);
390 persistentPropertyCreator.addPropertiesForRemainingDescriptors();
391
392 entity.verify();
393
394 if (persistentPropertyAccessorFactory.isSupported(entity)) {
395 entity.setPersistentPropertyAccessorFactory(persistentPropertyAccessorFactory);
396 }
397
398 } catch (RuntimeException e) {
399 persistentEntities.remove(typeInformation);
400 throw e;
401 }
402
403 } catch (BeansException e) {
404 throw new MappingException(e.getMessage(), e);
405 } finally {
406 write.unlock();
407 }
408
409
410 if (applicationEventPublisher != null && entity != null) {
411 applicationEventPublisher.publishEvent(new MappingContextEvent<>(this, entity));
412 }
413
414 return Optional.of(entity);
415 }
416
417
421 @Override
422 public Collection<TypeInformation<?>> getManagedTypes() {
423
424 try {
425
426 read.lock();
427 return Collections.unmodifiableSet(new HashSet<>(persistentEntities.keySet()));
428
429 } finally {
430 read.unlock();
431 }
432 }
433
434
441 protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
442
443
451 protected abstract P createPersistentProperty(Property property, E owner, SimpleTypeHolder simpleTypeHolder);
452
453
457 @Override
458 public void afterPropertiesSet() {
459 initialize();
460 }
461
462
466 public void initialize() {
467 initialEntitySet.forEach(this::addPersistentEntity);
468 }
469
470
480 protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
481
482 if (simpleTypeHolder.isSimpleType(type.getType())) {
483 return false;
484 }
485
486 return !KotlinDetector.isKotlinType(type.getType()) || KotlinReflectionUtils.isSupportedKotlinClass(type.getType());
487 }
488
489
494 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
495 private final class PersistentPropertyCreator implements FieldCallback {
496
497 private final @NonNull E entity;
498 private final @NonNull Map<String, PropertyDescriptor> descriptors;
499 private final @NonNull Map<String, PropertyDescriptor> remainingDescriptors;
500
501 public PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> descriptors) {
502 this(entity, descriptors, descriptors);
503 }
504
505
509 public void doWith(Field field) {
510
511 String fieldName = field.getName();
512 TypeInformation<?> type = entity.getTypeInformation();
513
514 ReflectionUtils.makeAccessible(field);
515
516 Property property = Optional.ofNullable(descriptors.get(fieldName))
517 .map(it -> Property.of(type, field, it))
518 .orElseGet(() -> Property.of(type, field));
519
520 createAndRegisterProperty(property);
521
522 this.remainingDescriptors.remove(fieldName);
523 }
524
525
531 public void addPropertiesForRemainingDescriptors() {
532
533 remainingDescriptors.values().stream()
534 .filter(Property::supportsStandalone)
535 .map(it -> Property.of(entity.getTypeInformation(), it))
536 .filter(PersistentPropertyFilter.INSTANCE::matches)
537 .forEach(this::createAndRegisterProperty);
538 }
539
540 private void createAndRegisterProperty(Property input) {
541
542 P property = createPersistentProperty(input, entity, simpleTypeHolder);
543
544 if (property.isTransient()) {
545 return;
546 }
547
548 if (!input.isFieldBacked() && !property.usePropertyAccess()) {
549 return;
550 }
551
552 entity.addPersistentProperty(property);
553
554 if (property.isAssociation()) {
555 entity.addAssociation(property.getRequiredAssociation());
556 }
557
558 if (entity.getType().equals(property.getRawType())) {
559 return;
560 }
561
562 property.getPersistentEntityTypes().forEach(AbstractMappingContext.this::addPersistentEntity);
563 }
564 }
565
566
572 static enum PersistentPropertyFilter implements FieldFilter {
573
574 INSTANCE;
575
576 private static final Streamable<PropertyMatch> UNMAPPED_PROPERTIES;
577
578 static {
579
580 Set<PropertyMatch> matches = new HashSet<>();
581 matches.add(new PropertyMatch("class", null));
582 matches.add(new PropertyMatch("this\\$.*", null));
583 matches.add(new PropertyMatch("metaClass", "groovy.lang.MetaClass"));
584
585 UNMAPPED_PROPERTIES = Streamable.of(matches);
586 }
587
588
592 public boolean matches(Field field) {
593
594 if (Modifier.isStatic(field.getModifiers())) {
595 return false;
596 }
597
598 return !UNMAPPED_PROPERTIES.stream()
599 .anyMatch(it -> it.matches(field.getName(), field.getType()));
600 }
601
602
608 public boolean matches(Property property) {
609
610 Assert.notNull(property, "Property must not be null!");
611
612 if (!property.hasAccessor()) {
613 return false;
614 }
615
616 return !UNMAPPED_PROPERTIES.stream()
617 .anyMatch(it -> it.matches(property.getName(), property.getType()));
618 }
619
620
626 static class PropertyMatch {
627
628 private final @Nullable String namePattern, typeName;
629
630
637 public PropertyMatch(@Nullable String namePattern, @Nullable String typeName) {
638
639 Assert.isTrue(!(namePattern == null && typeName == null), "Either name pattern or type name must be given!");
640
641 this.namePattern = namePattern;
642 this.typeName = typeName;
643 }
644
645
652 public boolean matches(String name, Class<?> type) {
653
654 Assert.notNull(name, "Name must not be null!");
655 Assert.notNull(type, "Type must not be null!");
656
657 if (namePattern != null && !name.matches(namePattern)) {
658 return false;
659 }
660
661 if (typeName != null && !type.getName().equals(typeName)) {
662 return false;
663 }
664
665 return true;
666 }
667 }
668 }
669 }
670