INT-2218 - Chain Parser Validation Improvements
Components within Chain: Add parser validation For reference see: https://jira.springsource.org/browse/INT-2218 INT-2218 - Code review changes INT-2218 - Fix HttpOutboundGatewayParserTests INT-2218 - Remove input-channel validation Was already covered by PR #592
This commit is contained in:
committed by
Gary Russell
parent
c9f73a8a49
commit
7055845424
@@ -13,10 +13,13 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -24,6 +27,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.handler.MessageHandlerChain;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <chain> element.
|
||||
@@ -32,14 +37,14 @@ import org.springframework.integration.handler.MessageHandlerChain;
|
||||
* @author Iwein Fuld
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @author Gunnar Hillert
|
||||
*/
|
||||
public class ChainParser extends AbstractConsumerEndpointParser {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageHandlerChain.class);
|
||||
ManagedList handlerList = new ManagedList();
|
||||
ManagedList<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
|
||||
NodeList children = element.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
@@ -63,12 +68,38 @@ 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder parseChild(Element element, ParserContext parserContext, BeanDefinition parentDefinition) {
|
||||
|
||||
BeanDefinitionHolder holder = null;
|
||||
|
||||
if ("bean".equals(element.getLocalName())) {
|
||||
holder = parserContext.getDelegate().parseBeanDefinitionElement(element, parentDefinition);
|
||||
}
|
||||
else {
|
||||
|
||||
this.validateChild(element, parserContext);
|
||||
|
||||
BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(element, parentDefinition);
|
||||
if (beanDefinition == null) {
|
||||
parserContext.getReaderContext().error("child BeanDefinition must not be null", element);
|
||||
|
||||
@@ -20,8 +20,10 @@ 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;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -208,6 +210,23 @@ public class ChainElementsFailureTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainResequencerPoller() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("resequencer-poller");
|
||||
fail("Expected a XmlBeanDefinitionStoreException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: " +
|
||||
"'int:resequencer' must not define a 'poller' sub-element " +
|
||||
"when used within a chain.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationContext bootStrap(String configProperty) throws Exception {
|
||||
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
pfb.setLocation(new ClassPathResource("org/springframework/integration/config/xml/chain-elements-config.properties"));
|
||||
|
||||
@@ -75,3 +75,10 @@ resequencer=\
|
||||
<int:chain input-channel="input"> \
|
||||
<int:resequencer input-channel="fail"/> \
|
||||
</int:chain>
|
||||
|
||||
resequencer-poller=\
|
||||
<int:chain input-channel="input"> \
|
||||
<int:resequencer> \
|
||||
<int:poller fixed-rate="5000" max-messages-per-poll="10" />\
|
||||
</int:resequencer> \
|
||||
</int:chain>
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.file.config;
|
||||
|
||||
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 org.junit.Test;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @since 2.2
|
||||
*/
|
||||
public class ChainElementsTests {
|
||||
|
||||
@Test
|
||||
public void chainOutboundGateway() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("file-oubound-gateway");
|
||||
fail("Expected a BeanDefinitionParsingException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: The " +
|
||||
"'request-channel' attribute isn't allowed for a nested (e.g. " +
|
||||
"inside a <chain/>) endpoint element: 'int-file:outbound-gateway' " +
|
||||
"with id='myFileOutboundGateway'.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainOutboundGatewayWithInputChannel() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("file-oubound-gateway-input-channel");
|
||||
fail("Expected a XmlBeanDefinitionStoreException to be thrown.");
|
||||
}
|
||||
catch (XmlBeanDefinitionStoreException e) {
|
||||
assertEquals("cvc-complex-type.3.2.2: Attribute 'input-channel' is not" +
|
||||
" allowed to appear in element 'int-file:outbound-gateway'.", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ApplicationContext bootStrap(String configProperty) throws Exception {
|
||||
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
pfb.setLocation(new ClassPathResource("org/springframework/integration/file/config/chain-elements-config.properties"));
|
||||
pfb.afterPropertiesSet();
|
||||
Properties prop = pfb.getObject();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(prop.getProperty("xmlheaders")).append(prop.getProperty(configProperty)).append(prop.getProperty("xmlfooter"));
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(buffer.toString().getBytes());
|
||||
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
|
||||
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
|
||||
reader.loadBeanDefinitions(new InputStreamResource(stream));
|
||||
ac.refresh();
|
||||
return ac;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
xmlheaders=\
|
||||
<?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:int="http://www.springframework.org/schema/integration" \
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file" \
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd \
|
||||
http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd \
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
xmlfooter= </beans>
|
||||
|
||||
file-oubound-gateway=<int:chain input-channel="fileOutboundGatewayInsideChain" output-channel="nullChannel">\
|
||||
<int-file:outbound-gateway id="myFileOutboundGateway" request-channel="request" directory="${java.io.tmpdir}/anyDir" delete-source-files="true"/>\
|
||||
</int:chain>
|
||||
file-oubound-gateway-input-channel=<int:chain input-channel="fileOutboundGatewayInsideChain" output-channel="nullChannel">\
|
||||
<int-file:outbound-gateway id="myFileOutboundGateway" input-channel="request" directory="${java.io.tmpdir}/anyDir" delete-source-files="true"/>\
|
||||
</int:chain>
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.http.config;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
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;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @since 2.2
|
||||
*/
|
||||
public class ChainElementsTests {
|
||||
|
||||
@Test
|
||||
public void chainOutboundGateway() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("http-oubound-gateway");
|
||||
fail("Expected a BeanDefinitionParsingException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: The " +
|
||||
"'request-channel' attribute isn't allowed for a nested " +
|
||||
"(e.g. inside a <chain/>) endpoint element: 'int-http:outbound-gateway'.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ApplicationContext bootStrap(String configProperty) throws Exception {
|
||||
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
pfb.setLocation(new ClassPathResource("org/springframework/integration/http/config/chain-elements-config.properties"));
|
||||
pfb.afterPropertiesSet();
|
||||
Properties prop = pfb.getObject();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(prop.getProperty("xmlheaders")).append(prop.getProperty(configProperty)).append(prop.getProperty("xmlfooter"));
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(buffer.toString().getBytes());
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
|
||||
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
|
||||
reader.loadBeanDefinitions(new InputStreamResource(stream));
|
||||
ac.refresh();
|
||||
return ac;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
xmlheaders=\
|
||||
<?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:int="http://www.springframework.org/schema/integration" \
|
||||
xmlns:int-http="http://www.springframework.org/schema/integration/http" \
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd \
|
||||
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd \
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
xmlfooter= </beans>
|
||||
|
||||
http-oubound-gateway=<int:chain id="httpChain" input-channel="httpOutboundGatewayInsideChain" output-channel="nullChannel">\
|
||||
<int-http:outbound-gateway request-channel="requestChannel" url="http://google.com/" http-method="POST"/>\
|
||||
</int:chain>
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.xml.config;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
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;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @since 2.2
|
||||
*/
|
||||
public class ChainElementsTests {
|
||||
|
||||
@Test
|
||||
public void chainXPathTransformer() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("xpath-transformer");
|
||||
fail("Expected a BeanDefinitionParsingException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: " +
|
||||
"The 'input-channel' attribute isn't allowed for a nested " +
|
||||
"(e.g. inside a <chain/>) endpoint element: 'int-xml:xpath-transformer'.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainXPathTransformerId() throws Exception {
|
||||
|
||||
this.bootStrap("xpath-transformer-id");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainXPathRouterOrder() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("xpath-router-order");
|
||||
fail("Expected a BeanDefinitionParsingException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: " +
|
||||
"'int-xml:xpath-router' must not define an 'order' attribute " +
|
||||
"when used within a chain.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainXPathTransformerPoller() throws Exception {
|
||||
|
||||
try {
|
||||
this.bootStrap("xpath-transformer-poller");
|
||||
fail("Expected a BeanDefinitionParsingException to be thrown.");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
final String expectedMessage = "Configuration problem: " +
|
||||
"'int-xml:xpath-transformer' must not define a 'poller' " +
|
||||
"sub-element when used within a chain.";
|
||||
final String actualMessage = e.getMessage();
|
||||
assertTrue("Error message did not start with '" + expectedMessage +
|
||||
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chainXPathTransformerSuccess() throws Exception {
|
||||
this.bootStrap("xpath-transformer-success");
|
||||
}
|
||||
|
||||
private ApplicationContext bootStrap(String configProperty) throws Exception {
|
||||
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
pfb.setLocation(new ClassPathResource("org/springframework/integration/xml/config/chain-elements-config.properties"));
|
||||
pfb.afterPropertiesSet();
|
||||
Properties prop = pfb.getObject();
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(prop.getProperty("xmlheaders")).append(prop.getProperty(configProperty)).append(prop.getProperty("xmlfooter"));
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(buffer.toString().getBytes());
|
||||
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
|
||||
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
|
||||
reader.loadBeanDefinitions(new InputStreamResource(stream));
|
||||
ac.refresh();
|
||||
return ac;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
xmlheaders=\
|
||||
<?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:int="http://www.springframework.org/schema/integration" \
|
||||
xmlns:int-xml="http://www.springframework.org/schema/integration/xml" \
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd \
|
||||
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd \
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
xmlfooter= </beans>
|
||||
|
||||
xpath-transformer=<int:chain input-channel="input">\
|
||||
<int-xml:xpath-transformer input-channel="fail" xpath-expression="/person/@name" />\
|
||||
</int:chain>
|
||||
xpath-transformer-id=<int:chain input-channel="input">\
|
||||
<int-xml:xpath-transformer id="fail" xpath-expression="/person/@name" />\
|
||||
</int:chain>
|
||||
xpath-router-order=<int:chain input-channel="input">\
|
||||
<int-xml:xpath-router order="1">\
|
||||
<int-xml:xpath-expression expression="/name"/>\
|
||||
</int-xml:xpath-router>\
|
||||
</int:chain>
|
||||
xpath-transformer-success=<int:chain input-channel="input">\
|
||||
<int-xml:xpath-transformer xpath-expression="/person/@name" />\
|
||||
</int:chain>
|
||||
xpath-transformer-poller=<int:chain input-channel="input">\
|
||||
<int-xml:xpath-transformer xpath-expression="/person/@name">\
|
||||
<int:poller id="poller" fixed-rate="5000" max-messages-per-poll="10" />\
|
||||
</int-xml:xpath-transformer>\
|
||||
</int:chain>
|
||||
@@ -13,10 +13,10 @@
|
||||
progression. For example, it is fairly common to provide a Transformer before other components. Similarly, when
|
||||
providing a <emphasis>Filter</emphasis> before some other component in a chain, you are essentially creating a
|
||||
<ulink url="http://www.eaipatterns.com/MessageSelector.html">Selective Consumer</ulink>. In either case, the
|
||||
chain only requires a single <code>input-channel</code> and a single <code>output-channel</code> eliminating
|
||||
chain only requires a single <code>input-channel</code> and a single <code>output-channel</code> eliminating
|
||||
the need to define channels for each individual component.
|
||||
<tip>
|
||||
Spring Integration's <interfacename>Filter</interfacename> provides a boolean property <methodname>throwExceptionOnRejection</methodname>.
|
||||
Spring Integration's <interfacename>Filter</interfacename> provides a boolean property <methodname>throwExceptionOnRejection</methodname>.
|
||||
When providing multiple Selective Consumers on the same point-to-point channel with different acceptance criteria,
|
||||
this value should be set to 'true' (the default is false) so that the dispatcher will know that the Message was
|
||||
rejected and as a result will attempt to pass the Message on to other subscribers. If the Exception were not
|
||||
@@ -68,7 +68,7 @@
|
||||
</para>
|
||||
<para>
|
||||
The <header-enricher> element used in the above example will set a message header named "foo" with a value
|
||||
of "bar" on the message. A header enricher is a specialization of <interfacename>Transformer</interfacename>
|
||||
of "bar" on the message. A header enricher is a specialization of <interfacename>Transformer</interfacename>
|
||||
that touches only header values. You could obtain the same result by implementing a MessageHandler that did the
|
||||
header modifications and wiring that as a bean, but the header-enricher is obviously a simpler option.
|
||||
</para>
|
||||
@@ -84,11 +84,52 @@
|
||||
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
|
||||
</int:chain>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
<para><emphasis>Disallowed Attributes and Elements</emphasis></para>
|
||||
<para>
|
||||
It is important to note that certain attributes, such as
|
||||
<emphasis role="bold">order</emphasis> and <emphasis role="bold">input-channel</emphasis>
|
||||
are not allowed to be specified on components used within a
|
||||
<emphasis>chain</emphasis>. The same is true for the <emphasis role="bold">poller</emphasis>
|
||||
sub-element.
|
||||
</para>
|
||||
<important>
|
||||
<para>
|
||||
For the <emphasis>Spring Integration</emphasis> core components, the
|
||||
XML Schema itself will enforce some of these constraints. However, for non-core
|
||||
components or your own custom components, these constraints are enforced
|
||||
by the XML namespace parser, not by the XML Schema.
|
||||
</para>
|
||||
<para>
|
||||
These XML namespace parser constraints were added with
|
||||
<emphasis>Spring Integration 2.2</emphasis>. The XML namespace parser
|
||||
will throw an <classname>BeanDefinitionParsingException</classname> if you try to use disallowed
|
||||
attributes and elements.
|
||||
</para>
|
||||
</important>
|
||||
<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.
|
||||
</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.
|
||||
</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>
|
||||
<para><emphasis>Calling a Chain from within a Chain</emphasis></para>
|
||||
|
||||
<para>
|
||||
Sometimes you need to make a nested call to another chain from within a chain and then come
|
||||
back and continue execution within the original chain.
|
||||
To accomplish this you can utilize a Messaging Gateway by including a <gateway> element.
|
||||
For example:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[ <int:chain id="main-chain" input-channel="in" output-channel="out">
|
||||
<int:header-enricher>
|
||||
<int:header name="name" value="Many" />
|
||||
@@ -117,7 +158,7 @@
|
||||
<bean class="org.foo.SampleService" />
|
||||
</int:service-activator>
|
||||
</int:chain>]]></programlisting>
|
||||
|
||||
<para>
|
||||
In the above example the <emphasis>nested-chain-a</emphasis> will be called at the end of
|
||||
<emphasis>main-chain</emphasis> processing by the 'gateway' element configured there. While in
|
||||
<emphasis>nested-chain-a</emphasis> a call to a <emphasis>nested-chain-b</emphasis> will be made
|
||||
|
||||
Reference in New Issue
Block a user