SGF-863 - Add support in the XML Namespace for Region to AsyncEventQueue and GatewaySender associations by ID.

This commit is contained in:
John Blum
2019-07-15 18:37:37 -07:00
parent b6ff6de1af
commit dc91f82daf
7 changed files with 258 additions and 45 deletions

View File

@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.CacheListener;
import org.apache.geode.cache.CacheLoader;
@@ -29,7 +31,9 @@ import org.apache.geode.cache.ExpirationAttributes;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.wan.GatewaySender;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The LookupRegionFactoryBean class is a concrete implementation of ResolvableRegionFactoryBean for handling
@@ -66,6 +70,9 @@ public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K
private Integer evictionMaximum;
private String[] asyncEventQueueIds;
private String[] gatewaySenderIds;
@Override
public void afterPropertiesSet() throws Exception {
@@ -73,10 +80,10 @@ public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K
Optional.ofNullable(getRegion().getAttributesMutator()).ifPresent(attributesMutator -> {
Arrays.stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
.map(AsyncEventQueue::getId)
.forEach(attributesMutator::addAsyncEventQueueId);
// AsyncEventQueues (AEQ)
getConfiguredAsyncEventQueueIds().forEach(attributesMutator::addAsyncEventQueueId);
// CacheListeners
Arrays.stream(nullSafeArray(this.cacheListeners, CacheListener.class))
.forEach(attributesMutator::addCacheListener);
@@ -102,12 +109,43 @@ public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K
Optional.ofNullable(this.regionTimeToLive).ifPresent(attributesMutator::setRegionTimeToLive);
}
Arrays.stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
.map(GatewaySender::getId)
.forEach(attributesMutator::addGatewaySenderId);
// GatewaySenders
getConfiguredGatewaySenderIds().forEach(attributesMutator::addGatewaySenderId);
});
}
private Set<String> getConfiguredAsyncEventQueueIds() {
Set<String> asyncEventQueueIds = new HashSet<>();
Arrays.stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
.map(AsyncEventQueue::getId)
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
Arrays.stream(nullSafeArray(this.asyncEventQueueIds, String.class))
.filter(StringUtils::hasText)
.map(String::trim)
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
return asyncEventQueueIds;
}
private Set<String> getConfiguredGatewaySenderIds() {
Set<String> gatewaySenderIds = new HashSet<>();
Arrays.stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
.map(GatewaySender::getId)
.collect(Collectors.toCollection(() -> gatewaySenderIds));
Arrays.stream(nullSafeArray(this.gatewaySenderIds, String.class))
.filter(StringUtils::hasText)
.map(String::trim)
.collect(Collectors.toCollection(() -> gatewaySenderIds));
return gatewaySenderIds;
}
@Override
public final boolean isLookupEnabled() {
return true;
@@ -117,6 +155,10 @@ public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K
this.asyncEventQueues = asyncEventQueues;
}
public void setAsyncEventQueueIds(String[] asyncEventQueueIds) {
this.asyncEventQueueIds = asyncEventQueueIds;
}
public void setCacheListeners(CacheListener<K, V>[] cacheListeners) {
this.cacheListeners = cacheListeners;
}
@@ -161,6 +203,10 @@ public class LookupRegionFactoryBean<K, V> extends ResolvableRegionFactoryBean<K
this.gatewaySenders = gatewaySenders;
}
public void setGatewaySenderIds(String[] gatewaySenderIds) {
this.gatewaySenderIds = gatewaySenderIds;
}
public void setRegionIdleTimeout(ExpirationAttributes regionIdleTimeout) {
setStatisticsEnabled(regionIdleTimeout != null);
this.regionIdleTimeout = regionIdleTimeout;

View File

@@ -18,9 +18,12 @@ package org.springframework.data.gemfire;
import static java.util.Arrays.stream;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
@@ -138,6 +141,9 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
private String diskStoreName;
private String[] asyncEventQueueIds;
private String[] gatewaySenderIds;
/**
* Creates a new {@link Region} with the given {@link String name}.
*
@@ -252,8 +258,7 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
regionFactory.setStatisticsEnabled(resolveStatisticsEnabled());
stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
.forEach(asyncEventQueue -> regionFactory.addAsyncEventQueueId(asyncEventQueue.getId()));
getConfiguredAsyncEventQueueIds().forEach(regionFactory::addAsyncEventQueueId);
stream(nullSafeArray(this.cacheListeners, CacheListener.class)).forEach(regionFactory::addCacheListener);
@@ -279,8 +284,7 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
Optional.ofNullable(this.evictionAttributes).ifPresent(regionFactory::setEvictionAttributes);
stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
.forEach(gatewaySender -> regionFactory.addGatewaySenderId(gatewaySender.getId()));
getConfiguredGatewaySenderIds().forEach(regionFactory::addGatewaySenderId);
Optional.ofNullable(this.keyConstraint).ifPresent(regionFactory::setKeyConstraint);
@@ -312,6 +316,38 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
return regionFactory;
}
private Set<String> getConfiguredAsyncEventQueueIds() {
Set<String> asyncEventQueueIds = new HashSet<>();
Arrays.stream(nullSafeArray(this.asyncEventQueues, AsyncEventQueue.class))
.map(AsyncEventQueue::getId)
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
Arrays.stream(nullSafeArray(this.asyncEventQueueIds, String.class))
.filter(StringUtils::hasText)
.map(String::trim)
.collect(Collectors.toCollection(() -> asyncEventQueueIds));
return asyncEventQueueIds;
}
private Set<String> getConfiguredGatewaySenderIds() {
Set<String> gatewaySenderIds = new HashSet<>();
Arrays.stream(nullSafeArray(this.gatewaySenders, GatewaySender.class))
.map(GatewaySender::getId)
.collect(Collectors.toCollection(() -> gatewaySenderIds));
Arrays.stream(nullSafeArray(this.gatewaySenderIds, String.class))
.filter(StringUtils::hasText)
.map(String::trim)
.collect(Collectors.toCollection(() -> gatewaySenderIds));
return gatewaySenderIds;
}
/*
* (non-Javadoc)
*
@@ -627,6 +663,10 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
this.asyncEventQueues = asyncEventQueues;
}
public void setAsyncEventQueueIds(String[] asyncEventQueueIds) {
this.asyncEventQueueIds = asyncEventQueueIds;
}
/**
* Sets the {@link RegionAttributes} used to configure this {@link Region}.
*
@@ -760,8 +800,10 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
* @see org.apache.geode.cache.DataPolicy
*/
public DataPolicy getDataPolicy() {
return Optional.ofNullable(this.dataPolicy)
.orElseThrow(() -> newIllegalStateException("Data Policy has not been properly resolved yet"));
Assert.state(this.dataPolicy != null, "Data Policy has not been properly resolved yet");
return this.dataPolicy;
}
/**
@@ -775,8 +817,6 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
this.diskStoreName = diskStoreName;
}
// TODO: review/add Javadoc from here forward...
public void setEntryIdleTimeout(ExpirationAttributes entryIdleTimeout) {
this.entryIdleTimeout = entryIdleTimeout;
}
@@ -801,6 +841,10 @@ public abstract class PeerRegionFactoryBean<K, V> extends ConfigurableRegionFact
this.gatewaySenders = gatewaySenders;
}
public void setGatewaySenderIds(String[] gatewaySenderIds) {
this.gatewaySenderIds = gatewaySenderIds;
}
/**
* Configures whether to enable this {@link Region} with the ability to store data in {@literal off-heap memory}.
*

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.xml;
import java.util.HashMap;
@@ -23,8 +22,7 @@ import java.util.Optional;
import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
import org.apache.geode.cache.wan.GatewaySender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@@ -37,6 +35,9 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
/**
@@ -107,10 +108,10 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
mergeRegionTemplateAttributes(element, parserContext, regionBuilder, regionAttributesBuilder);
String resolvedCacheRef = ParsingUtils.resolveCacheReference(element.getAttribute("cache-ref"));
String resolvedCacheReference = ParsingUtils.resolveCacheReference(element.getAttribute("cache-ref"));
if (!subRegion) {
regionBuilder.addPropertyReference("cache", resolvedCacheRef);
regionBuilder.addPropertyReference("cache", resolvedCacheReference);
ParsingUtils.setPropertyValue(element, regionBuilder, "close");
ParsingUtils.setPropertyValue(element, regionBuilder, "destroy");
}
@@ -158,7 +159,7 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
}
if (!subRegion) {
parseSubRegions(element, parserContext, resolvedCacheRef);
parseSubRegions(element, parserContext, resolvedCacheReference);
}
}
@@ -255,7 +256,7 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
String name = element.getAttribute(NAME_ATTRIBUTE);
return (StringUtils.hasText(name) ? name : element.getAttribute(ID_ATTRIBUTE));
return StringUtils.hasText(name) ? name : element.getAttribute(ID_ATTRIBUTE);
}
private String buildSubRegionPath(String parentName, String regionName) {
@@ -269,7 +270,6 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return regionPath;
}
/* (non-Javadoc) */
private BeanDefinition parseSubRegion(Element element, ParserContext parserContext, String subRegionPath,
String cacheRef) {
@@ -288,7 +288,6 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return beanDefinition;
}
/* (non-Javadoc) */
private String getParentRegionPathFrom(String regionPath) {
int index = regionPath.lastIndexOf("/");
@@ -302,13 +301,14 @@ abstract class AbstractRegionParser extends AbstractSingleBeanDefinitionParser {
return parentPath;
}
/* (non-Javadoc) */
protected void validateDataPolicyShortcutAttributesMutualExclusion(Element element, ParserContext parserContext) {
if (element.hasAttribute("data-policy") && element.hasAttribute("shortcut")) {
parserContext.getReaderContext().error(String.format(
"Only one of [data-policy, shortcut] may be specified with element '%1$s'.", element.getTagName()),
element);
String message = String.format("Only one of [data-policy, shortcut] may be specified with element [%s]",
element.getTagName());
parserContext.getReaderContext().error(message, element);
}
}
}

View File

@@ -123,6 +123,7 @@ abstract class ParsingUtils {
static Object getBeanReference(Element element, ParserContext parserContext, String refAttributeName) {
String refAttributeValue = element.getAttribute(refAttributeName);
Object returnValue = null;
if (StringUtils.hasText(refAttributeValue)) {
if (!DomUtils.getChildElements(element).isEmpty()) {
@@ -134,10 +135,10 @@ abstract class ParsingUtils {
parserContext.getReaderContext().error(message, element);
}
return null;
returnValue = new RuntimeBeanReference(refAttributeValue);
}
return new RuntimeBeanReference(refAttributeValue);
return returnValue;
}
static Object parseRefOrNestedCustomElement(Element element, ParserContext parserContext,
@@ -377,11 +378,13 @@ abstract class ParsingUtils {
static void parseOptionalRegionAttributes(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionAttributesBuilder) {
setPropertyValue(element, regionAttributesBuilder, "async-event-queue-ids");
setPropertyValue(element, regionAttributesBuilder, "cloning-enabled");
setPropertyValue(element, regionAttributesBuilder, "concurrency-level");
setPropertyValue(element, regionAttributesBuilder, "disk-synchronous");
setPropertyValue(element, regionAttributesBuilder, "enable-async-conflation");
setPropertyValue(element, regionAttributesBuilder, "enable-subscription-conflation");
setPropertyValue(element, regionAttributesBuilder, "gateway-sender-ids");
setPropertyValue(element, regionAttributesBuilder, "ignore-jta", "ignoreJTA");
setPropertyValue(element, regionAttributesBuilder, "index-update-type");
setPropertyValue(element, regionAttributesBuilder, "initial-capacity");

View File

@@ -626,6 +626,15 @@ use inner bean declarations.
</xsd:choice>
<xsd:group ref="subRegionGroup" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="async-event-queue-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, AsyncEventQueue (AEQ) IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the AEQ. If the member is
primary or a secondary, then the nested async-event-queue or the async-event-queue-ref element should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cloning-enabled" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[[
@@ -641,6 +650,16 @@ in the cache. GemFire default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="gateway-sender-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, GatewaySender IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the GatewaySender.
If the member is primary or a secondary, then the nested gateway-sender or the gateway-sender-ref element
should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1050,6 +1069,34 @@ use inner bean declarations.
</xsd:element>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="async-event-queue-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, AsyncEventQueue (AEQ) IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the AEQ. If the member is
primary or a secondary, then the nested async-event-queue or the async-event-queue-ref element should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="gateway-sender-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, GatewaySender IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the GatewaySender.
If the member is primary or a secondary, then the nested gateway-sender or the gateway-sender-ref element
should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-jta" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether operations on this Region participates in active JTA transactions or ignores them and operates
outside of transactions. This is primarily used in cache loaders, writers, and listeners that need to perform
non-transactional operations on a Region, such as caching a result set. GemFire default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="index-update-type" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -626,6 +626,15 @@ use inner bean declarations.
</xsd:choice>
<xsd:group ref="subRegionGroup" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="async-event-queue-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, AsyncEventQueue (AEQ) IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the AEQ. If the member is
primary or a secondary, then the nested async-event-queue or the async-event-queue-ref element should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cloning-enabled" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[[
@@ -641,6 +650,16 @@ in the cache. GemFire default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="gateway-sender-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, GatewaySender IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the GatewaySender.
If the member is primary or a secondary, then the nested gateway-sender or the gateway-sender-ref element
should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1050,6 +1069,34 @@ use inner bean declarations.
</xsd:element>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="async-event-queue-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, AsyncEventQueue (AEQ) IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the AEQ. If the member is
primary or a secondary, then the nested async-event-queue or the async-event-queue-ref element should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="gateway-sender-ids" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Comma-delimited list of Apache Geode, GatewaySender IDs configured for this Region. This attribute is used
when the member of the cluster hosting this Region is not the primary or secondary for the GatewaySender.
If the member is primary or a secondary, then the nested gateway-sender or the gateway-sender-ref element
should be declared instead.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-jta" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether operations on this Region participates in active JTA transactions or ignores them and operates
outside of transactions. This is primarily used in cache loaders, writers, and listeners that need to perform
non-transactional operations on a Region, such as caching a result set. GemFire default is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="index-update-type" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -13,13 +13,12 @@
* 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.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Matchers.matches;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -29,9 +28,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -50,15 +51,20 @@ public class AbstractRegionParserUnitTests {
private AbstractRegionParser regionParser = new TestRegionParser();
protected void assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate(String localName) {
Element mockElement = mock(Element.class);
when(mockElement.getLocalName()).thenReturn(localName);
assertThat(regionParser.isRegionTemplate(mockElement)).isEqualTo(nullSafeEndsWith(localName, "-template"));
verify(mockElement, times(1)).getLocalName();
}
protected void assertIsSubRegionWhenElementLocalNameEndsWithRegion(String localName) {
Element mockElement = mock(Element.class);
Node mockNode = mock(Node.class);
when(mockElement.getParentNode()).thenReturn(mockNode);
@@ -71,12 +77,14 @@ public class AbstractRegionParserUnitTests {
}
protected boolean nullSafeEndsWith(String localName, String suffix) {
return (localName != null && localName.endsWith(suffix));
return localName != null && localName.endsWith(suffix);
}
@Test
public void getBeanClassIsEqualToTestRegionFactoryBean() {
AbstractRegionParser regionParserSpy = spy(AbstractRegionParser.class);
Element mockElement = mock(Element.class);
doReturn(AbstractRegionParserUnitTests.class).when(regionParserSpy).getRegionFactoryClass();
@@ -89,24 +97,31 @@ public class AbstractRegionParserUnitTests {
@Test
public void getParentNameWhenTemplateIsSet() {
Element mockElement = mock(Element.class);
when(mockElement.getAttribute(eq("template"))).thenReturn("test");
assertThat(regionParser.getParentName(mockElement)).isEqualTo("test");
verify(mockElement, times(1)).getAttribute(eq("template"));
}
@Test
public void getParentNameWhenTemplateIsUnset() {
Element mockElement = mock(Element.class);
when(mockElement.getAttribute(eq("template"))).thenReturn(null);
assertThat(regionParser.getParentName(mockElement)).isNull();
verify(mockElement, times(1)).getAttribute(eq("template"));
}
@Test
public void isRegionTemplateWithRegionTemplateElementsIsTrue() {
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("client-region-template");
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("local-region-template");
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("partitioned-region-template");
@@ -115,6 +130,7 @@ public class AbstractRegionParserUnitTests {
@Test
public void isRegionTemplateWithRegionElementsIsFalse() {
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("client-region");
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("local-region");
assertIsRegionTemplateWhenElementLocalNameEndsWithTemplate("partitioned-region");
@@ -133,6 +149,7 @@ public class AbstractRegionParserUnitTests {
@Test
public void isSubRegionWithRegionElementIsTrue() {
assertIsSubRegionWhenElementLocalNameEndsWithRegion("client-region");
assertIsSubRegionWhenElementLocalNameEndsWithRegion("local-region");
assertIsSubRegionWhenElementLocalNameEndsWithRegion("partitioned-region");
@@ -141,6 +158,7 @@ public class AbstractRegionParserUnitTests {
@Test
public void isSubRegionWithRegionTemplateElementIsFalse() {
assertIsSubRegionWhenElementLocalNameEndsWithRegion("client-region-template");
assertIsSubRegionWhenElementLocalNameEndsWithRegion("local-region-template");
assertIsSubRegionWhenElementLocalNameEndsWithRegion("partitioned-region-template");
@@ -160,11 +178,13 @@ public class AbstractRegionParserUnitTests {
@Test
public void doParseWithAbstractRegionTemplate() {
AbstractRegionParser regionParserSpy = spy(AbstractRegionParser.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
Element mockElement = mock(Element.class);
Node mockNode = mock(Node.class);
when(mockElement.getLocalName()).thenReturn("partitioned-region-template");
@@ -175,13 +195,14 @@ public class AbstractRegionParserUnitTests {
assertThat(builder.getRawBeanDefinition().isAbstract()).isTrue();
verify(regionParserSpy, times(1)).doParse(eq(mockElement), isNull(ParserContext.class), eq(builder));
verify(regionParserSpy, times(1)).doParseRegion(eq(mockElement), isNull(ParserContext.class),
verify(regionParserSpy, times(1)).doParse(eq(mockElement), isNull(), eq(builder));
verify(regionParserSpy, times(1)).doParseRegion(eq(mockElement), isNull(),
eq(builder), eq(false));
}
@Test
public void doParseWithNonAbstractSubRegion() {
AbstractRegionParser regionParserSpy = spy(AbstractRegionParser.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
@@ -197,19 +218,20 @@ public class AbstractRegionParserUnitTests {
assertThat(builder.getRawBeanDefinition().isAbstract()).isFalse();
verify(regionParserSpy, times(1)).doParse(eq(mockElement), isNull(ParserContext.class), eq(builder));
verify(regionParserSpy, times(1)).doParseRegion(eq(mockElement), isNull(ParserContext.class),
verify(regionParserSpy, times(1)).doParse(eq(mockElement), isNull(), eq(builder));
verify(regionParserSpy, times(1)).doParseRegion(eq(mockElement), isNull(),
eq(builder), eq(true));
}
@Test
public void validateDataPolicyShortcutAttributesMutualExclusion() {
Element mockElement = mock(Element.class);
when(mockElement.hasAttribute(matches("data-policy"))).thenReturn(false);
when(mockElement.hasAttribute(matches("shortcut"))).thenReturn(false);
regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
this.regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
verify(mockElement).hasAttribute(eq("data-policy"));
verify(mockElement, never()).hasAttribute(eq("shortcut"));
@@ -217,12 +239,13 @@ public class AbstractRegionParserUnitTests {
@Test
public void validateDataPolicyShortcutAttributesMutualExclusionWithDataPolicy() {
Element mockElement = mock(Element.class);
when(mockElement.hasAttribute(matches("data-policy"))).thenReturn(true);
when(mockElement.hasAttribute(matches("shortcut"))).thenReturn(false);
regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
this.regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
verify(mockElement).hasAttribute(eq("data-policy"));
verify(mockElement).hasAttribute(eq("shortcut"));
@@ -230,12 +253,13 @@ public class AbstractRegionParserUnitTests {
@Test
public void validateDataPolicyShortcutAttributesMutualExclusionWithShortcut() {
Element mockElement = mock(Element.class);
when(mockElement.hasAttribute(matches("data-policy"))).thenReturn(false);
when(mockElement.hasAttribute(matches("shortcut"))).thenReturn(true);
regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
this.regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, null);
verify(mockElement).hasAttribute(eq("data-policy"));
verify(mockElement, never()).hasAttribute(eq("shortcut"));
@@ -243,7 +267,9 @@ public class AbstractRegionParserUnitTests {
@Test
public void validateDataPolicyShortcutAttributesMutualExclusionWithDataPolicyAndShortcut() {
Element mockElement = mock(Element.class);
XmlReaderContext mockReaderContext = mock(XmlReaderContext.class);
ParserContext mockParserContext = new ParserContext(mockReaderContext, null);
@@ -252,10 +278,10 @@ public class AbstractRegionParserUnitTests {
when(mockElement.hasAttribute(matches("shortcut"))).thenReturn(true);
when(mockElement.getTagName()).thenReturn("local-region");
regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, mockParserContext);
this.regionParser.validateDataPolicyShortcutAttributesMutualExclusion(mockElement, mockParserContext);
verify(mockReaderContext).error(
eq("Only one of [data-policy, shortcut] may be specified with element 'local-region'."),
eq("Only one of [data-policy, shortcut] may be specified with element [local-region]"),
eq(mockElement));
}