DATAGEODE-14 - Improve IndexFactoryBean's resilience and options for handling GemFire IndexExistsExceptions and IndexNameConflictExceptions.

Related JIRA: https://jira.spring.io/browse/SGF-637

(cherry picked from commit e868c58db9d245adab005b6a5ae1cc0a94275cf3)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2017-06-22 16:21:11 -07:00
parent c22ebe6bd1
commit 08b1a147aa
19 changed files with 2523 additions and 917 deletions

View File

@@ -16,25 +16,21 @@
package org.springframework.data.gemfire;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeCollection;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.IndexExistsException;
import org.apache.geode.cache.query.IndexNameConflictException;
import org.apache.geode.cache.query.QueryService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -43,26 +39,49 @@ import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
/**
* The IndexConflictsIntegrationTest class...
* Integration tests with test cases testing the numerous conflicting {@link Index} configurations.
*
* An {@link IndexExistsException} is thrown when 2 or more {@link Index Indexes} share the same definition
* but have different names.
*
* An {@link IndexNameConflictException} is thrown when 2 or more {@link Index Indexes} share the same name
* but have potentially different definitions.
*
* @author John Blum
* @see org.junit.Test
* @link https://jira.spring.io/browse/SGF-432
* @see org.apache.geode.cache.GemFireCache
* @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.context.annotation.Import
* @see <a href="https://jira.spring.io/browse/SGF-432">IndexFactoryBean traps IndexExistsException instead of IndexNameConflictException</a>
* @see <a href="https://jira.spring.io/browse/SGF-637">Improve IndexFactoryBean's resilience and options for handling GemFire IndexExistsExceptions and IndexNameConflictExceptions</a>
* @since 1.6.3
*/
public class IndexConflictsIntegrationTest {
protected void assertIndex(Index index, String expectedName, String expectedExpression, String expectedFromClause,
private static final AtomicBoolean IGNORE = new AtomicBoolean(false);
private static final AtomicBoolean OVERRIDE = new AtomicBoolean(false);
private ConfigurableApplicationContext applicationContext;
private void assertIndex(Index index, String expectedName, String expectedExpression, String expectedFromClause,
IndexType expectedType) {
assertThat(index, is(notNullValue()));
assertThat(index.getName(), is(equalTo(expectedName)));
assertThat(index.getIndexedExpression(), is(equalTo(expectedExpression)));
assertThat(index.getFromClause(), is(equalTo(expectedFromClause)));
assertThat(IndexType.valueOf(index.getType()), is(equalTo(expectedType)));
assertThat(index).isNotNull();
assertThat(index.getName()).isEqualTo(expectedName);
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
assertThat(IndexType.valueOf(index.getType())).isEqualTo(expectedType);
}
protected boolean close(ConfigurableApplicationContext applicationContext) {
private void assertIndexCount(int count) {
assertThat(getIndexCount()).isEqualTo(count);
}
private boolean close(ConfigurableApplicationContext applicationContext) {
if (applicationContext != null) {
applicationContext.close();
return !(applicationContext.isActive() || applicationContext.isRunning());
@@ -71,185 +90,260 @@ public class IndexConflictsIntegrationTest {
return true;
}
private Index getIndex(String indexName) {
for (Index index : nullSafeCollection(getQueryService().getIndexes())) {
if (index.getName().equalsIgnoreCase(indexName)) {
return index;
}
}
return null;
}
private int getIndexCount() {
return nullSafeCollection(getQueryService().getIndexes()).size();
}
private QueryService getQueryService() {
return this.applicationContext.getBean("gemfireCache", GemFireCache.class).getQueryService();
}
private boolean hasIndex(String indexName) {
return (getIndex(indexName) != null);
}
private ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
return new AnnotationConfigApplicationContext(annotatedClasses);
}
@Before
public void setup() {
System.getProperties().remove("gemfire.cache.region.index.override");
assertThat(System.getProperties().containsKey("gemfire.cache.region.index.override"), is(false));
assertThat(IGNORE.get()).isFalse();
assertThat(OVERRIDE.get()).isFalse();
}
@Test(expected = BeanCreationException.class)
public void indexDefinitionConflictThrowsException() {
ConfigurableApplicationContext applicationContext = null;
@After
public void tearDown() {
OVERRIDE.set(false);
IGNORE.set(false);
assertThat(close(this.applicationContext)).isTrue();
}
@Test
public void indexDefinitionConflictIgnoresIndex() {
assertThat(IGNORE.compareAndSet(false, true)).isTrue();
this.applicationContext = newApplicationContext(IndexDefinitionConflictConfiguration.class);
assertThat(this.applicationContext.containsBean("customerIdIndex")).isTrue();
assertThat(this.applicationContext.containsBean("customerIdentifierIndex")).isTrue();
assertIndexCount(1);
assertThat(hasIndex("customerIdIndex")).isTrue();
assertThat(hasIndex("customerIdentifierIndex")).isFalse();
Index customersIdIndex = getIndex("customerIdIndex");
assertIndex(customersIdIndex, "customerIdIndex",
"id", "/Customers", IndexType.PRIMARY_KEY);
}
@Test
public void indexDefinitionConflictOverridesIndex() {
assertThat(OVERRIDE.compareAndSet(false, true)).isTrue();
this.applicationContext = newApplicationContext(IndexDefinitionConflictConfiguration.class);
assertThat(this.applicationContext.containsBean("customerIdIndex")).isTrue();
assertThat(this.applicationContext.containsBean("customerIdentifierIndex")).isTrue();
assertIndexCount(1);
assertThat(hasIndex("customerIdIndex")).isFalse();
assertThat(hasIndex("customerIdentifierIndex")).isTrue();
Index customersIdentifierIndex = getIndex("customerIdentifierIndex");
assertIndex(customersIdentifierIndex, "customerIdentifierIndex",
"id", "/Customers", IndexType.PRIMARY_KEY);
}
@Test(expected = IndexExistsException.class)
public void indexDefinitionConflictThrowsIndexExistsException() throws Throwable {
try {
applicationContext = new AnnotationConfigApplicationContext(
IndexDefinitionConflictGemFireConfiguration.class);
this.applicationContext = newApplicationContext(IndexDefinitionConflictConfiguration.class);
}
catch (BeanCreationException expected) {
assertThat(expected.getMessage(), containsString("Error creating bean with name 'customerIdentityIndex'"
+ " defined in org.springframework.data.gemfire.IndexConflictsIntegrationTest$IndexDefinitionConflictGemFireConfiguration:"
+ " Invocation of init method failed"));
assertThat(expected.getCause(), is(instanceOf(GemfireIndexException.class)));
assertThat(expected.getCause().getMessage(),
containsString("An Index with a different name having the same definition"
+ " as this Index (customerIdentityIndex) already exists"));
assertThat(expected.getCause().getCause(), is(instanceOf(IndexExistsException.class)));
assertThat(expected.getCause().getCause().getMessage(), is(equalTo("Similar Index Exists")));
throw expected;
}
finally {
assertThat(close(applicationContext), is(true));
assertThat(expected).hasMessageStartingWith("Error creating bean with name 'customerIdentifierIndex'");
assertThat(expected).hasCauseInstanceOf(GemfireIndexException.class);
String existingIndexDefinition = String.format(IndexFactoryBean.BASIC_INDEX_DEFINITION,
"id", "/Customers", IndexType.PRIMARY_KEY);
assertThat(expected.getCause()).hasMessageStartingWith(String.format(
"An Index with a different name [customerIdIndex] having the same definition [%s] already exists",
existingIndexDefinition));
assertThat(expected.getCause()).hasCauseInstanceOf(IndexExistsException.class);
assertThat(expected.getCause().getCause()).hasMessage("Similar Index Exists");
assertThat(expected.getCause().getCause()).hasNoCause();
throw expected.getCause().getCause();
}
}
@Test
public void indexNameConflictOverridesExistingIndex() {
ConfigurableApplicationContext applicationContext = null;
public void indexNameConflictIgnoresIndex() {
try {
applicationContext = new AnnotationConfigApplicationContext(IndexNameConflictGemFireConfiguration.class);
assertThat(IGNORE.compareAndSet(false, true)).isTrue();
assertThat(applicationContext.getBeansOfType(Index.class).size(), is(equalTo(2)));
this.applicationContext = newApplicationContext(IndexNameConflictConfiguration.class);
Cache gemfireCache = applicationContext.getBean("gemfireCache", Cache.class);
assertThat(this.applicationContext.containsBean("customerLastNameIndex")).isTrue();
assertThat(this.applicationContext.containsBean("customerFirstNameIndex")).isTrue();
assertIndexCount(1);
assertThat(hasIndex(IndexNameConflictConfiguration.INDEX_NAME)).isTrue();
assertThat(gemfireCache.getQueryService().getIndexes().size(), is(equalTo(1)));
Index customerLastNameIndex = getIndex(IndexNameConflictConfiguration.INDEX_NAME);
Index customerLastNameIndex = applicationContext.getBean("customerLastNameIndex", Index.class);
assertIndex(customerLastNameIndex, IndexNameConflictGemFireConfiguration.INDEX_NAME,
"lastName", "/Customers", IndexType.HASH);
Index customerFirstNameIndex = applicationContext.getBean("customerFirstNameIndex", Index.class);
assertIndex(customerFirstNameIndex, IndexNameConflictGemFireConfiguration.INDEX_NAME,
"firstName", "/Customers", IndexType.FUNCTIONAL);
assertThat(customerFirstNameIndex, is(not(sameInstance(customerLastNameIndex))));
assertThat(gemfireCache.getQueryService().getIndexes().iterator().next(),
is(sameInstance(customerFirstNameIndex)));
}
finally {
assertThat(close(applicationContext), is(true));
}
assertIndex(customerLastNameIndex, IndexNameConflictConfiguration.INDEX_NAME,
"lastName", "/Customers", IndexType.HASH);
}
@Test
public void indexNameConflictReturnsExistingIndex() {
ConfigurableApplicationContext applicationContext = null;
public void indexNameConflictOverridesIndex() {
assertThat(OVERRIDE.compareAndSet(false, true)).isTrue();
this.applicationContext = newApplicationContext(IndexNameConflictConfiguration.class);
assertThat(this.applicationContext.getBeansOfType(Index.class)).hasSize(2);
assertThat(this.applicationContext.containsBean("customerLastNameIndex")).isTrue();
assertThat(this.applicationContext.containsBean("customerFirstNameIndex")).isTrue();
assertIndexCount(1);
assertThat(hasIndex(IndexNameConflictConfiguration.INDEX_NAME)).isTrue();
Index customerFirstNameIndex = getIndex(IndexNameConflictConfiguration.INDEX_NAME);
assertIndex(customerFirstNameIndex, IndexNameConflictConfiguration.INDEX_NAME,
"firstName", "/Customers", IndexType.FUNCTIONAL);
}
@Test(expected = IndexNameConflictException.class)
public void indexNameConflictThrowsIndexNameConflictException() throws Throwable {
try {
System.setProperty("gemfire.cache.region.index.override", Boolean.FALSE.toString());
assertThat(System.getProperty("gemfire.cache.region.index.override", "true"),
is(equalTo(Boolean.FALSE.toString())));
applicationContext = new AnnotationConfigApplicationContext(IndexNameConflictGemFireConfiguration.class);
assertThat(applicationContext.getBeansOfType(Index.class).size(), is(equalTo(2)));
Cache gemfireCache = applicationContext.getBean("gemfireCache", Cache.class);
assertThat(gemfireCache.getQueryService().getIndexes().size(), is(equalTo(1)));
Index customerLastNameIndex = applicationContext.getBean("customerLastNameIndex", Index.class);
assertIndex(customerLastNameIndex, IndexNameConflictGemFireConfiguration.INDEX_NAME,
"lastName", "/Customers", IndexType.HASH);
Index customerFirstNameIndex = applicationContext.getBean("customerFirstNameIndex", Index.class);
assertIndex(customerFirstNameIndex, IndexNameConflictGemFireConfiguration.INDEX_NAME,
"lastName", "/Customers", IndexType.HASH);
assertThat(customerFirstNameIndex, is(sameInstance(customerLastNameIndex)));
assertThat(gemfireCache.getQueryService().getIndexes().iterator().next(),
is(sameInstance(customerLastNameIndex)));
this.applicationContext = newApplicationContext(IndexNameConflictConfiguration.class);
}
finally {
System.getProperties().remove("gemfire.cache.region.index.override");
catch (BeanCreationException expected) {
if (applicationContext != null) {
applicationContext.close();
}
assertThat(expected).hasMessageStartingWith("Error creating bean with name 'customerFirstNameIndex'");
assertThat(expected).hasCauseInstanceOf(GemfireIndexException.class);
assertThat(expected.getCause()).hasMessageStartingWith(String.format(
"An Index with the same name [%s] having possibly a different definition already exists;",
IndexNameConflictConfiguration.INDEX_NAME));
assertThat(expected.getCause()).hasCauseInstanceOf(IndexNameConflictException.class);
assertThat(expected.getCause().getCause()).hasMessage(String.format("Index named ' %s ' already exists.",
IndexNameConflictConfiguration.INDEX_NAME));
assertThat(expected.getCause().getCause()).hasNoCause();
throw expected.getCause().getCause();
}
}
@Configuration
@SuppressWarnings("unused")
public static class BaseGemFireConfiguration {
public static class GemFireConfiguration {
@Bean
public Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", IndexConflictsIntegrationTest.class.getSimpleName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "warning");
return gemfireProperties;
}
@Bean
public CacheFactoryBean gemfireCache() {
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
cacheFactoryBean.setClose(true);
cacheFactoryBean.setProperties(gemfireProperties());
cacheFactoryBean.setUseBeanFactoryLocator(false);
return cacheFactoryBean;
}
@Bean(name = "Customers")
public ReplicatedRegionFactoryBean customersRegion(Cache gemfireCache) {
public ReplicatedRegionFactoryBean customersRegion(GemFireCache gemfireCache) {
ReplicatedRegionFactoryBean customersRegionFactory = new ReplicatedRegionFactoryBean();
customersRegionFactory.setCache(gemfireCache);
customersRegionFactory.setName("Customers");
customersRegionFactory.setClose(false);
customersRegionFactory.setPersistent(false);
return customersRegionFactory;
}
}
@Configuration
@Import(BaseGemFireConfiguration.class)
@Import(GemFireConfiguration.class)
@SuppressWarnings("unused")
public static class IndexDefinitionConflictGemFireConfiguration {
public static class IndexDefinitionConflictConfiguration {
@Bean
public IndexFactoryBean customerIdIndex(Cache gemfireCache) {
public IndexFactoryBean customerIdIndex(GemFireCache gemfireCache) {
IndexFactoryBean indexFactoryBean = new IndexFactoryBean();
indexFactoryBean.setCache(gemfireCache);
indexFactoryBean.setExpression("id");
indexFactoryBean.setFrom("/Customers");
indexFactoryBean.setType(IndexType.PRIMARY_KEY);
return indexFactoryBean;
}
@Bean
@DependsOn("customerIdIndex")
public IndexFactoryBean customerIdentityIndex(Cache gemfireCache) {
public IndexFactoryBean customerIdentifierIndex(GemFireCache gemfireCache) {
IndexFactoryBean indexFactoryBean = new IndexFactoryBean();
indexFactoryBean.setCache(gemfireCache);
indexFactoryBean.setExpression("id");
indexFactoryBean.setIgnoreIfExists(IGNORE.get());
indexFactoryBean.setFrom("/Customers");
indexFactoryBean.setOverride(OVERRIDE.get());
indexFactoryBean.setType(IndexType.PRIMARY_KEY);
return indexFactoryBean;
}
}
@Configuration
@Import(BaseGemFireConfiguration.class)
@Import(GemFireConfiguration.class)
@SuppressWarnings("unused")
public static class IndexNameConflictGemFireConfiguration {
public static class IndexNameConflictConfiguration {
protected static final String INDEX_NAME = "CustomerNameIdx";
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertyPlaceholderConfigurer();
}
@Bean
public IndexFactoryBean customerLastNameIndex(Cache gemfireCache,
@Value("${gemfire.cache.region.index.override:true}") boolean override) {
public IndexFactoryBean customerLastNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean indexFactoryBean = new IndexFactoryBean();
@@ -257,7 +351,6 @@ public class IndexConflictsIntegrationTest {
indexFactoryBean.setExpression("lastName");
indexFactoryBean.setFrom("/Customers");
indexFactoryBean.setName(INDEX_NAME);
indexFactoryBean.setOverride(override);
indexFactoryBean.setType(IndexType.HASH);
return indexFactoryBean;
@@ -265,20 +358,19 @@ public class IndexConflictsIntegrationTest {
@Bean
@DependsOn("customerLastNameIndex")
public IndexFactoryBean customerFirstNameIndex(Cache gemfireCache,
@Value("${gemfire.cache.region.index.override:true}") boolean override) {
public IndexFactoryBean customerFirstNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean indexFactoryBean = new IndexFactoryBean();
indexFactoryBean.setCache(gemfireCache);
indexFactoryBean.setExpression("firstName");
indexFactoryBean.setFrom("/Customers");
indexFactoryBean.setIgnoreIfExists(IGNORE.get());
indexFactoryBean.setName(INDEX_NAME);
indexFactoryBean.setOverride(override);
indexFactoryBean.setOverride(OVERRIDE.get());
indexFactoryBean.setType(IndexType.FUNCTIONAL);
return indexFactoryBean;
}
}
}

View File

@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -43,7 +42,10 @@ import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.lucene.LuceneIndexFactory;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.geode.cache.query.Index;
import org.apache.geode.cache.query.IndexExistsException;
import org.apache.geode.cache.query.IndexNameConflictException;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.internal.concurrent.ConcurrentHashSet;
import org.apache.lucene.analysis.Analyzer;
import org.junit.After;
import org.junit.Test;
@@ -76,7 +78,7 @@ import org.springframework.data.gemfire.config.annotation.test.entities.Replicat
*/
public class EnableIndexingConfigurationUnitTests {
private static final Set<Index> indexes = new HashSet<>();
private static final Set<Index> indexes = new ConcurrentHashSet<>();
private ConfigurableApplicationContext applicationContext;
@@ -102,16 +104,19 @@ public class EnableIndexingConfigurationUnitTests {
}
/* (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());
private static Index findIndexByName(String indexName) {
for (Index index : indexes) {
if (index.getName().equalsIgnoreCase(indexName)) {
return index;
}
}
return null;
}
/* (non-Javadoc) */
protected void assertLuceneIndex(LuceneIndex index, String name, String regionPath, String... fields) {
private void assertLuceneIndex(LuceneIndex index, String name, String regionPath, String... fields) {
assertThat(index).isNotNull();
assertThat(index.getName()).isEqualTo(name);
assertThat(index.getRegionPath()).isEqualTo(regionPath);
@@ -120,68 +125,87 @@ public class EnableIndexingConfigurationUnitTests {
}
/* (non-Javadoc) */
protected ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
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);
assertThat(index.getFromClause()).isEqualTo(from);
assertThat(index.getType()).isEqualTo(indexType.getGemfireIndexType());
}
/* (non-Javadoc) */
private ConfigurableApplicationContext newApplicationContext(Class<?>... annotatedClasses) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(annotatedClasses);
applicationContext.registerShutdownHook();
return applicationContext;
}
@Test
public void persistentEntityIndexesCreatedSuccessfully() {
applicationContext = newApplicationContext(IndexedPersistentEntityConfiguration.class);
public void persistentEntityIndexesAreCreated() {
applicationContext = newApplicationContext(IndexingEnabledWithIndexedPersistentEntityConfiguration.class);
assertLuceneIndexes(applicationContext);
assertOqlIndexes(applicationContext);
}
private void assertLuceneIndexes(ConfigurableApplicationContext applicationContext) {
LuceneIndex luceneIndex = applicationContext.getBean("TitleLuceneIdx", LuceneIndex.class);
assertLuceneIndex(luceneIndex, "TitleLuceneIdx", "Customers", "title");
}
private void assertOqlIndexes(ConfigurableApplicationContext applicationContext) {
Index customersIdIdx = applicationContext.getBean("CustomersIdKeyIdx", Index.class);
assertIndex(customersIdIdx, "CustomersIdKeyIdx", "id", "Customers", IndexType.KEY);
Index customersIdIndex = applicationContext.getBean("CustomersIdKeyIdx", Index.class);
Index customersFirstNameIdx = applicationContext.getBean("CustomersFirstNameFunctionalIdx", Index.class);
assertOqlIndex(customersIdIndex, "CustomersIdKeyIdx", "id", "Customers", IndexType.KEY);
assertIndex(customersFirstNameIdx, "CustomersFirstNameFunctionalIdx", "first_name",
Index customersFirstNameIndex = applicationContext.getBean("CustomersFirstNameFunctionalIdx", Index.class);
assertOqlIndex(customersFirstNameIndex, "CustomersFirstNameFunctionalIdx", "first_name",
"/LoyalCustomers", IndexType.FUNCTIONAL);
Index lastNameIdx = applicationContext.getBean("LastNameIdx", Index.class);
Index lastNameIndex = applicationContext.getBean("LastNameIdx", Index.class);
assertIndex(lastNameIdx, "LastNameIdx", "surname", "Customers", IndexType.HASH);
assertOqlIndex(lastNameIndex, "LastNameIdx", "surname", "Customers", IndexType.HASH);
}
@Test
public void persistentEntityIndexesWillNotBeCreated() {
applicationContext = newApplicationContext(NoIndexesCreatedForIndexedPersistentEntityConfiguration.class);
public void persistentEntityIndexesAreNotCreated() {
applicationContext = newApplicationContext(IndexingNotEnabledWithIndexedPersistentEntityConfiguration.class);
Map<String, Index> indexes = applicationContext.getBeansOfType(Index.class);
assertThat(indexes).isNotNull();
assertThat(indexes.isEmpty()).isTrue();
assertThat(indexes).isEmpty();
}
@Test
public void indexAnnotatedEntityPropertyDoesNotOverrideIndexBeanDefinition() {
applicationContext = newApplicationContext(IndexAnnotatedEntityPropertyDoesNotOverrideBeanDefinitionConfiguration.class);
public void indexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinition() {
Index lastNameIdx = applicationContext.getBean("LastNameIdx", Index.class);
applicationContext = newApplicationContext(
IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinitionConfiguration.class);
assertIndex(lastNameIdx, "LastNameIdx", "last_name", "/People", IndexType.HASH);
}
Index firstNameIndex = applicationContext.getBean("LoyalCustomersFirstNameFunctionalIdx", Index.class);
@Test
public void indexAnnotatedEntityPropertyOverridesIndexBeanDefinition() {
applicationContext = newApplicationContext(IndexAnnotatedEntityPropertyOverridesIndexBeanDefinitionConfiguration.class);
Index customersFirstNameIdx = applicationContext.getBean("CustomersFirstNameFunctionalIdx", Index.class);
assertIndex(customersFirstNameIdx, "CustomersFirstNameFunctionalIdx",
assertOqlIndex(firstNameIndex, "LoyalCustomersFirstNameFunctionalIdx",
"first_name", "/LoyalCustomers", IndexType.FUNCTIONAL);
assertThat(findIndexByName("CustomersFirstNameFunctionalIdx")).isNull();
}
@Test
public void indexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameName() {
applicationContext = newApplicationContext(
IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameNameConfiguration.class);
Index lastNameIndex = applicationContext.getBean("LastNameIdx", Index.class);
assertOqlIndex(lastNameIndex, "LastNameIdx", "last_name", "/People", IndexType.HASH);
}
@Configuration
@@ -195,10 +219,10 @@ public class EnableIndexingConfigurationUnitTests {
}
Cache mockQueryService(Cache mockCache) throws Exception {
QueryService mockQueryService = mock(QueryService.class);
when(mockCache.getQueryService()).thenReturn(mockQueryService);
when(mockQueryService.getIndexes()).thenReturn(indexes);
when(mockQueryService.createHashIndex(anyString(), anyString(), anyString()))
.thenAnswer(new HashIndexAnswer());
@@ -209,11 +233,24 @@ public class EnableIndexingConfigurationUnitTests {
when(mockQueryService.createKeyIndex(anyString(), anyString(), anyString()))
.thenAnswer(new KeyIndexAnswer());
when(mockQueryService.getIndexes()).thenReturn(indexes);
doAnswer(invocation -> {
Index indexToRemove = invocation.getArgument(0);
indexes.remove(findIndexByName(indexToRemove.getName()));
return null;
}).when(mockQueryService).removeIndex(any(Index.class));
return mockCache;
}
@SuppressWarnings("unchecked")
Cache mockRegionFactory(Cache mockCache) {
RegionFactory mockRegionFactory = mock(RegionFactory.class);
when(mockCache.createRegionFactory()).thenReturn(mockRegionFactory);
@@ -273,16 +310,22 @@ public class EnableIndexingConfigurationUnitTests {
@Override
public Index answer(InvocationOnMock invocation) throws Throwable {
IndexType indexType = getType();
String name = invocation.getArgument(0);
String expression = invocation.getArgument(1);
String from = invocation.getArgument(2);
validateIndexDefinition(name, expression, from, indexType);
validateIndexName(name);
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());
when(mockIndex.getType()).thenReturn(indexType.getGemfireIndexType());
indexes.add(mockIndex);
@@ -291,6 +334,30 @@ public class EnableIndexingConfigurationUnitTests {
abstract IndexType getType();
private void validateIndexDefinition(String name, String expression, String fromClause, IndexType type)
throws IndexExistsException {
for (Index index : indexes) {
if (index.getIndexedExpression().equalsIgnoreCase(expression)
&& index.getFromClause().equalsIgnoreCase(fromClause)
&& index.getType().equals(type.getGemfireIndexType())) {
throw new IndexExistsException(String.format(
"Index [%1$s] has the same definition as existing Index [%2$s]",
name, index.getName()));
}
}
}
private void validateIndexName(String name) throws IndexNameConflictException {
for (Index index : indexes) {
if (index.getName().equalsIgnoreCase(name)) {
throw new IndexNameConflictException(String.format("Index with name [%s] already exists", name));
}
}
}
}
static class FunctionalIndexAnswer extends AbstractIndexAnswer {
@@ -322,7 +389,7 @@ public class EnableIndexingConfigurationUnitTests {
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
LocalRegionEntity.class, ReplicateRegionEntity.class }))
static class IndexedPersistentEntityConfiguration extends GemFireConfiguration {
private static class IndexingEnabledWithIndexedPersistentEntityConfiguration extends GemFireConfiguration {
}
@@ -330,7 +397,7 @@ public class EnableIndexingConfigurationUnitTests {
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
LocalRegionEntity.class, ReplicateRegionEntity.class }))
static class NoIndexesCreatedForIndexedPersistentEntityConfiguration extends GemFireConfiguration {
private static class IndexingNotEnabledWithIndexedPersistentEntityConfiguration extends GemFireConfiguration {
}
@@ -339,42 +406,46 @@ public class EnableIndexingConfigurationUnitTests {
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
LocalRegionEntity.class, ReplicateRegionEntity.class }))
static class IndexAnnotatedEntityPropertyDoesNotOverrideBeanDefinitionConfiguration extends GemFireConfiguration {
static class IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameDefinitionConfiguration
extends GemFireConfiguration {
@Bean
@SuppressWarnings("unused")
IndexFactoryBean firstNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean firstNameIndex = new IndexFactoryBean();
firstNameIndex.setCache(gemfireCache);
firstNameIndex.setName("LoyalCustomersFirstNameFunctionalIdx");
firstNameIndex.setExpression("first_name");
firstNameIndex.setFrom("/LoyalCustomers");
firstNameIndex.setType(IndexType.FUNCTIONAL);
return firstNameIndex;
}
}
@EnableIndexing
@EnableEntityDefinedRegions(basePackageClasses = NonEntity.class,
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
LocalRegionEntity.class, ReplicateRegionEntity.class }))
static class IndexAnnotatedEntityPropertyIsIgnoredWithExistingIndexHavingSameNameConfiguration
extends GemFireConfiguration {
@Bean
@SuppressWarnings("unused")
IndexFactoryBean lastNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean lastNameIndex = new IndexFactoryBean();
lastNameIndex.setCache(gemfireCache);
lastNameIndex.setName("LastNameIdx");
lastNameIndex.setExpression("last_name");
lastNameIndex.setFrom("/People");
lastNameIndex.setName("LastNameIdx");
lastNameIndex.setType(IndexType.HASH);
return lastNameIndex;
}
}
@EnableIndexing
@EnableEntityDefinedRegions(basePackageClasses = NonEntity.class,
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ClientRegionEntity.class, CollocatedPartitionRegionEntity.class, GenericRegionEntity.class,
LocalRegionEntity.class, ReplicateRegionEntity.class }))
static class IndexAnnotatedEntityPropertyOverridesIndexBeanDefinitionConfiguration extends GemFireConfiguration {
@Bean
@SuppressWarnings("unused")
IndexFactoryBean firstNameIndex(GemFireCache gemfireCache) {
IndexFactoryBean firstNameIndex = new IndexFactoryBean();
firstNameIndex.setCache(gemfireCache);
firstNameIndex.setExpression("given_name");
firstNameIndex.setFrom("/ProspectiveCustomers");
firstNameIndex.setName("CustomersFirstNameFunctionalIdx");
firstNameIndex.setType(IndexType.HASH);
return firstNameIndex;
}
}
}

View File

@@ -42,7 +42,7 @@ public class PartitionRegionEntity {
@Id
private Long id;
@Indexed(expression = "first_name", from = "/LoyalCustomers", override = true, type = IndexType.FUNCTIONAL)
@Indexed(expression = "first_name", from = "/LoyalCustomers", type = IndexType.FUNCTIONAL)
private String firstName;
@Indexed(name = "LastNameIdx", expression = "surname")

View File

@@ -16,6 +16,7 @@
package org.springframework.data.gemfire.config.xml;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import javax.annotation.Resource;
@@ -24,13 +25,16 @@ import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Index;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.gemfire.IndexFactoryBean;
import org.springframework.data.gemfire.test.GemfireTestApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
/**
* The IndexNamespaceTest is a test suite of test cases testing the functionality of GemFire Index creation using
* the Spring Data GemFire XML namespace (XSD).
* Integration tests with test cases testing the functionality of GemFire Index creation using
* the Spring Data GemFire XML namespace (XSD) and the {@link IndexParser}.
*
* @author Costin Leau
* @author David Turanski
@@ -42,30 +46,42 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
* @since 1.1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(locations="index-ns.xml", initializers=GemfireTestApplicationContextInitializer.class)
@SuppressWarnings({ "deprecation", "unused" })
public class IndexNamespaceTest {
private static final String TEST_REGION_NAME = "IndexedRegion";
@Resource(name = "simple")
private Index simple;
@Autowired
private ApplicationContext applicationContext;
@Resource(name = "basic")
private Index basic;
@Resource(name = "complex")
private Index complex;
@Test
public void testBasicIndex() throws Exception {
assertEquals("simple", simple.getName());
assertEquals("status", simple.getIndexedExpression());
assertEquals(Region.SEPARATOR + TEST_REGION_NAME, simple.getFromClause());
assertEquals(TEST_REGION_NAME, simple.getRegion().getName());
assertEquals(org.apache.geode.cache.query.IndexType.FUNCTIONAL, simple.getType());
public void basicIndexIsCorrect() throws Exception {
assertEquals("basic", basic.getName());
assertEquals("status", basic.getIndexedExpression());
assertEquals(Region.SEPARATOR + TEST_REGION_NAME, basic.getFromClause());
assertEquals(TEST_REGION_NAME, basic.getRegion().getName());
assertEquals(org.apache.geode.cache.query.IndexType.FUNCTIONAL, basic.getType());
}
@Test
public void testComplexIndex() throws Exception {
public void basicIndexFactoryBeanIsCorrect() {
IndexFactoryBean basicIndexFactoryBean = applicationContext.getBean("&basic", IndexFactoryBean.class);
assertThat(basicIndexFactoryBean.isIgnoreIfExists()).isFalse();
assertThat(basicIndexFactoryBean.isOverride()).isFalse();
}
@Test
public void complexIndexIsCorrect() throws Exception {
assertEquals("complex-index", complex.getName());
assertEquals("tsi.name", complex.getIndexedExpression());
assertEquals(Region.SEPARATOR + TEST_REGION_NAME + " tsi", complex.getFromClause());
@@ -73,4 +89,13 @@ public class IndexNamespaceTest {
assertEquals(org.apache.geode.cache.query.IndexType.HASH, complex.getType());
}
@Test
public void indexWithIgnoreAndOverrideIsCorrect() {
IndexFactoryBean indexFactoryBean =
applicationContext.getBean("&index-with-ignore-and-override", IndexFactoryBean.class);
assertThat(indexFactoryBean.isIgnoreIfExists()).isTrue();
assertThat(indexFactoryBean.isOverride()).isTrue();
}
}

View File

@@ -129,6 +129,7 @@ public class LuceneOperationsIntegrationTests {
@Test
public void findsDoctorDoesAsTypePersonSuccessfully() {
Collection<Person> doctorDoes = template.queryForValues("title: Doctor*", "title");
assertThat(doctorDoes).isNotNull();
@@ -139,6 +140,7 @@ public class LuceneOperationsIntegrationTests {
@Test
@SuppressWarnings("all")
public void findsMasterDoesAsTypeUserSuccessfully() {
List<User> masterDoes = template.query("title: Master*", "title", User.class);
assertThat(masterDoes).isNotNull();

View File

@@ -133,6 +133,26 @@ public class AbstractFactoryBeanSupportUnitTests {
verify(mockLog, times(1)).info(eq("info log test"));
}
@Test
public void logsWarningWhenWarnIsEnabled() {
when(mockLog.isWarnEnabled()).thenReturn(true);
factoryBeanSupport.logWarning("%s log test", "warn");
verify(mockLog, times(1)).isWarnEnabled();
verify(mockLog, times(1)).warn(eq("warn log test"));
}
@Test
public void logsWarningWhenErrorIsEnabled() {
when(mockLog.isErrorEnabled()).thenReturn(true);
factoryBeanSupport.logError("%s log test", "error");
verify(mockLog, times(1)).isErrorEnabled();
verify(mockLog, times(1)).error(eq("error log test"));
}
@Test
public void suppressesDebugLoggingWhenDebugIsDisabled() {
when(mockLog.isDebugEnabled()).thenReturn(false);
@@ -153,6 +173,26 @@ public class AbstractFactoryBeanSupportUnitTests {
verify(mockLog, never()).info(any());
}
@Test
public void suppressesWarnLoggingWhenWarnIsDisabled() {
when(mockLog.isWarnEnabled()).thenReturn(false);
factoryBeanSupport.logWarning(() -> "test");
verify(mockLog, times(1)).isWarnEnabled();
verify(mockLog, never()).warn(any());
}
@Test
public void suppressesErrorLoggingWhenInfoIsDisabled() {
when(mockLog.isErrorEnabled()).thenReturn(false);
factoryBeanSupport.logError(() -> "test");
verify(mockLog, times(1)).isErrorEnabled();
verify(mockLog, never()).error(any());
}
private static class TestFactoryBeanSupport<T> extends AbstractFactoryBeanSupport<T> {
@Override