/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.gemfire.config.xml;
import java.util.List;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.gemfire.FixedPartitionAttributesFactoryBean;
import org.springframework.data.gemfire.PartitionAttributesFactoryBean;
import org.springframework.data.gemfire.PartitionedRegionFactoryBean;
import org.springframework.data.gemfire.RegionAttributesFactoryBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Bean definition parser for the <gfe:partitioned-region> SDG XML namespace (XSD) element.
*
* To avoid eager evaluations, Region attributes are declared as a nested bean definitions.
*
* @author Costin Leau
* @author David Turanski
* @author John Blum
* @see org.springframework.data.gemfire.PartitionedRegionFactoryBean
* @see AbstractRegionParser
*/
class PartitionedRegionParser extends AbstractRegionParser {
/**
* {@inheritDoc}
*/
@Override
protected Class<?> getRegionFactoryClass() {
return PartitionedRegionFactoryBean.class;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
protected void doParseRegion(Element element, ParserContext parserContext, BeanDefinitionBuilder regionBuilder,
boolean subRegion) {
validateDataPolicyShortcutAttributesMutualExclusion(element, parserContext);
BeanDefinitionBuilder regionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
RegionAttributesFactoryBean.class);
doParseRegionConfiguration(element, parserContext, regionBuilder, regionAttributesBuilder, subRegion);
regionBuilder.addPropertyValue("attributes", regionAttributesBuilder.getBeanDefinition());
BeanDefinitionBuilder partitionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
PartitionAttributesFactoryBean.class);
mergeTemplateRegionPartitionAttributes(element, parserContext, regionBuilder, partitionAttributesBuilder);
parseColocatedWith(element, regionBuilder, partitionAttributesBuilder, "colocated-with");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "copies", "redundantCopies");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "local-max-memory");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "recovery-delay");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "startup-recovery-delay");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "total-buckets", "totalNumBuckets");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, "total-max-memory");
Element partitionResolverSubElement = DomUtils.getChildElementByTagName(element, "partition-resolver");
if (partitionResolverSubElement != null) {
partitionAttributesBuilder.addPropertyValue("partitionResolver",
parsePartitionResolver(partitionResolverSubElement, parserContext, regionBuilder));
}
Element partitionListenerSubElement = DomUtils.getChildElementByTagName(element, "partition-listener");
if (partitionListenerSubElement != null) {
partitionAttributesBuilder.addPropertyValue("partitionListeners",
parsePartitionListeners(partitionListenerSubElement, parserContext, regionBuilder));
}
List<Element> fixedPartitionSubElements = DomUtils.getChildElementsByTagName(element, "fixed-partition");
if (!CollectionUtils.isEmpty(fixedPartitionSubElements)){
@SuppressWarnings("rawtypes")
ManagedList fixedPartitionAttributes = new ManagedList();
for (Element fixedPartition : fixedPartitionSubElements) {
BeanDefinitionBuilder fixedPartitionAttributesBuilder = BeanDefinitionBuilder.genericBeanDefinition(
FixedPartitionAttributesFactoryBean.class);
ParsingUtils.setPropertyValue(fixedPartition, fixedPartitionAttributesBuilder, "partition-name");
ParsingUtils.setPropertyValue(fixedPartition, fixedPartitionAttributesBuilder, "num-buckets");
ParsingUtils.setPropertyValue(fixedPartition, fixedPartitionAttributesBuilder, "primary");
fixedPartitionAttributes.add(fixedPartitionAttributesBuilder.getBeanDefinition());
}
partitionAttributesBuilder.addPropertyValue("fixedPartitionAttributes", fixedPartitionAttributes);
}
regionAttributesBuilder.addPropertyValue("partitionAttributes", partitionAttributesBuilder.getBeanDefinition());
}
/* (non-Javadoc) */
void mergeTemplateRegionPartitionAttributes(Element element, ParserContext parserContext,
BeanDefinitionBuilder regionBuilder, BeanDefinitionBuilder partitionAttributesBuilder) {
String regionTemplateName = getParentName(element);
if (StringUtils.hasText(regionTemplateName)) {
if (parserContext.getRegistry().containsBeanDefinition(regionTemplateName)) {
BeanDefinition templateRegion = parserContext.getRegistry().getBeanDefinition(regionTemplateName);
BeanDefinition templateRegionAttributes = getRegionAttributesBeanDefinition(templateRegion);
if (templateRegionAttributes != null) {
if (templateRegionAttributes.getPropertyValues().contains("partitionAttributes")) {
PropertyValue partitionAttributesProperty = templateRegionAttributes.getPropertyValues()
.getPropertyValue("partitionAttributes");
Object partitionAttributes = partitionAttributesProperty.getValue();
if (partitionAttributes instanceof BeanDefinition) {
partitionAttributesBuilder.getRawBeanDefinition().overrideFrom(
(BeanDefinition) partitionAttributes);
}
}
}
}
else {
parserContext.getReaderContext().error(String.format(
"The Region template [%1$s] must be 'defined before' the Region [%2$s] referring to the template!",
regionTemplateName, resolveId(element, regionBuilder.getRawBeanDefinition(), parserContext)), element);
}
}
}
/* (non-Javadoc) */
private void parseColocatedWith(Element element, BeanDefinitionBuilder regionBuilder,
BeanDefinitionBuilder partitionAttributesBuilder, String attributeName) {
// NOTE rather than using a dependency (with depends-on) we could also set the colocatedWith property of the
// PartitionAttributesFactoryBean with a reference to the Region "this" Partitioned Region will be colocated
// with, where the colocated-with attribute refers to the the bean name/alias of the other, depended on Region
// providing that the Region's name is also a bean alias of the bean definition.
//ParsingUtils.setPropertyReference(element, partitionAttributesBuilder, attributeName, "colocatedWith");
ParsingUtils.setPropertyValue(element, partitionAttributesBuilder, attributeName);
String colocatedWithBeanAlias = element.getAttribute(attributeName);
if (StringUtils.hasText(colocatedWithBeanAlias)) {
regionBuilder.addDependsOn(colocatedWithBeanAlias);
}
}
/* (non-Javadoc) */
private Object parsePartitionResolver(Element subElement, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrSingleNestedBeanDeclaration(subElement, parserContext, builder);
}
/* (non-Javadoc) */
private Object parsePartitionListeners(Element subElement, ParserContext parserContext,
BeanDefinitionBuilder builder) {
return ParsingUtils.parseRefOrNestedBeanDeclaration(subElement, parserContext, builder);
}
}