GH-2765: Add discardChannel for splitter (#2883)
* GH-2765: Add discardChannel for splitter Fixes https://github.com/spring-projects/spring-integration/issues/2765 When encountering empty collections, splitter should be able to send the result to a discard channel. Currently, when encountering an empty collection, the splitter ends the flow. Some use-cases may rely on a custom split function which may returns empty collections. These use-cases should be able to define a discard channel so they can proceed with a possible compensation flow. * Add `discardChannel` option to the `AbstractMessageSplitter` * Delegate `discardChannel` population from everywhere it is possible: DSL, XML, `AbstractMessageSplitter` extension like `FileSplitter` etc. * Fix `FileSplitterTests` for broken charset * Document new feature; fix some typos and out-dated code sample * * Fix `SplitterFactoryBean` for NPE on the `discardChannelName` propagation * * Check `this.discardChannel` first * `Assert.state()` in `doInit()` for mutual exclusiveness
This commit is contained in:
committed by
Gary Russell
parent
7fa1161f5b
commit
818be4cbe8
@@ -42,7 +42,8 @@ public class XPathMessageSplitterParser extends AbstractConsumerEndpointParser {
|
||||
Assert.isTrue(xPathExpressionNodes.getLength() <= 1, "At most one xpath-expression child may be specified.");
|
||||
boolean hasChild = xPathExpressionNodes.getLength() == 1;
|
||||
boolean hasReference = StringUtils.hasText(xPathExpressionRef);
|
||||
Assert.isTrue(hasChild ^ hasReference, "Exactly one of 'xpath-expression' or 'xpath-expression-ref' is required.");
|
||||
Assert.isTrue(hasChild ^ hasReference,
|
||||
"Exactly one of 'xpath-expression' or 'xpath-expression-ref' is required.");
|
||||
if (hasChild) {
|
||||
Element xpathExpressionElement = (Element) xPathExpressionNodes.item(0);
|
||||
builder.addConstructorArgValue(xpathExpressionElement.getAttribute("expression"));
|
||||
@@ -51,12 +52,14 @@ public class XPathMessageSplitterParser extends AbstractConsumerEndpointParser {
|
||||
else {
|
||||
builder.addConstructorArgReference(xPathExpressionRef);
|
||||
}
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "doc-builder-factory", "documentBuilder");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "doc-builder-factory",
|
||||
"documentBuilder");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "create-documents");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "iterator");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "output-properties");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "discard-channel", "discardChannelName");
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -820,6 +820,19 @@
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="discard-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.messaging.MessageChannel" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The channel where the splitter will send the messages that return an empty container from
|
||||
split function.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
apply-sequence="false"
|
||||
create-documents="true"
|
||||
output-properties="outputProperties"
|
||||
iterator="false">
|
||||
iterator="false"
|
||||
discard-channel="nullChannel">
|
||||
<xpath-expression expression="/orders/order"/>
|
||||
</xpath-splitter>
|
||||
|
||||
|
||||
@@ -45,13 +45,16 @@ import org.springframework.util.MultiValueMap;
|
||||
@DirtiesContext
|
||||
public class XPathSplitterParserTests {
|
||||
|
||||
@Autowired @Qualifier("xpathSplitter.handler")
|
||||
@Autowired
|
||||
@Qualifier("xpathSplitter.handler")
|
||||
private MessageHandler xpathSplitter;
|
||||
|
||||
@Autowired @Qualifier("xpathSplitter")
|
||||
@Autowired
|
||||
@Qualifier("xpathSplitter")
|
||||
private EventDrivenConsumer consumer;
|
||||
|
||||
@Autowired @Qualifier("outputProperties")
|
||||
@Autowired
|
||||
@Qualifier("outputProperties")
|
||||
private Properties outputProperties;
|
||||
|
||||
@Autowired
|
||||
@@ -64,10 +67,11 @@ public class XPathSplitterParserTests {
|
||||
assertThat(TestUtils.getPropertyValue(this.xpathSplitter, "returnIterator", Boolean.class)).isFalse();
|
||||
assertThat(TestUtils.getPropertyValue(this.xpathSplitter, "outputProperties")).isSameAs(this.outputProperties);
|
||||
assertThat(TestUtils.getPropertyValue(this.xpathSplitter,
|
||||
"xpathExpression.xpathExpression.xpath.m_patternString",
|
||||
String.class)).isEqualTo("/orders/order");
|
||||
"xpathExpression.xpathExpression.xpath.m_patternString", String.class))
|
||||
.isEqualTo("/orders/order");
|
||||
assertThat(TestUtils.getPropertyValue(xpathSplitter, "order")).isEqualTo(2);
|
||||
assertThat(TestUtils.getPropertyValue(xpathSplitter, "messagingTemplate.sendTimeout")).isEqualTo(123L);
|
||||
assertThat(TestUtils.getPropertyValue(this.xpathSplitter, "discardChannelName")).isEqualTo("nullChannel");
|
||||
assertThat(TestUtils.getPropertyValue(consumer, "phase")).isEqualTo(-1);
|
||||
assertThat(TestUtils.getPropertyValue(consumer, "autoStartup", Boolean.class)).isFalse();
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
Reference in New Issue
Block a user