INT-1932 added support for 'delimiters' attribute on the <splitter> element (when not using a 'ref', 'expression', or inner bean)

This commit is contained in:
Mark Fisher
2011-07-12 14:14:15 -04:00
parent af7bc5ac74
commit 5bffa91a1b
8 changed files with 171 additions and 52 deletions

View File

@@ -39,6 +39,8 @@ public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBe
private volatile boolean applySequence = true;
private volatile String delimiters;
public void setSendTimeout(Long sendTimeout) {
this.sendTimeout = sendTimeout;
@@ -56,6 +58,10 @@ public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBe
this.applySequence = applySequence;
}
public void setDelimiters(String delimiters) {
this.delimiters = delimiters;
}
@Override
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
Assert.notNull(targetObject, "targetObject must not be null");
@@ -95,6 +101,11 @@ public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBe
if (this.sendTimeout != null) {
splitter.setSendTimeout(sendTimeout);
}
if (this.delimiters != null) {
Assert.isTrue(splitter instanceof DefaultMessageSplitter, "The 'delimiters' property is only available" +
" for a Splitter definition where no 'ref', 'expression', or inner bean has been provided.");
((DefaultMessageSplitter) splitter).setDelimiters(this.delimiters);
}
splitter.setRequiresReply(requiresReply);
splitter.setApplySequence(applySequence);
return splitter;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2011 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.
@@ -41,5 +41,7 @@ public class SplitterParser extends AbstractDelegatingConsumerEndpointParser {
@Override
void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "delimiters");
}
}

View File

@@ -1088,24 +1088,7 @@ endpoint itself is a Polling Consumer for a channel with a queue.
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="service-activator" type="expressionOrInnerEndpointDefinitionAware" />
<xsd:element name="splitter">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
<xsd:attribute name="apply-sequence" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation>
Set this flag to false to prevent adding sequence related headers in this splitter. This
can be
convenient in cases where the set sequence numbers conflict with downstream custom
aggregations.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="splitter" type="splitter-type" />
<xsd:element name="transformer" type="expressionOrInnerEndpointDefinitionAware" />
<xsd:element name="header-enricher" type="header-enricher-type" />
<xsd:element name="header-filter" type="header-filter-type" />
@@ -2356,34 +2339,48 @@ Name of the header whose value will be used to route messages
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
<xsd:extension base="splitter-type">
<xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Specify whether the splitter method must return a non-null value. This value will be
FALSE by
default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or
expression) returns a NULL value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="apply-sequence" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation>
Set this flag to false to prevent adding sequence related headers in this splitter. This
can be
convenient in cases where the set sequence numbers conflict with downstream custom
aggregations.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="splitter-type">
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Specify whether the splitter method must return a non-null value. This value will be
FALSE by default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or expression) returns a NULL value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="apply-sequence" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Set this flag to false to prevent adding sequence related headers in this splitter.
This can be convenient in cases where the set sequence numbers conflict with downstream
custom aggregations.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delimiters" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Provide one or more delimiters (as a single String value, e.g. delimiters=",;:") for
tokenizing String-typed payload values. This attribute is only allowed if no 'ref' or
'expression' have been provided since that is when the DefaultMessageSplitter would
be used, and it's the implementation that contains the "delimiters" property.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="aggregator">
<xsd:annotation>
<xsd:documentation>

View File

@@ -19,13 +19,14 @@
<splitter input-channel="inDefault" output-channel="out" />
<splitter input-channel="inDelimiters" output-channel="out" delimiters=",;/ " />
<beans:bean id="splitterBeanXmlConfig"
class="org.springframework.integration.splitter.SplitterIntegrationTests$TestSplitter" />
<beans:bean id="splitterBeanAnnotationConfig"
class="org.springframework.integration.splitter.SplitterIntegrationTests$TestSplitter" />
<beans:bean
class="org.springframework.integration.splitter.SplitterIntegrationTests$Receiver" />
<beans:bean class="org.springframework.integration.splitter.SplitterIntegrationTests$Receiver" />
</beans:beans>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="out">
<queue/>
</channel>
<splitter input-channel="in" output-channel="out" delimiters="," expression="'foo,bar'"/>
</beans:beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="out">
<queue/>
</channel>
<splitter input-channel="in" output-channel="out" delimiters=",;/ ">
<beans:bean id="bean" class="org.springframework.integration.splitter.SplitterIntegrationTests$Receiver"/>
</splitter>
</beans:beans>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="out">
<queue/>
</channel>
<splitter input-channel="in" output-channel="out" delimiters=",;/ " ref="bean"/>
<beans:bean id="bean" class="org.springframework.integration.splitter.SplitterIntegrationTests$Receiver"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,20 @@
package org.springframework.integration.splitter;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.annotation.MessageEndpoint;
@@ -40,23 +43,24 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Iwein Fuld
* @author Alexander Peters
* @author Mark Fisher
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class SplitterIntegrationTests {
@Autowired
@Qualifier("inAnnotated")
MessageChannel inAnnotated;
@Autowired
@Qualifier("inMethodInvoking")
MessageChannel inMethodInvoking;
@Autowired
@Qualifier("inDefault")
MessageChannel inDefault;
@Autowired
MessageChannel inDelimiters;
@Autowired
MethodInvokingSplitter splitter;
@@ -67,9 +71,13 @@ public class SplitterIntegrationTests {
@Autowired
Receiver receiver;
@Before
public void clearWords() {
receiver.receivedWords.clear();
}
@MessageEndpoint
public static class Receiver {
private List<String> receivedWords = new ArrayList<String>();
@ServiceActivator(inputChannel = "out")
@@ -113,7 +121,55 @@ public class SplitterIntegrationTests {
assertTrue(receiver.receivedWords.containsAll(words));
assertTrue(words.containsAll(receiver.receivedWords));
}
@Test
public void delimiterSplitter() throws Exception {
inDelimiters.send(new GenericMessage<String>("one,two, three; four/five"));
assertTrue(receiver.receivedWords.containsAll(Arrays.asList("one", "two", "three", "four", "five")));
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithRef() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidRef.xml", SplitterIntegrationTests.class);
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertNotNull(cause);
assertTrue(cause instanceof IllegalArgumentException);
assertTrue(cause.getMessage().contains("'delimiters' property is only available"));
throw cause;
}
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithInnerBean() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidInnerBean.xml", SplitterIntegrationTests.class);
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertNotNull(cause);
assertTrue(cause instanceof IllegalArgumentException);
assertTrue(cause.getMessage().contains("'delimiters' property is only available"));
throw cause;
}
}
@Test(expected = IllegalArgumentException.class)
public void delimitersNotAllowedWithExpression() throws Throwable {
try {
new ClassPathXmlApplicationContext("SplitterIntegrationTests-invalidExpression.xml", SplitterIntegrationTests.class);
}
catch (BeanCreationException e) {
Throwable cause = e.getMostSpecificCause();
assertNotNull(cause);
assertTrue(cause instanceof IllegalArgumentException);
assertTrue(cause.getMessage().contains("'delimiters' property is only available"));
throw cause;
}
}
@Test
public void channelResolver_isNotNull() throws Exception {
splitter.setOutputChannel(null);