From 57421d0b013319c0c163e17bdee7d73ef3572f9c Mon Sep 17 00:00:00 2001 From: mikereiche Date: Mon, 14 Dec 2020 15:31:59 -0800 Subject: [PATCH 1/2] DATACOUCH-650 Autoindexing only works from SpringJunitConfig Due to the interdependency in bean creation, Autoindexing does not work in a normal spring-boot application. This change overrides the addPersistentEntity and getPersistentEntity of CouchbaseMappingContext to avoid entries cached before the indexCreator was listening from preventing MappingContextEvents from being published after the indexCreator is listening. It also exposes the indexCreators classesSeen map so that MappingEvents for an entity are not re-published after the indexCreator has seen them. --- .../core/mapping/CouchbaseMappingContext.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index 1becd978f..8732a7b18 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -19,18 +19,23 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; +import java.util.Optional; + /** * Default implementation of a {@link org.springframework.data.mapping.context.MappingContext} for Couchbase using * {@link BasicCouchbasePersistentEntity} and {@link BasicCouchbasePersistentProperty} as primary abstractions. * * @author Michael Nitschinger + * @author Michael Reiche */ public class CouchbaseMappingContext extends AbstractMappingContext, CouchbasePersistentProperty> @@ -50,6 +55,7 @@ public class CouchbaseMappingContext private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY; private boolean autoIndexCreation = true; + private ApplicationEventPublisher eventPublisher; /** * Configures the {@link FieldNamingStrategy} to be used to determine the field name if no manual mapping is applied. @@ -101,6 +107,15 @@ protected CouchbasePersistentProperty createPersistentProperty(Property property @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { context = applicationContext; + super.setApplicationContext(applicationContext); + } + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + eventPublisher = applicationEventPublisher; + if (this.eventPublisher == null) { + this.eventPublisher = context; + } } public boolean isAutoIndexCreation() { @@ -111,4 +126,26 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { this.autoIndexCreation = autoCreateIndexes; } + // this override is not sufficient + @Override + protected Optional> addPersistentEntity(TypeInformation typeInformation) { + Optional> entity = super.addPersistentEntity(typeInformation); + + if (this.eventPublisher != null && entity.isPresent()) { + this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); + } + + return entity; + } + + @Override + public BasicCouchbasePersistentEntity getPersistentEntity(TypeInformation typeInformation) { + Optional> entity = super.addPersistentEntity(typeInformation); + + if (this.eventPublisher != null && entity.isPresent()) { + this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); + } + + return entity.isPresent() ? entity.get() : null; + } } From 44e32f2ba8cf98fffe5b4629943285cea69e5b8b Mon Sep 17 00:00:00 2001 From: mikereiche Date: Mon, 14 Dec 2020 17:20:34 -0800 Subject: [PATCH 2/2] DATACOUCH-550 - Autoindexing does not work spring-boot application. Due to the interdependencies of beans, autoindexing does not work in the startup of a normal spring-boot application. Processing of entities occurs during the initialization of CouchbaseMappinContext, which occurs before initialization of MappingCouchbaseConverter, which occurs before the initialization of CouchbaseTemplate - which creates the indexCreator listener. So when the AbstractMappingContext (CouchbaseMappingContext) is publishing MappingContextEvents - there is not yet any indexCreator. And subsequent processing of entities finds the entities cached, and therefore does not publish MappingContextEvents. This change overrides the addPersistentEntity() and getPersistentEntity() methods of AbstractMappingContext such that caching does not preven the MappingContextEvents from being published. This change also exposes indexCreator classesSeen map to CouchbaseMappingContext so that once an entity has been processed by indexCreator, it is not published again. Autoindexing by SpringJUnitConfig did work prior to this change. --- .../couchbase/core/CouchbaseTemplate.java | 4 ++ ...CouchbasePersistentEntityIndexCreator.java | 3 ++ .../core/mapping/CouchbaseMappingContext.java | 43 ++++++++++++++----- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 4458d98a1..2648a8e7e 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -166,6 +166,10 @@ private void prepareIndexCreator(final ApplicationContext context) { if (context instanceof ConfigurableApplicationContext && indexCreator != null) { ((ConfigurableApplicationContext) context).addApplicationListener(indexCreator); + if (mappingContext instanceof CouchbaseMappingContext) { + CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext; + cmc.setIndexCreator(indexCreator); + } } } } diff --git a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java index 17d2753cc..39ca60b7f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java +++ b/src/main/java/org/springframework/data/couchbase/core/index/CouchbasePersistentEntityIndexCreator.java @@ -121,4 +121,7 @@ public boolean isIndexCreatorFor(final MappingContext context) { return this.mappingContext.equals(context); } + public boolean hasSeen(CouchbasePersistentEntity entity) { + return classesSeen.containsKey(entity.getType()); + } } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java index 8732a7b18..6421dcd60 100644 --- a/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java @@ -16,10 +16,13 @@ package org.springframework.data.couchbase.core.mapping; +import java.util.Optional; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContextEvent; import org.springframework.data.mapping.model.FieldNamingStrategy; @@ -28,8 +31,6 @@ import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; -import java.util.Optional; - /** * Default implementation of a {@link org.springframework.data.mapping.context.MappingContext} for Couchbase using * {@link BasicCouchbasePersistentEntity} and {@link BasicCouchbasePersistentProperty} as primary abstractions. @@ -56,6 +57,7 @@ public class CouchbaseMappingContext private boolean autoIndexCreation = true; private ApplicationEventPublisher eventPublisher; + private CouchbasePersistentEntityIndexCreator indexCreator = null; /** * Configures the {@link FieldNamingStrategy} to be used to determine the field name if no manual mapping is applied. @@ -126,26 +128,45 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { this.autoIndexCreation = autoCreateIndexes; } - // this override is not sufficient + /** + * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has + * already been cached + * + * @param typeInformation - entity type + */ @Override protected Optional> addPersistentEntity(TypeInformation typeInformation) { Optional> entity = super.addPersistentEntity(typeInformation); if (this.eventPublisher != null && entity.isPresent()) { - this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); + if (this.indexCreator != null) { + if (!indexCreator.hasSeen(entity.get())) { + this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); + } + } } - return entity; } + /** + * override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has + * already been cached. Instead, user our own addPersistEntity that will. + * + * @param typeInformation - entity type + */ @Override public BasicCouchbasePersistentEntity getPersistentEntity(TypeInformation typeInformation) { - Optional> entity = super.addPersistentEntity(typeInformation); - - if (this.eventPublisher != null && entity.isPresent()) { - this.eventPublisher.publishEvent(new MappingContextEvent(this, entity.get())); - } - + Optional> entity = addPersistentEntity(typeInformation); return entity.isPresent() ? entity.get() : null; } + + /** + * capture the indexCreator when it has been added as a listener. only publishEvent() if the indexCreator hasn't + * already seen the class. + * + * @param indexCreator + */ + public void setIndexCreator(CouchbasePersistentEntityIndexCreator indexCreator) { + this.indexCreator = indexCreator; + } }