DATAGEODE-101 - Add support for setting a LuceneSerializer on LuceneIndex creation.

This commit is contained in:
John Blum
2018-04-27 14:08:58 -07:00
parent 251f1f12e0
commit d16bbc8d28
6 changed files with 148 additions and 19 deletions

View File

@@ -53,22 +53,27 @@ class LuceneIndexParser extends AbstractSingleBeanDefinitionParser {
*/
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
ParsingUtils.setCacheReference(element, builder);
ParsingUtils.setPropertyValue(element, builder, "name", "indexName");
ParsingUtils.setPropertyValue(element, builder, "destroy");
ParsingUtils.setPropertyReference(element, builder, "lucene-service-ref", "luceneService");
ParsingUtils.setPropertyValue(element, builder, "name", "indexName");
ParsingUtils.setPropertyReference(element, builder, "region-ref", "region");
ParsingUtils.setPropertyValue(element, builder, "region-path");
Optional.ofNullable(element.getAttribute("fields")).filter(StringUtils::hasText).ifPresent((fields) -> {
builder.addPropertyValue("fields", Arrays.stream(fields.split(","))
.map(String::trim).collect(Collectors.toList()));
});
Optional.ofNullable(element.getAttribute("fields"))
.filter(StringUtils::hasText)
.ifPresent(fields -> builder.addPropertyValue("fields",
Arrays.stream(fields.split(",")).map(String::trim).collect(Collectors.toList())));
Optional.ofNullable(DomUtils.getChildElementByTagName(element, "field-analyzers"))
.ifPresent((fieldAnalyzersElement) -> builder.addPropertyValue("fieldAnalyzers",
.ifPresent(fieldAnalyzersElement -> builder.addPropertyValue("fieldAnalyzers",
ParsingUtils.parseRefOrSingleNestedBeanDeclaration(fieldAnalyzersElement, parserContext, builder)));
Optional.ofNullable(DomUtils.getChildElementByTagName(element, "lucene-serializer"))
.ifPresent(luceneSerializerElement -> builder.addPropertyValue("luceneSerializer",
ParsingUtils.parseRefOrSingleNestedBeanDeclaration(luceneSerializerElement, parserContext, builder)));
}
}

View File

@@ -36,6 +36,7 @@ import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.lucene.LuceneIndexFactory;
import org.apache.geode.cache.lucene.LuceneSerializer;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.geode.cache.lucene.LuceneServiceProvider;
import org.apache.lucene.analysis.Analyzer;
@@ -84,7 +85,8 @@ public class LuceneIndexFactoryBean extends AbstractFactoryBeanSupport<LuceneInd
@Override
public void configure(String beanName, LuceneIndexFactoryBean bean) {
nullSafeCollection(indexConfigurers).forEach(indexConfigurer -> indexConfigurer.configure(beanName, bean));
nullSafeCollection(indexConfigurers)
.forEach(indexConfigurer -> indexConfigurer.configure(beanName, bean));
}
};
@@ -92,6 +94,8 @@ public class LuceneIndexFactoryBean extends AbstractFactoryBeanSupport<LuceneInd
private LuceneIndex luceneIndex;
private LuceneSerializer luceneSerializer;
private LuceneService luceneService;
private Map<String, Analyzer> fieldAnalyzers;
@@ -190,6 +194,8 @@ public class LuceneIndexFactoryBean extends AbstractFactoryBeanSupport<LuceneInd
indexFactory.setFields(fieldAnalyzers);
}
Optional.ofNullable(getLuceneSerializer()).ifPresent(indexFactory::setLuceneSerializer);
indexFactory = postProcess(indexFactory);
indexFactory.create(indexName, regionPath);
@@ -607,7 +613,32 @@ public class LuceneIndexFactoryBean extends AbstractFactoryBeanSupport<LuceneInd
}
/**
* Sets a reference to the {@link LuceneService} used by this {@link FactoryBean} to create the {@link LuceneIndex}.
* Configures a reference to the {@link LuceneSerializer} used to convert {@link Object objects}
* to Lucene documents for the {@link LuceneIndex} created by this {@link LuceneIndexFactoryBean}.
*
* @param luceneSerializer {@link LuceneSerializer} used to convert {@link Object objects}
* to Lucene documents for the {@link LuceneIndex}.
* @see org.apache.geode.cache.lucene.LuceneSerializer
*/
public void setLuceneSerializer(LuceneSerializer luceneSerializer) {
this.luceneSerializer = luceneSerializer;
}
/**
* Returns a reference to the {@link LuceneSerializer} used to convert {@link Object objects}
* to Lucene documents for the {@link LuceneIndex} created by this {@link LuceneIndexFactoryBean}.
*
* @return a {@link LuceneSerializer} used to convert {@link Object objects}
* to Lucene documents for the {@link LuceneIndex}.
* @see org.apache.geode.cache.lucene.LuceneSerializer
*/
protected LuceneSerializer getLuceneSerializer() {
return this.luceneSerializer;
}
/**
* Configures a reference to the {@link LuceneService} used by this {@link FactoryBean}
* to create the {@link LuceneIndex}.
*
* @param luceneService {@link LuceneService} used to create the {@link LuceneIndex}.
* @see org.apache.geode.cache.lucene.LuceneService

View File

@@ -2433,10 +2433,19 @@ Defines a GemFire Lucene index.
<xsd:element name="field-analyzers" type="beanDeclarationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Mapping of field names to Lucene (per field) Analyzers.
Mapping of field names to Lucene (per field) org.apache.lucene.analysis.Analyzers.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="lucene-serializer" type="beanDeclarationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Registers a bean as a LuceneSerializer with this Lucene Index bean definition, which will be used to convert objects
into Lucene documents for this index. The bean must implement org.apache.geode.cache.lucene.LuceneSerializer
and may be nested or referred to as a reference.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>

View File

@@ -34,10 +34,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.lucene.LuceneIndexFactory;
import org.apache.geode.cache.lucene.LuceneSerializer;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.lucene.analysis.Analyzer;
import org.junit.Test;
@@ -50,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@@ -93,12 +96,17 @@ public class LuceneNamespaceUnitTests {
@Qualifier("IndexFour")
private LuceneIndex luceneIndexFour;
@Autowired
private LuceneSerializer luceneSerializer;
private static String[] asArray(List<String> list) {
return list.toArray(new String[list.size()]);
}
private static String[] toStringArray(Object[] array) {
String[] stringArray = new String[array.length];
int index = 0;
for (Object element : array) {
@@ -108,13 +116,14 @@ public class LuceneNamespaceUnitTests {
return stringArray;
}
protected void assertLuceneIndex(LuceneIndex index, String name, String regionPath) {
private void assertLuceneIndex(LuceneIndex index, String name, String regionPath) {
assertThat(index).isNotNull();
assertThat(index.getName()).isEqualTo(name);
assertThat(index.getRegionPath()).isEqualTo(regionPath);
}
protected void assertLuceneIndexWithFieldAnalyzers(LuceneIndex index, String name, String regionPath,
private void assertLuceneIndexWithFieldAnalyzers(LuceneIndex index, String name, String regionPath,
String... keys) {
assertLuceneIndex(index, name, regionPath);
@@ -123,7 +132,8 @@ public class LuceneNamespaceUnitTests {
assertThat(index.getFieldNames()).isEmpty();
}
protected void assertLuceneIndexWithFields(LuceneIndex index, String name, String regionPath, String... fieldNames) {
private void assertLuceneIndexWithFields(LuceneIndex index, String name, String regionPath, String... fieldNames) {
assertLuceneIndex(index, name, regionPath);
assertThat(index.getFieldAnalyzers()).isEmpty();
assertThat(index.getFieldNames()).contains(fieldNames);
@@ -131,6 +141,7 @@ public class LuceneNamespaceUnitTests {
@Test
public void luceneServiceConfigurationAndInteractionsAreCorrect() {
assertThat(this.luceneService).isNotNull();
verify(this.luceneService, times(4)).createIndexFactory();
verify(this.luceneService, never()).destroyIndex(anyString(), anyString());
@@ -138,26 +149,38 @@ public class LuceneNamespaceUnitTests {
@Test
public void luceneIndexOneIsConfiguredCorrectly() {
assertLuceneIndexWithFields(this.luceneIndexOne, "IndexOne", "/Example",
"fieldOne", "fieldTwo");
assertThat(this.luceneIndexOne.getLuceneSerializer()).isNull();
}
@Test
public void luceneIndexTwoIsConfiguredCorrectly() {
assertLuceneIndexWithFieldAnalyzers(this.luceneIndexTwo, "IndexTwo", "/AnotherExample",
"fieldOne", "fieldTwo");
assertThat(this.luceneIndexTwo.getLuceneSerializer()).isInstanceOf(LuceneSerializer.class);
}
@Test
public void luceneIndexThreeIsConfiguredCorrectly() {
assertLuceneIndexWithFields(this.luceneIndexThree, "IndexThree", "/Example",
"singleField");
assertThat(this.luceneIndexThree.getLuceneSerializer()).isNull();
}
@Test
public void luceneIndexFourIsConfiguredCorrectly() {
assertLuceneIndexWithFieldAnalyzers(this.luceneIndexFour, "IndexFour", "/YetAnotherExample",
"singleField");
assertThat(this.luceneIndexFour.getLuceneSerializer()).isEqualTo(luceneSerializer);
}
public static class LuceneNamespaceUnitTestsBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@@ -183,10 +206,13 @@ public class LuceneNamespaceUnitTests {
@Override
@SuppressWarnings("unchecked")
public LuceneService getObject() throws Exception {
return Optional.ofNullable(this.luceneService).orElseGet(() -> {
this.luceneService = mock(LuceneService.class);
when(this.luceneService.createIndexFactory()).thenAnswer(invocation -> {
LuceneIndexFactory mockLuceneIndexFactory = mock(LuceneIndexFactory.class);
List<String> fieldNames = new ArrayList<>();
@@ -203,8 +229,15 @@ public class LuceneNamespaceUnitTests {
return mockLuceneIndexFactory;
});
Answer<LuceneIndex> mockLuceneIndex =
mockLuceneIndex(this.luceneService, fieldAnalyzers, fieldNames);
AtomicReference<LuceneSerializer> luceneSerializer = new AtomicReference<>(null);
when(mockLuceneIndexFactory.setLuceneSerializer(any())).thenAnswer(setLuceneSerializerInvocation -> {
luceneSerializer.set(setLuceneSerializerInvocation.getArgument(0));
return mockLuceneIndexFactory;
});
Answer mockLuceneIndex =
mockLuceneIndex(this.luceneService, fieldAnalyzers, fieldNames, luceneSerializer);
doAnswer(mockLuceneIndex).when(mockLuceneIndexFactory).create(anyString(), anyString());
@@ -217,18 +250,21 @@ public class LuceneNamespaceUnitTests {
@SuppressWarnings("unchecked")
private Answer<LuceneIndex> mockLuceneIndex(LuceneService mockLuceneService,
Map<String, Analyzer> fieldAnalyzers, List<String> fieldNames) {
Map<String, Analyzer> fieldAnalyzers, List<String> fieldNames,
AtomicReference<LuceneSerializer> luceneSerializer) {
return invocation -> {
String indexName = invocation.getArgument(0);
String regionPath = invocation.getArgument(1);
LuceneIndex mockLuceneIndex = mock(LuceneIndex.class, indexName);
when(mockLuceneIndex.getName()).thenReturn(indexName);
when(mockLuceneIndex.getRegionPath()).thenReturn(regionPath);
when(mockLuceneIndex.getFieldAnalyzers()).thenReturn(fieldAnalyzers);
when(mockLuceneIndex.getFieldNames()).thenReturn(asArray(fieldNames));
when(mockLuceneIndex.getLuceneSerializer()).thenAnswer(it -> luceneSerializer.get());
when(mockLuceneIndex.getName()).thenReturn(indexName);
when(mockLuceneIndex.getRegionPath()).thenReturn(regionPath);
when(mockLuceneService.getIndex(eq(indexName), eq(regionPath))).thenReturn(mockLuceneIndex);
return mockLuceneIndex;
@@ -237,7 +273,9 @@ public class LuceneNamespaceUnitTests {
@Override
public Class<?> getObjectType() {
return Optional.ofNullable(this.luceneService).<Class<?>>map(LuceneService::getClass)
return Optional.ofNullable(this.luceneService)
.<Class<?>>map(LuceneService::getClass)
.orElse(LuceneService.class);
}
@@ -271,4 +309,24 @@ public class LuceneNamespaceUnitTests {
return this.name;
}
}
public static class MockLuceneSerializerFactoryBean implements FactoryBean<LuceneSerializer> {
private LuceneSerializer luceneSerializer;
@Nullable @Override
public LuceneSerializer getObject() throws Exception {
return Optional.ofNullable(this.luceneSerializer)
.orElseGet(() -> this.luceneSerializer = mock(LuceneSerializer.class));
}
@Nullable @Override
public Class<?> getObjectType() {
return Optional.ofNullable(this.luceneSerializer)
.<Class<?>>map(LuceneSerializer::getClass)
.orElse(LuceneSerializer.class);
}
}
}

View File

@@ -38,6 +38,7 @@ import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.lucene.LuceneIndexFactory;
import org.apache.geode.cache.lucene.LuceneSerializer;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.lucene.analysis.Analyzer;
import org.junit.Before;
@@ -81,6 +82,9 @@ public class LuceneIndexFactoryBeanUnitTests {
@Mock
private LuceneIndexFactory mockLuceneIndexFactory;
@Mock
private LuceneSerializer mockLuceneSerializer;
@Mock
private LuceneService mockLuceneService;
@@ -155,6 +159,7 @@ public class LuceneIndexFactoryBeanUnitTests {
verify(mockLuceneService, times(1)).createIndexFactory();
verify(mockLuceneIndexFactory, times(1))
.setFields(eq(LuceneService.REGION_VALUE_FIELD));
verify(mockLuceneIndexFactory, never()).setLuceneSerializer(any(LuceneSerializer.class));
verify(mockLuceneIndexFactory, times(1))
.create(eq("ExampleIndex"), eq("/Example"));
verify(mockLuceneService, times(1))
@@ -167,6 +172,7 @@ public class LuceneIndexFactoryBeanUnitTests {
Map<String, Analyzer> fieldAnalyzers = Collections.singletonMap("fieldOne", mockAnalyzer);
factoryBean.setFieldAnalyzers(fieldAnalyzers);
factoryBean.setLuceneSerializer(mockLuceneSerializer);
factoryBean.setLuceneService(mockLuceneService);
when(mockLuceneService.getIndex(eq("ExampleIndex"), eq("/Example"))).thenReturn(mockLuceneIndex);
@@ -174,11 +180,13 @@ public class LuceneIndexFactoryBeanUnitTests {
assertThat(factoryBean.getFieldAnalyzers()).isEqualTo(fieldAnalyzers);
assertThat(factoryBean.getFields()).isEmpty();
assertThat(factoryBean.getLuceneService()).isSameAs(mockLuceneService);
assertThat(factoryBean.createLuceneIndex("ExampleIndex", "/Example")).isEqualTo(mockLuceneIndex);
assertThat(factoryBean.createLuceneIndex("ExampleIndex", "/Example"))
.isEqualTo(mockLuceneIndex);
verify(factoryBean, times(1)).postProcess(eq(mockLuceneIndexFactory));
verify(mockLuceneService, times(1)).createIndexFactory();
verify(mockLuceneIndexFactory, times(1)).setFields(eq(fieldAnalyzers));
verify(mockLuceneIndexFactory, times(1)).setLuceneSerializer(eq(mockLuceneSerializer));
verify(mockLuceneIndexFactory, times(1))
.create(eq("ExampleIndex"), eq("/Example"));
verify(mockLuceneService, times(1))
@@ -189,6 +197,7 @@ public class LuceneIndexFactoryBeanUnitTests {
public void createLuceneIndexWithTargetedFields() {
factoryBean.setFields("fieldOne", "fieldTwo");
factoryBean.setLuceneSerializer(mockLuceneSerializer);
factoryBean.setLuceneService(mockLuceneService);
when(mockLuceneService.getIndex(eq("ExampleIndex"), eq("/Example"))).thenReturn(mockLuceneIndex);
@@ -202,6 +211,7 @@ public class LuceneIndexFactoryBeanUnitTests {
verify(mockLuceneService, times(1)).createIndexFactory();
verify(mockLuceneIndexFactory, times(1))
.setFields(eq("fieldOne"), eq("fieldTwo"));
verify(mockLuceneIndexFactory, times(1)).setLuceneSerializer(eq(mockLuceneSerializer));
verify(mockLuceneIndexFactory, times(1))
.create(eq("ExampleIndex"), eq("/Example"));
verify(mockLuceneService, times(1))
@@ -667,6 +677,16 @@ public class LuceneIndexFactoryBeanUnitTests {
factoryBean.setIndexName(null);
}
@Test
public void setAndGetLuceneSerializer() {
assertThat(factoryBean.getLuceneSerializer()).isNull();
factoryBean.setLuceneSerializer(mockLuceneSerializer);
assertThat(factoryBean.getLuceneSerializer()).isEqualTo(mockLuceneSerializer);
}
@Test(expected = IllegalStateException.class)
public void getUninitializedIndexName() {

View File

@@ -41,6 +41,9 @@
</entry>
</map>
</gfe:field-analyzers>
<gfe:lucene-serializer>
<bean class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$MockLuceneSerializerFactoryBean"/>
</gfe:lucene-serializer>
</gfe:lucene-index>
<gfe:lucene-index id="IndexThree" fields="singleField" lucene-service-ref="luceneService" region-ref="Example"/>
@@ -52,8 +55,11 @@
</entry>
</util:map>
<bean id="MockLuceneSerializer" class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$MockLuceneSerializerFactoryBean"/>
<gfe:lucene-index id="IndexFour" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
<gfe:field-analyzers ref="indexFourFieldAnalyzers"/>
<gfe:lucene-serializer ref="MockLuceneSerializer"/>
</gfe:lucene-index>
</beans>