diff --git a/src/main/java/org/springframework/data/gemfire/IndexFactoryBean.java b/src/main/java/org/springframework/data/gemfire/IndexFactoryBean.java index a246ea8e..4a7b59bb 100644 --- a/src/main/java/org/springframework/data/gemfire/IndexFactoryBean.java +++ b/src/main/java/org/springframework/data/gemfire/IndexFactoryBean.java @@ -16,6 +16,8 @@ package org.springframework.data.gemfire; +import java.util.Collection; + import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -30,103 +32,131 @@ import com.gemstone.gemfire.cache.query.IndexExistsException; import com.gemstone.gemfire.cache.query.QueryService; /** - * Factory bean for easy declarative creation of GemFire Indexes. + * Spring FactoryBean for easy declarative creation of GemFire Indexes. * * @author Costin Leau * @author David Turanski + * @author John Blum + * @see org.springframework.beans.factory.InitializingBean + * @see org.springframework.beans.factory.BeanNameAware + * @see org.springframework.beans.factory.FactoryBean + * @see com.gemstone.gemfire.cache.RegionService + * @see com.gemstone.gemfire.cache.query.Index + * @see com.gemstone.gemfire.cache.query.QueryService + * @since 1.0.0 */ -public class IndexFactoryBean implements InitializingBean, BeanNameAware, FactoryBean { +public class IndexFactoryBean implements InitializingBean, FactoryBean, BeanNameAware { + + private boolean override = true; private Index index; + + private IndexType indexType; + private QueryService queryService; + private RegionService cache; + private String beanName; - private String name; private String expression; private String from; private String imports; - private String type; - private boolean override = true; + private String name; public void afterPropertiesSet() throws Exception { - if (queryService == null) { - if (cache != null) { - queryService = cache.getQueryService(); - } + Assert.notNull(cache, "The GemFire Cache reference must not be null!"); + + queryService = lookupQueryService(); + + Assert.notNull(queryService, "A QueryService is required for Index creation!"); + Assert.hasText(expression, "The Index 'expression' is required!"); + Assert.hasText(from, "The Index 'from' clause (a Region's full-path) is required!"); + + if (IndexType.isKey(indexType)) { + Assert.isNull(imports, "The 'imports' property is not supported for a Key Index."); } - Assert.notNull(queryService, "Query service required for index creation"); - Assert.hasText(expression, "Index expression is required"); - Assert.hasText(from, "Index from clause (regionPath) is required"); + String indexName = (StringUtils.hasText(name) ? name : beanName); - if (StringUtils.hasText(type)) { - if (type.equalsIgnoreCase("KEY") || type.equalsIgnoreCase("PRIMARY_KEY")) { - Assert.isNull(imports, "The imports property is not supported for a key index"); - } - } - - String indexName = StringUtils.hasText(name) ? name : beanName; - - Assert.hasText(indexName, "Index bean id or name is required"); + Assert.hasText(indexName, "The Index bean id or name is required!"); index = createIndex(queryService, indexName); } - private Index createIndex(QueryService queryService, String indexName) throws Exception { + QueryService lookupQueryService() { + return (queryService != null ? queryService + : (cache instanceof ClientCache ? ((ClientCache) cache).getLocalQueryService() + : cache.getQueryService())); + } + + Index createIndex(QueryService queryService, String indexName) throws Exception { + Index existingIndex = getExistingIndex(queryService, indexName); - Index existingIndex = null; - - for (Index idx : queryService.getIndexes()) { - if (idx.getName().equals(indexName)) { - existingIndex = idx; - break; - } - } if (existingIndex != null) { - if (!override) { - return existingIndex; - } else { + if (override) { queryService.removeIndex(existingIndex); } + else { + return existingIndex; + } } - Index index = null; try { - if ("KEY".equalsIgnoreCase(type) || "PRIMARY_KEY".equalsIgnoreCase(type)) { - - index = queryService.createKeyIndex(indexName, expression, from); - - } else if ("HASH".equalsIgnoreCase(type)) { - if (StringUtils.hasText(imports)) { - index = queryService.createHashIndex(indexName, expression, from, imports); - } else { - index = queryService.createHashIndex(indexName, expression, from); - } - } else { - if (StringUtils.hasText(imports)) { - index = queryService.createIndex(indexName, expression, from, imports); - } else { - index = queryService.createIndex(indexName, expression, from); - } + if (IndexType.isKey(indexType)) { + return queryService.createKeyIndex(indexName, expression, from); } - return index; - - } catch (IndexExistsException e) { - for (Index idx : queryService.getIndexes()) { - if (idx.getName().equals(indexName)) { - return idx; - } + else if (IndexType.isHash(indexType)) { + return createHashIndex(queryService, indexName, expression, from, imports); } - } catch (Exception e) { + else { + return createFunctionalIndex(queryService, indexName, expression, from, imports); + } + } + catch (IndexExistsException e) { + return getExistingIndex(queryService, indexName); + } + catch (Exception e) { if (existingIndex != null) { - if (CollectionUtils.isEmpty(queryService.getIndexes()) - || !queryService.getIndexes().contains(existingIndex)) { + Collection indexes = queryService.getIndexes(); + + if (CollectionUtils.isEmpty(indexes) || !indexes.contains(existingIndex)) { queryService.getIndexes().add(existingIndex); + return existingIndex; } } + + throw e; + } + } + + Index createFunctionalIndex(QueryService queryService, String indexName, String expression, String from, + String imports) throws Exception { + if (StringUtils.hasText(imports)) { + return queryService.createIndex(indexName, expression, from, imports); + } + else { + return queryService.createIndex(indexName, expression, from); + } + } + + Index createHashIndex(QueryService queryService, String indexName, String expression, String from, + String imports) throws Exception { + if (StringUtils.hasText(imports)) { + return queryService.createHashIndex(indexName, expression, from, imports); + } + else { + return queryService.createHashIndex(indexName, expression, from); + } + } + + Index getExistingIndex(QueryService queryService, String indexName) { + for (Index index : queryService.getIndexes()) { + if (index.getName().equalsIgnoreCase(indexName)) { + return index; + } } - return index; + return null; } public Index getObject() { @@ -195,7 +225,18 @@ public class IndexFactoryBean implements InitializingBean, BeanNameAware, Factor * @param type the type to set */ public void setType(String type) { - this.type = type; + setType(IndexType.valueOfIgnoreCase(type)); + } + + /** + * Sets the type of GemFire Index to create. + * + * @param indexType the IndexType enumerated value indicating the type of GemFire Index + * that will be created by this Spring FactoryBean. + * @see org.springframework.data.gemfire.IndexType + */ + public void setType(IndexType indexType) { + this.indexType = indexType; } /** @@ -204,4 +245,5 @@ public class IndexFactoryBean implements InitializingBean, BeanNameAware, Factor public void setOverride(boolean override) { this.override = override; } -} \ No newline at end of file + +} diff --git a/src/main/java/org/springframework/data/gemfire/IndexType.java b/src/main/java/org/springframework/data/gemfire/IndexType.java new file mode 100644 index 00000000..d1a936eb --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/IndexType.java @@ -0,0 +1,151 @@ +/* + * Copyright 2010-2013 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; + +/** + * The IndexType class is an enumerated type of GemFire Index Types. + * + * @author John Blum + * @see com.gemstone.gemfire.cache.query.IndexType + * @since 1.5.2 + */ +@SuppressWarnings({ "deprecation", "unused" }) +public enum IndexType { + FUNCTIONAL(com.gemstone.gemfire.cache.query.IndexType.FUNCTIONAL), + HASH(com.gemstone.gemfire.cache.query.IndexType.HASH), + PRIMARY_KEY(com.gemstone.gemfire.cache.query.IndexType.PRIMARY_KEY), + KEY(com.gemstone.gemfire.cache.query.IndexType.PRIMARY_KEY); + + private final com.gemstone.gemfire.cache.query.IndexType gemfireIndexType; + + /** + * Constructs an instance of the IndexType enum initialized with the given GemFire IndexType. + * + * @param gemfireIndexType the corresponding GemFire IndexType + * @see com.gemstone.gemfire.cache.query.IndexType + */ + IndexType(final com.gemstone.gemfire.cache.query.IndexType gemfireIndexType) { + this.gemfireIndexType = gemfireIndexType; + } + + /** + * Null-safe operation to determine if the IndexType is a "FUNCTIONAL" Index. + * + * @param indexType the IndexType to evaluate. + * @return a boolean value indicating whether the IndexType is a "FUNCTIONAL" Index. + * @see #isFunctional() + */ + public static boolean isFunctional(final IndexType indexType) { + return (indexType != null && indexType.isFunctional()); + } + + /** + * Null-safe operation to determine if the IndexType is a "HASH" Index. + * + * @param indexType the IndexType to evaluate. + * @return a boolean value indicating whether the IndexType is a "HASH" Index. + * @see #isHash() + */ + public static boolean isHash(final IndexType indexType) { + return (indexType != null && indexType.isHash()); + } + + /** + * Null-safe operation to determine if the IndexType is a "KEY" Index. + * + * @param indexType the IndexType to evaluate. + * @return a boolean value indicating whether the IndexType is a "KEY" Index. + * @see #isFunctional() + */ + public static boolean isKey(final IndexType indexType) { + return (indexType != null && indexType.isKey()); + } + + /** + * Returns an IndexType given the corresponding GemFire IndexType or null if no SDG IndexType + * corresponds to the GemFire IndexType. + * + * @param gemfireIndexType the GemFire IndexType. + * @return a IndexType matching the GemFire IndexType or null if the GemFire IndexType does not match + * any IndexType in this enumeration. + * @see com.gemstone.gemfire.cache.query.IndexType + */ + public static IndexType valueOf(final com.gemstone.gemfire.cache.query.IndexType gemfireIndexType) { + for (IndexType indexType : values()) { + if (indexType.getGemfireIndexType().equals(gemfireIndexType)) { + return indexType; + } + } + + return null; + } + + /** + * Returns an IndexType matching the given String. + * + * @param value the String value describing the matching IndexType. + * @return an IndexType matching the given String. + * @see java.lang.String#equalsIgnoreCase(String) + */ + public static IndexType valueOfIgnoreCase(final String value) { + for (IndexType indexType : values()) { + if (indexType.name().equalsIgnoreCase(value)) { + return indexType; + } + } + + return null; + } + + /** + * Gets the matching GemFire IndexType for this IndexType enumerated value. + * + * @return the matching GemFire IndexType. + * @see com.gemstone.gemfire.cache.query.IndexType + */ + public com.gemstone.gemfire.cache.query.IndexType getGemfireIndexType() { + return gemfireIndexType; + } + + /** + * Determines whether this IndexType is "FUNCTIONAL". + * + * @return a boolean value indicating whether this IndexType is "FUNCTIONAL". + */ + public boolean isFunctional() { + return this.equals(FUNCTIONAL); + } + + /** + * Determines whether this IndexType is a "HASH" Index. + * + * @return a boolean value indicating whether this IndexType is a "HASH" Index. + */ + public boolean isHash() { + return this.equals(HASH); + } + + /** + * Determines whether this IndexType is a "KEY" Index. + * + * @return a boolean value indicating whether this IndexType is a "KEY" Index. + */ + public boolean isKey() { + return (this.equals(KEY) || this.equals(PRIMARY_KEY)); + } + +} diff --git a/src/main/java/org/springframework/data/gemfire/IndexTypeConverter.java b/src/main/java/org/springframework/data/gemfire/IndexTypeConverter.java new file mode 100644 index 00000000..aa7ed872 --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/IndexTypeConverter.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2013 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; + +import java.beans.PropertyEditorSupport; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * The IndexTypeConverter class is a Spring Converter implementation as well as a JavaBeans PropertyEditor + * that converts a given String value into a proper IndexType. + * + * @author John Blum + * @see java.beans.PropertyEditorSupport + * @see org.springframework.core.convert.converter.Converter + * @see org.springframework.data.gemfire.IndexType + * @since 1.5.2 + */ +@SuppressWarnings("unused") +public class IndexTypeConverter extends PropertyEditorSupport implements Converter { + + /** + * Asserts that the given String value was successfully converted into a IndexType. + * + * @param value the String value to convert into an appropriate IndexType. + * @param indexType the converted IndexType. + * @return the IndexType is non-null. + * @throws java.lang.IllegalArgumentException if the IndexType argument was null, indicating that + * the given String value could not be converted into an appropriate IndexType. + * @see org.springframework.data.gemfire.IndexType + */ + private IndexType assertConverted(final String value, final IndexType indexType) { + Assert.notNull(indexType, String.format("Failed to convert String (%1$s) into an IndexType!", value)); + return indexType; + } + + /** + * Converts the given String value into an appropriate IndexType + * + * @param value the String to convert into a corresponding IndexType enumerated value. + * @return an IndexType converted from the given String value. + * @throws java.lang.IllegalArgumentException if the given String could not be converted into + * an appropriate IndexType enumerated value. + * @see #assertConverted(String, IndexType) + * @see org.springframework.data.gemfire.IndexType#valueOfIgnoreCase(String) + * @see org.springframework.util.StringUtils#trimWhitespace(String) + */ + @Override + public IndexType convert(final String value) { + return assertConverted(value, IndexType.valueOfIgnoreCase(StringUtils.trimWhitespace(value))); + } + + /** + * Sets the value of this PropertyEditor as a IndexType enumerated value converted from the given String text. + * + * @param text the String to convert into a corresponding IndexType enumerated value. + * @throws java.lang.IllegalArgumentException if the given String could not be converted into + * an appropriate IndexType enumerated value. + * @see #convert(String) + * @see #setValue(Object) + */ + @Override + public void setAsText(final String text) throws IllegalArgumentException { + setValue(convert(text)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/IndexFactoryBeanTest.java b/src/test/java/org/springframework/data/gemfire/IndexFactoryBeanTest.java new file mode 100644 index 00000000..aca92fb0 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/IndexFactoryBeanTest.java @@ -0,0 +1,589 @@ +/* + * Copyright 2010-2013 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.query.Index; +import com.gemstone.gemfire.cache.query.IndexExistsException; +import com.gemstone.gemfire.cache.query.QueryService; + +/** + * The IndexFactoryBeanTest class is a test suite of test cases testing the contract and functionality + * of the IndexFactoryBean class. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.IndexFactoryBean + * @see com.gemstone.gemfire.cache.Cache + * @see com.gemstone.gemfire.cache.client.ClientCache + * @see com.gemstone.gemfire.cache.query.Index + * @see com.gemstone.gemfire.cache.query.QueryService + * @since 1.5.2 + */ +public class IndexFactoryBeanTest { + + @Test + public void testAfterPropertiesSet() throws Exception { + QueryService mockQueryService = mock(QueryService.class, "testAfterPropertiesSet.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testAfterPropertiesSet.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + final Index mockIndex = mock(Index.class, "testAfterPropertiesSet.MockIndex"); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean() { + @Override Index createIndex(final QueryService queryService, final String indexName) throws Exception { + return mockIndex; + } + }; + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setName("TestIndex"); + indexFactoryBean.setExpression("id"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setType("key"); + indexFactoryBean.afterPropertiesSet(); + + assertEquals(mockIndex, indexFactoryBean.getObject()); + assertEquals(mockIndex, indexFactoryBean.getObject()); // assert Index really is a 'Singleton' + assertTrue(Index.class.isAssignableFrom(indexFactoryBean.getObjectType())); + assertTrue(indexFactoryBean.isSingleton()); + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithNullCache() throws Exception { + try { + new IndexFactoryBean().afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("The GemFire Cache reference must not be null!", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithNullQueryService() throws Exception { + try { + Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWithNullQueryService.MockCache"); + + when(mockCache.getQueryService()).thenReturn(null); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("A QueryService is required for Index creation!", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithUnspecifiedExpression() throws Exception { + try { + QueryService mockQueryService = mock(QueryService.class, + "testAfterPropertiesSetWithUnspecifiedExpression.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWithUnspecifiedExpression.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("The Index 'expression' is required!", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithUnspecifiedFromClause() throws Exception { + try { + QueryService mockQueryService = mock(QueryService.class, + "testAfterPropertiesSetWithUnspecifiedFromClause.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWithUnspecifiedFromClause.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setExpression("id"); + indexFactoryBean.afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("The Index 'from' clause (a Region's full-path) is required!", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithUnspecifiedIndexName() throws Exception { + try { + QueryService mockQueryService = mock(QueryService.class, + "testAfterPropertiesSetWithUnspecifiedIndexName.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWithUnspecifiedIndexName.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setExpression("id"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setImports("org.example.DomainType"); + indexFactoryBean.setType("hash"); + indexFactoryBean.afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("The Index bean id or name is required!", expected.getMessage()); + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void testAfterPropertiesSetWithInvalidTypeUsingImports() throws Exception { + try { + QueryService mockQueryService = mock(QueryService.class, + "testAfterPropertiesSetWithInvalidTypeUsingImports.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testAfterPropertiesSetWithInvalidTypeUsingImports.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setExpression("id"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setImports("org.example.DomainType"); + indexFactoryBean.setType("PriMary_Key"); + indexFactoryBean.afterPropertiesSet(); + } + catch (IllegalArgumentException expected) { + assertEquals("The 'imports' property is not supported for a Key Index.", expected.getMessage()); + throw expected; + } + } + + @Test + public void testLookupQueryService() { + QueryService mockQueryServiceOne = mock(QueryService.class, "testLookupQueryService.MockQueryService.One"); + QueryService mockQueryServiceTwo = mock(QueryService.class, "testLookupQueryService.MockQueryService.Two"); + + Cache mockCache = mock(Cache.class, "testLookupQueryService.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryServiceTwo); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setQueryService(mockQueryServiceOne); + + assertSame(mockQueryServiceOne, indexFactoryBean.lookupQueryService()); + + verify(mockCache, never()).getQueryService(); + } + + @Test + public void testLookupQueryServiceOnClientCache() { + QueryService mockQueryService = mock(QueryService.class, "testLookupQueryServiceOnClientCache.MockQueryService"); + + ClientCache mockClientCache = mock(ClientCache.class, "testLookupQueryServiceOnClientCache.MockClientCache"); + + when(mockClientCache.getLocalQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockClientCache); + + assertSame(mockQueryService, indexFactoryBean.lookupQueryService()); + + verify(mockClientCache, times(1)).getLocalQueryService(); + } + + @Test + public void testLookupQueryServiceOnPeerCache() { + QueryService mockQueryService = mock(QueryService.class, "testLookupQueryServiceOnPeerCache.MockQueryService"); + + Cache mockCache = mock(Cache.class, "testLookupQueryServiceOnPeerCache.MockCache"); + + when(mockCache.getQueryService()).thenReturn(mockQueryService); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setCache(mockCache); + indexFactoryBean.setQueryService(null); + + assertSame(mockQueryService, indexFactoryBean.lookupQueryService()); + + verify(mockCache, times(1)).getQueryService(); + } + + @Test + public void testCreateIndexReturnsNewKeyIndex() throws Exception { + Index mockIndex = mock(Index.class, "testCreateIndexReturnsNewKeyIndex.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateIndexReturnsNewKeyIndex.MockQueryService"); + + when(mockQueryService.getIndexes()).thenReturn(Collections.emptyList()); + when(mockQueryService.createKeyIndex(eq("KeyIndex"), eq("id"), eq("/Example"))).thenReturn(mockIndex); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("id"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setName("KeyIndex"); + indexFactoryBean.setOverride(false); + indexFactoryBean.setType("key"); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "KeyIndex"); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateIndexReturnsNewHashIndex() throws Exception { + Index mockIndex = mock(Index.class, "testCreateIndexReturnsNewHashIndex.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateIndexReturnsNewHashIndex.MockQueryService"); + + when(mockQueryService.getIndexes()).thenReturn(Collections.emptyList()); + when(mockQueryService.createHashIndex(eq("HashIndex"), eq("name"), eq("/Animals"), eq("org.example.Dog"))) + .thenReturn(mockIndex); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("name"); + indexFactoryBean.setFrom("/Animals"); + indexFactoryBean.setImports("org.example.Dog"); + indexFactoryBean.setName("HashIndex"); + indexFactoryBean.setOverride(false); + indexFactoryBean.setType("HasH"); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "HashIndex"); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateIndexReturnsNewFunctionalIndex() throws Exception { + Index mockIndex = mock(Index.class, "testCreateIndexReturnsNewFunctionalIndex.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, + "testCreateIndexReturnsNewFunctionalIndex.MockQueryService"); + + when(mockQueryService.getIndexes()).thenReturn(Collections.emptyList()); + when(mockQueryService.createIndex(eq("FunctionalIndex"), eq("someField"), eq("/Example"))) + .thenReturn(mockIndex); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("someField"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setImports(" "); + indexFactoryBean.setName("FunctionalIndex"); + indexFactoryBean.setOverride(false); + indexFactoryBean.setType((String) null); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "FunctionalIndex"); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateIndexReturnsExistingIndex() throws Exception { + Index mockExistingIndex = mock(Index.class, "testCreateIndexReturnsExistingIndex.MockIndex.Existing"); + Index mockIndexTwo = mock(Index.class, "testCreateIndexReturnsExistingIndex.MockIndex.Two"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateIndexReturnsExistingIndex.MockQueryService"); + + when(mockExistingIndex.getName()).thenReturn("ExistingIndex"); + when(mockIndexTwo.getName()).thenReturn("IndexTwo"); + when(mockQueryService.getIndexes()).thenReturn(Arrays.asList(mockExistingIndex, mockIndexTwo)); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setName("ExistingIndex"); + indexFactoryBean.setOverride(false); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "ExistingIndex"); + + assertSame(mockExistingIndex, actualIndex); + } + + @Test + public void testCreateIndexReturnsExistingIndexForIndexExistsException() throws Exception { + Index mockExistingIndex = mock(Index.class, + "testCreateIndexReturnsExistingIndexForIndexExistsException.MockIndex.Existing"); + + QueryService mockQueryService = mock(QueryService.class, + "testCreateIndexReturnsExistingIndexForIndexExistsException.MockQueryService"); + + when(mockExistingIndex.getName()).thenReturn("ExistingIndex"); + when(mockQueryService.getIndexes()).thenReturn(Collections.singletonList(mockExistingIndex)); + when(mockQueryService.createKeyIndex(eq("ExistingIndex"), eq("id"), eq("/Example"))) + .thenThrow(new IndexExistsException("test")); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("id"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setName("ExistingIndex"); + indexFactoryBean.setOverride(true); + indexFactoryBean.setType("PRIMARY_KEY"); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "ExistingIndex"); + + assertSame(mockExistingIndex, actualIndex); + + verify(mockExistingIndex, times(2)).getName(); + verify(mockQueryService, times(2)).getIndexes(); + verify(mockQueryService, times(1)).removeIndex(same(mockExistingIndex)); + } + + @Test + public void testCreateIndexOverridesExistingIndex() throws Exception { + Index mockExistingIndex = mock(Index.class, "testCreateIndexOverridesExistingIndex.MockIndex.Existing"); + Index mockOverridingIndex = mock(Index.class, "testCreateIndexOverridesExistingIndex.MockIndex.Overriding"); + + QueryService mockQueryService = mock(QueryService.class, + "testCreateIndexOverridesExistingIndex.MockQueryService"); + + when(mockExistingIndex.getName()).thenReturn("ExistingIndex"); + when(mockOverridingIndex.getName()).thenReturn("OverridingIndex"); + when(mockQueryService.getIndexes()).thenReturn(Collections.singletonList(mockExistingIndex)); + when(mockQueryService.createHashIndex(eq("ExistingIndex"), eq("someField"), eq("/Example"), + eq("org.example.DomainType"))).thenReturn(mockOverridingIndex); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("someField"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setImports("org.example.DomainType"); + indexFactoryBean.setName("OverridingIndex"); + indexFactoryBean.setType("HASH"); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "ExistingIndex"); + + assertSame(mockOverridingIndex, actualIndex); + + verify(mockQueryService, times(1)).getIndexes(); + verify(mockQueryService, times(1)).removeIndex(same(mockExistingIndex)); + } + + @Test + public void testCreateIndexAddsExistingIndexOnException() throws Exception { + final Index mockExistingIndex = mock(Index.class, "testCreateIndexAddsExistingIndexOnException.MockIndex.Existing"); + final Index mockIndexTwo = mock(Index.class, "testCreateIndexAddsExistingIndexOnException.MockIndex.Two"); + + final List indexes = new ArrayList(1); + + indexes.add(mockIndexTwo); + + QueryService mockQueryService = mock(QueryService.class, + "testCreateIndexAddsExistingIndexOnException.MockQueryService"); + + when(mockExistingIndex.getName()).thenReturn("ExistingIndex"); + when(mockIndexTwo.getName()).thenReturn("IndexTwo"); + + when(mockQueryService.getIndexes()).then(new Answer>() { + private boolean called = false; + + private synchronized boolean wasCalledOnce() { + boolean localCalled = this.called; + this.called = true; + return localCalled; + } + + @Override + public Collection answer(final InvocationOnMock invocationOnMock) throws Throwable { + return (wasCalledOnce() ? indexes : Collections.singletonList(mockExistingIndex)); + } + }); + + when(mockQueryService.createIndex(eq("ExistingIndex"), eq("someField"), eq("/Example"))) + .thenThrow(new RuntimeException("test")); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("someField"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setName("ExistingIndex"); + indexFactoryBean.setOverride(true); + indexFactoryBean.setType("FUNCTIONAL"); + + assertEquals(1, indexes.size()); + assertFalse(indexes.contains(mockExistingIndex)); + + Index actualIndex = indexFactoryBean.createIndex(mockQueryService, "ExistingIndex"); + + assertSame(mockExistingIndex, actualIndex); + assertEquals(2, indexes.size()); + assertTrue(indexes.contains(mockExistingIndex)); + + verify(mockQueryService, times(3)).getIndexes(); + verify(mockQueryService, times(1)).removeIndex(same(mockExistingIndex)); + } + + @Test(expected = RuntimeException.class) + public void testCreateIndexThrowsException() throws Exception { + Index mockExistingIndex = mock(Index.class, "testCreateIndexThrowsException.MockIndex.Existing"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateIndexThrowsException.MockQueryService"); + + when(mockExistingIndex.getName()).thenReturn("ExistingIndex"); + when(mockQueryService.getIndexes()).thenReturn(Collections.singletonList(mockExistingIndex)); + when(mockQueryService.createIndex(eq("ExistingIndex"), eq("someField"), eq("/Example"))) + .thenThrow(new RuntimeException("test")); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + indexFactoryBean.setExpression("someField"); + indexFactoryBean.setFrom("/Example"); + indexFactoryBean.setName("ExistingIndex"); + + try { + indexFactoryBean.createIndex(mockQueryService, "ExistingIndex"); + } + catch (RuntimeException expected) { + assertEquals("test", expected.getMessage()); + throw expected; + } + finally { + verify(mockQueryService, times(2)).getIndexes(); + verify(mockQueryService, times(1)).removeIndex(same(mockExistingIndex)); + } + } + + @Test + public void testCreateFunctionalIndex() throws Exception { + Index mockIndex = mock(Index.class, "testCreateFunctionalIndex.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateFunctionalIndex.MockQueryService"); + + when(mockQueryService.createIndex(eq("FunctionalIndex"), eq("purchaseDate"), eq("/Orders"))) + .thenReturn(mockIndex); + + Index actualIndex = new IndexFactoryBean().createFunctionalIndex(mockQueryService, "FunctionalIndex", + "purchaseDate", "/Orders", null); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateFunctionalIndexWithImports() throws Exception { + Index mockIndex = mock(Index.class, "testCreateFunctionalIndexWithImports.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateFunctionalIndexWithImports.MockQueryService"); + + when(mockQueryService.createIndex(eq("FunctionalIndexWithImports"), eq("purchaseDate"), eq("/Orders"), + eq("org.example.Order"))).thenReturn(mockIndex); + + Index actualIndex = new IndexFactoryBean().createFunctionalIndex(mockQueryService, "FunctionalIndexWithImports", + "purchaseDate", "/Orders", "org.example.Order"); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateHashIndex() throws Exception { + Index mockIndex = mock(Index.class, "testCreateHashIndex.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateHashIndex.MockQueryService"); + + when(mockQueryService.createHashIndex(eq("HashIndex"), eq("name"), eq("/People"))).thenReturn(mockIndex); + + Index actualIndex = new IndexFactoryBean().createHashIndex(mockQueryService, "HashIndex", + "name", "/People", " "); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testCreateHashIndexWithImports() throws Exception { + Index mockIndex = mock(Index.class, "testCreateHashIndexWithImports.MockIndex"); + + QueryService mockQueryService = mock(QueryService.class, "testCreateHashIndexWithImports.MockQueryService"); + + when(mockQueryService.createHashIndex(eq("HashIndexWithImports"), eq("name"), eq("/People"), + eq("org.example.Person"))).thenReturn(mockIndex); + + Index actualIndex = new IndexFactoryBean().createHashIndex(mockQueryService, "HashIndexWithImports", + "name", "/People", "org.example.Person"); + + assertSame(mockIndex, actualIndex); + } + + @Test + public void testGetExistingIndex() { + Index mockIndexOne = mock(Index.class, "testGetExistingIndex.MockIndex.One"); + Index mockIndexTwo = mock(Index.class, "testGetExistingIndex.MockIndex.Two"); + Index mockIndexThree = mock(Index.class, "testGetExistingIndex.MockIndex.Two"); + + QueryService mockQueryService = mock(QueryService.class, "testGetExistingIndex.MockQueryService"); + + when(mockIndexOne.getName()).thenReturn("PrimaryIndex"); + when(mockIndexTwo.getName()).thenReturn("SecondaryIndex"); + when(mockIndexThree.getName()).thenReturn("TernaryIndex"); + when(mockQueryService.getIndexes()).thenReturn(Arrays.asList(mockIndexOne, mockIndexTwo, mockIndexThree)); + + IndexFactoryBean indexFactoryBean = new IndexFactoryBean(); + + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, null)); + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, "")); + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, " ")); + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, "Primary Index")); + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, "Secondary_Index")); + assertNull(indexFactoryBean.getExistingIndex(mockQueryService, "QuadIndex")); + assertSame(mockIndexOne, indexFactoryBean.getExistingIndex(mockQueryService, "PRIMARYINDEX")); + assertSame(mockIndexTwo, indexFactoryBean.getExistingIndex(mockQueryService, "SecondaryIndex")); + assertSame(mockIndexThree, indexFactoryBean.getExistingIndex(mockQueryService, "ternaryindex")); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/IndexTypeConverterTest.java b/src/test/java/org/springframework/data/gemfire/IndexTypeConverterTest.java new file mode 100644 index 00000000..d4366e07 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/IndexTypeConverterTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2013 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.After; +import org.junit.Test; + +/** + * The IndexTypeConverterTest class is a test suite of test cases testing the contract and functionality + * of the IndexTypeConverter class. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.IndexType + * @see org.springframework.data.gemfire.IndexTypeConverter + * @since 1.5.2 + */ +public class IndexTypeConverterTest { + + private final IndexTypeConverter converter = new IndexTypeConverter(); + + @After + public void tearDown() { + converter.setValue(null); + } + + @Test + public void testConvert() { + assertEquals(IndexType.FUNCTIONAL, converter.convert("FUNCTIONAL")); + assertEquals(IndexType.HASH, converter.convert("hASh")); + assertEquals(IndexType.KEY, converter.convert("Key")); + assertEquals(IndexType.PRIMARY_KEY, converter.convert("primary_KEY")); + } + + @Test(expected = IllegalArgumentException.class) + public void testConvertWithInvalidValue() { + try { + converter.convert("function"); + } + catch (IllegalArgumentException expected) { + assertEquals("Failed to convert String (function) into an IndexType!", expected.getMessage()); + throw expected; + } + } + + @Test + public void testSetAsText() { + assertNull(converter.getValue()); + converter.setAsText("HasH"); + assertEquals(IndexType.HASH, converter.getValue()); + converter.setAsText("key"); + assertEquals(IndexType.KEY, converter.getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testSetAsTextWithIllegalValue() { + try { + converter.setAsText("invalid"); + } + catch (IllegalArgumentException expected) { + assertEquals("Failed to convert String (invalid) into an IndexType!", expected.getMessage()); + throw expected; + } + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/IndexTypeTest.java b/src/test/java/org/springframework/data/gemfire/IndexTypeTest.java new file mode 100644 index 00000000..c61c1287 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/IndexTypeTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2013 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * The IndexTypeTest class is a test suite of test cases testing the contract and functionality + * of the IndexType enum class. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.IndexType + * @since 1.5.2 + */ +@SuppressWarnings("deprecation") +public class IndexTypeTest { + + @Test + public void testGetGemFireIndexType() { + assertEquals(com.gemstone.gemfire.cache.query.IndexType.FUNCTIONAL, IndexType.FUNCTIONAL.getGemfireIndexType()); + assertEquals(com.gemstone.gemfire.cache.query.IndexType.HASH, IndexType.HASH.getGemfireIndexType()); + assertEquals(com.gemstone.gemfire.cache.query.IndexType.PRIMARY_KEY, IndexType.KEY.getGemfireIndexType()); + assertEquals(com.gemstone.gemfire.cache.query.IndexType.PRIMARY_KEY, IndexType.PRIMARY_KEY.getGemfireIndexType()); + } + + @Test + public void testValueOfGemFireIndexType() { + assertNull(IndexType.valueOf((com.gemstone.gemfire.cache.query.IndexType) null)); + assertEquals(IndexType.FUNCTIONAL, IndexType.valueOf(com.gemstone.gemfire.cache.query.IndexType.FUNCTIONAL)); + assertEquals(IndexType.HASH, IndexType.valueOf(com.gemstone.gemfire.cache.query.IndexType.HASH)); + assertEquals(IndexType.PRIMARY_KEY, IndexType.valueOf(com.gemstone.gemfire.cache.query.IndexType.PRIMARY_KEY)); + } + + @Test + public void testValueOfIgnoreCase() { + assertEquals(IndexType.FUNCTIONAL, IndexType.valueOfIgnoreCase("functional")); + assertEquals(IndexType.HASH, IndexType.valueOfIgnoreCase("HasH")); + assertEquals(IndexType.KEY, IndexType.valueOfIgnoreCase("Key")); + assertEquals(IndexType.PRIMARY_KEY, IndexType.valueOfIgnoreCase("PriMary_Key")); + } + + @Test + public void testValueOfIgnoreCaseWithInvalidValues() { + assertNull(IndexType.valueOfIgnoreCase(null)); + assertNull(IndexType.valueOfIgnoreCase("")); + assertNull(IndexType.valueOfIgnoreCase(" ")); + assertNull(IndexType.valueOfIgnoreCase("Prime_Index")); + assertNull(IndexType.valueOfIgnoreCase("SECONDARY_INDEX")); + assertNull(IndexType.valueOfIgnoreCase("unique_index")); + } + + @Test + public void testIsFunctional() { + assertTrue(IndexType.FUNCTIONAL.isFunctional()); + assertFalse(IndexType.HASH.isFunctional()); + assertFalse(IndexType.KEY.isFunctional()); + assertFalse(IndexType.PRIMARY_KEY.isFunctional()); + } + + @Test + public void testIsNullSafeFunctional() { + assertFalse(IndexType.isFunctional(null)); + assertTrue(IndexType.isFunctional(IndexType.FUNCTIONAL)); + assertFalse(IndexType.isFunctional(IndexType.HASH)); + } + + @Test + public void testIsHash() { + assertFalse(IndexType.FUNCTIONAL.isHash()); + assertTrue(IndexType.HASH.isHash()); + assertFalse(IndexType.KEY.isHash()); + assertFalse(IndexType.PRIMARY_KEY.isHash()); + } + + @Test + public void testIsNullSafeHash() { + assertFalse(IndexType.isHash(null)); + assertTrue(IndexType.isHash(IndexType.HASH)); + assertFalse(IndexType.isHash(IndexType.KEY)); + } + + @Test + public void testIsKey() { + assertFalse(IndexType.FUNCTIONAL.isKey()); + assertFalse(IndexType.HASH.isKey()); + assertTrue(IndexType.KEY.isKey()); + assertTrue(IndexType.PRIMARY_KEY.isKey()); + } + + @Test + public void testIsNullSafeKey() { + assertFalse(IndexType.isKey(null)); + assertFalse(IndexType.isKey(IndexType.FUNCTIONAL)); + assertTrue(IndexType.isKey(IndexType.KEY)); + assertTrue(IndexType.isKey(IndexType.PRIMARY_KEY)); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/client/ClientCacheIndexingTest.java b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIndexingTest.java new file mode 100644 index 00000000..62d40869 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/client/ClientCacheIndexingTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2010-2013 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.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.gemfire.fork.ServerProcess; +import org.springframework.data.gemfire.process.ProcessExecutor; +import org.springframework.data.gemfire.process.ProcessWrapper; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.data.gemfire.test.support.ThreadUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; + +import com.gemstone.gemfire.cache.GemFireCache; +import com.gemstone.gemfire.cache.client.ClientCache; +import com.gemstone.gemfire.cache.query.Index; +import com.gemstone.gemfire.cache.query.IndexType; +import com.gemstone.gemfire.cache.query.QueryService; + +/** + * The ClientCacheIndexingTest class is a test suite of test cases testing the creation and application of indexes + * on client Regions of a GemFire ClientCache using the <gfe:index/> tag element in the Spring Data GemFire + * XML namespace and configuration meta-data, which is backed by the IndexFactoryBean. + * + * @author John Blum + * @see org.junit.Test + * @see org.junit.runner.RunWith + * @see org.springframework.data.gemfire.IndexFactoryBean + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * @see com.gemstone.gemfire.cache.GemFireCache + * @see com.gemstone.gemfire.cache.client.ClientCache + * @see com.gemstone.gemfire.cache.query.Index + * @see com.gemstone.gemfire.cache.query.QueryService + * @since 1.5.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@SuppressWarnings("unused") +public class ClientCacheIndexingTest { + + private static ProcessWrapper serverProcess; + + @Autowired + private ClientCache clientCache; + + @Autowired + private Index exampleIndex; + + @BeforeClass + public static void setup() throws IOException { + String serverName = "GemFireIndexingCacheServer"; + + File serverWorkingDirectory = new File(FileSystemUtils.WORKING_DIRECTORY, serverName.toLowerCase()); + + Assert.isTrue(serverWorkingDirectory.isDirectory() || serverWorkingDirectory.mkdirs()); + + List arguments = new ArrayList(); + + arguments.add("-Dgemfire.name=" + serverName); + arguments.add("/org/springframework/data/gemfire/client/ClientCacheIndexingTest-server-context.xml"); + + serverProcess = ProcessExecutor.launch(serverWorkingDirectory, ServerProcess.class, + arguments.toArray(new String[arguments.size()])); + + waitForServerStart(TimeUnit.SECONDS.toMillis(20)); + + System.out.println("GemFire Cache Server Process for ClientCache Indexing should be running..."); + } + + private static void waitForServerStart(final long milliseconds) { + ThreadUtils.timedWait(milliseconds, TimeUnit.MILLISECONDS.toMillis(500), new ThreadUtils.WaitCondition() { + private File serverPidControlFile = new File(serverProcess.getWorkingDirectory(), + ServerProcess.getServerProcessControlFilename()); + + @Override public boolean waiting() { + return !serverPidControlFile.isFile(); + } + }); + } + + @AfterClass + public static void tearDown() { + serverProcess.shutdown(); + + if (Boolean.valueOf(System.getProperty("spring.gemfire.fork.clean", Boolean.TRUE.toString()))) { + org.springframework.util.FileSystemUtils.deleteRecursively(serverProcess.getWorkingDirectory()); + } + } + + protected Index getIndex(final GemFireCache gemfireCache, final String indexName) { + QueryService queryService = (gemfireCache instanceof ClientCache + ? ((ClientCache) gemfireCache).getLocalQueryService() : gemfireCache.getQueryService()); + + for (Index index : queryService.getIndexes()) { + if (index.getName().equals(indexName)) { + return index; + } + } + + return null; + } + + @Test + @SuppressWarnings("deprecation") + public void testIndexByName() { + assertNotNull("The GemFire ClientCache was not properly configured and initialized!", clientCache); + + Index actualIndex = getIndex(clientCache, "ExampleIndex"); + + assertNotNull(actualIndex); + assertEquals("ExampleIndex", actualIndex.getName()); + assertEquals(IndexType.HASH, actualIndex.getType()); + assertSame(exampleIndex, actualIndex); + } + +} diff --git a/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java b/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java new file mode 100644 index 00000000..47881c88 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/fork/ServerProcess.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2013 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.fork; + +import java.io.File; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.data.gemfire.process.support.ProcessUtils; +import org.springframework.data.gemfire.test.support.FileSystemUtils; +import org.springframework.util.Assert; + +/** + * The ServerProcess class is a main Java class using Spring Data GemFire to configure and bootstrap + * a GemFire Cache Server process. + * + * @author John Blum + * @see org.springframework.context.ConfigurableApplicationContext + * @see org.springframework.context.support.ClassPathXmlApplicationContext + * @since 1.5.2 + */ +public class ServerProcess { + + public static void main(final String[] args) throws Throwable { + ConfigurableApplicationContext applicationContext = null; + + try { + Assert.notEmpty(args, String.format("Usage: >java -cp ... %1$s %2$s", ServerProcess.class.getName(), + "classpath:/to/applicationContext.xml")); + + applicationContext = new ClassPathXmlApplicationContext(args[0]); + applicationContext.registerShutdownHook(); + + ProcessUtils.writePid(new File(FileSystemUtils.WORKING_DIRECTORY, getServerProcessControlFilename()), + ProcessUtils.currentPid()); + + ProcessUtils.waitForStopSignal(); + } + catch (Throwable e) { + e.printStackTrace(System.err); + throw e; + } + finally { + if (applicationContext != null) { + applicationContext.close(); + } + } + } + + public static String getServerProcessControlFilename() { + return ServerProcess.class.getSimpleName().toLowerCase().concat(".pid"); + } + +} diff --git a/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-context.xml b/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-context.xml new file mode 100644 index 00000000..1ff7f8e1 --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-server-context.xml b/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-server-context.xml new file mode 100644 index 00000000..af7d97cd --- /dev/null +++ b/src/test/resources/org/springframework/data/gemfire/client/ClientCacheIndexingTest-server-context.xml @@ -0,0 +1,23 @@ + + + + + ClientCacheIndexingTestServer + 0 + warning + + + + + + +