/*
* 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.amqp.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Base class for inbound adapter parsers for the AMQP namespace.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*
* @since 2.1
*/
abstract class AbstractAmqpInboundAdapterParser extends AbstractSingleBeanDefinitionParser {
private static final String[] CONTAINER_VALUE_ATTRIBUTES = {
"acknowledge-mode",
"channel-transacted",
"concurrent-consumers",
"consumers-per-queue",
"expose-listener-channel",
"phase",
"prefetch-count",
"queue-names",
"recovery-interval",
"receive-timeout",
"shutdown-timeout",
"tx-size",
"missing-queues-fatal"
};
private static final String[] CONTAINER_REFERENCE_ATTRIBUTES = {
"advice-chain",
"connection-factory",
"error-handler",
"message-properties-converter",
"task-executor",
"transaction-attribute",
"transaction-manager"
};
private final String adapterClassName;
AbstractAmqpInboundAdapterParser(String adapterClassName) {
Assert.hasText(adapterClassName, "adapterClassName is required");
this.adapterClassName = adapterClassName;
}
@Override
protected final String getBeanClassName(Element element) {
return this.adapterClassName;
}
@Override
protected final boolean shouldGenerateId() {
return false;
}
@Override
protected final boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String listenerContainerRef = element.getAttribute("listener-container");
if (StringUtils.hasText(listenerContainerRef)) {
assertNoContainerAttributes(element, parserContext);
builder.addConstructorArgReference(listenerContainerRef);
}
else {
BeanDefinition listenerContainerBeanDef = this.buildListenerContainer(element, parserContext);
builder.addConstructorArgValue(listenerContainerBeanDef);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter");
BeanDefinitionBuilder mapperBuilder = BeanDefinitionBuilder
.genericBeanDefinition(DefaultAmqpHeaderMapper.class);
mapperBuilder.setFactoryMethod("inboundMapper");
IntegrationNamespaceUtils.configureHeaderMapper(element, builder, parserContext, mapperBuilder, null);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "phase");
this.configureChannels(element, parserContext, builder);
}
protected abstract void configureChannels(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
private BeanDefinition buildListenerContainer(Element element, ParserContext parserContext) {
if (!element.hasAttribute("queue-names")) {
parserContext.getReaderContext().error("If no 'listener-container' reference is provided, " +
"the 'queue-names' attribute is required.", element);
}
String consumersPerQueue = element.getAttribute("consumers-per-queue");
BeanDefinitionBuilder builder;
if (StringUtils.hasText(consumersPerQueue)) {
builder = BeanDefinitionBuilder.genericBeanDefinition(DirectMessageListenerContainer.class);
if (StringUtils.hasText(element.getAttribute("concurrent-consumers"))) {
parserContext.getReaderContext().error("'consumers-per-queue' and 'concurrent-consumers' are mutually "
+ "exclusive", element);
}
if (StringUtils.hasText(element.getAttribute("tx-size"))) {
parserContext.getReaderContext().error("'tx-size' is not allowed with 'consumers-per-queue'", element);
}
if (StringUtils.hasText(element.getAttribute("receive-timeout"))) {
parserContext.getReaderContext().error("'receive-timeout' is not allowed with 'consumers-per-queue'",
element);
}
builder.addPropertyValue("consumersPerQueue", consumersPerQueue);
}
else {
builder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMessageListenerContainer.class);
}
String connectionFactoryRef = element.getAttribute("connection-factory");
if (!StringUtils.hasText(connectionFactoryRef)) {
connectionFactoryRef = "rabbitConnectionFactory";
}
builder.addConstructorArgReference(connectionFactoryRef);
for (String attributeName : CONTAINER_VALUE_ATTRIBUTES) {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attributeName);
}
for (String attributeName : CONTAINER_REFERENCE_ATTRIBUTES) {
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, attributeName);
}
return builder.getBeanDefinition();
}
private void assertNoContainerAttributes(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
List<String> allContainerAttributes = new ArrayList<String>(Arrays.asList(CONTAINER_VALUE_ATTRIBUTES));
allContainerAttributes.addAll(Arrays.asList(CONTAINER_REFERENCE_ATTRIBUTES));
for (String attributeName : allContainerAttributes) {
if (StringUtils.hasText(element.getAttribute(attributeName))) {
parserContext.getReaderContext().error("Attribute '" + attributeName
+ "' is not allowed when a 'listener-container' reference has been provided", source);
}
}
}
}