Skip to content

DATACOUCH-550 - 41x revert index creator refactoring. #1088

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
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 @@ -157,9 +157,9 @@
</dependency>

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
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 @@ -282,6 +281,7 @@ public CouchbaseMappingContext couchbaseMappingContext(CustomConversions customC
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
mappingContext.setAutoIndexCreation(autoIndexCreation());

return mappingContext;
}
Expand All @@ -293,18 +293,6 @@ 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,10 +19,17 @@
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.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.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 @@ -31,7 +38,6 @@
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Aaron Whiteside
* @author Jorge Rodriguez Martin
* @since 3.0
*/
Expand All @@ -40,7 +46,9 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
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, converter, new JacksonTranslationService());
Expand All @@ -52,6 +60,14 @@ public CouchbaseTemplate(final CouchbaseClientFactory clientFactory, final Couch
this.converter = converter;
this.templateSupport = new CouchbaseTemplateSupport(converter, translationService);
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService);

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 @@ -140,7 +156,28 @@ 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);
if (mappingContext instanceof CouchbaseMappingContext) {
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
cmc.setIndexCreator(indexCreator);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,56 +20,39 @@

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.CouchbaseClientFactory;
import org.springframework.data.couchbase.core.CouchbaseOperations;
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;

/**
* Encapsulates the logic of creating indices.
*
* @author Michael Nitschinger
* @author Aaron Whiteside
*/
public class CouchbasePersistentEntityIndexCreator implements InitializingBean, ApplicationListener<MappingContextEvent<?, ?>> {
public class CouchbasePersistentEntityIndexCreator implements 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 CouchbaseClientFactory clientFactory;
private final boolean enabled;
private final CouchbaseOperations couchbaseOperations;

public CouchbasePersistentEntityIndexCreator(final CouchbaseMappingContext mappingContext,
final CouchbaseClientFactory clientFactory, final String typeKey, final boolean enabled) {
final CouchbaseOperations operations) {
this.mappingContext = mappingContext;
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.");
}
this.couchbaseOperations = operations;
this.indexResolver = QueryIndexResolver.create(mappingContext, operations);
}

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

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

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

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

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

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

if (indexToCreate.getIndexPredicate() != null && !indexToCreate.getIndexPredicate().isEmpty()) {
Expand All @@ -124,10 +107,21 @@ private void createIndex(final IndexDefinitionHolder indexToCreate) {
cluster.query(statement.toString());
} catch (IndexExistsException ex) {
// ignored on purpose, rest is propagated
LOGGER.debug("Index \"{}\" already exists, ignoring.", indexToCreate.getIndexName());
LOGGER.debug("Index \"" + indexToCreate.getIndexName() + "\" already exists, ignoring.");
} 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,6 +20,7 @@
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 @@ -34,13 +35,13 @@
public class CouchbasePersistentEntityIndexResolver implements QueryIndexResolver {

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

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

@Override
Expand Down Expand Up @@ -134,6 +135,7 @@ 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,6 +15,7 @@
*/
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 @@ -42,9 +43,9 @@ public interface QueryIndexResolver {
*/
static QueryIndexResolver create(
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
String typeKey) {
CouchbaseOperations operations) {
Assert.notNull(mappingContext, "CouchbaseMappingContext must not be null!");
return new CouchbasePersistentEntityIndexResolver(mappingContext, typeKey);
return new CouchbasePersistentEntityIndexResolver(mappingContext, operations);
}

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

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 @@ -32,7 +37,6 @@
*
* @author Michael Nitschinger
* @author Michael Reiche
* @author Aaron Whiteside
*/
public class CouchbaseMappingContext
extends AbstractMappingContext<BasicCouchbasePersistentEntity<?>, CouchbasePersistentProperty>
Expand All @@ -51,6 +55,10 @@ 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 @@ -100,8 +108,65 @@ protected CouchbasePersistentProperty createPersistentProperty(Property property
*/
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
super.setApplicationContext(applicationContext);
context = applicationContext;
super.setApplicationContext(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;
}
}