diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java index 9464692d9c..4cf9775191 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java @@ -25,6 +25,7 @@ import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.message.MessageHandler; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Base class for FactoryBeans that create MessageHandler instances. @@ -32,7 +33,7 @@ import org.springframework.util.Assert; * @author Mark Fisher * @author Alexander Peters */ -public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, BeanFactoryAware { +public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, BeanFactoryAware { private volatile MessageHandler handler; @@ -40,6 +41,8 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, private volatile String targetMethodName; + private volatile String expression; + private volatile MessageChannel outputChannel; private volatile Integer order; @@ -59,6 +62,10 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, this.targetMethodName = targetMethodName; } + public void setExpression(String expression) { + this.expression = expression; + } + public void setOutputChannel(MessageChannel outputChannel) { this.outputChannel = outputChannel; } @@ -71,7 +78,7 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, this.beanFactory = beanFactory; } - public Object getObject() throws Exception { + public MessageHandler getObject() throws Exception { if (this.handler == null) { this.initializeHandler(); Assert.notNull(this.handler, "failed to create MessageHandler"); @@ -88,7 +95,7 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, return this.handler; } - public Class getObjectType() { + public Class getObjectType() { if (this.handler != null) { return this.handler.getClass(); } @@ -104,7 +111,21 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, if (this.initialized) { return; } - this.handler = this.createHandler(this.targetObject, this.targetMethodName); + if (this.targetObject == null) { + Assert.isTrue(!StringUtils.hasText(this.targetMethodName), + "The target method is only allowed when a target object (ref or inner bean) is also provided."); + } + if (this.targetObject != null) { + Assert.state(this.expression == null, + "The 'targetObject' and 'expression' properties are mutually exclusive."); + this.handler = this.createHandler(this.targetObject, this.targetMethodName); + } + else if (this.expression != null) { + this.handler = this.createExpressionEvaluatingHandler(this.expression); + } + else { + this.handler = this.createDefaultHandler(); + } this.initialized = true; } } @@ -114,4 +135,13 @@ public abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, */ protected abstract MessageHandler createHandler(Object targetObject, String targetMethodName); + protected MessageHandler createExpressionEvaluatingHandler(String expression) { + throw new UnsupportedOperationException(this.getClass().getName() + " does not support expressions."); + } + + protected MessageHandler createDefaultHandler() { + throw new IllegalArgumentException( + "Exactly one of the 'targetObject' or 'expression' property is required."); + } + } 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 f4418ea065..259acaf91d 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 @@ -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. @@ -20,7 +20,6 @@ import org.springframework.integration.message.MessageHandler; import org.springframework.integration.splitter.AbstractMessageSplitter; import org.springframework.integration.splitter.DefaultMessageSplitter; import org.springframework.integration.splitter.MethodInvokingSplitter; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -32,11 +31,6 @@ public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean { @Override protected MessageHandler createHandler(Object targetObject, String targetMethodName) { - if (targetObject == null) { - Assert.isTrue(!StringUtils.hasText(targetMethodName), - "'method' should only be provided when 'ref' is also provided"); - return new DefaultMessageSplitter(); - } if (targetObject instanceof AbstractMessageSplitter) { return (AbstractMessageSplitter) targetObject; } @@ -45,4 +39,9 @@ public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean { : new MethodInvokingSplitter(targetObject); } + @Override + protected MessageHandler createDefaultHandler() { + return new DefaultMessageSplitter(); + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java index 072ad2be4e..611abecf8b 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/TransformerFactoryBean.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. @@ -16,6 +16,8 @@ package org.springframework.integration.config; +import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; +import org.springframework.integration.handler.MessageProcessor; import org.springframework.integration.message.MessageHandler; import org.springframework.integration.transformer.MessageTransformingHandler; import org.springframework.integration.transformer.MethodInvokingTransformer; @@ -46,4 +48,11 @@ public class TransformerFactoryBean extends AbstractMessageHandlerFactoryBean { return new MessageTransformingHandler(transformer); } + @Override + protected MessageHandler createExpressionEvaluatingHandler(String expression) { + MessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); + Transformer transformer = new MethodInvokingTransformer(processor); + return new MessageTransformingHandler(transformer); + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java index d5b109cfae..e9499f77d3 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java @@ -38,6 +38,8 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit protected static final String METHOD_ATTRIBUTE = "method"; + protected static final String EXPRESSION_ATTRIBUTE = "expression"; + @Override protected boolean shouldGenerateId() { diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java index 518b8fdd92..99842869da 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceUtils.java @@ -182,7 +182,6 @@ public abstract class IntegrationNamespaceUtils { targetBuilder.addPropertyReference("pollerMetadata", pollerMetadataRef); } - @SuppressWarnings("unchecked") public static BeanDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext){ // parses out inner bean definition for concrete implementation if defined List childElements = DomUtils.getChildElementsByTagName(element, "bean"); @@ -199,4 +198,5 @@ public abstract class IntegrationNamespaceUtils { " are not allowed together."); return innerDefinition; } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java index b4c6e00fe3..f3808661a8 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/TransformerParser.java @@ -32,26 +32,43 @@ public class TransformerParser extends AbstractConsumerEndpointParser { @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - BeanDefinition innerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( IntegrationNamespaceUtils.BASE_PACKAGE + ".config.TransformerFactoryBean"); - - if (innerDefinition != null){ - builder.addPropertyValue("targetObject", innerDefinition); - } else { - String ref = element.getAttribute(REF_ATTRIBUTE); - if (!StringUtils.hasText(ref)) { - parserContext.getReaderContext().error("Either \"ref\" attribute or inner bean () definition of concrete implementation of " + - "this Transformer is required.", element); + + 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 { + parserContext.getReaderContext().error("Exactly one of the 'ref' attribute, 'expression' attribute, " + + "or inner bean () definition for this Transformer is required.", element); + return null; + } 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 new file mode 100644 index 0000000000..95f60d81cd --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java @@ -0,0 +1,66 @@ +/* + * 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.handler; + +import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParseException; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.core.Message; +import org.springframework.integration.message.MessageHandlingException; + +/** + * A {@link MessageProcessor} implementation that evaluates a SpEL expression + * with the Message itself as the root object within the evaluation context. + * + * @author Mark Fisher + * @since 2.0 + */ +public class ExpressionEvaluatingMessageProcessor implements MessageProcessor { + + 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); + } + catch (ParseException e) { + throw new IllegalArgumentException("Failed to parse expression.", e); + } + } + + public Object processMessage(Message message) { + StandardEvaluationContext context = new StandardEvaluationContext(message); + context.addPropertyAccessor(new MapAccessor()); + try { + return this.expression.getValue(context); + } + catch (EvaluationException e) { + throw new MessageHandlingException(message, "Expression evaluation failed.", e); + } + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/MethodInvokingTransformer.java b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/MethodInvokingTransformer.java index be576db517..bdae2f8ad6 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/MethodInvokingTransformer.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/MethodInvokingTransformer.java @@ -25,6 +25,7 @@ import org.springframework.integration.handler.MessageMappingMethodInvoker; import org.springframework.integration.handler.MessageProcessor; import org.springframework.integration.message.MessageBuilder; import org.springframework.integration.message.MessageHandlingException; +import org.springframework.util.Assert; /** * @author Mark Fisher @@ -34,6 +35,11 @@ public class MethodInvokingTransformer implements Transformer { private final MessageProcessor messageProcessor; + public MethodInvokingTransformer(MessageProcessor messageProcessor) { + Assert.notNull(messageProcessor, "messageProcessor must not be null"); + this.messageProcessor = messageProcessor; + } + public MethodInvokingTransformer(Object object, Method method) { this.messageProcessor = new MessageMappingMethodInvoker(object, method); } @@ -53,7 +59,7 @@ public class MethodInvokingTransformer implements Transformer { if (result == null) { return null; } - if (result instanceof Message) { + if (result instanceof Message) { return (Message) result; } if (result instanceof Properties && !(message.getPayload() instanceof Properties)) { @@ -65,7 +71,7 @@ public class MethodInvokingTransformer implements Transformer { } return builder.build(); } - if (result instanceof Map && !(message.getPayload() instanceof Map)) { + if (result instanceof Map && !(message.getPayload() instanceof Map)) { Map attributesToSet = (Map ) result; MessageBuilder builder = MessageBuilder.fromMessage(message); for (Object key : attributesToSet.keySet()) { 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 1faa1ee3d1..822e2f084f 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,12 +959,19 @@ - + Defines a Transformer. + + + + + + +