Java tutorial
/* * Copyright 2010-2019 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 * * https://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.apache.geode.cache.LossAction; import org.apache.geode.cache.MembershipAttributes; import org.apache.geode.cache.ResumptionAction; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Conventions; import org.springframework.data.gemfire.GemfireUtils; import org.springframework.data.gemfire.SubscriptionAttributesFactoryBean; import org.springframework.data.gemfire.config.support.GemfireFeature; import org.springframework.data.gemfire.eviction.EvictionAttributesFactoryBean; import org.springframework.data.gemfire.expiration.ExpirationAttributesFactoryBean; import org.springframework.data.gemfire.util.SpringUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; /** * 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 { protected static final String CACHE_PROPERTY_NAME = "cache"; protected static final String REGION_PROPERTY_NAME = "region"; protected static final String CACHE_REF_ATTRIBUTE_NAME = "cache-ref"; protected static final String REGION_REF_ATTRIBUTE_NAME = "region-ref"; static void setPropertyReference(Element element, BeanDefinitionBuilder builder, String attributeName, String propertyName) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyReference(propertyName, attributeValue); } } @SuppressWarnings("unused") static void setPropertyReference(Element element, BeanDefinitionBuilder builder, String attributeName) { setPropertyReference(element, builder, attributeName, Conventions.attributeNameToPropertyName(attributeName)); } static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attributeName, String propertyName, Object defaultValue) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyValue(propertyName, attributeValue); } else if (defaultValue != null) { builder.addPropertyValue(propertyName, defaultValue); } } static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attributeName, String propertyName) { setPropertyValue(element, builder, attributeName, propertyName, null); } static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attributeName) { setPropertyValue(element, builder, attributeName, Conventions.attributeNameToPropertyName(attributeName)); } static void setPropertyValue(BeanDefinitionBuilder builder, BeanDefinition source, String propertyName, boolean addDependsOn) { PropertyValue propertyValue = source.getPropertyValues().getPropertyValue(propertyName); if (propertyValue != null) { builder.addPropertyValue(propertyValue.getName(), propertyValue.getValue()); if (addDependsOn && propertyValue.getValue() instanceof RuntimeBeanReference) { builder.addDependsOn(((RuntimeBeanReference) propertyValue.getValue()).getBeanName()); } } } static void setPropertyValue(BeanDefinitionBuilder builder, BeanDefinition source, String propertyName) { setPropertyValue(builder, source, propertyName, false); } 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()) { String message = String.format( "Use either the [%1$s] attribute or a nested bean declaration for [%2$s] element, not both", refAttributeName, element.getLocalName()); parserContext.getReaderContext().error(message, element); } returnValue = new RuntimeBeanReference(refAttributeValue); } return returnValue; } static Object parseRefOrNestedCustomElement(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { Object beanRef = ParsingUtils.getBeanReference(element, parserContext, "bean"); return beanRef != null ? beanRef : parserContext.getDelegate().parseCustomElement(element, builder.getBeanDefinition()); } /** * 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(element, parserContext, builder, "ref", false); } static Object parseRefOrNestedBeanDeclaration(Element element, ParserContext parserContext, BeanDefinitionBuilder builder, String refAttributeName) { return parseRefOrNestedBeanDeclaration(element, parserContext, builder, refAttributeName, false); } 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(element, parserContext, refAttributeName); if (beanReference != null) { return beanReference; } // check nested declarations List<Element> childElements = DomUtils.getChildElements(element); // parse nested bean definition if (childElements.size() == 1) { return parserContext.getDelegate().parsePropertySubElement(childElements.get(0), builder.getRawBeanDefinition()); } else { // TODO also triggered when there are no child elements; need to change the message... if (single) { String message = String.format( "The element [%s] does not support multiple, nested bean definitions", element.getLocalName()); parserContext.getReaderContext().error(message, element); } } ManagedList<Object> list = new ManagedList<>(); for (Element childElement : childElements) { list.add(parserContext.getDelegate().parsePropertySubElement(childElement, builder.getRawBeanDefinition())); } return list; } static void parseCompressor(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { Element compressorElement = DomUtils.getChildElementByTagName(element, "compressor"); if (compressorElement != null) { regionAttributesBuilder.addPropertyValue("compressor", parseRefOrSingleNestedBeanDeclaration( compressorElement, parserContext, regionAttributesBuilder)); } } /** * Parses the eviction sub-element. Populates the given attribute factory with the proper attributes. * * @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(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { Element evictionElement = DomUtils.getChildElementByTagName(element, "eviction"); if (evictionElement != null) { BeanDefinitionBuilder evictionAttributesBuilder = BeanDefinitionBuilder .genericBeanDefinition(EvictionAttributesFactoryBean.class); setPropertyValue(evictionElement, evictionAttributesBuilder, "action"); setPropertyValue(evictionElement, evictionAttributesBuilder, "threshold"); setPropertyValue(evictionElement, evictionAttributesBuilder, "type"); Element objectSizerElement = DomUtils.getChildElementByTagName(evictionElement, "object-sizer"); if (objectSizerElement != null) { Object sizer = parseRefOrNestedBeanDeclaration(objectSizerElement, parserContext, evictionAttributesBuilder); evictionAttributesBuilder.addPropertyValue("objectSizer", sizer); } regionAttributesBuilder.addPropertyValue("evictionAttributes", evictionAttributesBuilder.getBeanDefinition()); return true; } return false; } /** * Parses the expiration sub-elements. Populates the given attribute factory with proper attributes. * * @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(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { boolean result = parseRegionEntryExpiration(element, "region-ttl", "regionTimeToLive", regionAttributesBuilder); result |= parseRegionEntryExpiration(element, "region-tti", "regionIdleTimeout", regionAttributesBuilder); result |= parseRegionEntryExpiration(element, "entry-ttl", "entryTimeToLive", regionAttributesBuilder); result |= parseRegionEntryExpiration(element, "entry-tti", "entryIdleTimeout", regionAttributesBuilder); result |= parseCustomExpiration(element, parserContext, "custom-entry-ttl", "customEntryTimeToLive", regionAttributesBuilder); result |= parseCustomExpiration(element, parserContext, "custom-entry-tti", "customEntryIdleTimeout", regionAttributesBuilder); if (result) { regionAttributesBuilder.addPropertyValue("statisticsEnabled", Boolean.TRUE); } return result; } private static boolean parseRegionEntryExpiration(Element rootElement, String elementName, String propertyName, BeanDefinitionBuilder regionAttributesBuilder) { Element expirationElement = DomUtils.getChildElementByTagName(rootElement, elementName); if (expirationElement != null) { BeanDefinitionBuilder expirationAttributesBuilder = BeanDefinitionBuilder .genericBeanDefinition(ExpirationAttributesFactoryBean.class); setPropertyValue(expirationElement, expirationAttributesBuilder, "action"); setPropertyValue(expirationElement, expirationAttributesBuilder, "timeout"); regionAttributesBuilder.addPropertyValue(propertyName, expirationAttributesBuilder.getBeanDefinition()); return true; } return false; } 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(expirationElement, parserContext, regionAttributesBuilder); regionAttributesBuilder.addPropertyValue(propertyName, customExpiry); return true; } return false; } @SuppressWarnings({ "deprecation", "unused" }) static void parseMembershipAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { Element membershipAttributes = DomUtils.getChildElementByTagName(element, "membership-attributes"); if (membershipAttributes != null) { String[] requiredRoles = StringUtils .commaDelimitedListToStringArray(membershipAttributes.getAttribute("required-roles")); String lossActionValue = membershipAttributes.getAttribute("loss-action"); LossAction lossAction = StringUtils.hasText(lossActionValue) ? LossAction.fromName(lossActionValue.toUpperCase().replace("-", "_")) : LossAction.NO_ACCESS; String resumptionActionValue = membershipAttributes.getAttribute("resumption-action"); ResumptionAction resumptionAction = StringUtils.hasText(resumptionActionValue) ? ResumptionAction.fromName(resumptionActionValue.toUpperCase().replace("-", "_")) : ResumptionAction.REINITIALIZE; regionAttributesBuilder.addPropertyValue("membershipAttributes", new MembershipAttributes(requiredRoles, lossAction, resumptionAction)); } } @SuppressWarnings("unused") static void parseOptionalRegionAttributes(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { 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, "ignore-jta", "ignoreJTA"); setPropertyValue(element, regionAttributesBuilder, "index-update-type"); setPropertyValue(element, regionAttributesBuilder, "initial-capacity"); setPropertyValue(element, regionAttributesBuilder, "is-lock-grantor", "lockGrantor"); setPropertyValue(element, regionAttributesBuilder, "key-constraint"); setPropertyValue(element, regionAttributesBuilder, "load-factor"); setPropertyValue(element, regionAttributesBuilder, "multicast-enabled"); setPropertyValue(element, regionAttributesBuilder, "off-heap"); setPropertyValue(element, regionAttributesBuilder, "publisher"); setPropertyValue(element, regionAttributesBuilder, "value-constraint"); String concurrencyChecksEnabled = element.getAttribute("concurrency-checks-enabled"); if (StringUtils.hasText(concurrencyChecksEnabled)) { ParsingUtils.setPropertyValue(element, regionAttributesBuilder, "concurrency-checks-enabled"); } } static void parseScope(Element element, BeanDefinitionBuilder builder) { String scopeAttributeValue = element.getAttribute("scope"); if (StringUtils.hasText(scopeAttributeValue)) { builder.addPropertyValue("scope", scopeAttributeValue); } } static void parseStatistics(Element element, BeanDefinitionBuilder regionAttributesBuilder) { setPropertyValue(element, regionAttributesBuilder, "statistics", "statisticsEnabled"); } /** * Parses the subscription sub-element. Populates the given attribute factory with the proper attributes. * * @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(Element element, ParserContext parserContext, BeanDefinitionBuilder regionAttributesBuilder) { Element subscriptionElement = DomUtils.getChildElementByTagName(element, "subscription"); if (subscriptionElement != null) { BeanDefinitionBuilder subscriptionAttributesBuilder = BeanDefinitionBuilder .genericBeanDefinition(SubscriptionAttributesFactoryBean.class); setPropertyValue(subscriptionElement, subscriptionAttributesBuilder, "type", "interestPolicy"); regionAttributesBuilder.addPropertyValue("subscriptionAttributes", subscriptionAttributesBuilder.getBeanDefinition()); return true; } return false; } static void parseTransportFilters(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { Element transportFilterElement = DomUtils.getChildElementByTagName(element, "transport-filter"); if (transportFilterElement != null) { builder.addPropertyValue("transportFilters", parseRefOrNestedBeanDeclaration(transportFilterElement, parserContext, builder)); } } @SuppressWarnings("unused") static void assertGemFireFeatureAvailable(Element element, ParserContext parserContext) { if (GemfireUtils.isGemfireFeatureUnavailable(element)) { String message = String.format("[%1$s] is not supported in %2$s v%3$s", element.getLocalName(), GemfireUtils.GEMFIRE_NAME, GemfireUtils.GEMFIRE_VERSION); parserContext.getReaderContext().error(message, element); } } static void setCacheReference(Element element, BeanDefinitionBuilder builder) { builder.addPropertyReference(CACHE_PROPERTY_NAME, resolveCacheReference(element)); } static String resolveCacheReference(Element element) { return resolveCacheReference(element.getAttribute(CACHE_REF_ATTRIBUTE_NAME)); } static String resolveCacheReference(String cacheReference) { return SpringUtils.defaultIfEmpty(cacheReference, GemfireConstants.DEFAULT_GEMFIRE_CACHE_NAME); } static void setRegionReference(Element element, BeanDefinitionBuilder builder) { builder.addPropertyReference(REGION_PROPERTY_NAME, resolveRegionReference(element)); } static String resolveRegionReference(Element element) { return element.getAttribute(REGION_REF_ATTRIBUTE_NAME); } static void throwExceptionWhenGemFireFeatureUnavailable(GemfireFeature feature, String elementName, String attributeName, ParserContext parserContext) { if (GemfireUtils.isGemfireFeatureUnavailable(feature)) { String messagePrefix = attributeName != null ? String.format("Attribute [%1$s] of element [%2$s]", attributeName, elementName) : String.format("Element [%s]", elementName); String message = String.format( "%1$s requires Pivotal GemFire version 7 or later. Current version is %2$s.", messagePrefix, GemfireUtils.GEMFIRE_VERSION); parserContext.getReaderContext().error(message, null); } } }