SGF-402 - Add SDG XML Namespace support for Lucene Integration.

(cherry picked from commit 4a05172a270366756f8e47e7bd9269db24d1b268)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2017-03-08 15:20:26 -08:00
parent d914820272
commit 3e9ca665d6
18 changed files with 4214 additions and 145 deletions

View File

@@ -31,7 +31,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedArray;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -111,8 +110,8 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
}
ParsingUtils.setPropertyValue(element, regionBuilder, "name");
ParsingUtils.setPropertyValue(element, regionBuilder, "ignore-if-exists", "lookupEnabled");
ParsingUtils.setPropertyValue(element, regionBuilder, "data-policy");
ParsingUtils.setPropertyValue(element, regionBuilder, "ignore-if-exists", "lookupEnabled");
ParsingUtils.setPropertyValue(element, regionBuilder, "persistent");
ParsingUtils.setPropertyValue(element, regionBuilder, "shortcut");
@@ -121,37 +120,13 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
regionBuilder.addDependsOn(element.getAttribute("disk-store-ref"));
}
ParsingUtils.parseOptionalRegionAttributes(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseSubscription(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseOptionalRegionAttributes(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseSubscription(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseStatistics(element, regionAttributesBuilder);
ParsingUtils.parseMembershipAttributes(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseExpiration(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseEviction(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseCompressor(parserContext, element, regionAttributesBuilder);
String enableGateway = element.getAttribute("enable-gateway");
String hubId = element.getAttribute("hub-id");
// Factory will enable gateway if it is not set and hub-id is set.
if (StringUtils.hasText(enableGateway)) {
if (GemfireUtils.isGemfireVersion7OrAbove()) {
log.warn("'enable-gateway' has been deprecated since Gemfire 7.0");
}
}
ParsingUtils.setPropertyValue(element, regionBuilder, "enable-gateway");
if (StringUtils.hasText(hubId)) {
if (GemfireUtils.isGemfireVersion7OrAbove()) {
log.warn("'hub-id' has been deprecated since Gemfire 7.0");
}
if (!CollectionUtils.isEmpty(DomUtils.getChildElementsByTagName(element, "gateway-sender"))) {
parserContext.getReaderContext().error("specifying both 'hub-id' and 'gateway-sender' is invalid",
element);
}
}
ParsingUtils.setPropertyValue(element, regionBuilder, "hub-id");
ParsingUtils.parseMembershipAttributes(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseExpiration(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseEviction(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseCompressor(element, parserContext, regionAttributesBuilder);
parseCollectionOfCustomSubElements(element, parserContext, regionBuilder, AsyncEventQueue.class.getName(),
"async-event-queue", "asyncEventQueues");
@@ -164,15 +139,15 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
for (Element subElement : subElements) {
if (subElement.getLocalName().equals("cache-listener")) {
regionBuilder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
else if (subElement.getLocalName().equals("cache-loader")) {
regionBuilder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
else if (subElement.getLocalName().equals("cache-writer")) {
regionBuilder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
}
@@ -233,7 +208,7 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
ManagedArray array = new ManagedArray(className, subElements.size());
for (Element subElement : subElements) {
array.add(ParsingUtils.parseRefOrNestedCustomElement(parserContext, subElement, builder));
array.add(ParsingUtils.parseRefOrNestedCustomElement(subElement, parserContext, builder));
}
builder.addPropertyValue(propertyName, array);

View File

@@ -96,8 +96,9 @@ class AsyncEventQueueParser extends AbstractSingleBeanDefinitionParser {
private void parseAsyncEventListener(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
Element asyncEventListenerElement = DomUtils.getChildElementByTagName(element, "async-event-listener");
Object asyncEventListener = ParsingUtils.parseRefOrSingleNestedBeanDeclaration(parserContext,
asyncEventListenerElement, builder);
Object asyncEventListener = ParsingUtils.parseRefOrSingleNestedBeanDeclaration(asyncEventListenerElement,
parserContext,
builder);
builder.addPropertyValue("asyncEventListener", asyncEventListener);

View File

@@ -93,7 +93,7 @@ class CacheParser extends AbstractSingleBeanDefinitionParser {
for (Element txListener : txListeners) {
transactionListeners.add(ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, txListener, builder));
txListener, parserContext, builder));
}
builder.addPropertyValue("transactionListeners", transactionListeners);
@@ -103,7 +103,7 @@ class CacheParser extends AbstractSingleBeanDefinitionParser {
if (txWriter != null) {
builder.addPropertyValue("transactionWriter", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, txWriter, builder));
txWriter, parserContext, builder));
}
Element gatewayConflictResolver = DomUtils.getChildElementByTagName(element, "gateway-conflict-resolver");
@@ -111,7 +111,7 @@ class CacheParser extends AbstractSingleBeanDefinitionParser {
if (gatewayConflictResolver != null) {
ParsingUtils.throwExceptionIfNotGemfireV7(element.getLocalName(), "gateway-conflict-resolver", parserContext);
builder.addPropertyValue("gatewayConflictResolver", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, gatewayConflictResolver, builder));
gatewayConflictResolver, parserContext, builder));
}
parseDynamicRegionFactory(element, builder);

View File

@@ -84,11 +84,11 @@ class ClientRegionParser extends AbstractRegionParser {
mergeRegionTemplateAttributes(element, parserContext, regionBuilder, regionAttributesBuilder);
ParsingUtils.parseOptionalRegionAttributes(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseOptionalRegionAttributes(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseStatistics(element, regionAttributesBuilder);
ParsingUtils.parseExpiration(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseEviction(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseCompressor(parserContext, element, regionAttributesBuilder);
ParsingUtils.parseExpiration(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseEviction(element, parserContext, regionAttributesBuilder);
ParsingUtils.parseCompressor(element, parserContext, regionAttributesBuilder);
regionBuilder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
@@ -101,15 +101,15 @@ class ClientRegionParser extends AbstractRegionParser {
if ("cache-listener".equals(subElementLocalName)) {
regionBuilder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
else if ("cache-loader".equals(subElementLocalName)) {
regionBuilder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
else if ("cache-writer".equals(subElementLocalName)) {
regionBuilder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, subElement, regionBuilder));
subElement, parserContext, regionBuilder));
}
else if ("key-interest".equals(subElementLocalName)) {
interests.add(parseKeyInterest(subElement, parserContext));
@@ -149,8 +149,9 @@ class ClientRegionParser extends AbstractRegionParser {
private Object parseKeyInterest(Element keyInterestElement, ParserContext parserContext) {
BeanDefinitionBuilder keyInterestBuilder = BeanDefinitionBuilder.genericBeanDefinition(KeyInterest.class);
keyInterestBuilder.addConstructorArgValue(ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext,
keyInterestElement, keyInterestBuilder, "key-ref"));
keyInterestBuilder.addConstructorArgValue(ParsingUtils.parseRefOrNestedBeanDeclaration(keyInterestElement,
parserContext,
keyInterestBuilder, "key-ref"));
parseCommonInterestAttributes(keyInterestElement, keyInterestBuilder);

View File

@@ -57,7 +57,7 @@ class FunctionServiceParser extends AbstractSimpleBeanDefinitionParser {
if (functionElement != null) {
builder.addPropertyValue("functions", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, functionElement, builder));
functionElement, parserContext, builder));
}
}

View File

@@ -75,14 +75,14 @@ class GatewaySenderParser extends AbstractSimpleBeanDefinitionParser {
if (eventFilterElement != null) {
builder.addPropertyValue("eventFilters", ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, eventFilterElement, builder));
eventFilterElement, parserContext, builder));
}
Element eventSubstitutionFilterElement = DomUtils.getChildElementByTagName(element, "event-substitution-filter");
if (eventSubstitutionFilterElement != null) {
builder.addPropertyValue("eventSubstitutionFilter", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, eventSubstitutionFilterElement, builder));
eventSubstitutionFilterElement, parserContext, builder));
}
ParsingUtils.parseTransportFilters(element, parserContext, builder);

View File

@@ -49,6 +49,8 @@ class GemfireNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("local-region", new LocalRegionParser());
registerBeanDefinitionParser("local-region-template", new LocalRegionParser());
registerBeanDefinitionParser("lookup-region", new LookupRegionParser());
registerBeanDefinitionParser("lucene-index", new LuceneIndexParser());
registerBeanDefinitionParser("lucene-service", new LuceneServiceParser());
registerBeanDefinitionParser("partitioned-region", new PartitionedRegionParser());
registerBeanDefinitionParser("partitioned-region-template", new PartitionedRegionParser());
registerBeanDefinitionParser("pool", new PoolParser());

View File

@@ -60,7 +60,7 @@ class LookupRegionParser extends AbstractRegionParser {
ParsingUtils.setPropertyValue(element, builder, "cloning-enabled");
ParsingUtils.setPropertyValue(element, builder, "eviction-maximum");
ParsingUtils.setPropertyValue(element, builder, "name");
ParsingUtils.parseExpiration(parserContext, element, builder);
ParsingUtils.parseExpiration(element, parserContext, builder);
parseCollectionOfCustomSubElements(element, parserContext, builder, AsyncEventQueue.class.getName(),
"async-event-queue", "asyncEventQueues");
@@ -71,22 +71,23 @@ class LookupRegionParser extends AbstractRegionParser {
Element cacheListenerElement = DomUtils.getChildElementByTagName(element, "cache-listener");
if (cacheListenerElement != null) {
builder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext,
cacheListenerElement, builder));
builder.addPropertyValue("cacheListeners", ParsingUtils.parseRefOrNestedBeanDeclaration(
cacheListenerElement, parserContext,
builder));
}
Element cacheLoaderElement = DomUtils.getChildElementByTagName(element, "cache-loader");
if (cacheLoaderElement != null) {
builder.addPropertyValue("cacheLoader", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, cacheLoaderElement, builder));
cacheLoaderElement, parserContext, builder));
}
Element cacheWriterElement = DomUtils.getChildElementByTagName(element, "cache-writer");
if (cacheWriterElement != null) {
builder.addPropertyValue("cacheWriter", ParsingUtils.parseRefOrSingleNestedBeanDeclaration(
parserContext, cacheWriterElement, builder));
cacheWriterElement, parserContext, builder));
}
if (!subRegion) {

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.xml;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Spring XML {@link AbstractSingleBeanDefinitionParser parser} for the {@link LuceneIndexFactoryBean} bean definition.
*
* @author John Blum
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
* @see org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean
* @since 1.1.0
*/
class LuceneIndexParser extends AbstractSingleBeanDefinitionParser {
/**
* @inheritDoc
*/
@Override
protected Class<?> getBeanClass(Element element) {
return LuceneIndexFactoryBean.class;
}
/**
* @inheritDoc
*/
@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.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(DomUtils.getChildElementByTagName(element, "field-analyzers"))
.ifPresent((fieldAnalyzersElement) -> builder.addPropertyValue("fieldAnalyzers",
ParsingUtils.parseRefOrSingleNestedBeanDeclaration(fieldAnalyzersElement, parserContext, builder)));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.xml;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.data.gemfire.search.lucene.LuceneServiceFactoryBean;
import org.w3c.dom.Element;
/**
* Spring XML {@link AbstractSingleBeanDefinitionParser parser} for a {@link LuceneServiceFactoryBean} bean definition.
*
* @author John Blum
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
* @see org.springframework.data.gemfire.search.lucene.LuceneServiceFactoryBean
* @since 1.1.0
*/
class LuceneServiceParser extends AbstractSingleBeanDefinitionParser {
/**
* @inheritDoc
*/
@Override
protected Class<?> getBeanClass(Element element) {
return LuceneServiceFactoryBean.class;
}
/**
* @inheritDoc
*/
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
super.doParse(element, builder);
ParsingUtils.setCacheReference(element, builder);
}
}

View File

@@ -40,12 +40,21 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Utilities used by the Spring Data GemFire XML Namespace parsers.
* Utilities used by the Spring Data GemFire XML Namespace Parsers.
*
* @author Costin Leau
* @author David Turanski
* @author Lyndon Adams
* @author John Blum
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.RuntimeBeanReference
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.support.ManagedList
* @see org.springframework.beans.factory.xml.ParserContext
* @see org.springframework.core.Conventions
* @see org.springframework.util.xml.DomUtils
* @see org.w3c.dom.Element
* @since 1.0.0
*/
abstract class ParsingUtils {
@@ -111,30 +120,8 @@ abstract class ParsingUtils {
setPropertyValue(builder, source, propertyName, false);
}
/**
* Utility method handling parsing of nested definition of the type:
*
* <pre>
* <tag ref="someBean"/>
* </pre>
*
* or
*
* <pre>
* <tag>
* <bean .... />
* </tag>
* </pre>
*
* @param element the XML element.
* @return Bean reference or nested Bean definition.
*/
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
BeanDefinitionBuilder builder) {
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, "ref", false);
}
static Object getBeanReference(Element element, ParserContext parserContext, String refAttributeName) {
static Object getBeanReference(ParserContext parserContext, Element element, String refAttributeName) {
String refAttributeValue = element.getAttribute(refAttributeName);
Object returnValue = null;
@@ -151,27 +138,61 @@ abstract class ParsingUtils {
return returnValue;
}
static Object parseRefOrNestedCustomElement(ParserContext parserContext, Element element,
static Object parseRefOrNestedCustomElement(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
Object beanRef = ParsingUtils.getBeanReference(parserContext, element, "bean");
Object beanRef = ParsingUtils.getBeanReference(element, parserContext, "bean");
return (beanRef != null ? beanRef : parserContext.getDelegate().parseCustomElement(
element, builder.getBeanDefinition()));
}
static Object parseRefOrSingleNestedBeanDeclaration(ParserContext parserContext, Element element,
/**
* Utility method handling parsing of nested bean definition of the type:
*
* <pre>
* <tag ref="someBean"/>
* </pre>
*
* or
*
* <pre>
* <tag>
* <bean .... />
* </tag>
* </pre>
*
* @param element the XML element.
* @return Bean reference or nested Bean definition.
*/
static Object parseRefOrNestedBeanDeclaration(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, "ref", true);
return parseRefOrNestedBeanDeclaration(element, parserContext, builder, "ref", false);
}
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
static Object parseRefOrNestedBeanDeclaration(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder, String refAttributeName) {
return parseRefOrNestedBeanDeclaration(parserContext, element, builder, refAttributeName, false);
return parseRefOrNestedBeanDeclaration(element, parserContext, builder, refAttributeName, false);
}
static Object parseRefOrNestedBeanDeclaration(ParserContext parserContext, Element element,
static Object parseRefOrSingleNestedBeanDeclaration(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return parseRefOrNestedBeanDeclaration(element, parserContext, builder, "ref", true);
}
static Object parseRefOrSingleNestedBeanDeclaration(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder, String refAttributeName) {
return parseRefOrNestedBeanDeclaration(element, parserContext, builder, refAttributeName, true);
}
static Object parseRefOrNestedBeanDeclaration(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder, String refAttributeName, boolean single) {
Object beanReference = getBeanReference(parserContext, element, refAttributeName);
Object beanReference = getBeanReference(element, parserContext, refAttributeName);
if (beanReference != null) {
return beanReference;
@@ -182,8 +203,8 @@ abstract class ParsingUtils {
// parse nested bean definition
if (childElements.size() == 1) {
return parserContext.getDelegate().parsePropertySubElement(childElements.get(0),
builder.getRawBeanDefinition());
return parserContext.getDelegate().parsePropertySubElement(
childElements.get(0), builder.getRawBeanDefinition());
}
else {
// TODO also triggered when there are no child elements; need to change the message...
@@ -194,7 +215,7 @@ abstract class ParsingUtils {
}
}
ManagedList<Object> list = new ManagedList<Object>();
ManagedList<Object> list = new ManagedList<>();
for (Element childElement : childElements) {
list.add(parserContext.getDelegate().parsePropertySubElement(childElement, builder.getRawBeanDefinition()));
@@ -206,12 +227,12 @@ abstract class ParsingUtils {
/**
* Parses the eviction sub-element. Populates the given attribute factory with the proper attributes.
*
* @param parserContext the context used for parsing the XML document.
* @param element the XML elements being parsed.
* @param parserContext the context used for parsing the XML document.
* @param regionAttributesBuilder the Region Attributes builder.
* @return true if parsing actually occurred, false otherwise.
*/
static boolean parseEviction(ParserContext parserContext, Element element,
static boolean parseEviction(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
Element evictionElement = DomUtils.getChildElementByTagName(element, "eviction");
@@ -227,7 +248,7 @@ abstract class ParsingUtils {
Element objectSizerElement = DomUtils.getChildElementByTagName(evictionElement, "object-sizer");
if (objectSizerElement != null) {
Object sizer = parseRefOrNestedBeanDeclaration(parserContext, objectSizerElement,
Object sizer = parseRefOrNestedBeanDeclaration(objectSizerElement, parserContext,
evictionAttributesBuilder);
evictionAttributesBuilder.addPropertyValue("objectSizer", sizer);
}
@@ -244,14 +265,14 @@ abstract class ParsingUtils {
/**
* Parses the subscription sub-element. Populates the given attribute factory with the proper attributes.
*
* @param parserContext the context used while parsing the XML document.
* @param element the XML element being parsed.
* @param parserContext the context used while parsing the XML document.
* @param regionAttributesBuilder the Region Attributes builder.
* @return true if parsing actually occurred, false otherwise.
*/
@SuppressWarnings("unused")
static boolean parseSubscription(ParserContext parserContext, Element element,
BeanDefinitionBuilder regionAttributesBuilder) {
static boolean parseSubscription(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
Element subscriptionElement = DomUtils.getChildElementByTagName(element, "subscription");
@@ -274,8 +295,9 @@ abstract class ParsingUtils {
Element transportFilterElement = DomUtils.getChildElementByTagName(element, "transport-filter");
if (transportFilterElement != null) {
builder.addPropertyValue("transportFilters", parseRefOrNestedBeanDeclaration(parserContext,
transportFilterElement, builder));
builder.addPropertyValue("transportFilters", parseRefOrNestedBeanDeclaration(transportFilterElement,
parserContext,
builder));
}
}
@@ -286,13 +308,13 @@ abstract class ParsingUtils {
/**
* Parses the expiration sub-elements. Populates the given attribute factory with proper attributes.
*
* @param parserContext the context used while parsing the XML document.
* @param element the XML element being parsed.
* @param parserContext the context used while parsing the XML document.
* @param regionAttributesBuilder the Region Attributes builder.
* @return a boolean indicating whether Region expiration attributes were specified.
*/
static boolean parseExpiration(ParserContext parserContext, Element element,
BeanDefinitionBuilder regionAttributesBuilder) {
static boolean parseExpiration(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
boolean result = parseExpiration(element, "region-ttl", "regionTimeToLive", regionAttributesBuilder);
@@ -313,8 +335,8 @@ abstract class ParsingUtils {
}
@SuppressWarnings("unused")
static void parseOptionalRegionAttributes(ParserContext parserContext, Element element,
BeanDefinitionBuilder regionAttributesBuilder) {
static void parseOptionalRegionAttributes(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
setPropertyValue(element, regionAttributesBuilder, "cloning-enabled");
setPropertyValue(element, regionAttributesBuilder, "concurrency-level");
@@ -343,8 +365,8 @@ abstract class ParsingUtils {
}
}
@SuppressWarnings("unused")
static void parseMembershipAttributes(ParserContext parserContext, Element element,
@SuppressWarnings({ "deprecation", "unused" })
static void parseMembershipAttributes(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
Element membershipAttributes = DomUtils.getChildElementByTagName(element, "membership-attributes");
@@ -410,26 +432,29 @@ abstract class ParsingUtils {
private static boolean parseCustomExpiration(Element rootElement, ParserContext parserContext, String elementName,
String propertyName, BeanDefinitionBuilder regionAttributesBuilder) {
Element expirationElement = DomUtils.getChildElementByTagName(rootElement, elementName);
if (expirationElement != null) {
Object customExpiry = parseRefOrSingleNestedBeanDeclaration(parserContext, expirationElement,
regionAttributesBuilder);
Object customExpiry =
parseRefOrSingleNestedBeanDeclaration(expirationElement, parserContext, regionAttributesBuilder);
regionAttributesBuilder.addPropertyValue(propertyName, customExpiry);
return true;
}
return false;
}
static void parseCompressor(ParserContext parserContext, Element element,
static void parseCompressor(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
Element compressorElement = DomUtils.getChildElementByTagName(element, "compressor");
if (compressorElement != null) {
regionAttributesBuilder.addPropertyValue("compressor", parseRefOrSingleNestedBeanDeclaration(
parserContext, compressorElement, regionAttributesBuilder));
compressorElement, parserContext, regionAttributesBuilder));
}
}

View File

@@ -174,13 +174,13 @@ class PartitionedRegionParser extends AbstractRegionParser {
private Object parsePartitionResolver(Element subElement, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrSingleNestedBeanDeclaration(parserContext, subElement, builder);
return ParsingUtils.parseRefOrSingleNestedBeanDeclaration(subElement, parserContext, builder);
}
/* (non-Javadoc) */
private Object parsePartitionListeners(Element subElement, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrNestedBeanDeclaration(parserContext, subElement, builder);
return ParsingUtils.parseRefOrNestedBeanDeclaration(subElement, parserContext, builder);
}
}

View File

@@ -89,7 +89,7 @@ class SnapshotServiceParser extends AbstractSingleBeanDefinitionParser {
if (isSnapshotFilterSpecified(snapshotMetadataElement)) {
snapshotMetadataBuilder.addConstructorArgValue(ParsingUtils.parseRefOrNestedBeanDeclaration(
parserContext, snapshotMetadataElement, snapshotMetadataBuilder, "filter-ref", true));
snapshotMetadataElement, parserContext, snapshotMetadataBuilder, "filter-ref", true));
}
snapshotMetadataBuilder.addConstructorArgValue(snapshotMetadataElement.getAttribute("format"));

View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/geode"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe="http://www.springframework.org/schema/geode"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/data/geode"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.1">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/context"
schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
schemaLocation="http://www.springframework.org/schema/data/repository/spring-repository.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/geode"
schemaLocation="http://www.springframework.org/schema/geode/spring-geode.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<!-- -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Namespace support for the Spring Data GemFire Client side data access.
]]></xsd:documentation>
</xsd:annotation>
<!-- Repositories -->
<xsd:element name="repositories">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="repository:repositories">
<xsd:attributeGroup ref="gemfire-repository-attributes"/>
<xsd:attributeGroup ref="repository:repository-attributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<!-- -->
<xsd:attributeGroup name="gemfire-repository-attributes">
<xsd:attribute name="mapping-context-ref" type="mappingContextRef">
<xsd:annotation>
<xsd:documentation>
The reference to a MappingContext. If not set a default one will be created.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<!-- Mapping -->
<xsd:simpleType name="mappingContextRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.data.gemfire.GemfireMappingContext"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
<!-- Function Executions -->
<xsd:element name="function-executions">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enables component scanning for annotated function execution interfaces.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="include-filter" type="context:filterType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls which eligible types to include for component scanning.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="exclude-filter" type="context:filterType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Controls which eligible types to exclude for component scanning.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="base-package" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the base package where function execution interfaces will be tried to be detected.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- DataSource -->
<xsd:element name="datasource">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a connection from a Cache client to a set of GemFire Cache Servers.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:choice minOccurs="1" maxOccurs="1">
<xsd:element name="locator" type="gfe:connectionType"
minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="server" type="gfe:connectionType"
minOccurs="1" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="free-connection-timeout"
type="xsd:string" use="optional" />
<xsd:attribute name="idle-timeout" type="xsd:string"
use="optional" />
<xsd:attribute name="load-conditioning-interval"
type="xsd:string" use="optional" />
<xsd:attribute name="max-connections" type="xsd:string"
use="optional" />
<xsd:attribute name="min-connections" type="xsd:string"
use="optional" />
<xsd:attribute name="multi-user-authentication"
type="xsd:string" use="optional" />
<xsd:attribute name="ping-interval" type="xsd:string"
use="optional" />
<xsd:attribute name="pr-single-hop-enabled"
type="xsd:string" use="optional" />
<xsd:attribute name="read-timeout" type="xsd:string"
use="optional" />
<xsd:attribute name="retry-attempts" type="xsd:string"
use="optional" />
<xsd:attribute name="server-group" type="xsd:string"
use="optional" />
<xsd:attribute name="socket-buffer-size" type="xsd:string"
use="optional" />
<xsd:attribute name="statistic-interval" type="xsd:string"
use="optional" />
<xsd:attribute name="subscription-ack-interval"
type="xsd:string" use="optional" />
<xsd:attribute name="subscription-enabled"
type="xsd:string" use="optional" />
<xsd:attribute name="subscription-message-tracking-timeout"
type="xsd:string" use="optional" />
<xsd:attribute name="subscription-redundancy"
type="xsd:string" use="optional" />
<xsd:attribute name="thread-local-connections"
type="xsd:string" use="optional" />
</xsd:complexType>
</xsd:element>
<!-- JSON support -->
<xsd:element name="json-region-autoproxy">
<xsd:annotation>
<xsd:documentation><![CDATA[
Enables A Spring AOP proxy to perform automatic conversion to and from JSON for appropriate region operations
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="region-refs" use="optional" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A comma delimited string of region names to include for JSON conversion. By default all regions are included.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pretty-print" use="optional" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A boolean value to specify whether returned JSON strings are pretty printed, false by default.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="convert-returned-collections" use="optional" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A boolean value to specify whether Collections returned by Region.getAll(), Region.values() should be converted from the
native GemFire PdxInstance type. True, by default but will incur significant overhead for large collections.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- Snapshot Service -->
<xsd:element name="snapshot-service">
<xsd:annotation>
<xsd:documentation><![CDATA[
Access to GemFire's Snapshot Service for taking snapshots of GemFire Cache and Region data.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="snapshot-import" type="snapshotMetadataType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies meta-data for a snapshot import.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="snapshot-export" type="snapshotMetadataType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Specifies meta-data for a snapshot export.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
ID of the GemFire [Cache|Region] SnapshotService bean in the Spring context.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-ref" type="xsd:string" use="optional" default="gemfireCache">
<xsd:annotation>
<xsd:documentation><![CDATA[
(Optional) Name of the GemFire Cache bean from which to extract data and record a snapshot (by default 'gemfireCache').
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="region-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
(Optional) Name of the GemFire Region bean from which to extract data and record a snapshot.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="suppress-import-on-init" type="xsd:string" default="false">
<xsd:annotation>
<xsd:documentation>
Determines whether imports are suppressed on initialization of the GemFire Snapshot Service.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- -->
<xsd:complexType name="snapshotMetadataType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Declares an element type defining snapshot meta-data.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:any namespace="##other" processContents="skip" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Inner bean definition. The nested declaration serves as an alternative to bean references (using
both in the same definition) is illegal.
]]></xsd:documentation>
</xsd:annotation>
</xsd:any>
</xsd:sequence>
<xsd:attribute name="location" type="xsd:string" use="required"/>
<xsd:attribute name="format" type="xsd:string" use="optional" default="GEMFIRE"/>
<xsd:attribute name="filter-ref" type="xsd:string" use="optional"/>
</xsd:complexType>
</xsd:schema>

View File

@@ -2273,6 +2273,117 @@ The type of index: FUNCTIONAL, HASH, PRIMARY_KEY.
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- Lucene Index & Service -->
<xsd:element name="lucene-index">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.gemfire.search.lucene.LuceneIndexFactoryBean"><![CDATA[
Defines a GemFire Lucene index.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.apache.geode.cache.lucene.LuceneIndex"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<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.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the LuceneIndex bean definition. If property 'name' is not set, it will be used as the index name as well.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-ref" type="xsd:string" use="optional" default="gemfireCache">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the bean defining the GemFire cache (by default 'gemfireCache').
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the LuceneIndex.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destroy" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Determines whether the LuceneIndex is destroyed on shutdown.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="fields" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
List of fields included in the Lucene Index.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="lucene-service-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to the single LuceneService.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="region-ref" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to the Region
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="region-path" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Fully-qualified pathname of the Region.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- -->
<xsd:element name="lucene-service">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.gemfire.search.lucene.LuceneServiceFactoryBean"><![CDATA[
Defines a GemFire LuceneService bean.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.apache.geode.cache.lucene.LuceneService"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Identifier (name) for the LuceneService bean definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-ref" type="xsd:string" use="optional" default="gemfireCache">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the bean defining the GemFire cache (by default 'gemfireCache').
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<!-- JNDI -->
<xsd:complexType name="jndiBindingType">
<xsd:sequence>
@@ -2535,36 +2646,6 @@ The port number of the connection (between 1 and 65535 inclusive).
</xsd:attribute>
</xsd:complexType>
<!-- -->
<xsd:complexType name="interestType" abstract="true">
<xsd:attribute name="durable" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether the Registered Interest is durable or not. Default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="receive-values" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether values are received with create and update events on keys of interest (true)
or only invalidations are received and the value will be received on the next get instead (false).
Default is true.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="result-policy" type="xsd:string" use="optional" default="KEYS_VALUES">
<xsd:annotation>
<xsd:documentation><![CDATA[
The result policy for this interest. Can be one of 'KEYS', 'KEYS_VALUES' (the default) or 'NONE'.
KEYS - Initializes the local Cache with the keys satisfying the request.
KEYS_VALUES - Initializes the local Cache with the keys and current values satisfying the request.
NONE - Does not initialize the local Cache.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!-- -->
<xsd:element name="cq-listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -2687,6 +2768,36 @@ Whether the resulting GemFire continuous query is durable or not.
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!-- -->
<xsd:complexType name="interestType" abstract="true">
<xsd:attribute name="durable" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether the Registered Interest is durable or not. Default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="receive-values" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether values are received with create and update events on keys of interest (true)
or only invalidations are received and the value will be received on the next get instead (false).
Default is true.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="result-policy" type="xsd:string" use="optional" default="KEYS_VALUES">
<xsd:annotation>
<xsd:documentation><![CDATA[
The result policy for this interest. Can be one of 'KEYS', 'KEYS_VALUES' (the default) or 'NONE'.
KEYS - Initializes the local Cache with the keys satisfying the request.
KEYS_VALUES - Initializes the local Cache with the keys and current values satisfying the request.
NONE - Does not initialize the local Cache.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!-- WAN Topology -->
<xsd:attributeGroup name="commonWANQueueAttributes">
<xsd:attribute name="batch-conflation-enabled" type="xsd:string" use="optional">

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.data.gemfire.config.xml;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
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.Collections;
import java.util.Map;
import java.util.Optional;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.lucene.LuceneIndex;
import org.apache.geode.cache.lucene.LuceneService;
import org.apache.lucene.analysis.Analyzer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Unit tests for the {@link LuceneServiceParser} and {@link LuceneIndexParser}.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.1.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class LuceneNamespaceUnitTests {
private static final String[] EMPTY_STRING_ARRAY = {};
@Autowired
private LuceneService luceneService;
@Autowired
@Qualifier("IndexOne")
private LuceneIndex luceneIndexOne;
@Autowired
@Qualifier("IndexTwo")
private LuceneIndex luceneIndexTwo;
@Autowired
@Qualifier("IndexThree")
private LuceneIndex luceneIndexThree;
@Autowired
@Qualifier("IndexFour")
private LuceneIndex luceneIndexFour;
protected 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,
String... keys) {
assertLuceneIndex(index, name, regionPath);
assertThat(index.getFieldNames()).isEmpty();
assertThat(index.getFieldAnalyzers()).hasSize(keys.length);
assertThat(index.getFieldAnalyzers()).containsKeys(keys);
}
protected void assertLuceneIndexWithFields(LuceneIndex index, String name, String regionPath, String... fieldNames) {
assertLuceneIndex(index, name, regionPath);
assertThat(index.getFieldAnalyzers()).isEmpty();
assertThat(index.getFieldNames()).contains(fieldNames);
}
@Test
@SuppressWarnings({ "deprecation", "unchecked" })
public void luceneServiceConfigurationAndInteractionsAreCorrect() {
assertThat(this.luceneService).isNotNull();
verify(this.luceneService, times(1))
.createIndex(eq("IndexOne"), eq("/Example"), eq("fieldOne"), eq("fieldTwo"));
verify(this.luceneService, times(1))
.createIndex(eq("IndexTwo"), eq("/AnotherExample"), isA(Map.class));
verify(this.luceneService, never()).destroyIndex(any(LuceneIndex.class));
}
@Test
public void luceneIndexOneIsConfiguredCorrectly() {
assertLuceneIndexWithFields(this.luceneIndexOne, "IndexOne", "/Example",
"fieldOne", "fieldTwo");
}
@Test
public void luceneIndexTwoIsConfiguredCorrectly() {
assertLuceneIndexWithFieldAnalyzers(this.luceneIndexTwo, "IndexTwo", "/AnotherExample",
"fieldOne", "fieldTwo");
}
@Test
public void luceneIndexThreeIsConfiguredCorrectly() {
assertLuceneIndexWithFields(this.luceneIndexThree, "IndexThree", "/Example",
"singleField");
}
@Test
public void luceneIndexFourIsConfiguredCorrectly() {
assertLuceneIndexWithFieldAnalyzers(this.luceneIndexFour, "IndexFour", "/YetAnotherExample",
"singleField");
}
public static class LuceneNamespaceUnitTestsBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.getBeanDefinition("luceneService")
.setBeanClassName(MockLuceneServiceFactoryBean.class.getName());
}
}
public static class MockLuceneServiceFactoryBean implements FactoryBean<LuceneService>, InitializingBean {
@SuppressWarnings("all")
private GemFireCache gemfireCache;
private LuceneService luceneService;
@Override
public void afterPropertiesSet() throws Exception {
assertThat(this.gemfireCache).describedAs("GemFireCache must not be null").isNotNull();
}
@Override
@SuppressWarnings("unchecked")
public LuceneService getObject() throws Exception {
return Optional.ofNullable(this.luceneService).orElseGet(() -> {
this.luceneService = mock(LuceneService.class);
Answer<LuceneIndex> mockLuceneIndex = newMockLuceneIndex(this.luceneService);
doAnswer(mockLuceneIndex).when(this.luceneService)
.createIndex(anyString(), anyString(), (String[]) any());
doAnswer(mockLuceneIndex).when(this.luceneService)
.createIndex(anyString(), anyString(), isA(Map.class));
return this.luceneService;
});
}
@SuppressWarnings("unchecked")
private Answer<LuceneIndex> newMockLuceneIndex(LuceneService mockLuceneService) {
return (invocationOnMock) -> {
String indexName = invocationOnMock.getArgument(0);
String regionPath = invocationOnMock.getArgument(1);
LuceneIndex mockLuceneIndex = mock(LuceneIndex.class, indexName);
when(mockLuceneIndex.getName()).thenReturn(indexName);
when(mockLuceneIndex.getRegionPath()).thenReturn(regionPath);
if (invocationOnMock.getArguments().length > 2) {
Object fields = invocationOnMock.getArgument(2);
if (fields instanceof Map) {
when(mockLuceneIndex.getFieldAnalyzers()).thenReturn((Map<String, Analyzer>) fields);
when(mockLuceneIndex.getFieldNames()).thenReturn(EMPTY_STRING_ARRAY);
}
else {
when(mockLuceneIndex.getFieldAnalyzers()).thenReturn(Collections.emptyMap());
when(mockLuceneIndex.getFieldNames()).thenReturn(extractFields(invocationOnMock));
}
}
when(mockLuceneService.getIndex(eq(indexName), eq(regionPath))).thenReturn(mockLuceneIndex);
return mockLuceneIndex;
};
}
private String[] asStringArray(Object fields) {
return (fields instanceof String[] ? (String[]) fields : String.valueOf(fields).split(", "));
}
@SuppressWarnings("all")
private String[] extractFields(InvocationOnMock invocationOnMock) {
String[] fields = new String[invocationOnMock.getArguments().length - 2];
System.arraycopy(invocationOnMock.getArguments(), 2, fields, 0, fields.length);
return fields;
}
@Override
public Class<?> getObjectType() {
return Optional.ofNullable(this.luceneService).<Class<?>>map(LuceneService::getClass)
.orElse(LuceneService.class);
}
public void setCache(GemFireCache gemfireCache) {
this.gemfireCache = gemfireCache;
}
}
public static class MockAnalyzerFactoryBean implements FactoryBean<Analyzer> {
@SuppressWarnings("unused")
private Analyzer analyzer;
private String name;
@Override
public Analyzer getObject() throws Exception {
return Optional.ofNullable(this.analyzer).orElseGet(() -> this.analyzer = mock(Analyzer.class, getName()));
}
@Override
public Class<?> getObjectType() {
return Optional.ofNullable(this.analyzer).<Class<?>>map(Analyzer::getClass).orElse(Analyzer.class);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
}

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<util:properties id="gemfireProperties">
<prop key="name">LuceneNamespaceUnitTests</prop>
<prop key="log-level">warning</prop>
</util:properties>
<bean class="org.springframework.data.gemfire.test.GemfireTestBeanPostProcessor"/>
<bean class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$LuceneNamespaceUnitTestsBeanFactoryPostProcessor"/>
<gfe:cache properties-ref="gemfireProperties"/>
<gfe:local-region id="Example" persistent="false"/>
<gfe:lucene-service id="luceneService"/>
<gfe:lucene-index id="IndexOne" destroy="true" fields="fieldOne, fieldTwo"
lucene-service-ref="luceneService" region-ref="Example"/>
<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
<gfe:field-analyzers>
<map>
<entry key="fieldOne">
<bean class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$MockAnalyzerFactoryBean"
p:name="MockAnalyzerOne"/>
</entry>
<entry key="fieldTwo">
<bean class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$MockAnalyzerFactoryBean"
p:name="MockAnalyzerTwo"/>
</entry>
</map>
</gfe:field-analyzers>
</gfe:lucene-index>
<gfe:lucene-index id="IndexThree" fields="singleField" lucene-service-ref="luceneService" region-ref="Example"/>
<util:map id="indexFourFieldAnalyzers">
<entry key="singleField">
<bean class="org.springframework.data.gemfire.config.xml.LuceneNamespaceUnitTests$MockAnalyzerFactoryBean"
p:name="MockAnalyzerThree"/>
</entry>
</util:map>
<gfe:lucene-index id="IndexFour" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
<gfe:field-analyzers ref="indexFourFieldAnalyzers"/>
</gfe:lucene-index>
</beans>