org.springframework.integration.config.xml.AbstractConsumerEndpointParser.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.config.xml.AbstractConsumerEndpointParser.java

Source

/*
 * Copyright 2002-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.integration.config.xml;

import java.util.Collection;
import java.util.List;

import org.w3c.dom.Element;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
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.support.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;

/**
 * Base class parser for elements that create Message Endpoints.
 *
 * @author Mark Fisher
 * @author Oleg Zhurakousky
 * @author Gary Russell
 * @author Artem Bilan
 */
public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinitionParser {

    protected static final String REF_ATTRIBUTE = "ref";

    protected static final String METHOD_ATTRIBUTE = "method";

    protected static final String EXPRESSION_ATTRIBUTE = "expression";

    @Override
    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
            throws BeanDefinitionStoreException {

        String id = element.getAttribute(ID_ATTRIBUTE);
        if (!StringUtils.hasText(id)) {
            id = element.getAttribute("name");
        }
        if (!StringUtils.hasText(id)) {
            id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry(),
                    parserContext.isNested());
        }
        return id;
    }

    /**
     * Parse the MessageHandler.
     *
     * @param element The element.
     * @param parserContext The parser context.
     * @return The bean definition builder.
     */
    protected abstract BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext);

    protected String getInputChannelAttributeName() {
        return "input-channel";
    }

    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder handlerBuilder = parseHandler(element, parserContext);
        IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "output-channel",
                "outputChannelName");
        IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "order");

        Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
        Element adviceChainElement = DomUtils.getChildElementByTagName(element,
                IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN);

        @SuppressWarnings("rawtypes")
        ManagedList adviceChain = IntegrationNamespaceUtils.configureAdviceChain(adviceChainElement, txElement,
                true, handlerBuilder.getRawBeanDefinition(), parserContext);

        boolean hasAdviceChain = !CollectionUtils.isEmpty(adviceChain);
        if (hasAdviceChain) {
            handlerBuilder.addPropertyValue("adviceChain", adviceChain);
        }

        AbstractBeanDefinition handlerBeanDefinition = handlerBuilder.getBeanDefinition();
        handlerBeanDefinition.setResource(parserContext.getReaderContext().getResource());
        String elementDescription = IntegrationNamespaceUtils.createElementDescription(element);
        handlerBeanDefinition.setSource(elementDescription);
        String inputChannelAttributeName = getInputChannelAttributeName();
        boolean hasInputChannelAttribute = element.hasAttribute(inputChannelAttributeName);
        if (parserContext.isNested()) {
            if (hasInputChannelAttribute) {
                parserContext.getReaderContext().error("The '" + inputChannelAttributeName
                        + "' attribute isn't allowed for a nested (e.g. inside a <chain/>) endpoint element: "
                        + elementDescription + ".", element);
            }
            if (!replyChannelInChainAllowed(element)
                    && StringUtils.hasText(element.getAttribute("reply-channel"))) {
                parserContext.getReaderContext()
                        .error("The 'reply-channel' attribute isn't"
                                + " allowed for a nested (e.g. inside a <chain/>) outbound gateway element: "
                                + elementDescription + ".", element);
            }
            return handlerBeanDefinition;
        } else {
            if (!hasInputChannelAttribute) {
                parserContext.getReaderContext().error("The '" + inputChannelAttributeName
                        + "' attribute is required for the top-level endpoint element: " + elementDescription + ".",
                        element);
            }
        }

        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(ConsumerEndpointFactoryBean.class);

        if (hasAdviceChain) {
            builder.addPropertyValue("adviceChain", adviceChain);
        }

        String handlerBeanName = BeanDefinitionReaderUtils.generateBeanName(handlerBeanDefinition,
                parserContext.getRegistry());
        String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element);
        parserContext.registerBeanComponent(
                new BeanComponentDefinition(handlerBeanDefinition, handlerBeanName, handlerAlias));

        builder.addPropertyReference("handler", handlerBeanName);

        String inputChannelName = element.getAttribute(inputChannelAttributeName);

        if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)) {
            registerChannelForCreation(parserContext, inputChannelName);
        }
        IntegrationNamespaceUtils.checkAndConfigureFixedSubscriberChannel(element, parserContext, inputChannelName,
                handlerBeanName);

        builder.addPropertyValue("inputChannelName", inputChannelName);
        List<Element> pollerElementList = DomUtils.getChildElementsByTagName(element, "poller");
        poller(element, parserContext, builder, pollerElementList);
        IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
                IntegrationNamespaceUtils.AUTO_STARTUP);
        IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.PHASE);
        IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, IntegrationNamespaceUtils.ROLE);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        String beanName = resolveId(element, beanDefinition, parserContext);
        parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName));
        return null;
    }

    private void poller(Element element, ParserContext parserContext, BeanDefinitionBuilder builder,
            List<Element> pollerElementList) {
        if (!CollectionUtils.isEmpty(pollerElementList)) {
            if (pollerElementList.size() != 1) {
                parserContext.getReaderContext()
                        .error("at most one poller element may be configured for an endpoint", element);
            }
            IntegrationNamespaceUtils.configurePollerMetadata(pollerElementList.get(0), builder, parserContext);
        }
    }

    private void registerChannelForCreation(ParserContext parserContext, String inputChannelName) {
        if (parserContext.getRegistry()
                .containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {

            BeanDefinition channelRegistry = parserContext.getRegistry()
                    .getBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
            ConstructorArgumentValues caValues = channelRegistry.getConstructorArgumentValues();
            ValueHolder vh = caValues.getArgumentValue(0, Collection.class);
            if (vh == null) { //although it should never happen if it does we can fix it
                caValues.addIndexedArgumentValue(0, new ManagedSet<String>());
            }

            @SuppressWarnings("unchecked")
            Collection<String> channelCandidateNames = (Collection<String>) caValues
                    .getArgumentValue(0, Collection.class).getValue(); // NOSONAR see comment above
            channelCandidateNames.add(inputChannelName); // NOSONAR
        } else {
            parserContext.getReaderContext().error(
                    "Failed to locate '" + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'",
                    parserContext.getRegistry());
        }
    }

    /**
     * Override to allow 'reply-channel' within a chain, for components where it
     * makes sense (e.g. enricher). Default is false for outbound gateways, else true.
     * @param element the element.
     * @return true to allow a reply channel attribute within a chain.
     * @since 4.3
     */
    protected boolean replyChannelInChainAllowed(Element element) {
        String localName = element.getLocalName();
        return localName == null || !localName.contains("outbound-gateway");
    }

}