INT-2275: any outbound-channel-adapter in <chain>
Add re-init logic for nested chains Add logic about nested element for AbstractChannelAdapterParser Refactor of DefaultOutboundChannelAdapterParser Test for non-last nested chain with some outbound-channel-adapter Improve XSD for chain-type Manual outbound-channel-adapter ability for chain Integration tests for all outbound-channel-adapter within <chain> Remove redundant 'return-value-required' attribute from <stored-proc-outbound-channel-adapter> Add support 'expectReply' for FileWritingMessageHandler INT-2275 polishing & refactor FileOutbound*Parser HttpRequestExecutingMessageHandlerTests polishing INT-2275: polishing JavaDoc
This commit is contained in:
committed by
Oleg Zhurakousky
parent
4d5b8d5be1
commit
45c429ee2b
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -25,18 +25,28 @@ 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.DirectChannel;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base parser for Channel Adapters.
|
||||
*
|
||||
*
|
||||
* Includes logic to determine {@link org.springframework.integration.MessageChannel}:
|
||||
* if 'channel' attribute is defined - uses its value as 'channelName';
|
||||
* if 'id' attribute is defined - creates {@link DirectChannel} at runtime and uses id's value as 'channelName';
|
||||
* if current component is defined as nested element inside any other components e.g. <chain>
|
||||
* 'id' and 'channel' attributes will be ignored and this component will not be parsed as
|
||||
* {@link org.springframework.integration.endpoint.AbstractEndpoint}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected final String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
|
||||
String id = element.getAttribute("id");
|
||||
protected final String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
String id = element.getAttribute(ID_ATTRIBUTE);
|
||||
if (!element.hasAttribute("channel")) {
|
||||
// the created channel will get the 'id', so the adapter's bean name includes a suffix
|
||||
id = id + ".adapter";
|
||||
@@ -57,13 +67,15 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio
|
||||
}
|
||||
|
||||
private String createDirectChannel(Element element, ParserContext parserContext) {
|
||||
String channelId = element.getAttribute("id");
|
||||
if (parserContext.isNested()) {
|
||||
return null;
|
||||
}
|
||||
String channelId = element.getAttribute(ID_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(channelId)) {
|
||||
parserContext.getReaderContext().error("The channel-adapter's 'id' attribute is required when no 'channel' "
|
||||
+ "reference has been provided, because that 'id' would be used for the created channel.", element);
|
||||
}
|
||||
BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.DirectChannel");
|
||||
BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(channelBuilder.getBeanDefinition(), channelId);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
|
||||
return channelId;
|
||||
@@ -72,7 +84,7 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio
|
||||
/**
|
||||
* Subclasses must implement this method to parse the adapter element.
|
||||
* The name of the MessageChannel bean is provided.
|
||||
*/
|
||||
*/
|
||||
protected abstract AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,22 +23,31 @@ 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.ParserContext;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
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.integration.core.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()) {
|
||||
return this.parseConsumer(element, parserContext);
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class);
|
||||
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.ConsumerEndpointFactoryBean");
|
||||
builder.addPropertyReference("handler", this.parseAndRegisterConsumer(element, parserContext));
|
||||
if (pollerElement != null) {
|
||||
if (!StringUtils.hasText(channelName)) {
|
||||
@@ -60,15 +69,13 @@ public abstract class AbstractOutboundChannelAdapterParser extends AbstractChann
|
||||
protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) {
|
||||
AbstractBeanDefinition definition = this.parseConsumer(element, parserContext);
|
||||
if (definition == null) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Consumer parsing must return a BeanComponentDefinition.", element);
|
||||
parserContext.getReaderContext().error("Consumer parsing must return an AbstractBeanDefinition.", element);
|
||||
}
|
||||
String order = element.getAttribute("order");
|
||||
String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
|
||||
if (StringUtils.hasText(order)) {
|
||||
definition.getPropertyValues().addPropertyValue("order", order);
|
||||
definition.getPropertyValues().addPropertyValue(IntegrationNamespaceUtils.ORDER, order);
|
||||
}
|
||||
String beanName = BeanDefinitionReaderUtils.generateBeanName(
|
||||
definition, parserContext.getRegistry());
|
||||
String beanName = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry());
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(definition, beanName));
|
||||
return beanName;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -18,16 +18,14 @@ package org.springframework.integration.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
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.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageHandler;
|
||||
import org.springframework.integration.handler.MethodInvokingMessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -39,7 +37,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class DefaultOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
protected String parseAndRegisterConsumer(Element element, ParserContext parserContext) {
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanComponentDefinition innerConsumerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
|
||||
|
||||
String consumerRef = element.getAttribute(IntegrationNamespaceUtils.REF_ATTRIBUTE);
|
||||
@@ -61,42 +60,27 @@ public class DefaultOutboundChannelAdapterParser extends AbstractOutboundChannel
|
||||
"The 'method' attribute cannot be used with the 'expression' attribute.", element);
|
||||
}
|
||||
|
||||
if (hasMethod | isExpression) {
|
||||
BeanDefinitionBuilder consumerBuilder = null;
|
||||
if (hasMethod) {
|
||||
consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingMessageHandler.class);
|
||||
if (isRef) {
|
||||
consumerBuilder.addConstructorArgReference(consumerRef);
|
||||
}
|
||||
else {
|
||||
consumerBuilder.addConstructorArgValue(innerConsumerDefinition);
|
||||
}
|
||||
consumerBuilder.addConstructorArgValue(methodName);
|
||||
BeanDefinitionBuilder consumerBuilder = null;
|
||||
|
||||
if (isExpression) {
|
||||
consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageHandler.class);
|
||||
RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(consumerExpressionString);
|
||||
consumerBuilder.addConstructorArgValue(expressionDef);
|
||||
}
|
||||
else {
|
||||
consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingMessageHandler.class);
|
||||
if (isRef) {
|
||||
consumerBuilder.addConstructorArgReference(consumerRef);
|
||||
}
|
||||
else {
|
||||
consumerBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageHandler.class);
|
||||
RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(consumerExpressionString);
|
||||
consumerBuilder.addConstructorArgValue(expressionDef);
|
||||
consumerBuilder.addConstructorArgValue(innerConsumerDefinition);
|
||||
}
|
||||
|
||||
consumerBuilder.addPropertyValue("componentType", "outbound-channel-adapter");
|
||||
String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
|
||||
if (StringUtils.hasText(order)) {
|
||||
consumerBuilder.addPropertyValue(IntegrationNamespaceUtils.ORDER, order);
|
||||
}
|
||||
consumerRef = BeanDefinitionReaderUtils.registerWithGeneratedName(consumerBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
consumerBuilder.addConstructorArgValue(hasMethod ? methodName : "handleMessage");
|
||||
}
|
||||
else if (isInnerConsumer) {
|
||||
consumerRef = innerConsumerDefinition.getBeanName();
|
||||
}
|
||||
Assert.hasText(consumerRef, "cannot determine consumer for 'outbound-channel-adapter'");
|
||||
return consumerRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
throw new UnsupportedOperationException();
|
||||
consumerBuilder.addPropertyValue("componentType", "outbound-channel-adapter");
|
||||
return consumerBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.integration.Message;
|
||||
@@ -34,6 +32,9 @@ import org.springframework.integration.support.channel.BeanFactoryChannelResolve
|
||||
import org.springframework.integration.support.channel.ChannelResolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A composite {@link MessageHandler} implementation that invokes a chain of
|
||||
* MessageHandler instances in order.
|
||||
@@ -65,8 +66,9 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class MessageHandlerChain extends AbstractMessageHandler implements MessageProducer, Ordered {
|
||||
public class MessageHandlerChain extends AbstractMessageHandler implements MessageProducer {
|
||||
|
||||
private volatile List<MessageHandler> handlers;
|
||||
|
||||
@@ -79,8 +81,6 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa
|
||||
*/
|
||||
private volatile Long sendTimeout = null;
|
||||
|
||||
private volatile int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private volatile ChannelResolver channelResolver;
|
||||
|
||||
private volatile boolean initialized;
|
||||
@@ -100,15 +100,6 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "chain";
|
||||
@@ -156,6 +147,13 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa
|
||||
}
|
||||
};
|
||||
((MessageProducer) handler).setOutputChannel(nextChannel);
|
||||
|
||||
// If this 'handler' is a nested non-last <chain>, it is necessary
|
||||
// to 'force' re-init it for check its configuration in conjunction with current MessageHandlerChain.
|
||||
if (handler instanceof MessageHandlerChain) {
|
||||
new DirectFieldAccessor(handler).setPropertyValue("initialized", false);
|
||||
((MessageHandlerChain) handler).afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
else if (handler instanceof MessageProducer) {
|
||||
MessageChannel replyChannel = new ReplyForwardingMessageChannel();
|
||||
|
||||
@@ -789,7 +789,7 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="outbound-channel-adapter">
|
||||
<xsd:element name="outbound-channel-adapter" type="methodInvokingChannelAdapterType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Defines a Channel Adapter that receives from a MessageChannel and passes to
|
||||
@@ -797,89 +797,75 @@
|
||||
MessageHandler.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="methodInvokingChannelAdapterType">
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a channel. This is particularly relevant when that channel
|
||||
is using a "failover" dispatching strategy. It has no effect when this
|
||||
endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="logging-channel-adapter">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="channelAdapterType">
|
||||
<xsd:attribute name="level" default="INFO">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
<xsd:extension base="outboundChannelAdapterType">
|
||||
<xsd:attributeGroup ref="loggingChannelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="loggingChannelAdapterTypeChain">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outboundChannelAdapterTypeChain">
|
||||
<xsd:attributeGroup ref="loggingChannelAdapterAttributes"/>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:attributeGroup name="loggingChannelAdapterAttributes">
|
||||
<xsd:attribute name="level" default="INFO">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specify the log level.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="FATAL" />
|
||||
<xsd:enumeration value="ERROR" />
|
||||
<xsd:enumeration value="WARN" />
|
||||
<xsd:enumeration value="INFO" />
|
||||
<xsd:enumeration value="DEBUG" />
|
||||
<xsd:enumeration value="TRACE" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="logger-name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Provide a name for the logger. This is useful when there are multiple logging Channel Adapters configured,
|
||||
and you would like to differentiate them within the actual log. By default the logger name will be the
|
||||
fully qualified class name of the LoggingHandler implementation.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expression">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="FATAL" />
|
||||
<xsd:enumeration value="ERROR" />
|
||||
<xsd:enumeration value="WARN" />
|
||||
<xsd:enumeration value="INFO" />
|
||||
<xsd:enumeration value="DEBUG" />
|
||||
<xsd:enumeration value="TRACE" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="logger-name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Provide a name for the logger. This is useful when there are multiple logging Channel Adapters configured,
|
||||
and you would like to differentiate them within the actual log. By default the logger name will be the
|
||||
fully qualified class name of the LoggingHandler implementation.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expression">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Provide a SpEL expression to be evaluated against the Message as the root object. For example,
|
||||
the default behavior is equivalent to an expression of "payload", or an expression may evaluate
|
||||
against the payload itself ("payload.address.city") or headers ("headers.foo"). This attribute and
|
||||
the 'log-full-message' attribute are mutually exclusive. See the documentation on the
|
||||
'log-full-message' attribute for more information.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="log-full-message">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="log-full-message">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specify whether to log the full message. This attribute and the 'expression' attribute are
|
||||
mutually exclusive. Setting this to true is equivalent to setting an expression value of "#root"
|
||||
since the Message is the root object against which the expression will be evaluated. If no
|
||||
'expression' is provided, and this value is false (the default), only the payload will be logged.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a channel. This is particularly relevant when that channel
|
||||
is using a "failover" dispatching strategy. It has no effect when this
|
||||
endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="methodInvokingOrExpressionEvaluatingAttributes">
|
||||
<xsd:attribute name="ref" type="xsd:string">
|
||||
@@ -945,18 +931,42 @@ endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
|
||||
<xsd:complexType name="methodInvokingChannelAdapterType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="channelAdapterType">
|
||||
<xsd:extension base="outboundChannelAdapterType">
|
||||
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="channelAdapterType">
|
||||
<xsd:complexType name="methodInvokingChannelAdapterTypeChain">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="outboundChannelAdapterTypeChain">
|
||||
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="outboundChannelAdapterType">
|
||||
<xsd:all>
|
||||
<xsd:element name="poller" type="basePollerType" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:all>
|
||||
<xsd:attributeGroup ref="channelAdapterAttributes" />
|
||||
<xsd:attribute name="order">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specifies the order for invocation when this endpoint is connected as a
|
||||
subscriber to a channel. This is particularly relevant when that channel
|
||||
is using a "failover" dispatching strategy. It has no effect when this
|
||||
endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="outboundChannelAdapterTypeChain">
|
||||
<xsd:all>
|
||||
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:all>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="service-activator">
|
||||
@@ -1369,38 +1379,44 @@ endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="chain-type">
|
||||
<xsd:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:sequence>
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
|
||||
|
||||
<xsd:element name="service-activator" type="expressionOrInnerEndpointDefinitionAware" />
|
||||
<xsd:element name="splitter" type="splitter-type" />
|
||||
<xsd:element name="transformer" type="expressionOrInnerEndpointDefinitionAware" />
|
||||
<xsd:element name="header-enricher" type="header-enricher-type" />
|
||||
<xsd:element name="header-filter" type="header-filter-type" />
|
||||
<xsd:element name="enricher" type="enricher-type" />
|
||||
<xsd:element name="filter" type="filter-type" />
|
||||
<xsd:element name="aggregator" type="aggregator-type" />
|
||||
<xsd:element name="resequencer" type="resequencer-type" />
|
||||
<xsd:element name="router" type="routerTypeChain" />
|
||||
<xsd:element name="payload-type-router" type="payloadTypeRouterTypeChain" />
|
||||
<xsd:element name="recipient-list-router" type="recipientListRouterTypeChain" />
|
||||
<xsd:element name="exception-type-router" type="exceptionTypeRouterTypeChain" />
|
||||
<xsd:element name="header-value-router" type="headerValueRouterTypeChain" />
|
||||
<xsd:element name="delayer" type="delayer-type" />
|
||||
<xsd:element name="gateway" type="innerGatewayType" />
|
||||
<xsd:element name="poller" type="basePollerType" />
|
||||
<xsd:element name="payload-serializing-transformer" type="payload-serializing-transformer-type" />
|
||||
<xsd:element name="payload-deserializing-transformer" type="payload-deserializing-transformer-type" />
|
||||
<xsd:element name="object-to-string-transformer" type="specialized-transformer-type" />
|
||||
<xsd:element name="object-to-map-transformer" type="specialized-transformer-type" />
|
||||
<xsd:element name="map-to-object-transformer" type="map-to-object-transformer-type" />
|
||||
<xsd:element name="object-to-json-transformer" type="object-to-json-transformer-type" />
|
||||
<xsd:element name="json-to-object-transformer" type="json-to-object-transformer-type" />
|
||||
<xsd:element name="claim-check-in" type="claimCheckInTypeChain" />
|
||||
<xsd:element name="claim-check-out" type="claimCheckOutTypeChain" />
|
||||
<xsd:element name="control-bus" type="control-bus-type" />
|
||||
<xsd:element name="chain" type="chain-type" />
|
||||
</xsd:choice>
|
||||
<xsd:element name="service-activator" type="expressionOrInnerEndpointDefinitionAware"/>
|
||||
<xsd:element name="splitter" type="splitter-type"/>
|
||||
<xsd:element name="transformer" type="expressionOrInnerEndpointDefinitionAware"/>
|
||||
<xsd:element name="header-enricher" type="header-enricher-type"/>
|
||||
<xsd:element name="header-filter" type="header-filter-type"/>
|
||||
<xsd:element name="enricher" type="enricher-type"/>
|
||||
<xsd:element name="filter" type="filter-type"/>
|
||||
<xsd:element name="aggregator" type="aggregator-type"/>
|
||||
<xsd:element name="resequencer" type="resequencer-type"/>
|
||||
<xsd:element name="router" type="routerTypeChain"/>
|
||||
<xsd:element name="payload-type-router" type="payloadTypeRouterTypeChain"/>
|
||||
<xsd:element name="recipient-list-router" type="recipientListRouterTypeChain"/>
|
||||
<xsd:element name="exception-type-router" type="exceptionTypeRouterTypeChain"/>
|
||||
<xsd:element name="header-value-router" type="headerValueRouterTypeChain"/>
|
||||
<xsd:element name="delayer" type="delayer-type"/>
|
||||
<xsd:element name="gateway" type="innerGatewayType"/>
|
||||
<xsd:element name="poller" type="basePollerType"/>
|
||||
<xsd:element name="payload-serializing-transformer" type="payload-serializing-transformer-type"/>
|
||||
<xsd:element name="payload-deserializing-transformer" type="payload-deserializing-transformer-type"/>
|
||||
<xsd:element name="object-to-string-transformer" type="specialized-transformer-type"/>
|
||||
<xsd:element name="object-to-map-transformer" type="specialized-transformer-type"/>
|
||||
<xsd:element name="map-to-object-transformer" type="map-to-object-transformer-type"/>
|
||||
<xsd:element name="object-to-json-transformer" type="object-to-json-transformer-type"/>
|
||||
<xsd:element name="json-to-object-transformer" type="json-to-object-transformer-type"/>
|
||||
<xsd:element name="claim-check-in" type="claimCheckInTypeChain"/>
|
||||
<xsd:element name="claim-check-out" type="claimCheckOutTypeChain"/>
|
||||
<xsd:element name="control-bus" type="control-bus-type"/>
|
||||
<xsd:element name="chain" type="chain-type"/>
|
||||
</xsd:choice>
|
||||
<xsd:choice minOccurs="0">
|
||||
<xsd:element name="outbound-channel-adapter" type="methodInvokingChannelAdapterTypeChain"/>
|
||||
<xsd:element name="logging-channel-adapter" type="loggingChannelAdapterTypeChain"/>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="poller">
|
||||
|
||||
@@ -96,6 +96,17 @@
|
||||
<service-activator ref="testHandler" />
|
||||
</chain>
|
||||
|
||||
<chain input-channel="outboundChannelAdapterChannel">
|
||||
<outbound-channel-adapter ref="testConsumer"/>
|
||||
</chain>
|
||||
|
||||
<chain input-channel="loggingChannelAdapterChannel">
|
||||
<transformer expression="payload.toUpperCase()"/>
|
||||
<logging-channel-adapter level="WARN"/>
|
||||
</chain>
|
||||
|
||||
<beans:bean id="testConsumer" class="org.springframework.integration.config.TestConsumer" />
|
||||
|
||||
<channel id="strings">
|
||||
<queue/>
|
||||
</channel>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,22 +16,17 @@
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Factory;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
@@ -43,6 +38,15 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.matchers.JUnitMatchers.both;
|
||||
import static org.junit.matchers.JUnitMatchers.containsString;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Iwein Fuld
|
||||
@@ -94,9 +98,19 @@ public class ChainParserTests {
|
||||
@Autowired
|
||||
private MessageChannel headerValueRouterWithMappingInput;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel loggingChannelAdapterChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel outboundChannelAdapterChannel;
|
||||
|
||||
@Autowired
|
||||
private TestConsumer testConsumer;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("claimCheckInput")
|
||||
private MessageChannel claimCheckInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("claimCheckOutput")
|
||||
private PollableChannel claimCheckOutput;
|
||||
@@ -241,6 +255,40 @@ public class ChainParserTests {
|
||||
assertEquals(message.getPayload(), reply.getPayload());
|
||||
}
|
||||
|
||||
@Test //INT-2275
|
||||
public void chainWithOutboundChannelAdapter() {
|
||||
this.outboundChannelAdapterChannel.send(successMessage);
|
||||
assertSame(successMessage, testConsumer.getLastMessage());
|
||||
}
|
||||
|
||||
@Test //INT-2275
|
||||
public void chainWithLoggingChannelAdapter() {
|
||||
Message<?> message = MessageBuilder.withPayload("test").build();
|
||||
PrintStream realOut = System.out;
|
||||
try {
|
||||
OutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
this.loggingChannelAdapterChannel.send(message);
|
||||
assertEquals("LoggingHandler: TEST", out.toString().trim());
|
||||
}
|
||||
finally {
|
||||
System.setOut(realOut);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class) //INT-2275
|
||||
public void invalidNestedChainWithLoggingChannelAdapter() {
|
||||
try {
|
||||
new ClassPathXmlApplicationContext("invalidNestedChainWithOutboundChannelAdapter-context.xml", this.getClass());
|
||||
fail("BeanCreationException is expected!");
|
||||
}
|
||||
catch (BeansException e) {
|
||||
assertEquals(IllegalArgumentException.class, e.getCause().getClass());
|
||||
assertThat(e.getMessage(), both(containsString("output channel was provided")).and(containsString("does not implement the MessageProducer")));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StubHandler extends AbstractReplyProducingMessageHandler {
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<chain input-channel="invalidNestedChain">
|
||||
<chain>
|
||||
<logging-channel-adapter/>
|
||||
</chain>
|
||||
<logging-channel-adapter/>
|
||||
</chain>
|
||||
|
||||
</beans:beans>
|
||||
@@ -13,16 +13,22 @@
|
||||
<queue capacity="2"/>
|
||||
</channel>
|
||||
|
||||
<channel id="channelC"/>
|
||||
|
||||
<outbound-channel-adapter id="adapter" channel="channel" ref="bean" method="out" order="99" auto-startup="false"/>
|
||||
|
||||
<beans:bean id="bean"
|
||||
class="org.springframework.integration.config.xml.MethodInvokingOutboundChannelAdapterParserTests$TestBean"/>
|
||||
class="org.springframework.integration.config.xml.DefaultOutboundChannelAdapterParserTests$TestBean"/>
|
||||
|
||||
<outbound-channel-adapter id="adapterB" channel="channelB" method="out" order="99" auto-startup="false">
|
||||
<beans:bean class="org.springframework.integration.config.xml.MethodInvokingOutboundChannelAdapterParserTests$TestBean"/>
|
||||
<beans:bean class="org.springframework.integration.config.xml.DefaultOutboundChannelAdapterParserTests$TestBean"/>
|
||||
<poller task-executor="executor" max-messages-per-poll="5" fixed-delay="20" />
|
||||
</outbound-channel-adapter>
|
||||
|
||||
<task:executor id="executor" pool-size="5" />
|
||||
|
||||
<outbound-channel-adapter id="adapterC" channel="channelC" order="99">
|
||||
<beans:bean class="org.springframework.integration.config.TestConsumer"/>
|
||||
</outbound-channel-adapter>
|
||||
|
||||
</beans:beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -25,6 +25,7 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.integration.config.TestConsumer;
|
||||
import org.springframework.integration.handler.MethodInvokingMessageHandler;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -32,10 +33,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class MethodInvokingOutboundChannelAdapterParserTests {
|
||||
public class DefaultOutboundChannelAdapterParserTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@@ -44,21 +46,29 @@ public class MethodInvokingOutboundChannelAdapterParserTests {
|
||||
@Test
|
||||
public void checkConfig() {
|
||||
Object adapter = context.getBean("adapter");
|
||||
assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup"));
|
||||
Object handler = TestUtils.getPropertyValue(adapter, "handler");
|
||||
assertEquals(MethodInvokingMessageHandler.class, handler.getClass());
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(99, handlerAccessor.getPropertyValue("order"));
|
||||
assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup"));
|
||||
assertEquals(99, TestUtils.getPropertyValue(handler, "order"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkConfigWithInnerBeanAndPoller() {
|
||||
Object adapter = context.getBean("adapterB");
|
||||
assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup"));
|
||||
Object handler = TestUtils.getPropertyValue(adapter, "handler");
|
||||
assertEquals(MethodInvokingMessageHandler.class, handler.getClass());
|
||||
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
|
||||
assertEquals(99, handlerAccessor.getPropertyValue("order"));
|
||||
assertEquals(Boolean.FALSE, TestUtils.getPropertyValue(adapter, "autoStartup"));
|
||||
assertEquals(99, TestUtils.getPropertyValue(handler, "order"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkConfigWithInnerMessageHandler() {
|
||||
Object adapter = context.getBean("adapterC");
|
||||
Object handler = TestUtils.getPropertyValue(adapter, "handler");
|
||||
assertEquals(MethodInvokingMessageHandler.class, handler.getClass());
|
||||
assertEquals(99, TestUtils.getPropertyValue(handler, "order"));
|
||||
Object targetObject = TestUtils.getPropertyValue(handler, "processor.delegate.targetObject");
|
||||
assertEquals(TestConsumer.class, targetObject.getClass());
|
||||
}
|
||||
|
||||
|
||||
@@ -68,4 +78,5 @@ public class MethodInvokingOutboundChannelAdapterParserTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user