diff --git a/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java b/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java index d274f5db..0329640a 100644 --- a/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java +++ b/src/main/java/org/springframework/data/gemfire/config/annotation/IndexConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.data.gemfire.config.annotation; import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeMap; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; import java.lang.annotation.Annotation; import java.util.Collections; @@ -37,6 +38,7 @@ 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.GemfireUtils; import org.springframework.data.gemfire.IndexFactoryBean; import org.springframework.data.gemfire.IndexType; import org.springframework.data.gemfire.config.xml.GemfireConstants; @@ -201,8 +203,9 @@ public class IndexConfiguration extends EntityDefinedRegionsConfiguration { indexFactoryBeanBuilder.addPropertyValue("expression", resolveExpression(persistentEntity, persistentProperty, indexedAttributes)); - indexFactoryBeanBuilder.addPropertyValue("from", - resolveFrom(persistentEntity, persistentProperty, indexedAttributes)); + String from = resolveFrom(persistentEntity, persistentProperty, indexedAttributes); + + indexFactoryBeanBuilder.addPropertyValue("from", toRegionPath(from)); indexFactoryBeanBuilder.addPropertyValue("ignoreIfExists", Boolean.TRUE); @@ -215,10 +218,36 @@ public class IndexConfiguration extends EntityDefinedRegionsConfiguration { indexFactoryBeanBuilder.addPropertyValue("type", resolveType(persistentEntity, persistentProperty, indexedAttributes, indexType).toString()); + indexFactoryBeanBuilder.addDependsOn(toRegionName(from)); + registry.registerBeanDefinition(indexName, indexFactoryBeanBuilder.getBeanDefinition()); }); } + /* (non-Javadoc) */ + private String toRegionName(String from) { + + return Optional.ofNullable(from) + .filter(StringUtils::hasText) + .map(it -> { + + boolean isSubRegionPath = from.lastIndexOf(Region.SEPARATOR) > 0; + + return (!isSubRegionPath && from.startsWith(Region.SEPARATOR) ? from.substring(1) : from); + + }) + .orElseThrow(() -> newIllegalArgumentException("From clause [%s] is required", from)); + } + + /* (non-Javadoc) */ + private String toRegionPath(String from) { + + return Optional.ofNullable(from) + .filter(StringUtils::hasText) + .map(it -> from.startsWith(Region.SEPARATOR) ? from : GemfireUtils.toRegionPath(from)) + .orElseThrow(() -> newIllegalArgumentException("From clause [%s] is required", from)); + } + /** * Registers a {@link LuceneIndex} for the {@link GemfirePersistentProperty} on the {@link GemfirePersistentEntity} * using the {@link Annotation} meta-data to define the Index. diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationIntegrationTests.java new file mode 100644 index 00000000..55a24f89 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationIntegrationTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2017 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 javax.annotation.Resource; + +import org.apache.geode.cache.DataPolicy; +import org.apache.geode.cache.Region; +import org.apache.geode.cache.client.ClientRegionShortcut; +import org.apache.geode.cache.query.Index; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.data.gemfire.IndexType; +import org.springframework.data.gemfire.mapping.annotation.Indexed; +import org.springframework.data.gemfire.test.model.Person; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests for {@link EnableIndexing} and the {@link Indexed} annotation. + * + * @author John Blum + * @see org.junit.Test + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.query.Index + * @see org.springframework.data.gemfire.config.annotation.EnableIndexing + * @see org.springframework.data.gemfire.mapping.annotation.Indexed + * @see org.springframework.data.gemfire.test.model.Person + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 2.0.3 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class EnableIndexingConfigurationIntegrationTests { + + @Resource(name = "People") + private Region people; + + @Resource(name = "PeopleIdKeyIdx") + private Index personIdKeyIndex; + + @Resource(name = "PeopleLastNameHashIdx") + private Index personLastNameHashIndex; + + @Test + public void peopleRegionIsSetupCorrectly() { + + assertThat(this.people).isNotNull(); + assertThat(this.people.getName()).isEqualTo("People"); + assertThat(this.people.getFullPath()).isEqualTo(GemfireUtils.toRegionPath("People")); + assertThat(this.people.getAttributes()).isNotNull(); + assertThat(this.people.getAttributes().getDataPolicy()).isEqualTo(DataPolicy.NORMAL); + } + + private 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()); + } + + /** + * @see From clause Region path error occurs when creating Indexes from application domain object fields annotated with @Indexed or @Id + */ + @Test + public void idKeyIndexAndLastNameHashIndexAreSetupCorrectly() { + + assertIndex(this.personIdKeyIndex, "PeopleIdKeyIdx", "id", "/People", IndexType.KEY); + assertIndex(this.personLastNameHashIndex, "PeopleLastNameHashIdx", + "lastName", "/People", IndexType.HASH); + } + + @ClientCacheApplication(logLevel = "none") + @EnableEntityDefinedRegions(basePackageClasses = Person.class, clientRegionShortcut = ClientRegionShortcut.LOCAL) + @EnableIndexing + static class TestConfiguration { + } +} diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationUnitTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationUnitTests.java index 6517b686..9f05e6df 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/EnableIndexingConfigurationUnitTests.java @@ -35,6 +35,7 @@ import java.util.Set; import org.apache.geode.cache.Cache; import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.RegionFactory; import org.apache.geode.cache.RegionShortcut; @@ -72,10 +73,22 @@ import org.springframework.data.gemfire.config.annotation.test.entities.Replicat * @author John Blum * @see org.junit.Test * @see org.mockito.Mockito - * @see EnableIndexing + * @see org.apache.geode.cache.GemFireCache + * @see org.apache.geode.cache.Region + * @see org.apache.geode.cache.lucene.LuceneIndex + * @see org.apache.geode.cache.query.Index + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.annotation.Bean + * @see org.springframework.context.annotation.Configuration + * @see org.springframework.data.gemfire.IndexFactoryBean + * @see org.springframework.data.gemfire.IndexType + * @see org.springframework.data.gemfire.config.annotation.EnableIndexing * @see org.springframework.data.gemfire.config.annotation.IndexConfiguration + * @see org.springframework.data.gemfire.mapping.annotation.Indexed + * @see org.springframework.data.gemfire.mapping.annotation.LuceneIndexed * @since 1.9.0 */ +@SuppressWarnings("unused") public class EnableIndexingConfigurationUnitTests { private static final Set indexes = new ConcurrentHashSet<>(); @@ -93,7 +106,9 @@ public class EnableIndexingConfigurationUnitTests { } private static String[] toStringArray(Object[] array) { + String[] stringArray = new String[array.length]; + int index = 0; for (Object element : array) { @@ -117,6 +132,7 @@ public class EnableIndexingConfigurationUnitTests { /* (non-Javadoc) */ private void assertLuceneIndex(LuceneIndex index, String name, String regionPath, String... fields) { + assertThat(index).isNotNull(); assertThat(index.getName()).isEqualTo(name); assertThat(index.getRegionPath()).isEqualTo(regionPath); @@ -126,6 +142,7 @@ public class EnableIndexingConfigurationUnitTests { /* (non-Javadoc) */ private void assertOqlIndex(Index index, String name, String expression, String from, IndexType indexType) { + assertThat(index).isNotNull(); assertThat(index.getName()).isEqualTo(name); assertThat(index.getIndexedExpression()).isEqualTo(expression); @@ -135,18 +152,21 @@ public class EnableIndexingConfigurationUnitTests { /* (non-Javadoc) */ private ConfigurableApplicationContext newApplicationContext(Class... annotatedClasses) { + ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(annotatedClasses); + applicationContext.registerShutdownHook(); + return applicationContext; } @Test public void persistentEntityIndexesAreCreated() { - applicationContext = newApplicationContext(IndexingEnabledWithIndexedPersistentEntityConfiguration.class); + this.applicationContext = newApplicationContext(IndexingEnabledWithIndexedPersistentEntityConfiguration.class); - assertLuceneIndexes(applicationContext); - assertOqlIndexes(applicationContext); + assertLuceneIndexes(this.applicationContext); + assertOqlIndexes(this.applicationContext); } private void assertLuceneIndexes(ConfigurableApplicationContext applicationContext) { @@ -160,7 +180,7 @@ public class EnableIndexingConfigurationUnitTests { Index customersIdIndex = applicationContext.getBean("CustomersIdKeyIdx", Index.class); - assertOqlIndex(customersIdIndex, "CustomersIdKeyIdx", "id", "Customers", IndexType.KEY); + assertOqlIndex(customersIdIndex, "CustomersIdKeyIdx", "id", "/Customers", IndexType.KEY); Index customersFirstNameIndex = applicationContext.getBean("CustomersFirstNameFunctionalIdx", Index.class); @@ -169,15 +189,16 @@ public class EnableIndexingConfigurationUnitTests { Index lastNameIndex = applicationContext.getBean("LastNameIdx", Index.class); - assertOqlIndex(lastNameIndex, "LastNameIdx", "surname", "Customers", IndexType.HASH); + assertOqlIndex(lastNameIndex, "LastNameIdx", "surname", "/Customers", IndexType.HASH); } @Test public void persistentEntityIndexesAreNotCreated() { - applicationContext = newApplicationContext(IndexingNotEnabledWithIndexedPersistentEntityConfiguration.class); + this.applicationContext = + newApplicationContext(IndexingNotEnabledWithIndexedPersistentEntityConfiguration.class); - Map indexes = applicationContext.getBeansOfType(Index.class); + Map indexes = this.applicationContext.getBeansOfType(Index.class); assertThat(indexes).isNotNull(); assertThat(indexes).isEmpty(); @@ -186,10 +207,10 @@ public class EnableIndexingConfigurationUnitTests { @Test public void indexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinition() { - applicationContext = newApplicationContext( - IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinitionConfiguration.class); + this.applicationContext = + newApplicationContext(IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinitionConfiguration.class); - Index firstNameIndex = applicationContext.getBean("LoyalCustomersFirstNameFunctionalIdx", Index.class); + Index firstNameIndex = this.applicationContext.getBean("LoyalCustomersFirstNameFunctionalIdx", Index.class); assertOqlIndex(firstNameIndex, "LoyalCustomersFirstNameFunctionalIdx", "first_name", "/LoyalCustomers", IndexType.FUNCTIONAL); @@ -200,10 +221,10 @@ public class EnableIndexingConfigurationUnitTests { @Test public void indexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameName() { - applicationContext = newApplicationContext( - IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameNameConfiguration.class); + this.applicationContext = + newApplicationContext(IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameNameConfiguration.class); - Index lastNameIndex = applicationContext.getBean("LastNameIdx", Index.class); + Index lastNameIndex = this.applicationContext.getBean("LastNameIdx", Index.class); assertOqlIndex(lastNameIndex, "LastNameIdx", "last_name", "/People", IndexType.HASH); } @@ -391,6 +412,10 @@ public class EnableIndexingConfigurationUnitTests { LocalRegionEntity.class, ReplicateRegionEntity.class })) private static class IndexingEnabledWithIndexedPersistentEntityConfiguration extends GemFireConfiguration { + @Bean("LoyalCustomers") + Region mockRegion() { + return mock(Region.class); + } } @EnableEntityDefinedRegions(basePackageClasses = NonEntity.class, @@ -409,6 +434,11 @@ public class EnableIndexingConfigurationUnitTests { static class IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinitionConfiguration extends GemFireConfiguration { + @Bean("LoyalCustomers") + Region mockRegion() { + return mock(Region.class); + } + @Bean @SuppressWarnings("unused") IndexFactoryBean firstNameIndex(GemFireCache gemfireCache) { @@ -433,6 +463,11 @@ public class EnableIndexingConfigurationUnitTests { static class IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameNameConfiguration extends GemFireConfiguration { + @Bean("LoyalCustomers") + Region mockRegion() { + return mock(Region.class); + } + @Bean @SuppressWarnings("unused") IndexFactoryBean lastNameIndex(GemFireCache gemfireCache) { diff --git a/src/test/java/org/springframework/data/gemfire/config/annotation/IndexConfigurerIntegrationTests.java b/src/test/java/org/springframework/data/gemfire/config/annotation/IndexConfigurerIntegrationTests.java index bed5d5ab..119697a8 100644 --- a/src/test/java/org/springframework/data/gemfire/config/annotation/IndexConfigurerIntegrationTests.java +++ b/src/test/java/org/springframework/data/gemfire/config/annotation/IndexConfigurerIntegrationTests.java @@ -39,6 +39,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.data.gemfire.IndexFactoryBean; +import org.springframework.data.gemfire.LocalRegionFactoryBean; import org.springframework.data.gemfire.config.annotation.test.entities.CollocatedPartitionRegionEntity; import org.springframework.data.gemfire.config.annotation.test.entities.NonEntity; import org.springframework.data.gemfire.mapping.annotation.ClientRegion; @@ -58,8 +59,9 @@ import org.springframework.data.gemfire.test.GemfireTestBeanPostProcessor; * @see org.springframework.data.gemfire.IndexFactoryBean * @see org.springframework.data.gemfire.config.annotation.IndexConfigurer * @see org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean - * @since 1.0.0 + * @since 2.0.0 */ +@SuppressWarnings("unused") public class IndexConfigurerIntegrationTests { private static ConfigurableApplicationContext applicationContext; @@ -118,7 +120,6 @@ public class IndexConfigurerIntegrationTests { @PeerCacheApplication @EnableIndexing - @SuppressWarnings("unused") @EnableEntityDefinedRegions(basePackageClasses = NonEntity.class, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, @@ -129,6 +130,18 @@ public class IndexConfigurerIntegrationTests { ) static class TestConfiguration { + @Bean("LoyalCustomers") + public LocalRegionFactoryBean localRegion(GemFireCache gemfireCache) { + + LocalRegionFactoryBean localRegion = new LocalRegionFactoryBean<>(); + + localRegion.setCache(gemfireCache); + localRegion.setClose(false); + localRegion.setPersistent(false); + + return localRegion; + } + @Bean GemfireTestBeanPostProcessor testBeanPostProcessor() { return new GemfireTestBeanPostProcessor(); diff --git a/src/test/java/org/springframework/data/gemfire/test/model/Person.java b/src/test/java/org/springframework/data/gemfire/test/model/Person.java index 58aa4104..560407e8 100644 --- a/src/test/java/org/springframework/data/gemfire/test/model/Person.java +++ b/src/test/java/org/springframework/data/gemfire/test/model/Person.java @@ -24,6 +24,7 @@ import java.util.Date; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.gemfire.mapping.annotation.Indexed; import org.springframework.data.gemfire.mapping.annotation.Region; import org.springframework.data.gemfire.test.support.IdentifierSequence; import org.springframework.data.gemfire.util.SpringUtils; @@ -40,6 +41,7 @@ import org.springframework.util.ObjectUtils; * @see org.springframework.data.annotation.Id * @see org.springframework.data.annotation.PersistenceConstructor * @see org.springframework.data.gemfire.mapping.annotation.Region + * @see org.springframework.data.gemfire.mapping.annotation.Indexed * @see org.springframework.data.gemfire.test.support.IdentifierSequence * @since 2.0.0 */ @@ -57,6 +59,8 @@ public class Person implements Comparable, Serializable { private Long id; private final String firstName; + + @Indexed private final String lastName; public static Date newBirthDate(int year, int month, int dayOfMonth) {