Merge pull request #753 from srt/INT-2755

* INT-2755:
  INT-2755: Support 'id' For <chain> Child Elements
This commit is contained in:
Gary Russell
2013-05-21 12:29:31 -04:00
30 changed files with 648 additions and 288 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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
@@ -23,6 +23,8 @@ import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.context.NamedComponent;
import org.springframework.integration.context.Orderable;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageProducer;
@@ -34,8 +36,10 @@ import org.springframework.util.CollectionUtils;
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*/
public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler> implements FactoryBean<MessageHandler>, BeanFactoryAware {
public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler>
implements FactoryBean<MessageHandler>, BeanFactoryAware {
private volatile H handler;
@@ -51,6 +55,7 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
private volatile List<Advice> adviceChain;
private volatile String componentName;
public AbstractSimpleMessageHandlerFactoryBean() {
super();
@@ -76,16 +81,19 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
this.adviceChain = adviceChain;
}
/**
* Sets the name of the handler component.
*
* @param componentName
*/
public void setComponentName(String componentName) {
this.componentName = componentName;
}
public H getObject() throws Exception {
if (this.handler == null) {
this.handler = this.createHandlerInternal();
Assert.notNull(this.handler, "failed to create MessageHandler");
if (this.handler instanceof MessageProducer && this.outputChannel != null) {
((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
}
if (this.handler instanceof Orderable && this.order != null) {
((Orderable) this.handler).setOrder(this.order.intValue());
}
}
return this.handler;
}
@@ -100,10 +108,19 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
if (handler instanceof BeanFactoryAware) {
((BeanFactoryAware) handler).setBeanFactory(getBeanFactory());
}
if (this.handler instanceof MessageProducer && this.outputChannel != null) {
((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
}
if (this.handler instanceof IntegrationObjectSupport && this.componentName != null) {
((IntegrationObjectSupport) this.handler).setComponentName(this.componentName);
}
if (!CollectionUtils.isEmpty(this.adviceChain) &&
this.handler instanceof AbstractReplyProducingMessageHandler) {
((AbstractReplyProducingMessageHandler) this.handler).setAdviceChain(this.adviceChain);
}
if (this.handler instanceof Orderable && this.order != null) {
((Orderable) this.handler).setOrder(this.order);
}
this.initialized = true;
}
if (handler instanceof InitializingBean) {
@@ -130,4 +147,4 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
return true;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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
@@ -13,15 +13,22 @@
package org.springframework.integration.config.xml;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
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;
@@ -41,23 +48,52 @@ import org.springframework.util.xml.DomUtils;
*/
public class ChainParser extends AbstractConsumerEndpointParser {
/**
* {@link BeanDefinition} attribute used to pass down the current bean id for nested chains, allowing full
* qualification of 'named' handlers within nested chains.
*
*/
private static final String SI_CHAIN_NESTED_ID_ATTRIBUTE = "SI.ChainParser.NestedId.Prefix";
private final Log logger = LogFactory.getLog(this.getClass());
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageHandlerChain.class);
ManagedList<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
logger.info("It is useful to provide an explicit 'id' attribute on 'chain' elements " +
"to simplify the identification of child elements in logs etc.");
}
String chainHandlerId = this.resolveId(element, builder.getRawBeanDefinition(), parserContext);
List<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
Set<String> handlerBeanNameSet = new HashSet<String>();
NodeList children = element.getChildNodes();
int childOrder = 0;
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && !"poller".equals(child.getLocalName())) {
BeanDefinitionHolder holder = this.parseChild((Element) child, parserContext, builder.getBeanDefinition());
if ("gateway".equals(child.getLocalName())){
BeanMetadataElement childBeanMetadata = this.parseChild(chainHandlerId, (Element) child, childOrder++,
parserContext, builder.getBeanDefinition());
if (childBeanMetadata instanceof RuntimeBeanReference) {
String handlerBeanName = ((RuntimeBeanReference) childBeanMetadata).getBeanName();
if (!handlerBeanNameSet.add(handlerBeanName)) {
parserContext.getReaderContext().error("A bean definition is already registered for " +
"beanName: '" + handlerBeanName + "' within the current <chain>.",
element);
return null;
}
}
if ("gateway".equals(child.getLocalName())) {
BeanDefinitionBuilder gwBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".gateway.RequestReplyMessageHandlerAdapter");
gwBuilder.addConstructorArgValue(holder);
gwBuilder.addConstructorArgValue(childBeanMetadata);
handlerList.add(gwBuilder.getBeanDefinition());
}
else {
handlerList.add(holder);
handlerList.add(childBeanMetadata);
}
}
}
@@ -68,31 +104,30 @@ public class ChainParser extends AbstractConsumerEndpointParser {
return builder;
}
private void validateChild(Element element, ParserContext parserContext) {
final Object source = parserContext.extractSource(element);
final String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
if (StringUtils.hasText(order)) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"an 'order' attribute when used within a chain.", source);
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
BeanDefinition containingBeanDefinition = parserContext.getContainingBeanDefinition();
if (containingBeanDefinition != null) {
String nestedChainIdPrefix = (String) containingBeanDefinition.getAttribute(SI_CHAIN_NESTED_ID_ATTRIBUTE);
if (StringUtils.hasText(nestedChainIdPrefix)) {
id = nestedChainIdPrefix + "$child." + id;
}
}
final List<Element> pollerChildElements = DomUtils
.getChildElementsByTagName(element, "poller");
if (!pollerChildElements.isEmpty()) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"a 'poller' sub-element when used within a chain.", source);
}
definition.setAttribute(SI_CHAIN_NESTED_ID_ATTRIBUTE, id);
return id;
}
private BeanDefinitionHolder parseChild(Element element, ParserContext parserContext, BeanDefinition parentDefinition) {
private BeanMetadataElement parseChild(String chainHandlerId, Element element, int order, ParserContext parserContext,
BeanDefinition parentDefinition) {
BeanDefinitionHolder holder = null;
String id = element.getAttribute(ID_ATTRIBUTE);
boolean hasId = StringUtils.hasText(id);
String handlerComponentName = chainHandlerId + "$child" + (hasId ? "." + id : "#" + order);
if ("bean".equals(element.getLocalName())) {
holder = parserContext.getDelegate().parseBeanDefinitionElement(element, parentDefinition);
}
@@ -103,13 +138,40 @@ public class ChainParser extends AbstractConsumerEndpointParser {
BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(element, parentDefinition);
if (beanDefinition == null) {
parserContext.getReaderContext().error("child BeanDefinition must not be null", element);
return null;
}
else {
String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, parserContext.getRegistry(), true);
holder = new BeanDefinitionHolder(beanDefinition, beanName);
holder = new BeanDefinitionHolder(beanDefinition, handlerComponentName + IntegrationNamespaceUtils.HANDLER_ALIAS_SUFFIX);
}
}
holder.getBeanDefinition().getPropertyValues().add("componentName", handlerComponentName);
if (hasId) {
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
return new RuntimeBeanReference(holder.getBeanName());
}
return holder;
}
private void validateChild(Element element, ParserContext parserContext) {
final Object source = parserContext.extractSource(element);
final String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
if (StringUtils.hasText(order)) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"an 'order' attribute when used within a chain.", source);
}
final List<Element> pollerChildElements = DomUtils.getChildElementsByTagName(element, "poller");
if (!pollerChildElements.isEmpty()) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"a 'poller' sub-element when used within a chain.", source);
}
}
}

