SGF-106 - Add annotation support for creating Indexes.
(cherry picked from commit ef5dd8b7cef32daa12c48e4b0d339bd90059b14f) Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
@@ -36,9 +36,12 @@ import org.springframework.core.annotation.AliasFor;
|
|||||||
*
|
*
|
||||||
* @author John Blum
|
* @author John Blum
|
||||||
* @see org.springframework.context.annotation.ComponentScan
|
* @see org.springframework.context.annotation.ComponentScan
|
||||||
|
* @see org.springframework.context.annotation.ComponentScan.Filter
|
||||||
* @see org.springframework.context.annotation.Import
|
* @see org.springframework.context.annotation.Import
|
||||||
* @see org.springframework.core.annotation.AliasFor
|
* @see org.springframework.core.annotation.AliasFor
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.EnableIndexes
|
||||||
* @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration
|
* @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.IndexConfiguration
|
||||||
* @see org.apache.geode.cache.Region
|
* @see org.apache.geode.cache.Region
|
||||||
* @since 1.9.0
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
@@ -46,7 +49,7 @@ import org.springframework.core.annotation.AliasFor;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@Documented
|
@Documented
|
||||||
@Import(EntityDefinedRegionsConfiguration.class)
|
@Import(IndexConfiguration.class)
|
||||||
@SuppressWarnings({ "unused" })
|
@SuppressWarnings({ "unused" })
|
||||||
public @interface EnableEntityDefinedRegions {
|
public @interface EnableEntityDefinedRegions {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.data.gemfire.config.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.apache.geode.cache.query.Index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EnableIndexes} annotation marks a Spring {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||||
|
* annotated application class to enable the creation of GemFire/Geode Indexes based on application persistent entity
|
||||||
|
* field/property annotations, such as the {@link @Id} and @Indexed annotations.
|
||||||
|
*
|
||||||
|
* @author John Blum
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.IndexConfiguration
|
||||||
|
* @see org.apache.geode.cache.query.Index
|
||||||
|
* @since 1.9.0
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
@SuppressWarnings({ "unused" })
|
||||||
|
public @interface EnableIndexes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether all GemFire/Geode {@link Index Indexes} will be defined before created.
|
||||||
|
* If set to {@literal true}, then all {@link Index Indexes} are defined first and the created
|
||||||
|
* in a single, bulk operation, thereby improving index creation efficiency.
|
||||||
|
*
|
||||||
|
* Defaults to false.
|
||||||
|
*/
|
||||||
|
boolean define() default false;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -75,10 +75,32 @@ import org.springframework.util.ObjectUtils;
|
|||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EntityDefinedRegionsConfiguration class...
|
* The {@link EntityDefinedRegionsConfiguration} class is Spring {@link ImportBeanDefinitionRegistrar} used in
|
||||||
|
* the {@link EnableEntityDefinedRegions} annotation to dynamically create GemFire/Geode {@link Region Regions}
|
||||||
|
* based on the application persistent entity classes.
|
||||||
*
|
*
|
||||||
* @author John Blum
|
* @author John Blum
|
||||||
* @since 1.0.0
|
* @see org.springframework.beans.factory.BeanClassLoaderAware
|
||||||
|
* @see org.springframework.beans.factory.BeanFactory
|
||||||
|
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||||
|
* @see org.springframework.beans.factory.config.BeanDefinition
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||||
|
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar
|
||||||
|
* @see org.springframework.data.gemfire.LocalRegionFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.PartitionedRegionFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.ReplicatedRegionFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.support.GemFireCacheTypeAwareRegionFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.support.GemFireComponentClassTypeScanner
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.ClientRegion
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfireMappingContext
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.LocalRegion
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.PartitionRegion
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.ReplicateRegion
|
||||||
|
* @see org.springframework.data.gemfire.mapping.annotation.Region
|
||||||
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class EntityDefinedRegionsConfiguration
|
public class EntityDefinedRegionsConfiguration
|
||||||
@@ -155,6 +177,7 @@ public class EntityDefinedRegionsConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc) */
|
/* (non-Javadoc) */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
protected ClassLoader getBeanClassLoader() {
|
protected ClassLoader getBeanClassLoader() {
|
||||||
return this.beanClassLoader;
|
return this.beanClassLoader;
|
||||||
}
|
}
|
||||||
@@ -229,7 +252,7 @@ public class EntityDefinedRegionsConfiguration
|
|||||||
GemfirePersistentEntity persistentEntity = getPersistentEntity(persistentEntityClass);
|
GemfirePersistentEntity persistentEntity = getPersistentEntity(persistentEntityClass);
|
||||||
|
|
||||||
registerRegionBeanDefinition(persistentEntity, strict, registry);
|
registerRegionBeanDefinition(persistentEntity, strict, registry);
|
||||||
postProcess(persistentEntity);
|
postProcess(importingClassMetadata, registry, persistentEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -594,11 +617,17 @@ public class EntityDefinedRegionsConfiguration
|
|||||||
* Performs addition post processing on the {@link GemfirePersistentEntity} to offer additional feature support
|
* Performs addition post processing on the {@link GemfirePersistentEntity} to offer additional feature support
|
||||||
* (e.g. dynamic Index creation).
|
* (e.g. dynamic Index creation).
|
||||||
*
|
*
|
||||||
|
* @param importingClassMetadata {@link AnnotationMetadata} for the importing application class.
|
||||||
|
* @param registry {@link BeanDefinitionRegistry} used to register Spring bean definitions.
|
||||||
* @param persistentEntity {@link GemfirePersistentEntity} to process.
|
* @param persistentEntity {@link GemfirePersistentEntity} to process.
|
||||||
* @return the given {@link GemfirePersistentEntity}.
|
* @return the given {@link GemfirePersistentEntity}.
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||||
|
* @see org.springframework.core.type.AnnotationMetadata
|
||||||
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
|
||||||
*/
|
*/
|
||||||
protected GemfirePersistentEntity<?> postProcess(GemfirePersistentEntity<?> persistentEntity) {
|
protected GemfirePersistentEntity<?> postProcess(AnnotationMetadata importingClassMetadata,
|
||||||
|
BeanDefinitionRegistry registry, GemfirePersistentEntity<?> persistentEntity) {
|
||||||
|
|
||||||
return persistentEntity;
|
return persistentEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.data.gemfire.config.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import org.apache.geode.cache.Region;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.gemfire.IndexFactoryBean;
|
||||||
|
import org.springframework.data.gemfire.IndexType;
|
||||||
|
import org.springframework.data.gemfire.config.xml.GemfireConstants;
|
||||||
|
import org.springframework.data.gemfire.mapping.GemfirePersistentEntity;
|
||||||
|
import org.springframework.data.gemfire.mapping.GemfirePersistentProperty;
|
||||||
|
import org.springframework.data.gemfire.mapping.annotation.Indexed;
|
||||||
|
import org.springframework.data.mapping.PropertyHandler;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link IndexConfiguration} class is a Spring {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar}
|
||||||
|
* and extension of {@link EntityDefinedRegionsConfiguration} used in the {@link EnableIndexes} annotation
|
||||||
|
* to dynamically create GemFire/Geode {@link org.apache.geode.cache.Region} Indexes based on
|
||||||
|
* {@link GemfirePersistentEntity} {@link GemfirePersistentProperty} annotations.
|
||||||
|
*
|
||||||
|
* @author John Blum
|
||||||
|
* @see java.lang.annotation.Annotation
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||||
|
* @see org.springframework.data.annotation.Id
|
||||||
|
* @see org.springframework.data.gemfire.IndexFactoryBean
|
||||||
|
* @see org.springframework.data.gemfire.IndexType
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.EntityDefinedRegionsConfiguration
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentProperty
|
||||||
|
* @see Indexed
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
* @see org.apache.geode.cache.query.Index
|
||||||
|
* @since 1.9.0
|
||||||
|
*/
|
||||||
|
public class IndexConfiguration extends EntityDefinedRegionsConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Annotation} {@link Class type} that configures and creates {@link Region} Indexes
|
||||||
|
* from application persistent entity properties.
|
||||||
|
*
|
||||||
|
* @return the {@link Annotation} {@link Class type} that configures and creates {@link Region Region} Indexes
|
||||||
|
* from application persistent entity properties.
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.EnableIndexes
|
||||||
|
* @see java.lang.annotation.Annotation
|
||||||
|
* @see java.lang.Class
|
||||||
|
*/
|
||||||
|
protected Class<? extends Annotation> getEnableIndexesAnnotationType() {
|
||||||
|
return EnableIndexes.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the {@link Annotation} {@link Class type} that configures and creates {@link Region} Indexes
|
||||||
|
* from application persistent entity properties.
|
||||||
|
*
|
||||||
|
* @return the name of the {@link Annotation} {@link Class type} that configures and creates {@link Region Region}
|
||||||
|
* Indexes from application persistent entity properties.
|
||||||
|
* @see #getEnableIndexesAnnotationType()
|
||||||
|
* @see java.lang.Class#getName()
|
||||||
|
*/
|
||||||
|
protected String getEnableIndexesAnnotationTypeName() {
|
||||||
|
return getEnableIndexesAnnotationType().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the simple name of the {@link Annotation} {@link Class type} that configures and creates {@link Region}
|
||||||
|
* Indexes from application persistent entity properties.
|
||||||
|
*
|
||||||
|
* @return the simple name of the {@link Annotation} {@link Class type} that configures and creates
|
||||||
|
* {@link Region Region} Indexes from application persistent entity properties.
|
||||||
|
* @see #getEnableIndexesAnnotationType()
|
||||||
|
* @see java.lang.Class#getSimpleName()
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected String getEnableIndexesAnnotationTypeSimpleName() {
|
||||||
|
return getEnableIndexesAnnotationType().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected GemfirePersistentEntity<?> postProcess(AnnotationMetadata importingClassMetadata,
|
||||||
|
final BeanDefinitionRegistry registry, GemfirePersistentEntity<?> persistentEntity) {
|
||||||
|
|
||||||
|
final GemfirePersistentEntity<?> localPersistentEntity =
|
||||||
|
super.postProcess(importingClassMetadata, registry, persistentEntity);
|
||||||
|
|
||||||
|
if (isAnnotationPresent(importingClassMetadata, getEnableIndexesAnnotationTypeName())) {
|
||||||
|
final AnnotationAttributes enableIndexesAttributes =
|
||||||
|
getAnnotationAttributes(importingClassMetadata, getEnableIndexesAnnotationTypeName());
|
||||||
|
|
||||||
|
localPersistentEntity.doWithProperties(new PropertyHandler<GemfirePersistentProperty>() {
|
||||||
|
@Override
|
||||||
|
public void doWithPersistentProperty(GemfirePersistentProperty persistentProperty) {
|
||||||
|
Id idAnnotation = persistentProperty.findAnnotation(Id.class);
|
||||||
|
|
||||||
|
if (idAnnotation != null) {
|
||||||
|
registerIndexBeanDefinition(enableIndexesAttributes, localPersistentEntity, persistentProperty,
|
||||||
|
IndexType.KEY, idAnnotation, registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Indexed indexedAnnotation = persistentProperty.findAnnotation(Indexed.class);
|
||||||
|
|
||||||
|
if (indexedAnnotation != null) {
|
||||||
|
registerIndexBeanDefinition(enableIndexesAttributes, localPersistentEntity, persistentProperty,
|
||||||
|
indexedAnnotation.type(), indexedAnnotation, registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return persistentEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an Index of the given {@link IndexType} for the {@link GemfirePersistentProperty}
|
||||||
|
* on the {@link GemfirePersistentEntity} using the {@link Annotation} meta-data to define the Index.
|
||||||
|
*
|
||||||
|
* @param enableIndexesAttributes {@link AnnotationAttributes} containing meta-data
|
||||||
|
* for the {@link EnableIndexes} annotation.
|
||||||
|
* @param persistentEntity {@link GemfirePersistentEntity} containing the {@link GemfirePersistentProperty}
|
||||||
|
* to be indexed.
|
||||||
|
* @param persistentProperty {@link GemfirePersistentProperty} for which the Index will be created.
|
||||||
|
* @param indexType {@link IndexType} enum specifying the Index type (e.g. KEY, HASH, etc).
|
||||||
|
* @param indexAnnotation Index {@link Annotation}.
|
||||||
|
* @param registry {@link BeanDefinitionRegistry} used to register the Index bean definition.
|
||||||
|
* @see java.lang.annotation.Annotation
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
|
||||||
|
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
|
||||||
|
* @see org.springframework.data.gemfire.IndexType
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity
|
||||||
|
* @see org.springframework.data.gemfire.mapping.GemfirePersistentProperty
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected void registerIndexBeanDefinition(AnnotationAttributes enableIndexesAttributes,
|
||||||
|
GemfirePersistentEntity<?> persistentEntity, GemfirePersistentProperty persistentProperty,
|
||||||
|
IndexType indexType, Annotation indexAnnotation, BeanDefinitionRegistry registry) {
|
||||||
|
|
||||||
|
if (indexAnnotation != null) {
|
||||||
|
AnnotationAttributes indexAnnotationAttributes = AnnotationAttributes.fromMap(
|
||||||
|
AnnotationUtils.getAnnotationAttributes(indexAnnotation));
|
||||||
|
|
||||||
|
BeanDefinitionBuilder indexFactoryBeanBuilder =
|
||||||
|
BeanDefinitionBuilder.genericBeanDefinition(IndexFactoryBean.class);
|
||||||
|
|
||||||
|
String indexName = resolveName(persistentEntity, persistentProperty,
|
||||||
|
indexAnnotationAttributes, indexType);
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyReference("cache", GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME);
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyValue("define", resolveDefine(enableIndexesAttributes));
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyValue("expression",
|
||||||
|
resolveExpression(persistentEntity, persistentProperty, indexAnnotationAttributes));
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyValue("from",
|
||||||
|
resolveFrom(persistentEntity, persistentProperty, indexAnnotationAttributes));
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyValue("name", indexName);
|
||||||
|
|
||||||
|
indexFactoryBeanBuilder.addPropertyValue("type",
|
||||||
|
resolveType(persistentEntity, persistentProperty, indexAnnotationAttributes, indexType).toString());
|
||||||
|
|
||||||
|
registry.registerBeanDefinition(indexName, indexFactoryBeanBuilder.getBeanDefinition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
private boolean resolveDefine(AnnotationAttributes enableIndexesAnnotationAttributes) {
|
||||||
|
return (enableIndexesAnnotationAttributes.containsKey("define")
|
||||||
|
&& enableIndexesAnnotationAttributes.getBoolean("define"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private String resolveExpression(GemfirePersistentEntity<?> persistentEntity,
|
||||||
|
GemfirePersistentProperty persistentProperty, AnnotationAttributes indexAnnotationAttributes) {
|
||||||
|
|
||||||
|
String expression = (indexAnnotationAttributes.containsKey("expression")
|
||||||
|
? indexAnnotationAttributes.getString("expression") : null);
|
||||||
|
|
||||||
|
return (StringUtils.hasText(expression) ? expression : persistentProperty.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private String resolveFrom(GemfirePersistentEntity<?> persistentEntity,
|
||||||
|
GemfirePersistentProperty persistentProperty, AnnotationAttributes indexAnnotationAttributes) {
|
||||||
|
|
||||||
|
String from = (indexAnnotationAttributes.containsKey("from")
|
||||||
|
? indexAnnotationAttributes.getString("from") : null);
|
||||||
|
|
||||||
|
return (StringUtils.hasText(from) ? from : persistentEntity.getRegionName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
private String resolveName(GemfirePersistentEntity<?> persistentEntity,
|
||||||
|
GemfirePersistentProperty persistentProperty, AnnotationAttributes indexAnnotationAttributes,
|
||||||
|
IndexType indexType) {
|
||||||
|
|
||||||
|
String indexName = (indexAnnotationAttributes.containsKey("name")
|
||||||
|
? indexAnnotationAttributes.getString("name") : null);
|
||||||
|
|
||||||
|
return (StringUtils.hasText(indexName) ? indexName
|
||||||
|
: generateIndexName(persistentEntity, persistentProperty, indexType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
private String generateIndexName(GemfirePersistentEntity persistentEntity,
|
||||||
|
GemfirePersistentProperty persistentProperty, IndexType indexType) {
|
||||||
|
|
||||||
|
return String.format("%1$s%2$s%3$sIdx", persistentEntity.getRegionName(),
|
||||||
|
StringUtils.capitalize(persistentProperty.getName()),
|
||||||
|
StringUtils.capitalize(indexType.name().toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private IndexType resolveType(GemfirePersistentEntity<?> persistentEntity,
|
||||||
|
GemfirePersistentProperty persistentProperty, AnnotationAttributes indexAnnotationAttributes,
|
||||||
|
IndexType indexType) {
|
||||||
|
|
||||||
|
IndexType resolvedIndexType = (indexAnnotationAttributes.containsKey("type")
|
||||||
|
? indexAnnotationAttributes.<IndexType>getEnum("type") : null);
|
||||||
|
|
||||||
|
return (resolvedIndexType != null ? resolvedIndexType : indexType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.data.gemfire.mapping.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.apache.geode.cache.query.Index;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
import org.springframework.data.gemfire.IndexType;
|
||||||
|
import org.springframework.data.gemfire.mapping.GemfirePersistentEntity;
|
||||||
|
import org.springframework.data.gemfire.mapping.GemfirePersistentProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Indexed} annotation is used to index a {@link GemfirePersistentEntity} {@link GemfirePersistentProperty},
|
||||||
|
* which creates a GemFire/Geode {@link Index} on a GemFire/Geode {@link org.apache.geode.cache.Region}.
|
||||||
|
*
|
||||||
|
* @author John Blum
|
||||||
|
* @see org.springframework.core.annotation.AliasFor
|
||||||
|
* @see org.springframework.data.gemfire.IndexType
|
||||||
|
* @see org.apache.geode.cache.Region
|
||||||
|
* @see org.apache.geode.cache.query.Index
|
||||||
|
* @since 1.9.0
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
@SuppressWarnings({ "unused" })
|
||||||
|
public @interface Indexed {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the Index.
|
||||||
|
*/
|
||||||
|
@AliasFor(attribute = "name")
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the Index.
|
||||||
|
*/
|
||||||
|
@AliasFor(attribute = "value")
|
||||||
|
String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression to index.
|
||||||
|
*/
|
||||||
|
String expression() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The GemFire/Geode {@link org.apache.geode.cache.Region} on which the Index is created.
|
||||||
|
*/
|
||||||
|
String from() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of Index to create.
|
||||||
|
*
|
||||||
|
* Defaults to {@link IndexType#HASH}.
|
||||||
|
*/
|
||||||
|
IndexType type() default IndexType.HASH;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.data.gemfire.config.annotation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.apache.geode.cache.Cache;
|
||||||
|
import org.apache.geode.cache.RegionAttributes;
|
||||||
|
import org.apache.geode.cache.RegionFactory;
|
||||||
|
import org.apache.geode.cache.RegionShortcut;
|
||||||
|
import org.apache.geode.cache.query.Index;
|
||||||
|
import org.apache.geode.cache.query.QueryService;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.FilterType;
|
||||||
|
import org.springframework.data.gemfire.IndexType;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.ClientRegionEntity;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.CollocatedPartitionRegionEntity;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.GenericRegionEntity;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.LocalRegionEntity;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.NonEntity;
|
||||||
|
import org.springframework.data.gemfire.config.annotation.test.entities.ReplicateRegionEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the {@link EnableIndexes} and {@link IndexConfiguration} class.
|
||||||
|
*
|
||||||
|
* @author John Blum
|
||||||
|
* @see org.junit.Test
|
||||||
|
* @see org.mockito.Mockito
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.EnableIndexes
|
||||||
|
* @see org.springframework.data.gemfire.config.annotation.IndexConfiguration
|
||||||
|
* @since 1.9.0
|
||||||
|
*/
|
||||||
|
public class EnableIndexesConfigurationUnitTests {
|
||||||
|
|
||||||
|
private ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
if (applicationContext != null) {
|
||||||
|
applicationContext.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
protected void assertIndex(Index index, String name, String expression, String from, IndexType indexType) {
|
||||||
|
assertThat(index).isNotNull();
|
||||||
|
assertThat(index.getName()).isEqualTo(name);
|
||||||
|
assertThat(index.getIndexedExpression()).isEqualTo(expression);
|
||||||
|
assertThat(index.getFromClause()).isEqualTo(from);
|
||||||
|
assertThat(index.getType()).isEqualTo(indexType.getGemfireIndexType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc) */
|
||||||
|
protected ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
|
||||||
|
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(annotatedClasses);
|
||||||
|
applicationContext.registerShutdownHook();
|
||||||
|
return applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pesistentEntityIndexesCreatedSuccessfully() {
|
||||||
|
applicationContext = newApplicationContext(IndexedPersistentEntityConfiguration.class);
|
||||||
|
|
||||||
|
Index customersIdIdx = applicationContext.getBean("CustomersIdKeyIdx", Index.class);
|
||||||
|
|
||||||
|
assertIndex(customersIdIdx, "CustomersIdKeyIdx", "id", "Customers", IndexType.KEY);
|
||||||
|
|
||||||
|
Index customersFirstNameIdx = applicationContext.getBean("CustomersFirstNameFunctionalIdx", Index.class);
|
||||||
|
|
||||||
|
assertIndex(customersFirstNameIdx, "CustomersFirstNameFunctionalIdx", "first_name", "/LoyalCustomers",
|
||||||
|
IndexType.FUNCTIONAL);
|
||||||
|
|
||||||
|
Index lastNameIdx = applicationContext.getBean("LastNameIdx", Index.class);
|
||||||
|
|
||||||
|
assertIndex(lastNameIdx, "LastNameIdx", "lastName", "Customers", IndexType.HASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static class CacheConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Cache gemfireCache() throws Exception {
|
||||||
|
Cache mockCache = mock(Cache.class);
|
||||||
|
QueryService mockQueryService = mock(QueryService.class);
|
||||||
|
RegionFactory mockRegionFactory = mock(RegionFactory.class);
|
||||||
|
|
||||||
|
when(mockCache.getQueryService()).thenReturn(mockQueryService);
|
||||||
|
when(mockCache.createRegionFactory()).thenReturn(mockRegionFactory);
|
||||||
|
when(mockCache.createRegionFactory(any(RegionAttributes.class))).thenReturn(mockRegionFactory);
|
||||||
|
when(mockCache.createRegionFactory(any(RegionShortcut.class))).thenReturn(mockRegionFactory);
|
||||||
|
when(mockCache.createRegionFactory(anyString())).thenReturn(mockRegionFactory);
|
||||||
|
|
||||||
|
when(mockQueryService.createHashIndex(anyString(), anyString(), anyString()))
|
||||||
|
.thenAnswer(new HashIndexAnswer());
|
||||||
|
|
||||||
|
when(mockQueryService.createIndex(anyString(), anyString(), anyString()))
|
||||||
|
.thenAnswer(new FunctionalIndexAnswer());
|
||||||
|
|
||||||
|
when(mockQueryService.createKeyIndex(anyString(), anyString(), anyString()))
|
||||||
|
.thenAnswer(new KeyIndexAnswer());
|
||||||
|
|
||||||
|
return mockCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static abstract class AbstractIndexAnswer implements Answer<Index> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Index answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
String name = invocation.getArgumentAt(0, String.class);
|
||||||
|
String expression = invocation.getArgumentAt(1, String.class);
|
||||||
|
String from = invocation.getArgumentAt(2, String.class);
|
||||||
|
|
||||||
|
Index mockIndex = mock(Index.class, name);
|
||||||
|
|
||||||
|
when(mockIndex.getName()).thenReturn(name);
|
||||||
|
when(mockIndex.getIndexedExpression()).thenReturn(expression);
|
||||||
|
when(mockIndex.getFromClause()).thenReturn(from);
|
||||||
|
when(mockIndex.getType()).thenReturn(getType().getGemfireIndexType());
|
||||||
|
|
||||||
|
return mockIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract IndexType getType();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FunctionalIndexAnswer extends AbstractIndexAnswer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IndexType getType() {
|
||||||
|
return IndexType.FUNCTIONAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HashIndexAnswer extends AbstractIndexAnswer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IndexType getType() {
|
||||||
|
return IndexType.HASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class KeyIndexAnswer extends AbstractIndexAnswer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
IndexType getType() {
|
||||||
|
return IndexType.KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableIndexes
|
||||||
|
@EnableEntityDefinedRegions(basePackageClasses = NonEntity.class,
|
||||||
|
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
|
||||||
|
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
|
||||||
|
LocalRegionEntity.class, ReplicateRegionEntity.class }))
|
||||||
|
static class IndexedPersistentEntityConfiguration extends CacheConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
package org.springframework.data.gemfire.config.annotation.test.entities;
|
package org.springframework.data.gemfire.config.annotation.test.entities;
|
||||||
|
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.gemfire.IndexType;
|
||||||
|
import org.springframework.data.gemfire.mapping.annotation.Indexed;
|
||||||
import org.springframework.data.gemfire.mapping.annotation.PartitionRegion;
|
import org.springframework.data.gemfire.mapping.annotation.PartitionRegion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +40,10 @@ public class PartitionRegionEntity {
|
|||||||
@Id
|
@Id
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Indexed(expression = "first_name", from = "/LoyalCustomers", type = IndexType.FUNCTIONAL)
|
||||||
private String firstName;
|
private String firstName;
|
||||||
|
|
||||||
|
@Indexed(name = "LastNameIdx")
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user