diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java index 259acaf91d..0df3c9670e 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java @@ -19,6 +19,7 @@ package org.springframework.integration.config; import org.springframework.integration.message.MessageHandler; import org.springframework.integration.splitter.AbstractMessageSplitter; import org.springframework.integration.splitter.DefaultMessageSplitter; +import org.springframework.integration.splitter.ExpressionEvaluatingSplitter; import org.springframework.integration.splitter.MethodInvokingSplitter; import org.springframework.util.StringUtils; @@ -39,6 +40,11 @@ public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean { : new MethodInvokingSplitter(targetObject); } + @Override + protected MessageHandler createExpressionEvaluatingHandler(String expression) { + return new ExpressionEvaluatingSplitter(expression); + } + @Override protected MessageHandler createDefaultHandler() { return new DefaultMessageSplitter(); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java index db8b79837f..1408d791d3 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/SplitterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -33,18 +33,41 @@ public class SplitterParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - BeanDefinition innerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SplitterFactoryBean"); - if (innerDefinition != null){ - builder.addPropertyValue("targetObject", innerDefinition); - } else if (element.hasAttribute(REF_ATTRIBUTE)) { - String ref = element.getAttribute(REF_ATTRIBUTE); - builder.addPropertyReference("targetObject", ref); - if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) { - String method = element.getAttribute(METHOD_ATTRIBUTE); - builder.addPropertyValue("targetMethodName", method); + + BeanDefinition innerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); + String ref = element.getAttribute(REF_ATTRIBUTE); + String expression = element.getAttribute(EXPRESSION_ATTRIBUTE); + boolean hasRef = StringUtils.hasText(ref); + boolean hasExpression = StringUtils.hasText(expression); + + if (innerDefinition != null) { + if (hasRef || hasExpression) { + parserContext.getReaderContext().error( + "Neither 'ref' nor 'expression' are permitted when an inner bean () is configured.", element); + return null; } + builder.addPropertyValue("targetObject", innerDefinition); + } + else if (hasRef) { + builder.addPropertyReference("targetObject", ref); + } + else if (hasExpression) { + builder.addPropertyValue("expression", expression); + } + else { + // will create a DefaultSplitter + return builder; + } + + String method = element.getAttribute(METHOD_ATTRIBUTE); + if (StringUtils.hasText(method)) { + if (hasExpression) { + parserContext.getReaderContext().error( + "A 'method' attribute is not permitted when configuring an 'expression'.", element); + } + builder.addPropertyValue("targetMethodName", method); } return builder; } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java index 95f60d81cd..a656c18fbe 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java @@ -36,16 +36,20 @@ import org.springframework.integration.message.MessageHandlingException; */ public class ExpressionEvaluatingMessageProcessor implements MessageProcessor { - private final ExpressionParser parser = new SpelExpressionParser( + /*private final ExpressionParser parser = new SpelExpressionParser( SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull | SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize); + */ private final Expression expression; public ExpressionEvaluatingMessageProcessor(String expression) { try { - this.expression = this.parser.parseExpression(expression); + ExpressionParser parser = new SpelExpressionParser( + SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull | + SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize); + this.expression = parser.parseExpression(expression); } catch (ParseException e) { throw new IllegalArgumentException("Failed to parse expression.", e); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/AbstractMessageProcessingSplitter.java b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/AbstractMessageProcessingSplitter.java new file mode 100644 index 0000000000..a7681a94db --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/AbstractMessageProcessingSplitter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2009 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.splitter; + +import org.springframework.integration.core.Message; +import org.springframework.integration.handler.MessageProcessor; +import org.springframework.util.Assert; + +/** + * Base class for Message Splitter implementations that delegate to a + * {@link MessageProcessor} instance. + * + * @author Mark Fisher + * @since 2.0 + */ +abstract class AbstractMessageProcessingSplitter extends AbstractMessageSplitter { + + private final MessageProcessor messageProcessor; + + + protected AbstractMessageProcessingSplitter(MessageProcessor messageProcessor) { + Assert.notNull(messageProcessor, "messageProcessor must not be null"); + this.messageProcessor = messageProcessor; + } + + @Override + protected final Object splitMessage(Message message) { + return this.messageProcessor.processMessage(message); + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/ExpressionEvaluatingSplitter.java b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/ExpressionEvaluatingSplitter.java new file mode 100644 index 0000000000..55955fb424 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/ExpressionEvaluatingSplitter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2009 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.splitter; + +import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; + +/** + * A Message Splitter implementation that evaluates the specified SpEL + * expression. The result of evaluation will typically be a Collection or + * Array. If the result is not a Collection or Array, then the single Object + * will be returned as the payload of a single reply Message. + * + * @author Mark Fisher + * @since 2.0 + */ +public class ExpressionEvaluatingSplitter extends AbstractMessageProcessingSplitter { + + public ExpressionEvaluatingSplitter(String expression) { + super(new ExpressionEvaluatingMessageProcessor(expression)); + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/MethodInvokingSplitter.java b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/MethodInvokingSplitter.java index 5816490e96..5b9dcc4d44 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/MethodInvokingSplitter.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/MethodInvokingSplitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -19,7 +19,6 @@ package org.springframework.integration.splitter; import java.lang.reflect.Method; import org.springframework.integration.annotation.Splitter; -import org.springframework.integration.core.Message; import org.springframework.integration.handler.MessageMappingMethodInvoker; /** @@ -31,27 +30,18 @@ import org.springframework.integration.handler.MessageMappingMethodInvoker; * * @author Mark Fisher */ -public class MethodInvokingSplitter extends AbstractMessageSplitter { - - private final MessageMappingMethodInvoker invoker; - +public class MethodInvokingSplitter extends AbstractMessageProcessingSplitter { public MethodInvokingSplitter(Object object, Method method) { - this.invoker = new MessageMappingMethodInvoker(object, method); + super(new MessageMappingMethodInvoker(object, method)); } public MethodInvokingSplitter(Object object, String methodName) { - this.invoker = new MessageMappingMethodInvoker(object, methodName); + super(new MessageMappingMethodInvoker(object, methodName)); } public MethodInvokingSplitter(Object object) { - this.invoker = new MessageMappingMethodInvoker(object, Splitter.class); - } - - - @Override - protected Object splitMessage(Message message) { - return this.invoker.processMessage(message); + super(new MessageMappingMethodInvoker(object, Splitter.class)); } } diff --git a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index 822e2f084f..771490f532 100644 --- a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -959,19 +959,12 @@ - + Defines a Transformer. - - - - - - - @@ -1205,7 +1198,7 @@ - + Defines a Splitter. @@ -1440,6 +1433,14 @@ + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests-context.xml new file mode 100644 index 0000000000..f3b830e939 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests.java new file mode 100644 index 0000000000..655df8fbe5 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/splitter/SpelSplitterIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2009 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.splitter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.integration.channel.PollableChannel; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class SpelSplitterIntegrationTests { + + @Autowired @Qualifier("input") + private MessageChannel input; + + @Autowired @Qualifier("output") + private PollableChannel output; + + + @Test + public void split() { + Message message = MessageBuilder.withPayload(new TestBean()).build(); + this.input.send(message); + assertEquals(new Integer(1), output.receive(0).getPayload()); + assertEquals(new Integer(2), output.receive(0).getPayload()); + assertEquals(new Integer(3), output.receive(0).getPayload()); + assertEquals(new Integer(4), output.receive(0).getPayload()); + assertNull(output.receive(0)); + } + + + static class TestBean { + + private final List numbers = new ArrayList(); + + public TestBean() { + for (int i = 1; i <= 10; i++) { + this.numbers.add(i); + } + } + + public List getNumbers() { + return this.numbers; + } + } + +}