View File

@@ -175,34 +175,6 @@ public class MessageHandlerChain extends AbstractMessageHandler implements Messa
}
}
@Override
public void setComponentName(String componentName) {
super.setComponentName(componentName);
int i = 0;
if (this.handlers != null) {
for (MessageHandler messageHandler : this.handlers) {
try {
MessageHandler targetHandler = messageHandler;
if (AopUtils.isAopProxy(targetHandler)) {
Object target = ((Advised) targetHandler).getTargetSource().getTarget();
if (target instanceof MessageHandler) {
targetHandler = (MessageHandler) target;
}
}
if (targetHandler instanceof IntegrationObjectSupport) {
((IntegrationObjectSupport) targetHandler).setComponentName(componentName + ".handler#" + i);
}
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Could not set component name for handler "
+ messageHandler + " for " + componentName + " :" + e.getMessage());
}
}
i++; // increment, regardless of whether we assigned a component name
}
}
}
/**
* SmartLifecycle implementation (delegates to the {@link #handlers})
*/

View File

@@ -727,6 +727,7 @@
Defines a Messaging Gateway to be used within a chain.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string" use="optional" />
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1048,6 +1049,7 @@
<xsd:all>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="service-activator">
@@ -1067,7 +1069,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
@@ -1090,6 +1092,15 @@
Base type for Message-handling endpoints.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
'id' value:
- Identifies the underlying Spring bean definition (AbstractEndpoint)
- as MessageHandler bean alias together with suffix '.handler'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -1166,7 +1177,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="enricher-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1301,6 +1312,7 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="propertySubElementType">
@@ -1370,17 +1382,6 @@
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
'id' value is used:
- as the 'AbstractEndpoint' bean 'id' if this tag is defined as root element.
- as the DelayHandler bean alias together with suffix '.handler'
- as the 'messageGroupId' property of DelayHandler together with suffix '.messageGroupId'
in the operations of the MessageGroupStore for scheduling delayed messages.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-delay" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1437,6 +1438,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:element name="bridge">
@@ -1452,7 +1454,8 @@
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="poller" />
</xsd:choice>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -1468,7 +1471,7 @@
<xsd:element name="poller" type="basePollerType" minOccurs="0"/>
<xsd:group ref="chain-elements-group" maxOccurs="unbounded"/>
</xsd:choice>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -1479,6 +1482,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -1519,6 +1523,7 @@
<xsd:sequence>
<xsd:group ref="chain-elements-group"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -1710,7 +1715,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="header-enricher-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1855,6 +1860,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="userDefinedHeaderType">
@@ -1970,7 +1976,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="header-filter-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1998,6 +2004,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="transformer">
@@ -2009,7 +2016,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2026,7 +2033,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="specialized-transformer-charset-aware-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2040,7 +2047,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="specialized-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2054,7 +2061,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="map-to-object-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2085,6 +2092,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="object-to-json-transformer">
@@ -2096,7 +2104,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="object-to-json-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2139,6 +2147,7 @@
</xsd:appinfo>-->
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="json-to-object-transformer">
@@ -2150,7 +2159,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="json-to-object-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2185,6 +2194,7 @@
</xsd:appinfo>-->
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="payload-serializing-transformer">
@@ -2196,7 +2206,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="payload-serializing-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2220,6 +2230,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="payload-deserializing-transformer">
@@ -2231,7 +2242,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="payload-deserializing-transformer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2255,6 +2266,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="syslog-to-map-transformer">
@@ -2264,7 +2276,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -2285,7 +2298,7 @@
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element ref="poller" />
</xsd:sequence>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2313,7 +2326,7 @@
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element ref="poller" />
</xsd:sequence>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2346,6 +2359,7 @@
</xsd:complexType>
<xsd:complexType name="commonClaimCheckType">
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="message-store" default="messageStore">
<xsd:annotation>
<xsd:documentation>
@@ -2367,6 +2381,7 @@
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="poller" />
</xsd:choice>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="specialized-transformer-charset-aware-type">
@@ -2394,7 +2409,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="filter-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2946,16 +2961,6 @@
-->
<xsd:attributeGroup name="topLevelRouterAttributeGroup">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[Identifies the underlying Spring bean
definition which in case of Routers is an instance of
EventDrivenConsumer or PollingConsumer depending on whether
the Router's "input-channel" is a "SubscribableChannel" or
"PollableChannel", respectively. This is an "optional" attribute.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" default="true">
<xsd:annotation>
<xsd:documentation>
@@ -2998,6 +3003,16 @@
-->
<xsd:complexType name="abstractRouterType" abstract="true">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[Identifies the underlying Spring bean
definition which in case of Routers is an instance of
EventDrivenConsumer or PollingConsumer depending on whether
the Router's "input-channel" is a "SubscribableChannel" or
"PollableChannel", respectively. This is an "optional" attribute.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-output-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -3099,7 +3114,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="splitter-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3149,7 +3164,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="aggregator-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3318,7 +3333,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="resequencer-type">
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3787,7 +3802,7 @@ The list of component name patterns you want to track (e.g., tracked-components
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:element name="poller" type="basePollerType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3805,6 +3820,7 @@ The list of component name patterns you want to track (e.g., tracked-components
3) get/set or shutdown methods on configurable TaskExecutors or TaskSchedulers
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:attributeGroup name="inputOutputChannelGroup">
@@ -3864,19 +3880,6 @@ endpoint itself is a Polling Consumer for a channel with a queue.
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="inputOutputChannelGroupWithId">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
'id' value:
- Identifies the underlying Spring bean definition (AbstractEndpoint)
- as MessageHandler bean alias together with suffix '.handler'
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="inputOutputChannelGroup"/>
</xsd:attributeGroup>
<xsd:attributeGroup name="subscribersAttributeGroup">
<xsd:attribute name="max-subscribers" type="xsd:string">
<xsd:annotation>

View File

@@ -1,78 +1,89 @@
<?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
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">
<channel id="pollableInput1">
<queue />
<queue/>
</channel>
<channel id="pollableInput2">
<queue />
<queue/>
</channel>
<channel id="output">
<queue />
<queue/>
</channel>
<channel id="replyOutput">
<queue />
<queue/>
</channel>
<chain input-channel="filterInput" output-channel="output">
<filter ref="typeSelector" />
<service-activator ref="testHandler" />
<chain id="filterChain" input-channel="filterInput" output-channel="output">
<filter id="filterWithinChain" ref="typeSelector"/>
<service-activator id="serviceActivatorWithinChain" ref="testHandler"/>
</chain>
<chain input-channel="headerEnricherInput">
<header-enricher>
<chain id="headerEnricherChain" input-channel="headerEnricherInput">
<header-enricher id="headerEnricherWithinChain">
<reply-channel ref="replyOutput"/>
<correlation-id value="ABC"/>
<header name="testValue" value="XYZ" />
<header name="testRef" ref="testHeaderValue" />
<header name="testValue" value="XYZ"/>
<header name="testRef" ref="testHeaderValue"/>
</header-enricher>
<service-activator ref="testHandler" />
<service-activator ref="testHandler"/>
</chain>
<chain input-channel="pollableInput1" output-channel="output">
<filter ref="typeSelector" />
<poller fixed-delay="10000" />
<service-activator ref="testHandler" />
<filter ref="typeSelector"/>
<poller fixed-delay="10000"/>
<service-activator ref="testHandler"/>
</chain>
<chain input-channel="pollableInput2" output-channel="output">
<service-activator ref="testHandler" />
<service-activator ref="testHandler"/>
<poller ref="topLevelPoller"/>
</chain>
<poller id="topLevelPoller" fixed-delay="5000" />
<poller id="topLevelPoller" fixed-delay="5000"/>
<chain input-channel="beanInput" output-channel="output">
<beans:bean
class="org.springframework.integration.config.ChainParserTests$StubHandler" />
class="org.springframework.integration.config.ChainParserTests$StubHandler"/>
</chain>
<chain input-channel="aggregatorInput" output-channel="output">
<aggregator ref="aggregatorBean" method="aggregate" />
<chain>
<filter ref="typeSelector" />
<service-activator ref="testHandler" />
<chain id="aggregatorChain" input-channel="aggregatorInput" output-channel="output">
<aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
<chain id="nestedChain">
<filter id="filterWithinNestedChain" ref="typeSelector"/>
<service-activator ref="testHandler"/>
<chain id="doubleNestedChain">
<filter id="filterWithinDoubleNestedChain" ref="typeSelector"/>
</chain>
</chain>
</chain>
<chain input-channel="payloadTypeRouterInput">
<payload-type-router>
<chain id="aggregatorChain2" input-channel="aggregatorInput" output-channel="output">
<aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
<chain id="nestedChain">
<filter id="filterWithinNestedChain" ref="typeSelector"/>
<service-activator ref="testHandler"/>
</chain>
</chain>
<chain id="payloadTypeRouterChain" input-channel="payloadTypeRouterInput">
<payload-type-router id="payloadTypeRouterWithinChain">
<mapping type="java.lang.String" channel="strings"/>
<mapping type="java.lang.Number" channel="numbers"/>
</payload-type-router>
</chain>
<chain input-channel="headerValueRouterInput">
<header-value-router header-name="routingHeader"/>
<chain id="headerValueRouterChain" input-channel="headerValueRouterInput">
<header-value-router id="headerValueRouterWithinChain" header-name="routingHeader"/>
</chain>
<chain input-channel="headerValueRouterWithMappingInput">
@@ -82,31 +93,74 @@
</header-value-router>
</chain>
<chain id="chainWithClaimChecks" input-channel="claimCheckInput" output-channel="claimCheckOutput">
<claim-check-in/>
<claim-check-out/>
</chain>
<channel id="claimCheckOutput">
<queue/>
</channel>
<chain id="chainWithSendTimeout" input-channel="chainWithSendTimeoutInput" output-channel="output" send-timeout="9876">
<filter ref="typeSelector" />
<service-activator ref="testHandler" />
<chain id="chainWithClaimChecks" input-channel="claimCheckInput" output-channel="claimCheckOutput">
<claim-check-in id="claimCheckInWithinChain"/>
<claim-check-out id="claimCheckOutWithinChain"/>
</chain>
<chain input-channel="outboundChannelAdapterChannel">
<outbound-channel-adapter ref="testConsumer"/>
<channel id="claimCheckOutput">
<queue/>
</channel>
<chain id="chainWithSendTimeout" input-channel="chainWithSendTimeoutInput" output-channel="output"
send-timeout="9876">
<filter ref="typeSelector"/>
<service-activator ref="testHandler"/>
</chain>
<chain id="outboundChain" input-channel="outboundChannelAdapterChannel">
<outbound-channel-adapter id="outboundChannelAdapterWithinChain" ref="testConsumer"/>
</chain>
<chain id="logChain" input-channel="loggingChannelAdapterChannel">
<object-to-string-transformer charset="cp1251"/>
<transformer expression="payload.toUpperCase()"/>
<logging-channel-adapter level="WARN"/>
<transformer id="transformerWithinChain" expression="payload.toUpperCase()"/>
<logging-channel-adapter id="loggingChannelAdapterWithinChain" level="WARN"/>
</chain>
<beans:bean id="testConsumer" class="org.springframework.integration.config.TestConsumer" />
<chain id="subComponentsIdSupport1" input-channel="subComponentsIdSupport1Channel">
<splitter id="splitterWithinChain"/>
<resequencer id="resequencerWithinChain"/>
<enricher id="enricherWithinChain" requires-reply="false">
<property name="foo" value="bar"/>
</enricher>
<header-filter id="headerFilterWithinChain" header-names="foo"/>
<payload-serializing-transformer id="payloadSerializingTransformerWithinChain"/>
<payload-deserializing-transformer id="payloadDeserializingTransformerWithinChain"/>
<gateway id="gatewayWithinChain" request-channel="strings"/>
<object-to-string-transformer id="objectToStringTransformerWithinChain"/>
<object-to-map-transformer id="objectToMapTransformerWithinChain"/>
<map-to-object-transformer id="mapToObjectTransformerWithinChain"
type="org.springframework.integration.config.ChainParserTests$FooPojo"/>
<object-to-json-transformer id="objectToJsonTransformerWithinChain"/>
<json-to-object-transformer id="jsonToObjectTransformerWithinChain"
type="org.springframework.integration.config.ChainParserTests$FooPojo"/>
<control-bus id="controlBusWithinChain"/>
<router id="routerWithinChain" expression="foo"/>
</chain>
<chain id="exceptionTypeRouterChain" input-channel="subComponentsIdSupport2Channel">
<exception-type-router id="exceptionTypeRouterWithinChain">
<mapping channel="numbers" exception-type="org.springframework.integration.MessageRejectedException"/>
</exception-type-router>
</chain>
<chain id="recipientListRouterChain" input-channel="subComponentsIdSupport3Channel">
<recipient-list-router id="recipientListRouterWithinChain">
<recipient channel="strings"/>
<recipient channel="numbers"/>
</recipient-list-router>
</chain>
<chain id="chainReplayRequired" input-channel="chainReplayRequiredChannel">
<transformer id="transformerReplayRequired" expression="null"/>
</chain>
<chain id="chainMessageRejectedException" input-channel="chainMessageRejectedExceptionChannel">
<filter id="filterMessageRejectedException" expression="false" throw-exception-on-rejection="true"/>
</chain>
<beans:bean id="testConsumer" class="org.springframework.integration.config.TestConsumer"/>
<channel id="strings">
<queue/>
@@ -117,21 +171,21 @@
</channel>
<beans:bean id="aggregatorBean"
class="org.springframework.integration.config.ChainParserTests$StubAggregator" />
class="org.springframework.integration.config.ChainParserTests$StubAggregator"/>
<beans:bean id="testHeaderValue" class="java.lang.Integer">
<beans:constructor-arg value="123" />
<beans:constructor-arg value="123"/>
</beans:bean>
<beans:bean id="typeSelector"
class="org.springframework.integration.selector.PayloadTypeSelector">
<beans:constructor-arg value="java.lang.String" />
class="org.springframework.integration.selector.PayloadTypeSelector">
<beans:constructor-arg value="java.lang.String"/>
</beans:bean>
<beans:bean id="testHandler"
class="org.springframework.integration.config.TestHandler">
<beans:constructor-arg value="1" />
<beans:property name="replyMessageText" value="foo" />
class="org.springframework.integration.config.TestHandler">
<beans:constructor-arg value="1"/>
<beans:property name="replyMessageText" value="foo"/>
</beans:bean>
<beans:bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore"/>

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -38,23 +39,30 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
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.MessageRejectedException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.MessageMatcher;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.MessageTransformingHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
@@ -70,6 +78,9 @@ import org.springframework.util.StringUtils;
@RunWith(SpringJUnit4ClassRunner.class)
public class ChainParserTests {
@Autowired
private BeanFactory beanFactory;
@Autowired
@Qualifier("filterInput")
private MessageChannel filterInput;
@@ -141,6 +152,12 @@ public class ChainParserTests {
@Autowired
private PollableChannel numbers;
@Autowired
private MessageChannel chainReplayRequiredChannel;
@Autowired
private MessageChannel chainMessageRejectedExceptionChannel;
public static Message<?> successMessage = MessageBuilder.withPayload("success").build();
@Factory
@@ -324,6 +341,81 @@ public class ChainParserTests {
assertEquals(false, TestUtils.getPropertyValue(handlerChain, "running"));
}
@Test
public void testInt2755SubComponentsIdSupport() {
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1.handler"));
assertTrue(this.beanFactory.containsBean("filterChain$child.filterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("filterChain$child.serviceActivatorWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain$child.aggregatorWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.filterWithinNestedChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.doubleNestedChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.doubleNestedChain$child.filterWithinDoubleNestedChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain2.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.aggregatorWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.nestedChain.handler"));
assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.nestedChain$child.filterWithinNestedChain.handler"));
assertTrue(this.beanFactory.containsBean("payloadTypeRouterChain$child.payloadTypeRouterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("headerValueRouterChain$child.headerValueRouterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("chainWithClaimChecks$child.claimCheckInWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("chainWithClaimChecks$child.claimCheckOutWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("outboundChain$child.outboundChannelAdapterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("logChain$child.transformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("logChain$child.loggingChannelAdapterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.splitterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.resequencerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.enricherWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.headerFilterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.payloadSerializingTransformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.payloadDeserializingTransformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.gatewayWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToStringTransformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToMapTransformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.mapToObjectTransformerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.controlBusWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.routerWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("exceptionTypeRouterChain$child.exceptionTypeRouterWithinChain.handler"));
assertTrue(this.beanFactory.containsBean("recipientListRouterChain$child.recipientListRouterWithinChain.handler"));
MessageHandlerChain chain = this.beanFactory.getBean("headerEnricherChain.handler", MessageHandlerChain.class);
List handlers = TestUtils.getPropertyValue(chain, "handlers", List.class);
assertTrue(handlers.get(0) instanceof MessageTransformingHandler);
assertEquals("headerEnricherChain$child.headerEnricherWithinChain", TestUtils.getPropertyValue(handlers.get(0), "componentName"));
assertEquals("headerEnricherChain$child.headerEnricherWithinChain.handler", TestUtils.getPropertyValue(handlers.get(0), "beanName"));
assertTrue(this.beanFactory.containsBean("headerEnricherChain$child.headerEnricherWithinChain.handler"));
assertTrue(handlers.get(1) instanceof ServiceActivatingHandler);
assertEquals("headerEnricherChain$child#1", TestUtils.getPropertyValue(handlers.get(1), "componentName"));
assertNull(TestUtils.getPropertyValue(handlers.get(1), "beanName"));
assertFalse(this.beanFactory.containsBean("headerEnricherChain$child#1.handler"));
}
@Test
public void testInt2755SubComponentException() {
GenericMessage<String> testMessage = new GenericMessage<String>("test");
try {
this.chainReplayRequiredChannel.send(testMessage);
fail("Expected ReplyRequiredException");
}
catch (Exception e) {
assertTrue(e instanceof ReplyRequiredException);
assertTrue(e.getMessage().contains("'chainReplayRequired$child.transformerReplayRequired'"));
}
try {
this.chainMessageRejectedExceptionChannel.send(testMessage);
fail("Expected MessageRejectedException");
}
catch (Exception e) {
assertTrue(e instanceof MessageRejectedException);
assertTrue(e.getMessage().contains("chainMessageRejectedException$child.filterMessageRejectedException"));
}
}
public static class StubHandler extends AbstractReplyProducingMessageHandler {
@Override
@@ -339,4 +431,20 @@ public class ChainParserTests {
return StringUtils.collectionToCommaDelimitedString(strings);
}
}
public static class FooPojo {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
}

View File

@@ -1,6 +1,5 @@
package org.springframework.integration.config.xml;
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -14,14 +13,17 @@ package org.springframework.integration.config.xml;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.util.Properties;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
@@ -35,6 +37,7 @@ import org.springframework.core.io.InputStreamResource;
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Artem Bilan
*
*/
public class ChainElementsFailureTests {
@@ -227,12 +230,25 @@ public class ChainElementsFailureTests {
}
}
@Test
public void testInt2755DetectDuplicateHandlerId() throws Exception {
try {
this.bootStrap("duplicate-handler-id");
fail("Expected a BeanDefinitionParsingException to be thrown.");
}
catch (BeanDefinitionParsingException e) {
assertTrue(e.getMessage().contains("A bean definition is already registered for " +
"beanName: 'foo$child.bar.handler' within the current <chain>."));
}
}
private ApplicationContext bootStrap(String configProperty) throws Exception {
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
pfb.setLocation(new ClassPathResource("org/springframework/integration/config/xml/chain-elements-config.properties"));
pfb.afterPropertiesSet();
Properties prop = pfb.getObject();
StringBuffer buffer = new StringBuffer();
StringBuilder buffer = new StringBuilder();
buffer.append(prop.getProperty("xmlheaders")).append(prop.getProperty(configProperty)).append(prop.getProperty("xmlfooter"));
ByteArrayInputStream stream = new ByteArrayInputStream(buffer.toString().getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
@@ -243,7 +259,7 @@ public class ChainElementsFailureTests {
return ac;
}
public static class Sampleservice {
public static class SampleService {
public String echo(String value){
return value;
}

View File

@@ -20,6 +20,7 @@ import static org.junit.Assert.assertNotSame;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +37,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@Ignore
public class NestedChainParserTests {
@Autowired

View File

@@ -10,7 +10,7 @@ xmlfooter= </beans>
service-activator=\
<int:chain input-channel="input"> \
<int:service-activator input-channel="fail"> \
<bean class="org.springframework.integration.config.xml.ChainElementsFailureTests$Sampleservice"/> \
<bean class="org.springframework.integration.config.xml.ChainElementsFailureTests$SampleService"/> \
</int:service-activator> \
</int:chain>
@@ -81,4 +81,9 @@ resequencer-poller=\
<int:resequencer> \
<int:poller fixed-rate="5000" max-messages-per-poll="10" />\
</int:resequencer> \
</int:chain>
</int:chain>
duplicate-handler-id=\
<int:chain id="foo" input-channel="input"> \
<int:splitter id="bar"/> \
<int:aggregator id="bar"/> \
</int:chain>

View File

@@ -172,21 +172,6 @@ public class MessageHandlerChainTests {
chain.afterPropertiesSet();
}
@Test
public void componentNaming() {
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
handlers.add(producer1);
handlers.add(handler1); // this one won't be named
handlers.add(producer2);
handlers.add(producer3);
MessageHandlerChain chain = new MessageHandlerChain();
chain.setHandlers(handlers);
chain.setComponentName("testChain");
assertEquals("testChain.handler#0", producer1.getComponentName());
assertEquals("testChain.handler#2", producer2.getComponentName());
assertEquals("testChain.handler#3", producer3.getComponentName());
}
private static class ProducingHandlerStub extends IntegrationObjectSupport implements MessageHandler, MessageProducer {
private volatile MessageChannel output;
@@ -199,7 +184,7 @@ public class MessageHandlerChainTests {
public void setOutputChannel(MessageChannel channel) {
this.output = channel;
}
public void handleMessage(Message<?> message) {

View File

@@ -46,6 +46,7 @@ import static org.junit.Assert.assertNull;
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Artem Bilan
*/
public class MessageHistoryIntegrationTests {
@@ -69,61 +70,65 @@ public class MessageHistoryIntegrationTests {
public void handleMessage(Message<?> message) {
Iterator<Properties> historyIterator = message.getHeaders().get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator();
Properties event1 = historyIterator.next();
assertEquals("sampleGateway", event1.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("gateway", event1.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event = historyIterator.next();
assertEquals("sampleGateway", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("gateway", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event2 = historyIterator.next();
assertEquals("bridgeInChannel", event2.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event2.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("bridgeInChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event3 = historyIterator.next();
assertEquals("testBridge", event3.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("bridge", event3.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("testBridge", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("bridge", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event4 = historyIterator.next();
assertEquals("headerEnricherChannel", event4.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event4.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("headerEnricherChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event5 = historyIterator.next();
assertEquals("testHeaderEnricher", event5.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("transformer", event5.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("testHeaderEnricher", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("transformer", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event6 = historyIterator.next();
assertEquals("chainChannel", event6.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event6.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("chainChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event7 = historyIterator.next();
assertEquals("sampleChain", event7.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("chain", event7.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("sampleChain", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("chain", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event8 = historyIterator.next();
assertEquals("filterChannel", event8.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event8.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("sampleChain$child.service-activator-within-chain", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("service-activator", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event9 = historyIterator.next();
assertEquals("testFilter", event9.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("filter", event9.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("filterChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event10 = historyIterator.next();
assertEquals("splitterChannel", event10.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event10.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("testFilter", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("filter", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event11 = historyIterator.next();
assertEquals("testSplitter", event11.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("splitter", event11.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("splitterChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event12 = historyIterator.next();
assertEquals("aggregatorChannel", event12.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event12.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("testSplitter", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("splitter", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event13 = historyIterator.next();
assertEquals("testAggregator", event13.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("aggregator", event13.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("aggregatorChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
Properties event14 = historyIterator.next();
assertEquals("endOfThePipeChannel", event14.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event14.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("testAggregator", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("aggregator", event.getProperty(MessageHistory.TYPE_PROPERTY));
event = historyIterator.next();
assertEquals("endOfThePipeChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(message);

View File

@@ -5,34 +5,35 @@
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">
<int:gateway id="sampleGateway"
<int:gateway id="sampleGateway"
service-interface="org.springframework.integration.history.MessageHistoryIntegrationTests.SampleGateway"
default-request-channel="bridgeInChannel"/>
<int:channel id="bridgeInChannel"/>
<int:bridge id="testBridge" input-channel="bridgeInChannel" output-channel="headerEnricherChannel"/>
<int:header-enricher id="testHeaderEnricher" input-channel="headerEnricherChannel" output-channel="chainChannel">
<int:header name="foo" value="foo"/>
</int:header-enricher>
<int:chain id="sampleChain" input-channel="chainChannel" output-channel="filterChannel">
<int:header-enricher>
<int:header name="baz" value="baz"/>
</int:header-enricher>
<int:service-activator id="service-activator-within-chain" expression="payload"/>
</int:chain>
<int:filter id="testFilter" input-channel="filterChannel"
<int:filter id="testFilter" input-channel="filterChannel"
output-channel="splitterChannel" expression="payload.equals('hello')"/>
<int:splitter id="testSplitter" input-channel="splitterChannel" output-channel="aggregatorChannel"/>
<int:aggregator id="testAggregator" input-channel="aggregatorChannel" output-channel="endOfThePipeChannel"/>
<int:channel id="endOfThePipeChannel"/>
<bean class="org.springframework.integration.history.MessageHistoryConfigurer"/>
</beans>

View File

@@ -17,4 +17,8 @@
<beans:bean id="testBean" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$TestBean"/>
<chain id="transformerChain" input-channel="transformerChainInput">
<transformer expression="null"/>
</chain>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 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.
@@ -17,7 +17,9 @@
package org.springframework.integration.transformer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,12 +28,15 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@@ -46,6 +51,9 @@ public class SpelTransformerIntegrationTests {
@Autowired @Qualifier("output")
private PollableChannel output;
@Autowired
private MessageChannel transformerChainInput;
@Test
public void simple() {
@@ -63,6 +71,16 @@ public class SpelTransformerIntegrationTests {
assertEquals("testFOO", result.getPayload());
}
@Test
public void testInt2755ChainChildIdWithinExceptionMessage() {
try {
this.transformerChainInput.send(new GenericMessage<String>("foo"));
}
catch (ReplyRequiredException e) {
assertThat(e.getMessage(), Matchers.containsString("No reply produced by handler 'transformerChain$child#0'"));
}
}
static class TestBean {

View File

@@ -18,7 +18,7 @@
<si:header-enricher>
<si:header name="#{T(org.springframework.integration.file.FileHeaders).FILENAME}" value="${test.file}"/>
</si:header-enricher>
<file:outbound-channel-adapter directory="${work.dir}"/>
<file:outbound-channel-adapter id="file-outbound-channel-adapter-within-chain" directory="${work.dir}"/>
</si:chain>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">

View File

@@ -28,6 +28,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -61,6 +62,9 @@ public class FileOutboundChannelAdapterInsideChainTests {
@Autowired
private MessageChannel outboundChainChannel;
@Autowired
private BeanFactory beanFactory;
private static File workDir;
@BeforeClass

View File

@@ -33,7 +33,7 @@
delete-source-files="true"/>
<si:chain input-channel="fileOutboundGatewayInsideChain" output-channel="output">
<outbound-gateway directory="${java.io.tmpdir}/anyDir" delete-source-files="true"/>
<outbound-gateway id="file-outbound-gateway-within-chain" directory="${java.io.tmpdir}/anyDir" delete-source-files="true"/>
</si:chain>
<!--suppress SpringModelInspection -->

View File

@@ -19,7 +19,9 @@ package org.springframework.integration.file;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
@@ -150,6 +152,10 @@ public class FileOutboundGatewayIntegrationTests {
@Test //INT-1029
public void moveInsideTheChain() throws Exception {
// INT-2755
Object bean = this.beanFactory.getBean("org.springframework.integration.handler.MessageHandlerChain#0$child.file-outbound-gateway-within-chain.handler");
assertTrue(bean instanceof FileWritingMessageHandler);
fileOutboundGatewayInsideChain.send(message);
List<Message<?>> result = outputChannel.clear();
assertThat(result.size(), is(1));

View File

@@ -54,7 +54,7 @@
maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="integration:inputOutputChannelGroupWithId" />
<xsd:attributeGroup ref="integration:inputOutputChannelGroup" />
<xsd:attribute name="customizer" type="xsd:string">
<xsd:annotation>
<xsd:documentation>

View File

@@ -39,7 +39,7 @@
<int:chain input-channel="tcpOutboundGatewayInsideChain" output-channel="replyChannel">
<ip:tcp-outbound-gateway connection-factory="crLfClient2"/>
<ip:tcp-outbound-gateway id="tcp-outbound-gateway-within-chain" connection-factory="crLfClient2"/>
</int:chain>
<int:channel id="replyChannel" >

View File

@@ -173,6 +173,7 @@ public class TcpConfigOutboundGatewayTests {
@Test //INT-1029
public void testOutboundInsideChain() throws Exception {
// this.ctx.getBean("tcp-outbound-gateway-within-chain.handler", TcpOutboundGateway.class);
tcpOutboundGatewayInsideChain.send(MessageBuilder.withPayload("test").build());
byte[] bytes = (byte[]) replyChannel.receive().getPayload();
assertEquals("echo:test", new String(bytes).trim());

View File

@@ -73,7 +73,7 @@ public class JdbcOutboundGatewayParserTests {
@SuppressWarnings("unchecked")
Map<String, ?> payload = (Map<String, ?>) reply.getPayload();
assertEquals("bar", payload.get("name"));
JdbcOutboundGateway gateway = context.getBean(JdbcOutboundGateway.class);
JdbcOutboundGateway gateway = context.getBean("jdbcGateway.handler", JdbcOutboundGateway.class);
assertEquals(23, TestUtils.getPropertyValue(gateway, "order"));
Object gw = context.getBean("jdbcGateway");
assertEquals(1, adviceCalled);
@@ -204,6 +204,10 @@ public class JdbcOutboundGatewayParserTests {
@Test //INT-1029
public void testOutboundGatewayInsideChain() {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("handlingMapPayloadJdbcOutboundGatewayTest.xml", getClass());
//INT-2755
assertNotNull(context.getBean("org.springframework.integration.handler.MessageHandlerChain#0$child.jdbc-outbound-gateway-within-chain.handler",
JdbcOutboundGateway.class));
MessageChannel channel = context.getBean("jdbcOutboundGatewayInsideChain", MessageChannel.class);
channel.send(MessageBuilder.withPayload(Collections.singletonMap("foo", "bar")).build());

View File

@@ -24,7 +24,7 @@
</outbound-gateway>
<si:chain input-channel="jdbcOutboundGatewayInsideChain" output-channel="replyChannel">
<outbound-gateway query="select * from foos where id=:headers[id]"
<outbound-gateway id="jdbc-outbound-gateway-within-chain" query="select * from foos where id=:headers[id]"
update="insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])"
data-source="dataSource"/>

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:jmx="http://www.springframework.org/schema/integration/jmx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:si="http://www.springframework.org/schema/integration"
xmlns:jmx="http://www.springframework.org/schema/integration/jmx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
@@ -24,15 +24,16 @@
object-name="test.publisher:name=publisher"
default-notification-type="default.type">
<jmx:request-handler-advice-chain>
<bean class="org.springframework.integration.jmx.config.NotificationPublishingChannelAdapterParserTests$FooADvice" />
<bean class="org.springframework.integration.jmx.config.NotificationPublishingChannelAdapterParserTests$FooADvice"/>
</jmx:request-handler-advice-chain>
</jmx:notification-publishing-channel-adapter>
<si:chain input-channel="publishingWithinChainChannel">
<si:chain id="chainWithJmxNotificationPublishing" input-channel="publishingWithinChainChannel">
<jmx:notification-publishing-channel-adapter
object-name="test.publisher:name=publisher-chain"
default-notification-type="default.type"/>
id="jmx-notification-publishing-channel-adapter-within-chain"
object-name="test.publisher:name=publisher-chain"
default-notification-type="default.type"/>
</si:chain>
<bean id="testListener" class="org.springframework.integration.jmx.config.TestListener"/>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -20,16 +20,22 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.ObjectName;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jmx.JmxHeaders;
import org.springframework.integration.jmx.NotificationPublishingMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -53,6 +59,12 @@ public class NotificationPublishingChannelAdapterParserTests {
@Autowired
private MessageChannel publishingWithinChainChannel;
@Autowired
private BeanFactory beanFactory;
@Autowired
private MBeanServer server;
private static volatile int adviceCalled;
@After
@@ -60,7 +72,6 @@ public class NotificationPublishingChannelAdapterParserTests {
listener.lastNotification = null;
}
@Test
public void publishStringMessage() throws Exception {
adviceCalled = 0;
@@ -102,6 +113,8 @@ public class NotificationPublishingChannelAdapterParserTests {
@Test //INT-2275
public void publishStringMessageWithinChain() throws Exception {
assertNotNull(this.beanFactory.getBean("chainWithJmxNotificationPublishing$child.jmx-notification-publishing-channel-adapter-within-chain.handler",
MessageHandler.class));
assertNull(listener.lastNotification);
Message<?> message = MessageBuilder.withPayload("XYZ")
.setHeader(JmxHeaders.NOTIFICATION_TYPE, "test.type").build();
@@ -111,9 +124,15 @@ public class NotificationPublishingChannelAdapterParserTests {
assertEquals("XYZ", notification.getMessage());
assertEquals("test.type", notification.getType());
assertNull(notification.getUserData());
Set<ObjectName> names = server.queryNames(
new ObjectName("org.springframework.integration:type=MessageHandler," +
"name=chainWithJmxNotificationPublishing$child.jmx-notification-publishing-channel-adapter-within-chain.handler,*")
, null);
assertEquals(1, names.size());
}
private static class TestData {
}
public static class FooADvice extends AbstractRequestHandlerAdvice {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -40,6 +40,7 @@ import org.springframework.util.CollectionUtils;
* @author Amol Nayak
* @author Gunnar Hillert
* @author Gary Russell
* @author Artem Bilan
* @since 2.2
*
*/
@@ -69,6 +70,8 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
private long replyTimeout;
private volatile String componentName;
/**
* Constructor taking an {@link JpaExecutor} that wraps all JPA Operations.
*
@@ -114,6 +117,16 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
public void setReplyTimeout(long replyTimeout) {
this.replyTimeout = replyTimeout;
}
/**
* Sets the name of the handler component.
*
* @param componentName
*/
public void setComponentName(String componentName) {
this.componentName = componentName;
}
@Override
public Class<?> getObjectType() {
return MessageHandler.class;
@@ -128,6 +141,7 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
jpaOutboundGateway.setOutputChannel(this.outputChannel);
jpaOutboundGateway.setOrder(this.order);
jpaOutboundGateway.setSendTimeout(replyTimeout);
jpaOutboundGateway.setComponentName(this.componentName);
if (this.adviceChain != null) {
jpaOutboundGateway.setAdviceChain(this.adviceChain);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,6 +18,7 @@ package org.springframework.integration.mail;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.DataInputStream;
@@ -30,7 +31,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.mapping.MessageMappingException;
import org.springframework.integration.message.GenericMessage;
@@ -48,6 +51,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
public class MailSendingMessageHandlerContextTests {
@Autowired
@Qualifier("mailSendingMessageConsumer")
private MailSendingMessageHandler handler;
@Autowired
@@ -56,6 +60,9 @@ public class MailSendingMessageHandlerContextTests {
@Autowired
private MessageChannel sendMailOutboundChainChannel;
@Autowired
private BeanFactory beanFactory;
@Before
public void reset() {
@@ -106,6 +113,7 @@ public class MailSendingMessageHandlerContextTests {
@Test //INT-2275
public void mailOutboundChannelAdapterWithinChain() {
assertNotNull(this.beanFactory.getBean("org.springframework.integration.handler.MessageHandlerChain#0$child.mail-outbound-channel-adapter-within-chain.handler"));
this.sendMailOutboundChainChannel.send(MailTestsHelper.createIntegrationMessage());
SimpleMailMessage mailMessage = MailTestsHelper.createSimpleMailMessage();
assertEquals("no mime message should have been sent", 0, this.mailSender.getSentMimeMessages().size());

View File

@@ -22,7 +22,7 @@
</bean>
<int:chain input-channel="sendMailOutboundChainChannel">
<int-mail:outbound-channel-adapter mail-sender="javaMailSender"/>
<int-mail:outbound-channel-adapter id="mail-outbound-channel-adapter-within-chain" mail-sender="javaMailSender"/>
</int:chain>
</beans>

View File

@@ -53,7 +53,7 @@
</section>
<section id="chain-namespace">
<title>Configuring Chain</title>
<title>Configuring a Chain</title>
<para>
The &lt;chain&gt; element provides an <code>input-channel</code> attribute, and if the last element in the chain is capable
of producing reply messages (optional), it also supports an <code>output-channel</code> attribute. The sub-elements are then
@@ -106,22 +106,62 @@
attributes and elements.
</para>
</important>
<para><emphasis>'id' Attribute</emphasis></para>
<para>
The <emphasis>id</emphasis> attribute, however, is allowed to be specified.
In fact, the <link linkend='delayer'><emphasis>Delayer</emphasis></link>
component actually requires the <emphasis>id</emphasis> attribute to be present.
Beginning with Spring Integration 3.0, if a chain element is given an <emphasis>id</emphasis>, the
bean name for the element is a combination of the chain's <emphasis>id</emphasis> and the <emphasis>id</emphasis>
of the element itself. Elements without an <emphasis>id</emphasis> are not registered
as beans, but they are given <code>componentName</code>s that include the chain id. For example:
<programlisting language="xml"><![CDATA[<int:chain id="fooChain" input-channel="input">
<int:service-activator id="fooService" ref="someService" method="someMethod"/>
<int:object-to-json-transformer/>
</int:chain>]]></programlisting>
<itemizedlist>
<listitem>
The <code>&lt;chain&gt;</code> root element has an <emphasis>id</emphasis> 'fooChain'. So, the
<classname>AbstractEndpoint</classname> implementation (<classname>PollingConsumer</classname> or
<classname>EventDrivenConsumer</classname>, depending on the <emphasis>input-channel</emphasis> type)
bean takes this value as it's bean name.
</listitem>
<listitem>
The <classname>MessageHandlerChain</classname> bean acquires a bean alias 'fooChain.handler', which allows
direct access to this bean from the <interfacename>BeanFactory</interfacename>.
</listitem>
<listitem>
The <code>&lt;service-activator&gt;</code> is not a fully-fledged Messaging Endpoint (<classname>PollingConsumer</classname>
or <classname>EventDrivenConsumer</classname>) - it is simply a
<interfacename>MessageHandler</interfacename> within the <code>&lt;chain&gt;</code>. In this case,
the bean name registered with the <interfacename>BeanFactory</interfacename>
is 'fooChain$child.fooService.handler'.
</listitem>
<listitem>
The <emphasis>componentName</emphasis> of this <classname>ServiceActivatingHandler</classname> takes the
same value, but without the '.handler' suffix - 'fooChain$child.fooService'.
</listitem>
<listitem>
The last <code>&lt;chain&gt;</code> sub-component, <code>&lt;object-to-json-transformer&gt;</code>, doesn't have
an <emphasis>id</emphasis> attribute. Its <emphasis>componentName</emphasis> is based on its
position in the <code>&lt;chain&gt;</code>. In this case, it is 'fooChain$child#1'.
(The final element of the name is the order within the chain, beginning with '#0').
Note, this transformer isn't registered as a bean within the application context,
so, it doesn't get a <emphasis>beanName</emphasis>, however its <emphasis>componentName</emphasis> has
a value which is useful for logging etc.
</listitem>
</itemizedlist>
</para>
<para>
In most other cases, the <emphasis>id</emphasis> will generally be
ignored but may still add value for documentation purposes, and may also
be used for providing more meaningful log messages.
The <emphasis>id</emphasis> attribute for <code>&lt;chain&gt;</code> elements allows them to be eligible for
<link linkend='jmx-mbean-exporter'>JMX export</link> and they are trackable via <link linkend='message-history'>Message History</link>.
They can also be accessed from the <interfacename>BeanFactory</interfacename> using the appropriate bean name
as discussed above.
</para>
<note>
Currently, the XML Schema of the <emphasis>Spring Integration</emphasis> Core module
prevents you from setting the <emphasis>id</emphasis> attribute for
Core components within a Message Handler Chain. This may be relaxed in future,
to provide the benefits described above.
</note>
<tip>
<para>
It is useful to provide an explicit <emphasis>id</emphasis> attribute on <code>&lt;chain&gt;</code>s
to simplify the identification of sub-components in logs, and to provide
access to them from the <interfacename>BeanFactory</interfacename> etc.
</para>
</tip>
<para><emphasis>Calling a Chain from within a Chain</emphasis></para>
<para>

View File

@@ -135,7 +135,7 @@
please see <xref linkend="jdbc-message-store-generic"/>.
</para>
</section>
<section>
<section id="3.0-json-transformers">
<title>Jackson Support (JSON)</title>
<para>
A new abstraction for JSON conversion has been introduced. Implementations for Jackson 1.x
@@ -170,6 +170,17 @@
may set other headers.
</para>
</section>
<section id="3.0-id-for-chain-sub-components">
<title>Chain Elements 'id' Attribute</title>
<para>
Previously, the <emphasis>id</emphasis> attribute for elements within a <code>&lt;chain&gt;</code> was
ignored and, in some cases, disallowed. Now, the <emphasis>id</emphasis> attribute is allowed
for all elements within a <code>&lt;chain&gt;</code>. The bean names of chain elements is a combination
of the surrounding chain's <emphasis>id</emphasis> and the <emphasis>id</emphasis> of the element
itself. For example: 'fooChain$child.fooTransformer.handler'.
For more information see <xref linkend="chain"/>.
</para>
</section>
</section>
</chapter>