diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/ExpressionEvaluatingMessageListProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/ExpressionEvaluatingMessageListProcessor.java index 3e976e09d2..9f20326833 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/ExpressionEvaluatingMessageListProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/ExpressionEvaluatingMessageListProcessor.java @@ -18,7 +18,6 @@ package org.springframework.integration.aggregator; import java.util.Collection; -import org.springframework.context.expression.MapAccessor; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; @@ -52,7 +51,6 @@ public class ExpressionEvaluatingMessageListProcessor extends AbstractExpression public ExpressionEvaluatingMessageListProcessor(String expression) { try { this.expression = parser.parseExpression(expression); - this.getEvaluationContext().addPropertyAccessor(new MapAccessor()); } catch (ParseException e) { throw new IllegalArgumentException("Failed to parse expression.", e); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java deleted file mode 100644 index b046bc13ee..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractMessageHandlerFactoryBean.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2002-2010 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.config; - -import org.springframework.aop.TargetSource; -import org.springframework.aop.framework.Advised; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanInitializationException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.SpelParserConfiguration; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.integration.MessageChannel; -import org.springframework.integration.context.Orderable; -import org.springframework.integration.core.MessageHandler; -import org.springframework.integration.core.MessageProducer; -import org.springframework.integration.handler.MessageProcessor; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * Base class for FactoryBeans that create MessageHandler instances. - * - * @author Mark Fisher - * @author Alexander Peters - */ -abstract class AbstractMessageHandlerFactoryBean implements FactoryBean, BeanFactoryAware { - - private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - - - private volatile MessageHandler handler; - - private volatile Object targetObject; - - private volatile String targetMethodName; - - private volatile Expression expression; - - private volatile MessageChannel outputChannel; - - private volatile Integer order; - - private volatile boolean initialized; - - private final Object initializationMonitor = new Object(); - - private BeanFactory beanFactory; - - - public void setTargetObject(Object targetObject) { - this.targetObject = targetObject; - } - - public void setTargetMethodName(String targetMethodName) { - this.targetMethodName = targetMethodName; - } - - public void setExpressionString(String expressionString) { - this.expression = expressionParser.parseExpression(expressionString); - } - - public void setExpression(Expression expression) { - this.expression = expression; - } - - public void setOutputChannel(MessageChannel outputChannel) { - this.outputChannel = outputChannel; - } - - public void setOrder(Integer order) { - this.order = order; - } - - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - protected BeanFactory getBeanFactory() { - return this.beanFactory; - } - - public MessageHandler getObject() throws Exception { - if (this.handler == null) { - this.initializeHandler(); - Assert.notNull(this.handler, "failed to create MessageHandler"); - if (this.handler instanceof MessageProducer && this.outputChannel != null) { - ((MessageProducer) this.handler).setOutputChannel(this.outputChannel); - } - if (this.handler instanceof BeanFactoryAware) { - ((BeanFactoryAware) this.handler).setBeanFactory(beanFactory); - } - if (this.handler instanceof Orderable && this.order != null) { - ((Orderable) this.handler).setOrder(this.order.intValue()); - } - } - return this.handler; - } - - public Class getObjectType() { - if (this.handler != null) { - return this.handler.getClass(); - } - return MessageHandler.class; - } - - public boolean isSingleton() { - return true; - } - - private void initializeHandler() { - synchronized (this.initializationMonitor) { - if (this.initialized) { - return; - } - 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."); - if (this.targetObject instanceof MessageProcessor) { - this.handler = this.createMessageProcessingHandler((MessageProcessor) this.targetObject); - } - else { - this.handler = this.createMethodInvokingHandler(this.targetObject, this.targetMethodName); - } - } - else if (this.expression != null) { - this.handler = this.createExpressionEvaluatingHandler(this.expression); - } - else { - this.handler = this.createDefaultHandler(); - } - if (this.handler instanceof BeanFactoryAware) { - ((BeanFactoryAware) this.handler).setBeanFactory(beanFactory); - } - this.initialized = true; - } - if (this.handler instanceof InitializingBean) { - try { - ((InitializingBean) this.handler).afterPropertiesSet(); - } - catch (Exception e) { - throw new BeanInitializationException("failed to initialize MessageHandler", e); - } - } - } - - /** - * Subclasses must implement this method to create the MessageHandler. - */ - abstract MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName); - - MessageHandler createExpressionEvaluatingHandler(Expression expression) { - throw new UnsupportedOperationException(this.getClass().getName() + " does not support expressions."); - } - - MessageHandler createMessageProcessingHandler(MessageProcessor processor) { - return this.createMethodInvokingHandler(processor, "processMessage"); - } - - MessageHandler createDefaultHandler() { - throw new IllegalArgumentException( - "Exactly one of the 'targetObject' or 'expression' property is required."); - } - - @SuppressWarnings("unchecked") - T extractTypeIfPossible(Object targetObject, Class expectedType) { - if (targetObject == null) { - return null; - } - if (expectedType.isAssignableFrom(targetObject.getClass())) { - return (T) targetObject; - } - if (targetObject instanceof Advised) { - TargetSource targetSource = ((Advised) targetObject).getTargetSource(); - if (targetSource == null) { - return null; - } - try { - return extractTypeIfPossible(targetSource.getTarget(), expectedType); - } - catch (Exception e) { - throw new IllegalStateException(e); - } - } - return null; - } - -} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java new file mode 100644 index 0000000000..a3fb531904 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2010 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.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.context.Orderable; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.core.MessageProducer; +import org.springframework.util.Assert; + +/** + * @author Dave Syer + * + */ +public abstract class AbstractSimpleMessageHandlerFactoryBean implements +FactoryBean, BeanFactoryAware { + + private volatile boolean initialized; + private final Object initializationMonitor = new Object(); + private volatile MessageHandler handler; + private volatile MessageChannel outputChannel; + private volatile Integer order; + private BeanFactory beanFactory; + + public AbstractSimpleMessageHandlerFactoryBean() { + super(); + } + + public void setOutputChannel(MessageChannel outputChannel) { + this.outputChannel = outputChannel; + } + + public void setOrder(Integer order) { + this.order = order; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + protected BeanFactory getBeanFactory() { + return this.beanFactory; + } + + public MessageHandler getObject() throws Exception { + if (this.handler == null) { + this.handler = this.createHandlerInternal(); + Assert.notNull(this.handler, "failed to create MessageHandler"); + if (this.handler instanceof MessageProducer && this.outputChannel != null) { + ((MessageProducer) this.handler).setOutputChannel(this.outputChannel); + } + if (this.handler instanceof BeanFactoryAware) { + ((BeanFactoryAware) this.handler).setBeanFactory(beanFactory); + } + if (this.handler instanceof Orderable && this.order != null) { + ((Orderable) this.handler).setOrder(this.order.intValue()); + } + } + return this.handler; + } + + protected final MessageHandler createHandlerInternal() { + synchronized (this.initializationMonitor) { + if (this.initialized) { + // There was a problem when this method was called already + return null; + } + handler = createHandler(); + if (handler instanceof BeanFactoryAware) { + ((BeanFactoryAware) handler).setBeanFactory(getBeanFactory()); + } + this.initialized = true; + } + if (handler instanceof InitializingBean) { + try { + ((InitializingBean) handler).afterPropertiesSet(); + } + catch (Exception e) { + throw new BeanInitializationException("failed to initialize MessageHandler", e); + } + } + return handler; + } + + protected abstract MessageHandler createHandler(); + + public Class getObjectType() { + if (this.handler != null) { + return this.handler.getClass(); + } + return MessageHandler.class; + } + + public boolean isSingleton() { + return true; + } + +} \ No newline at end of file diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java new file mode 100644 index 0000000000..0b64b7fe69 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/AbstractStandardMessageHandlerFactoryBean.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2010 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.config; + +import org.springframework.aop.TargetSource; +import org.springframework.aop.framework.Advised; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.handler.MessageProcessor; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Base class for FactoryBeans that create MessageHandler instances. + * + * @author Mark Fisher + * @author Alexander Peters + */ +abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleMessageHandlerFactoryBean { + + private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, + true)); + + private volatile Object targetObject; + + private volatile String targetMethodName; + + private volatile Expression expression; + + public void setTargetObject(Object targetObject) { + this.targetObject = targetObject; + } + + public void setTargetMethodName(String targetMethodName) { + this.targetMethodName = targetMethodName; + } + + public void setExpressionString(String expressionString) { + this.expression = expressionParser.parseExpression(expressionString); + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + protected MessageHandler createHandler() { + MessageHandler handler; + 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."); + if (this.targetObject instanceof MessageProcessor) { + handler = this.createMessageProcessingHandler((MessageProcessor) this.targetObject); + } else { + handler = this.createMethodInvokingHandler(this.targetObject, this.targetMethodName); + } + } else if (this.expression != null) { + handler = this.createExpressionEvaluatingHandler(this.expression); + } else { + handler = this.createDefaultHandler(); + } + return handler; + } + + /** + * Subclasses must implement this method to create the MessageHandler. + */ + abstract MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName); + + MessageHandler createExpressionEvaluatingHandler(Expression expression) { + throw new UnsupportedOperationException(this.getClass().getName() + " does not support expressions."); + } + + MessageHandler createMessageProcessingHandler(MessageProcessor processor) { + return this.createMethodInvokingHandler(processor, "processMessage"); + } + + MessageHandler createDefaultHandler() { + throw new IllegalArgumentException("Exactly one of the 'targetObject' or 'expression' property is required."); + } + + @SuppressWarnings("unchecked") + T extractTypeIfPossible(Object targetObject, Class expectedType) { + if (targetObject == null) { + return null; + } + if (expectedType.isAssignableFrom(targetObject.getClass())) { + return (T) targetObject; + } + if (targetObject instanceof Advised) { + TargetSource targetSource = ((Advised) targetObject).getTargetSource(); + if (targetSource == null) { + return null; + } + try { + return extractTypeIfPossible(targetSource.getTarget(), expectedType); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + return null; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/FilterFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/FilterFactoryBean.java index 484278df82..c9b27d8c2d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/FilterFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/FilterFactoryBean.java @@ -31,7 +31,7 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @since 2.0 */ -public class FilterFactoryBean extends AbstractMessageHandlerFactoryBean { +public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean { private volatile MessageChannel discardChannel; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java index ba95bf6eb4..0ab0ddc0ad 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/RouterFactoryBean.java @@ -33,7 +33,7 @@ import org.springframework.util.StringUtils; * @author Oleg Zhurakousky * @author Dave Syer */ -public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean { +public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean { private volatile ChannelResolver channelResolver; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java index 6651c3b748..e40191c260 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/ServiceActivatorFactoryBean.java @@ -29,7 +29,7 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @since 2.0 */ -public class ServiceActivatorFactoryBean extends AbstractMessageHandlerFactoryBean { +public class ServiceActivatorFactoryBean extends AbstractStandardMessageHandlerFactoryBean { private volatile Long sendTimeout; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java index 92a4736230..f4532f9abd 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/SplitterFactoryBean.java @@ -31,7 +31,7 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @author Iwein Fuld */ -public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean { +public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBean { private volatile Long sendTimeout; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java index aa782aef31..2f83c7054c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/TransformerFactoryBean.java @@ -30,7 +30,7 @@ import org.springframework.util.StringUtils; * * @author Mark Fisher */ -public class TransformerFactoryBean extends AbstractMessageHandlerFactoryBean { +public class TransformerFactoryBean extends AbstractStandardMessageHandlerFactoryBean { private volatile Long sendTimeout; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ControlBusParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ControlBusParser.java new file mode 100644 index 0000000000..0ee1956159 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ControlBusParser.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.control.ExpressionPayloadMessageProcessor; +import org.w3c.dom.Element; + +/** + * @author Dave Syer + * @since 2.0 + */ +public class ControlBusParser extends AbstractConsumerEndpointParser { + + @Override + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(ExpressionControlBusFactoryBean.class); + builder.addConstructorArgValue(getMessageProcessorBeanDefinition(element, parserContext)); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "order"); + return builder; + } + + protected BeanMetadataElement getMessageProcessorBeanDefinition(Element element, ParserContext parserContext) { + return new RootBeanDefinition(ExpressionPayloadMessageProcessor.class); + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ExpressionControlBusFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ExpressionControlBusFactoryBean.java new file mode 100644 index 0000000000..6b7608da9c --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ExpressionControlBusFactoryBean.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean; +import org.springframework.integration.control.ExpressionPayloadMessageProcessor; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.handler.ServiceActivatingHandler; + +/** + * FactoryBean for creating {@link MessageHandler} instances to handle a message as a Groovy Script. + * + * @author Dave Syer + * + * @since 2.0 + */ +public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandlerFactoryBean { + + private volatile Long sendTimeout; + private final ExpressionPayloadMessageProcessor processor; + + public ExpressionControlBusFactoryBean(ExpressionPayloadMessageProcessor processor) { + this.processor = processor; + } + + public void setSendTimeout(Long sendTimeout) { + this.sendTimeout = sendTimeout; + } + + protected MessageHandler createHandler() { + ServiceActivatingHandler handler = new ServiceActivatingHandler(processor); + if (this.sendTimeout != null) { + handler.setSendTimeout(this.sendTimeout); + } + return handler; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java index c7f627dd91..ae428f17eb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java @@ -65,6 +65,7 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan registerBeanDefinitionParser("channel-interceptor", new GlobalChannelInterceptorParser()); registerBeanDefinitionParser("converter", new ConverterParser()); registerBeanDefinitionParser("message-history", new MessageHistoryParser()); + registerBeanDefinitionParser("control-bus", new ControlBusParser()); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/control/ExpressionPayloadMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/control/ExpressionPayloadMessageProcessor.java new file mode 100644 index 0000000000..e0c1e7c4e5 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/control/ExpressionPayloadMessageProcessor.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2010 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.control; + +import org.springframework.integration.Message; +import org.springframework.integration.handler.AbstractMessageProcessor; +import org.springframework.util.Assert; + +/** + * @author Dave Syer + * @since 2.0 + * + */ +public class ExpressionPayloadMessageProcessor extends AbstractMessageProcessor { + + public Object processMessage(Message message) { + Assert.state(message.getPayload() instanceof String, "Message payload must be a String expression"); + String expression = (String) message.getPayload(); + return evaluateExpression(expression, message); + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java index f40ad5a76d..b2281cf2e0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java @@ -16,7 +16,6 @@ package org.springframework.integration.handler; -import org.springframework.context.expression.MapAccessor; import org.springframework.expression.Expression; import org.springframework.expression.ParseException; import org.springframework.integration.Message; @@ -52,7 +51,6 @@ public class ExpressionEvaluatingMessageProcessor extends AbstractMessageProc Assert.notNull(expression, "The expression must not be null"); try { this.expression = expression; - this.getEvaluationContext().addPropertyAccessor(new MapAccessor()); this.expectedType = expectedType; } catch (ParseException e) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java b/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java index f0ac68f405..20ea3916fb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java @@ -1,17 +1,14 @@ /* * Copyright 2002-2010 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. + * + * 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.util; @@ -42,20 +39,18 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { private final BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); - public AbstractExpressionEvaluator() { this.evaluationContext.setTypeConverter(this.typeConverter); this.evaluationContext.addPropertyAccessor(new MapAccessor()); } - /** * Specify a BeanFactory in order to enable resolution via @beanName in the expression. */ public void setBeanFactory(final BeanFactory beanFactory) { if (beanFactory != null) { this.typeConverter.setBeanFactory(beanFactory); - this.getEvaluationContext().setBeanResolver(new SimpleBeanResolver(beanFactory)); + this.evaluationContext.setBeanResolver(new SimpleBeanResolver(beanFactory)); } } @@ -72,13 +67,11 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { protected T evaluateExpression(Expression expression, Message message, Class expectedType) { try { return evaluateExpression(expression, (Object) message, expectedType); - } - catch (EvaluationException e) { + } catch (EvaluationException e) { Throwable cause = e.getCause(); throw new MessageHandlingException(message, "Expression evaluation failed: " + expression.getExpressionString(), cause == null ? e : cause); - } - catch (Exception e) { + } catch (Exception e) { throw new MessageHandlingException(message, "Expression evaluation failed: " + expression.getExpressionString(), e); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java index 053bbafa28..55dd816c7a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java @@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.support.AopUtils; -import org.springframework.context.expression.MapAccessor; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; @@ -54,9 +53,9 @@ import org.springframework.integration.annotation.Payload; import org.springframework.integration.annotation.Payloads; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import org.springframework.util.ReflectionUtils.MethodCallback; import org.springframework.util.ReflectionUtils.MethodFilter; +import org.springframework.util.StringUtils; /** * A helper class for processors that invoke a method on a target Object using a combination of message payload(s) and @@ -192,7 +191,6 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator "Cannot convert to expected type (" + expectedType + ") from " + method); context.registerMethodFilter(targetType, filter); } - context.addPropertyAccessor(new MapAccessor()); context.setVariable("target", targetObject); } diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index 7773ac7671..5c1c34b30e 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -675,10 +675,10 @@ - + - - + + @@ -777,7 +777,8 @@ SpEL expression to be evaluated for each triggered execution. - The result of the evaluation will be passed as the payload of + The result of the evaluation will be + passed as the payload of the Message that is sent to the MessageChannel. @@ -801,7 +802,7 @@ - + @@ -811,7 +812,7 @@ - + @@ -835,9 +836,11 @@ - Specify whether the service 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. + Specify whether the service 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. @@ -958,7 +961,8 @@ delegate when scheduling the sending of delayed Messages. If not provided, the default will use a thread pool of - size 1. + size + 1. @@ -986,7 +990,7 @@ Specify whether tasks should be able to complete on shutdown. By default this is 'false'. - + @@ -1089,16 +1093,19 @@ - NOTE: The 'interval-trigger' sub-element is deprecated as of Spring Integration 2.0 and will be removed in version 2.1. - Use one of the interval trigger attributes instead ('fixed-delay' or 'fixed-rate'). + NOTE: The 'interval-trigger' sub-element is deprecated as of Spring Integration 2.0 and will be + removed in version + 2.1. + Use one of the interval trigger attributes instead ('fixed-delay' or 'fixed-rate'). - NOTE: The 'cron-trigger' sub-element is deprecated as of Spring Integration 2.0 and will be removed in version 2.1. - Use the 'cron' attribute instead. + NOTE: The 'cron-trigger' sub-element is deprecated as of Spring Integration 2.0 and will be + removed in version 2.1. + Use the 'cron' attribute instead. @@ -1304,7 +1311,8 @@ Boolean value to indicate whether this header value should overwrite an existing header - value for the same name. + value + for the same name. @@ -1327,13 +1335,15 @@ Specify the default boolean value for whether to overwrite existing header values. This will - only take effect for + only + take effect for sub-elements that do not provide their own 'overwrite' attribute. If the 'default-overwrite' attribute is not provided, then the specified header values will NOT overwrite any existing ones with the same - header names. + header + names. @@ -1359,7 +1369,8 @@ Reference to an Object to be invoked for header values. The 'method' attribute is required - along with this. + along + with this. @@ -1423,7 +1434,7 @@ - + @@ -1628,7 +1639,8 @@ Reference to a Jackson ObjectMapper instance to be provided optionally - if the default ObjectMapper configuration is not desirable. + if the default ObjectMapper + configuration is not desirable. @@ -1669,7 +1681,8 @@ Reference to a Jackson ObjectMapper instance to be provided optionally - if the default ObjectMapper configuration is not desirable. + if the default ObjectMapper + configuration is not desirable. @@ -1703,7 +1716,8 @@ Reference to a Serializer instance to convert from an object to a byte array. - This is optional. The default will use standard Java serialization. + This is optional. + The default will use standard Java serialization. @@ -1737,7 +1751,8 @@ Reference to a Deserializer instance to convert from a byte array to an object. - This is optional. The default will use standard Java deserialization. + This is optional. + The default will use standard Java deserialization. @@ -1752,7 +1767,8 @@ Defines a Transformer that stores a Message and returns a new Message whose - payload is the id of the stored Message. + payload is the id of + the stored Message. @@ -1761,7 +1777,8 @@ Defines a Transformer that accepts a Message whose payload is a UUID and - retrieves the Message associated with that id from a MessageStore if + retrieves the Message + associated with that id from a MessageStore if available (else null). @@ -1775,7 +1792,8 @@ Reference to the MessageStore to be used by this Claim Check transformer. - If not specified, the default reference will be to a bean named 'messageStore'. + If not specified, the + default reference will be to a bean named 'messageStore'. @@ -1882,9 +1900,11 @@ Name of the header whose value to use. - An expression to be evaluated to determine if this recipient should be included in the recipient + An expression to be evaluated to determine if this recipient should be included in the + recipient list for a given input Message. The evaluation result of the expression must be a boolean. - If this attribute is not defined, the channel will always be among the list of recipients. + If this + attribute is not defined, the channel will always be among the list of recipients. @@ -2112,18 +2132,21 @@ Name of the header whose value to use. - 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. + 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. - 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. + 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. @@ -2154,7 +2177,7 @@ Name of the header whose value to use. A SpEL expression to be evaluated against the input message list as its root object. - + @@ -2224,11 +2247,13 @@ Name of the header whose value to use. Reference to a MessageGroupStore for holding state in between message processing. The default - is to use a + is + to use a volatile in-memory store, which means that unprocessed messages will be lost if the JVM exits. To - customize the expiry of incomplete message groups + customize + the expiry of incomplete message groups configure the message store. @@ -2264,8 +2289,9 @@ Name of the header whose value to use. - Comparator for messages used to sort the sequence when released. Defaults to comparing - the sequence number header. + Comparator for messages used to sort the sequence when released. Defaults to comparing + the + sequence number header. @@ -2405,7 +2431,7 @@ Name of the header whose value to use. - + @@ -2435,7 +2461,7 @@ Name of the header whose value to use. - + @@ -2582,14 +2608,31 @@ only be one Message History writer per ApplicationContext hierarchy. - - + + + + + + + + + + + + Control bus that accepts messages in the form of Groovy scripts. The scripts should be provided as + String payloads + in incoming messages. Scripts can refer to beans in the context using the standard @beanName + convention. - - + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests-context.xml new file mode 100644 index 0000000000..80a25bd077 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests-context.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests.java new file mode 100644 index 0000000000..c55098edea --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusExplicitPollerTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ControlBusExplicitPollerTests { + + @Autowired + private MessageChannel input; + + @Autowired + private PollableChannel output; + + @Test + public void testDefaultEvaluationContext() { + Message message = MessageBuilder.withPayload("@service.convert('aardvark')+headers.foo").setHeader("foo", "bar").build(); + this.input.send(message); + assertEquals("catbar", output.receive(1000).getPayload()); + assertNull(output.receive(0)); + } + + public static class Service { + public String convert(String input) { + return "cat"; + } + } +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests-context.xml new file mode 100644 index 0000000000..906a908db3 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests-context.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests.java new file mode 100644 index 0000000000..9b02b6e24e --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusPollerTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ControlBusPollerTests { + + @Autowired + private MessageChannel input; + + @Autowired + private PollableChannel output; + + @Test + public void testDefaultEvaluationContext() { + Message message = MessageBuilder.withPayload("@service.convert('aardvark')+headers.foo").setHeader("foo", "bar").build(); + this.input.send(message); + assertEquals("catbar", output.receive(1000).getPayload()); + assertNull(output.receive(0)); + } + + public static class Service { + public String convert(String input) { + return "cat"; + } + } +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests-context.xml new file mode 100644 index 0000000000..b3d21043b8 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests.java new file mode 100644 index 0000000000..c5509345d7 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ControlBusTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class ControlBusTests { + + @Autowired + private MessageChannel input; + + @Autowired + private PollableChannel output; + + @Test + public void testDefaultEvaluationContext() { + Message message = MessageBuilder.withPayload("@service.convert('aardvark')+headers.foo").setHeader("foo", "bar").build(); + this.input.send(message); + assertEquals("catbar", output.receive(0).getPayload()); + assertNull(output.receive(0)); + } + + public static class Service { + public String convert(String input) { + return "cat"; + } + } +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java new file mode 100644 index 0000000000..efc0cbf85a --- /dev/null +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/BeanFactoryContextBindingCustomizer.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2010 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.groovy; + +import groovy.lang.Binding; +import groovy.lang.GroovyObject; +import groovy.lang.Script; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.scripting.groovy.GroovyObjectCustomizer; + +public class BeanFactoryContextBindingCustomizer implements GroovyObjectCustomizer, BeanFactoryAware { + + private ListableBeanFactory beanFactory; + + public BeanFactoryContextBindingCustomizer() { + this(null); + } + + public BeanFactoryContextBindingCustomizer(BeanFactory beanFactory) { + setBeanFactory(beanFactory); + } + + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory instanceof ListableBeanFactory ? (ListableBeanFactory) beanFactory : null; + } + + public void customize(GroovyObject goo) { + if (beanFactory != null) { + Binding binding = ((Script) goo).getBinding(); + for (String name : beanFactory.getBeanDefinitionNames()) { + binding.setVariable(name, beanFactory.getBean(name)); + } + } + } + +} \ No newline at end of file diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java index 22d2005605..7241559442 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptPayloadMessageProcessor.java @@ -20,6 +20,7 @@ import java.util.Map; import org.springframework.integration.Message; import org.springframework.integration.handler.AbstractScriptExecutingMessageProcessor; import org.springframework.scripting.ScriptSource; +import org.springframework.scripting.groovy.GroovyObjectCustomizer; import org.springframework.scripting.groovy.GroovyScriptFactory; import org.springframework.scripting.support.StaticScriptSource; import org.springframework.util.Assert; @@ -31,14 +32,18 @@ import org.springframework.util.Assert; */ public class GroovyScriptPayloadMessageProcessor extends AbstractScriptExecutingMessageProcessor { - private final Map map; + private final GroovyObjectCustomizer customizer; public GroovyScriptPayloadMessageProcessor() { - this(null); + this((GroovyObjectCustomizer)null); } public GroovyScriptPayloadMessageProcessor(Map map) { - this.map = map; + this(new MapContextBindingCustomizer(map)); + } + + public GroovyScriptPayloadMessageProcessor(GroovyObjectCustomizer customizer) { + this.customizer = customizer; } @Override @@ -52,7 +57,7 @@ public class GroovyScriptPayloadMessageProcessor extends AbstractScriptExecuting @Override protected Object executeScript(ScriptSource scriptSource, Message message) throws Exception { // Keeping everything local prevents PermGen (class instances) leaks... - MessageContextBindingCustomizer bindingCustomizer = new MessageContextBindingCustomizer(this.map); + MessageContextBindingCustomizer bindingCustomizer = new MessageContextBindingCustomizer(this.customizer); bindingCustomizer.setMessage(message); GroovyScriptFactory scriptFactory = new GroovyScriptFactory(this.getClass().getSimpleName(), bindingCustomizer); Object result = scriptFactory.getScriptedObject(scriptSource, null); diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java new file mode 100644 index 0000000000..a3a011feb4 --- /dev/null +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MapContextBindingCustomizer.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2010 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.groovy; + +import groovy.lang.Binding; +import groovy.lang.GroovyObject; +import groovy.lang.Script; + +import java.util.Map; + +import org.springframework.scripting.groovy.GroovyObjectCustomizer; +import org.springframework.util.Assert; + +public class MapContextBindingCustomizer implements GroovyObjectCustomizer { + + private final Map map; + + public MapContextBindingCustomizer(Map map) { + this.map = map; + } + + public void customize(GroovyObject goo) { + Assert.state(goo instanceof Script, "Expected a Script"); + if (this.map != null) { + Binding binding = ((Script) goo).getBinding(); + for (String key : map.keySet()) { + binding.setVariable(key, map.get(key)); + } + } + + } + +} \ No newline at end of file diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java index 8cd6c9f28f..212c43becb 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/MessageContextBindingCustomizer.java @@ -63,25 +63,4 @@ class MessageContextBindingCustomizer implements GroovyObjectCustomizer { binding.setVariable("headers", this.message.getHeaders()); } } - - private static class MapContextBindingCustomizer implements GroovyObjectCustomizer { - - private final Map map; - - public MapContextBindingCustomizer(Map map) { - this.map = map; - } - - public void customize(GroovyObject goo) { - Assert.state(goo instanceof Script, "Expected a Script"); - if (this.map != null) { - Binding binding = ((Script) goo).getBinding(); - for (String key : map.keySet()) { - binding.setVariable(key, map.get(key)); - } - } - - } - - } } \ No newline at end of file diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java new file mode 100644 index 0000000000..f362a6a55e --- /dev/null +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusFactoryBean.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2010 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.groovy.config; + +import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor; +import org.springframework.integration.handler.ServiceActivatingHandler; + +/** + * FactoryBean for creating {@link MessageHandler} instances to handle a message as a Groovy Script. + * + * @author Dave Syer + * + * @since 2.0 + */ +public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFactoryBean { + + private volatile Long sendTimeout; + private final GroovyScriptPayloadMessageProcessor processor; + + public GroovyControlBusFactoryBean(GroovyScriptPayloadMessageProcessor processor) { + this.processor = processor; + } + + public void setSendTimeout(Long sendTimeout) { + this.sendTimeout = sendTimeout; + } + + protected MessageHandler createHandler() { + return this.configureHandler(new ServiceActivatingHandler(processor)); + } + + private ServiceActivatingHandler configureHandler(ServiceActivatingHandler handler) { + if (this.sendTimeout != null) { + handler.setSendTimeout(this.sendTimeout); + } + return handler; + } + +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java new file mode 100644 index 0000000000..ad16732f4a --- /dev/null +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyControlBusParser.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2010 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.groovy.config; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.groovy.BeanFactoryContextBindingCustomizer; +import org.springframework.integration.groovy.GroovyScriptPayloadMessageProcessor; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * @author Dave Syer + * @since 2.0 + */ +public class GroovyControlBusParser extends AbstractConsumerEndpointParser { + + private static final String CUSTOMIZER_ATTRIBUTE = "customizer"; + + @Override + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GroovyControlBusFactoryBean.class); + builder.addConstructorArgValue(getMessageProcessorBeanDefinition(element, parserContext)); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "order"); + return builder; + } + + protected BeanMetadataElement getMessageProcessorBeanDefinition(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(GroovyScriptPayloadMessageProcessor.class); + String customizerAttr = element.getAttribute(CUSTOMIZER_ATTRIBUTE); + if (StringUtils.hasText(customizerAttr)) { + builder.addConstructorArgReference(customizerAttr.trim()); + } else { + builder.addConstructorArgValue(new RootBeanDefinition(BeanFactoryContextBindingCustomizer.class)); + } + return builder.getBeanDefinition(); + } + +} diff --git a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyNamespaceHandler.java b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyNamespaceHandler.java index 6ba09f2db3..fc5b8041b2 100644 --- a/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyNamespaceHandler.java +++ b/spring-integration-groovy/src/main/java/org/springframework/integration/groovy/config/GroovyNamespaceHandler.java @@ -26,6 +26,7 @@ public class GroovyNamespaceHandler extends AbstractIntegrationNamespaceHandler public void init() { this.registerBeanDefinitionParser("script", new GroovyScriptParser()); + this.registerBeanDefinitionParser("control-bus", new GroovyControlBusParser()); } } diff --git a/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.0.xsd b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.0.xsd index 78e9f9c967..c9bee625ee 100644 --- a/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.0.xsd +++ b/spring-integration-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-2.0.xsd @@ -1,14 +1,11 @@ - - + @@ -23,7 +20,8 @@ Resource location path for the Script. Either this or an inline script - as body text should be provided, but not both. + as body text should be + provided, but not both. @@ -31,7 +29,8 @@ Refresh delay for the script contents if specified as a resource - location (defaults to never refresh). + location (defaults to never + refresh). @@ -40,4 +39,34 @@ + + + + Control bus that accepts messages in the form of Groovy scripts. The scripts should be provided as + String payloads + in incoming messages + + + + + + + + + + A reference to a static GroovyObjectCustomizer that will be used to modify the Groovy scripts + sent to the control channel. By default a customizer is used that simply exposes all beans in the application + context by name in the scripts. + + + + + + + + + + + + diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml new file mode 100644 index 0000000000..0127e39c43 --- /dev/null +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java new file mode 100644 index 0000000000..b625ea1ac0 --- /dev/null +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyControlBusTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2010 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.groovy.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Dave Syer + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class GroovyControlBusTests { + + @Autowired + private MessageChannel input; + + @Autowired + private PollableChannel output; + + @Test + public void testOperationOfControlBus() { // long is > 3 + Message message = MessageBuilder.withPayload("def result = service.convert('aardvark'); def foo = headers.foo; result+foo").setHeader("foo", "bar").build(); + this.input.send(message); + assertEquals("catbar", output.receive(0).getPayload()); + assertNull(output.receive(0)); + } + + public static class Service { + public String convert(String input) { + return "cat"; + } + } +}