Skip to content

DATACOUCH-550 - Refactored the usage of CouchbasePersistentEntityIndexCreator #298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@
</dependency>

<dependency>
<groupId>com.github.Couchbase</groupId>
<groupId>com.couchbase.mock</groupId>
<artifactId>CouchbaseMock</artifactId>
<version>73e493d259</version>
<version>1.5.25</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
Expand Down Expand Up @@ -268,7 +269,6 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());

return mappingContext;
}
Expand All @@ -280,6 +280,18 @@ protected boolean autoIndexCreation() {
return false;
}

/**
* Creates a {@link CouchbasePersistentEntityIndexCreator} bean that takes on the responsibility of automatically
* creating indices.
*
* Does nothing if {@link #autoIndexCreation()} returns false.
*/
@Bean
public CouchbasePersistentEntityIndexCreator couchbasePersistentEntityIndexCreator(CouchbaseMappingContext couchbaseMappingContext,
CouchbaseClientFactory clientFactory) {
return new CouchbasePersistentEntityIndexCreator(couchbaseMappingContext, clientFactory, typeKey(), autoIndexCreation());
}

/**
* Register custom Converters in a {@link CustomConversions} object if required. These {@link CustomConversions} will
* be registered with the {@link #mappingCouchbaseConverter(CouchbaseMappingContext, CouchbaseCustomConversions)} )}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,8 @@
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexCreator;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.lang.Nullable;

import com.couchbase.client.java.Collection;

Expand All @@ -36,30 +29,21 @@
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Aaron Whiteside
* @since 3.0
*/
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContextAware {

private final CouchbaseClientFactory clientFactory;
private final CouchbaseConverter converter;
private final CouchbaseTemplateSupport templateSupport;
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
private @Nullable CouchbasePersistentEntityIndexCreator indexCreator;

public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
this.clientFactory = clientFactory;
this.converter = converter;
this.templateSupport = new CouchbaseTemplateSupport(converter);
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter);

this.mappingContext = this.converter.getMappingContext();
if (mappingContext instanceof CouchbaseMappingContext) {
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
if (cmc.isAutoIndexCreation()) {
indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this);
}
}
}

@Override
Expand Down Expand Up @@ -148,28 +132,7 @@ public ReactiveCouchbaseTemplate reactive() {

@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
prepareIndexCreator(applicationContext);
templateSupport.setApplicationContext(applicationContext);
reactiveCouchbaseTemplate.setApplicationContext(applicationContext);
}

