/* * Copyright 2002-2014 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 org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; 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.xml.ParserContext; import org.springframework.integration.config.ConsumerEndpointFactoryBean; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; /** * Base class for outbound Channel Adapter parsers. * * If this component is defined as the top-level element in the Spring application context it will produce * an {@link org.springframework.integration.endpoint.AbstractEndpoint} depending on the channel type. * If this component is defined as nested element (e.g., inside of the chain) it will produce * a {@link org.springframework.messaging.MessageHandler}. * * @author Mark Fisher * @author Gary Russell * @author Artem Bilan */ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChannelAdapterParser { @Override protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) { if (parserContext.isNested()) { if (channelName != null) { String elementDescription = IntegrationNamespaceUtils.createElementDescription(element); parserContext.getReaderContext().error( "The 'channel' attribute isn't allowed for " + elementDescription + " when it is used as a nested element," + " e.g. inside a <chain/>", element); } AbstractBeanDefinition consumerBeanDefinition = this.parseConsumer(element, parserContext); this.configureRequestHandlerAdviceChain(element, parserContext, consumerBeanDefinition, null); return consumerBeanDefinition; } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class); Element pollerElement = DomUtils.getChildElementByTagName(element, "poller"); BeanComponentDefinition handlerBeanComponentDefinition = this.doParseAndRegisterConsumer(element, parserContext); builder.addPropertyReference("handler", handlerBeanComponentDefinition.getBeanName()); IntegrationNamespaceUtils.checkAndConfigureFixedSubscriberChannel(element, parserContext, channelName, handlerBeanComponentDefinition.getBeanName()); if (pollerElement != null) { if (!StringUtils.hasText(channelName)) { parserContext.getReaderContext().error( "outbound channel adapter with a 'poller' requires a 'channel' to poll", element); } IntegrationNamespaceUtils.configurePollerMetadata(pollerElement, builder, parserContext); } builder.addPropertyValue("inputChannelName", channelName); this.configureRequestHandlerAdviceChain(element, parserContext, handlerBeanComponentDefinition.getBeanDefinition(), builder); return builder.getBeanDefinition(); } private void configureRequestHandlerAdviceChain(Element element, ParserContext parserContext, BeanDefinition handlerBeanDefinition, BeanDefinitionBuilder consumerBuilder) { Element txElement = DomUtils.getChildElementByTagName(element, "transactional"); Element adviceChainElement = DomUtils.getChildElementByTagName(element, IntegrationNamespaceUtils.REQUEST_HANDLER_ADVICE_CHAIN); @SuppressWarnings("rawtypes") ManagedList adviceChain = IntegrationNamespaceUtils.configureAdviceChain(adviceChainElement, txElement, handlerBeanDefinition, parserContext); if (!CollectionUtils.isEmpty(adviceChain)) { /* * For ARPMH, the advice chain is injected so just the handleRequestMessage method is advised. * Sometime ARPMHs do double duty as a gateway and a channel adapter. The parser subclass * can indicate this by overriding isUsingReplyProducer(), or we can try to determine it from * the bean class. */ boolean isReplyProducer = this.isUsingReplyProducer(); if (!isReplyProducer) { Class<?> beanClass = null; if (handlerBeanDefinition instanceof AbstractBeanDefinition) { AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) handlerBeanDefinition; if (abstractBeanDefinition.hasBeanClass()) { beanClass = abstractBeanDefinition.getBeanClass(); } } isReplyProducer = beanClass != null && AbstractReplyProducingMessageHandler.class.isAssignableFrom(beanClass); } if (isReplyProducer) { handlerBeanDefinition.getPropertyValues().add("adviceChain", adviceChain); } else if (consumerBuilder != null) { consumerBuilder.addPropertyValue("adviceChain", adviceChain); } else { String elementDescription = IntegrationNamespaceUtils.createElementDescription(element); parserContext.getReaderContext().error("'request-handler-advice-chain' isn't allowed for " + elementDescription + " within a <chain/>, because its Handler " + "isn't an AbstractReplyProducingMessageHandler", element); } } } /** * Override this method to control the registration process and return the bean name. * If parsing a bean definition whose name can be auto-generated, consider using * {@link #parseConsumer(Element, ParserContext)} instead. * * @param element The element. * @param parserContext The parser context. * @return The bean component definition. */ protected BeanComponentDefinition doParseAndRegisterConsumer(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = this.parseConsumer(element, parserContext); if (definition == null) { parserContext.getReaderContext().error("Consumer parsing must return an AbstractBeanDefinition.", element); } String order = element.getAttribute(IntegrationNamespaceUtils.ORDER); if (StringUtils.hasText(order)) { definition.getPropertyValues().addPropertyValue(IntegrationNamespaceUtils.ORDER, order); } String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element); BeanComponentDefinition beanComponentDefinition = new BeanComponentDefinition(definition, beanName, handlerAlias); parserContext.registerBeanComponent(beanComponentDefinition); return beanComponentDefinition; } /** * Override this method to return the BeanDefinition for the MessageConsumer. It will * be registered with a generated name. * * @param element The element. * @param parserContext The parser context. * @return The bean definition. */ protected abstract AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext); /** * Override this to signal that this channel adapter is actually using a AbstractReplyProducingMessageHandler * while it is not possible for this parser to determine that because, say, a FactoryBean is being used. * * @return false, unless overridden. */ protected boolean isUsingReplyProducer() { return false; } }