/*
* Copyright 2002-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.integration.config.xml;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the <poller> element.
*
* @author Mark Fisher
* @author Marius Bogoevici
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
*/
public class PollerParser extends AbstractBeanDefinitionParser {
private static final String MULTIPLE_TRIGGER_DEFINITIONS = "A <poller> cannot specify more than one trigger configuration.";
private static final String NO_TRIGGER_DEFINITIONS = "A <poller> must have one and only one trigger configuration.";
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
if (element.getAttribute("default").equals("true")) {
if (parserContext.getRegistry().isBeanNameInUse(PollerMetadata.DEFAULT_POLLER_METADATA_BEAN_NAME)) {
parserContext.getReaderContext().error(
"Only one default <poller/> element is allowed per context.", element);
}
if (StringUtils.hasText(id)) {
parserContext.getRegistry().registerAlias(id, PollerMetadata.DEFAULT_POLLER_METADATA_BEAN_NAME);
}
else {
id = PollerMetadata.DEFAULT_POLLER_METADATA_BEAN_NAME;
}
}
else if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"The 'id' attribute is required for a top-level poller element unless it is the default poller.",
element);
}
return id;
}
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder metadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(PollerMetadata.class);
if (element.hasAttribute("ref")) {
parserContext.getReaderContext().error(
"the 'ref' attribute must not be present on the top-level 'poller' element", element);
}
configureTrigger(element, metadataBuilder, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(metadataBuilder, element, "max-messages-per-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(metadataBuilder, element, "receive-timeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(metadataBuilder, element, "task-executor");
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");
IntegrationNamespaceUtils.configureAndSetAdviceChainIfPresent(adviceChainElement, txElement,
metadataBuilder.getRawBeanDefinition(), parserContext);
if (txElement != null) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(metadataBuilder, txElement,
"synchronization-factory", "transactionSynchronizationFactory");
}
else if (adviceChainElement != null) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(metadataBuilder, adviceChainElement,
"synchronization-factory", "transactionSynchronizationFactory");
}
String errorChannel = element.getAttribute("error-channel");
if (StringUtils.hasText(errorChannel)) {
BeanDefinitionBuilder errorHandler = BeanDefinitionBuilder.genericBeanDefinition(MessagePublishingErrorHandler.class);
errorHandler.addPropertyReference("defaultErrorChannel", errorChannel);
metadataBuilder.addPropertyValue("errorHandler", errorHandler.getBeanDefinition());
}
return metadataBuilder.getBeanDefinition();
}
private void configureTrigger(Element pollerElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) {
String triggerAttribute = pollerElement.getAttribute("trigger");
String fixedRateAttribute = pollerElement.getAttribute("fixed-rate");
String fixedDelayAttribute = pollerElement.getAttribute("fixed-delay");
String cronAttribute = pollerElement.getAttribute("cron");
String timeUnit = pollerElement.getAttribute("time-unit");
List<String> triggerBeanNames = new ArrayList<String>();
if (StringUtils.hasText(triggerAttribute)) {
if (StringUtils.hasText(timeUnit)) {
parserContext.getReaderContext().error("The 'time-unit' attribute cannot be used with a 'trigger' reference.", pollerElement);
}
triggerBeanNames.add(triggerAttribute);
}
if (StringUtils.hasText(fixedRateAttribute)) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PeriodicTrigger.class);
builder.addConstructorArgValue(fixedRateAttribute);
if (StringUtils.hasText(timeUnit)) {
builder.addConstructorArgValue(timeUnit);
}
builder.addPropertyValue("fixedRate", Boolean.TRUE);
String triggerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
builder.getBeanDefinition(), parserContext.getRegistry());
triggerBeanNames.add(triggerBeanName);
}
if (StringUtils.hasText(fixedDelayAttribute)) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PeriodicTrigger.class);
builder.addConstructorArgValue(fixedDelayAttribute);
if (StringUtils.hasText(timeUnit)) {
builder.addConstructorArgValue(timeUnit);
}
builder.addPropertyValue("fixedRate", Boolean.FALSE);
String triggerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
builder.getBeanDefinition(), parserContext.getRegistry());
triggerBeanNames.add(triggerBeanName);
}
if (StringUtils.hasText(cronAttribute)) {
if (StringUtils.hasText(timeUnit)) {
parserContext.getReaderContext().error("The 'time-unit' attribute cannot be used with a 'cron' trigger.", pollerElement);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CronTrigger.class);
builder.addConstructorArgValue(cronAttribute);
String triggerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
builder.getBeanDefinition(), parserContext.getRegistry());
triggerBeanNames.add(triggerBeanName);
}
if (triggerBeanNames.isEmpty()) {
parserContext.getReaderContext().error(NO_TRIGGER_DEFINITIONS, pollerElement);
}
if (triggerBeanNames.size() > 1) {
parserContext.getReaderContext().error(MULTIPLE_TRIGGER_DEFINITIONS, pollerElement);
}
targetBuilder.addPropertyReference("trigger", triggerBeanNames.get(0));
}
}