private void prepareIndexCreator(final ApplicationContext context) {
String[] indexCreators = context.getBeanNamesForType(CouchbasePersistentEntityIndexCreator.class);

for (String creator : indexCreators) {
CouchbasePersistentEntityIndexCreator creatorBean = context.getBean(creator,
CouchbasePersistentEntityIndexCreator.class);
if (creatorBean.isIndexCreatorFor(mappingContext)) {
return;
}
}

if (context instanceof ConfigurableApplicationContext && indexCreator != null) {
((ConfigurableApplicationContext) context).addApplicationListener(indexCreator);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any persistent entities added to a mapping context after application initialization (including creation of a new mapping context) will not be processed for autoindex generation.

Copy link
Contributor Author

@aaronjwhiteside aaronjwhiteside Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what are the actual use cases for these? I didn't see any tests for these use-cases/assumptions.

How would entities be added to the running application dynamically?

And for multiple MappingContext's they would need to create a corresponding new CouchbasePersistentEntityIndexCreator instance. Lots of places in the code make the assumption that there is only a single MappingContext.. for example the CouchbaseTemplate by way of CouchbaseConverter itself, so this isn't without precedent.

Copy link
Collaborator

@mikereiche mikereiche Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If UserX is outside the @EnableCouchbaseRepositories package, its indexes won't be generated.
I don't know who would do this - but there's nothing preventing it.

    @Autowired private ApplicationContext applicationContext; // added
    @Autowired private CouchbaseClientFactory couchbaseClientFactory; // added
    
        MappingContext mc= new CouchbaseMappingContext();
        ((CouchbaseMappingContext)mc).setApplicationContext(applicationContext); // added - second edit
        CouchbaseConverter cvtr = new MappingCouchbaseConverter(mc);
        CouchbaseTemplate tmpl  = new CouchbaseTemplate(couchbaseClientFactory, cvtr);
        tmpl.setApplicationContext(applicationContext); // added.  This was missing from initial post.
        UserX u3 = tmpl.findById(UserX.class).one(someId);

Copy link
Contributor Author

@aaronjwhiteside aaronjwhiteside Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it seems to me that this depends entirely on how the MappingContext was constructed.. it's typically the responsibility of the @Configuration bean to find and supply the context with potential entities..

In the vanilla Spring Data Couchbase, AbstractCoucbbaseConfiguration#getInitialEntitySet() uses org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider scanning from the base package of the concrete class that extends AbstractCoucbbaseConfiguration.

In Spring Boot org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataConfiguration uses org.springframework.boot.autoconfigure.domain.EntityScanner which has its own logic for resolving potential entities.

And finally if someone is manually constructing beans as in your example, and if they forget to call setInitialEntitySet() then that's incorrect usage/a bug on their part? In your example there won't ever be any MappingContextEvent's for the indexer to consume because no one called setInitialEntitySet() on the MappingContext.

I don't think it's ever been the concern of the MappingContext to know how entities are found/resolved, that's always been something that's been externalised? Separation of concern..

Without test cases for the assumptions, it's really hard to ever know what the original intent was, and even harder to make sure we don't break it when making changes. But working through it logically I can't devine any meaningful reason that it needs to stick around?

Especially since if you're going through the effort of creating multiple MappingContext's and associated beans that depend on said MappingContext's, the functionality already exists to make it work as is.. as long as you glue things together correctly and call setInitialEntitySet() on the bespoke MappingContext instance?

@daschl's name is on the git blame for those original lines, perhaps he might have some more insight/context?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"So it seems to me that this depends entirely on how the MappingContext was constructed."

It shouldn't and it doesn't. But changing CouchbasePersistentIndexCreator to a configuration bean would make it so.

"And finally if someone is manually constructing beans as in your example, and if they forget to call setInitialEntitySet() then that's incorrect usage/a bug on their part?"

No. Entities can be added afterwards, just as in the example I provided. The applicationListener allows that.

"I don't believe that it's ever been the concern of the MappingContext to know how entities are found/resolved, that's always been something that's been externalised? "

It's (Abstract)MappingContext that resolves entity classes producing PersistentEntity objects. Maybe I misunderstood something.

Copy link
Contributor Author

@aaronjwhiteside aaronjwhiteside Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I edited the example to add the missing setApplicationContext() call.

Actually it's needed on all the instances, both MappingCouchbaseConverter and CouchbaseMappingContext implement ApplicationContextAware. But at this point I feel the example is a little contrived, even more than you probably intended.

Suffice to say I can now appreciate that not all entities will be discovered at @Configuration time, and some can come at runtime. Whether that is a bug or not can be argued about, but since AbstractMappingContext explicitly deals with it, it makes sense we conform as well.

I'm not convinced that adding a second mechanism for auto-indexing is simpler than fixing the original mechanism.

The code/design in its current form is complex, it introduces unnecessary cognitive load for something that I've shown can be much simpler.

There is no need to pollute the CouchbaseTemplate class with concerns of glueing things together for the purpose of indexing.

The same goes for CouchbaseMappingContext class, it does not need to cross the boundary into the functionality of indexing.

This doesn't need to be a special case:
https://github.com/spring-projects/spring-data-couchbase/blob/master/src/main/java/org/springframework/data/couchbase/core/mapping/CouchbaseMappingContext.java#L141-L147

There doesn't need to be a questionable cyclic dependency between CouchbaseMappingContext and CouchbasePersistentEntityIndexCreator.

Keeping things simple means having a single place that deals with indexing, and that seems to be the CouchbasePersistentEntityIndexCreator, it's right there in the name..

CouchbasePersistentEntityIndexCreator does not need to leak details of its internal implementation, and require logic in CouchbaseMappingContext and CouchbaseTemplate to make it work correctly.

I don't claim this is the simplest it can be, but it IS much simpler than the current design.

127 lines removed and 44 added.

Indexing logic in a single self-contained location.

Clean separation of concerns.

Copy link
Collaborator

@mikereiche mikereiche Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last commit causes this failure:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-12-18 11:01:36.663 ERROR 17469 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   cmdRunner (field private com.test.UserRepository com.test.CmdRunner.userRepository)
      ↓
   couchbaseRepositoryOperationsMapping defined in class path resource [com/test/CouchbaseConfig.class]
┌─────┐
|  couchbaseTemplate defined in class path resource [com/test/CouchbaseConfig.class]
↑     ↓
|  couchbaseMappingConverter defined in class path resource [com/test/CouchbaseConfig.class]
↑     ↓
|  couchbaseMappingContext defined in class path resource [com/test/CouchbaseConfig.class]
↑     ↓
|  couchbasePersistentEntityIndexCreator defined in class path resource [com/test/CouchbaseConfig.class]
└─────┘

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After fixing the cyclic dependency, classes found later do not get processed as the mappingContext they are emitted from is different from the the mappingContext of the CouchbasePersistentEntityIndexCreator. This seems to be the reason that the indexCreator is tied to the template.

onApplicationEvent()
if (!enabled || !event.wasEmittedBy(mappingContext))

Copy link
Contributor Author

@aaronjwhiteside aaronjwhiteside Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikereiche crap, that'll serve me right for pushing a commit without running the build first.

fixed the cyclic dependency, and pushed.

I'm not seeing any more test failures now though

[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 118, Failures: 0, Errors: 0, Skipped: 0
....
[INFO] 
[INFO] Tests run: 37, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  41.636 s
[INFO] Finished at: 2020-12-19T10:04:56+11:00
[INFO] ------------------------------------------------------------------------

I also tested this in a Spring Boot (2.4.1) application we have, and other than having to manually create the CouchbasePersistentEntityIndexCreator bean because it's not in the spring boot auto configuration yet, I didn't have any issues.

Can you share a sample project or code that I can use to reproduce this issue? I can then also add test cases to this repo to ensure it doesn't get broken again..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emitted from is different from the the mappingContext of the CouchbasePersistentEntityIndexCreator. This seems to be the reason that the indexCreator is tied to the template.

So I've been thinking about this, I think I said it before but maybe not as succinctly. If you're explicitly creating a new MappingContext, then just like the need to explicitly create a dedicated CouchbaseConverter and CouchbaseTemplate that are bound to the new MappingContext, you should also create a dedicated CouchbasePersistentEntityIndexCreator. This seems to be the established pattern.

It shouldn't be seen as a burden, for most people they won't even know about it since it's hidden away in a @Configuration class. And for those that need to customize, then it becomes another implementation detail they have to deal with.. like all the others.

But thinking a little bit more, we could however modify the CouchbasePersistentEntityIndexCreator to take a List<MappingContext> in the constructor instead of a single MappingContext, but I feel this goes against the already established pattern?

And it won't work if you create things outside Spring, but I'm not very concerned with that. Because this is after all Spring Data Couchbase, so the expectation is that Spring is always involved to work it's magic, for better or worse.

if (mappingContext instanceof CouchbaseMappingContext) {
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
cmc.setIndexCreator(indexCreator);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,56 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationListener;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIndexResolver.IndexDefinitionHolder;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.MappingContextEvent;

import com.couchbase.client.core.error.IndexExistsException;
import com.couchbase.client.java.Cluster;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContextEvent;

public class CouchbasePersistentEntityIndexCreator implements ApplicationListener<MappingContextEvent<?, ?>> {
/**
* Encapsulates the logic of creating indices.
*
* @author Michael Nitschinger
* @author Aaron Whiteside
*/
public class CouchbasePersistentEntityIndexCreator implements InitializingBean, ApplicationListener<MappingContextEvent<?, ?>> {

private static final Logger LOGGER = LoggerFactory.getLogger(CouchbasePersistentEntityIndexCreator.class);

private final Map<Class<?>, Boolean> classesSeen = new ConcurrentHashMap<>();
private final CouchbaseMappingContext mappingContext;
private final QueryIndexResolver indexResolver;
private final CouchbaseOperations couchbaseOperations;
private final CouchbaseClientFactory clientFactory;
private final boolean enabled;

public CouchbasePersistentEntityIndexCreator(final CouchbaseMappingContext mappingContext,
final CouchbaseOperations operations) {
final CouchbaseClientFactory clientFactory, final String typeKey, final boolean enabled) {
this.mappingContext = mappingContext;
this.couchbaseOperations = operations;
this.indexResolver = QueryIndexResolver.create(mappingContext, operations);
this.clientFactory = clientFactory;
this.enabled = enabled;
this.indexResolver = QueryIndexResolver.create(mappingContext, typeKey);
}

@Override
public void afterPropertiesSet() throws Exception {
if (enabled) {
mappingContext.getPersistentEntities().forEach(this::checkForIndexes);
} else {
LOGGER.debug("Automatic index creation not enabled.");
}
}

@Override
public void onApplicationEvent(final MappingContextEvent<?, ?> event) {
if (!event.wasEmittedBy(mappingContext)) {
if (!enabled || !event.wasEmittedBy(mappingContext)) {
return;
}

Expand All @@ -71,7 +88,7 @@ private void checkForIndexes(final CouchbasePersistentEntity<?> entity) {
this.classesSeen.put(type, Boolean.TRUE);

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Analyzing class " + type + " for index information.");
LOGGER.debug("Analyzing class {} for index information.", type);
}

checkForAndCreateIndexes(entity);
Expand All @@ -93,10 +110,10 @@ private void checkForAndCreateIndexes(final CouchbasePersistentEntity<?> entity)
}

private void createIndex(final IndexDefinitionHolder indexToCreate) {
Cluster cluster = couchbaseOperations.getCouchbaseClientFactory().getCluster();
Cluster cluster = clientFactory.getCluster();

StringBuilder statement = new StringBuilder("CREATE INDEX ").append(indexToCreate.getIndexName()).append(" ON `")
.append(couchbaseOperations.getBucketName()).append("` (")
.append(clientFactory.getBucket().name()).append("` (")
.append(String.join(",", indexToCreate.getIndexFields())).append(")");

if (indexToCreate.getIndexPredicate() != null && !indexToCreate.getIndexPredicate().isEmpty()) {
Expand All @@ -107,21 +124,10 @@ private void createIndex(final IndexDefinitionHolder indexToCreate) {
cluster.query(statement.toString());
} catch (IndexExistsException ex) {
// ignored on purpose, rest is propagated
LOGGER.debug("Index \"" + indexToCreate.getIndexName() + "\" already exists, ignoring.");
LOGGER.debug("Index \"{}\" already exists, ignoring.", indexToCreate.getIndexName());
} catch (Exception ex) {
throw new DataIntegrityViolationException("Could not auto-create index with statement: " + statement.toString(),
ex);
}
}

/**
* Returns whether the current index creator was registered for the given {@link MappingContext}.
*/
public boolean isIndexCreatorFor(final MappingContext<?, ?> context) {
return this.mappingContext.equals(context);
}

public boolean hasSeen(CouchbasePersistentEntity<?> entity) {
return classesSeen.containsKey(entity.getType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.Document;
Expand All @@ -35,13 +34,13 @@
public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver {

private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final CouchbaseOperations operations;
private final String typeKey;

public CouchbasePersistentEntityIndexResolver(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
CouchbaseOperations operations) {
final String typeKey) {
this.mappingContext = mappingContext;
this.operations = operations;
this.typeKey = typeKey;
}

@Override
Expand Down Expand Up @@ -135,7 +134,6 @@ protected List<IndexDefinitionHolder> createCompositeQueryIndexDefinitions(final
}

private String getPredicate(final MappingCouchbaseEntityInformation<?, Object> entityInfo) {
String typeKey = operations.getConverter().getTypeKey();
String typeValue = entityInfo.getJavaType().getName();
return "`" + typeKey + "` = \"" + typeValue + "\"";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.springframework.data.couchbase.core.index;

import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
Expand Down Expand Up @@ -43,9 +42,9 @@ public interface QueryIndexResolver {
*/
static QueryIndexResolver create(
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
CouchbaseOperations operations) {
String typeKey) {
Assert.notNull(mappingContext, "CouchbaseMappingContext must not be null!");
return new CouchbasePersistentEntityIndexResolver(mappingContext, operations);
return new CouchbasePersistentEntityIndexResolver(mappingContext, typeKey);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@

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;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
Expand All @@ -37,6 +32,7 @@
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Aaron Whiteside
*/
public class CouchbaseMappingContext
extends AbstractMappingContext<BasicCouchbasePersistentEntity<?>, CouchbasePersistentProperty>
Expand All @@ -55,10 +51,6 @@ public class CouchbaseMappingContext
*/
private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY;

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.
* Defaults to a strategy using the plain property name.
Expand Down Expand Up @@ -108,65 +100,8 @@ protected CouchbasePersistentProperty createPersistentProperty(Property property
*/
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
super.setApplicationContext(applicationContext);
context = applicationContext;
}

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
eventPublisher = applicationEventPublisher;
if (this.eventPublisher == null) {
this.eventPublisher = context;
}
}

public boolean isAutoIndexCreation() {
return autoIndexCreation;
}

public void setAutoIndexCreation(boolean autoCreateIndexes) {
this.autoIndexCreation = autoCreateIndexes;
}

/**
* 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<BasicCouchbasePersistentEntity<?>> addPersistentEntity(TypeInformation<?> typeInformation) {
Optional<BasicCouchbasePersistentEntity<?>> entity = super.addPersistentEntity(typeInformation);

if (this.eventPublisher != null && entity.isPresent()) {
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<BasicCouchbasePersistentEntity<?>> 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;
}
}