INT-1507: add control bus to core and groovy

This commit is contained in:
Dave Syer
2010-10-25 10:04:47 -07:00
parent df3549cf70
commit 53dd793819
33 changed files with 1037 additions and 321 deletions

View File

@@ -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);

View File

@@ -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<MessageHandler>, 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<? extends MessageHandler> 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.");
}
<T> MessageHandler createMessageProcessingHandler(MessageProcessor<T> processor) {
return this.createMethodInvokingHandler(processor, "processMessage");
}
MessageHandler createDefaultHandler() {
throw new IllegalArgumentException(
"Exactly one of the 'targetObject' or 'expression' property is required.");
}
@SuppressWarnings("unchecked")
<T> T extractTypeIfPossible(Object targetObject, Class<T> 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;
}
}

View File

@@ -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<MessageHandler>, 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<? extends MessageHandler> getObjectType() {
if (this.handler != null) {
return this.handler.getClass();
}
return MessageHandler.class;
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -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.");
}
<T> MessageHandler createMessageProcessingHandler(MessageProcessor<T> processor) {
return this.createMethodInvokingHandler(processor, "processMessage");
}
MessageHandler createDefaultHandler() {
throw new IllegalArgumentException("Exactly one of the 'targetObject' or 'expression' property is required.");
}
@SuppressWarnings("unchecked")
<T> T extractTypeIfPossible(Object targetObject, Class<T> 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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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<Object> {
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);
}
}

View File

@@ -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<T> 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) {

View File

@@ -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 <code>@beanName</code> 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> T evaluateExpression(Expression expression, Message<?> message, Class<T> 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);
}

View File

@@ -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<T> extends AbstractExpressionEvaluator
"Cannot convert to expected type (" + expectedType + ") from " + method);
context.registerMethodFilter(targetType, filter);
}
context.addPropertyAccessor(new MapAccessor());
context.setVariable("target", targetObject);
}

View File

@@ -675,10 +675,10 @@
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
</xsd:choice>
<xsd:element name="header" type="headerSubElementType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="header" type="headerSubElementType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes"/>
<xsd:attributeGroup ref="channelAdapterAttributes"/>
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes" />
<xsd:attributeGroup ref="channelAdapterAttributes" />
</xsd:complexType>
</xsd:element>
@@ -777,7 +777,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
@@ -801,7 +802,7 @@
<xsd:complexType name="methodInvokingChannelAdapterType">
<xsd:complexContent>
<xsd:extension base="channelAdapterType">
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes"/>
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -811,7 +812,7 @@
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="channelAdapterAttributes"/>
<xsd:attributeGroup ref="channelAdapterAttributes" />
</xsd:complexType>
<xsd:element name="service-activator">
@@ -835,9 +836,11 @@
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -986,7 +990,7 @@
<xsd:documentation>
Specify whether tasks should be able to complete on shutdown. By
default this is 'false'.
</xsd:documentation>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
@@ -1089,16 +1093,19 @@
<xsd:element name="interval-trigger" type="intervalTriggerType">
<xsd:annotation>
<xsd:documentation>
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').
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="cron-trigger" type="cronTriggerType">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
@@ -1304,7 +1311,8 @@
<xsd:documentation>
Boolean value to indicate whether this header value should overwrite
an existing header
value for the same name.
value
for the same name.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
@@ -1327,13 +1335,15 @@
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
@@ -1359,7 +1369,8 @@
<xsd:documentation>
Reference to an Object to be invoked for header values.
The 'method' attribute is required
along with this.
along
with this.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref" />
@@ -1423,7 +1434,7 @@
<xsd:complexContent>
<xsd:extension base="referenceHeaderType">
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element name="expression" type="innerExpressionType"/>
<xsd:element name="expression" type="innerExpressionType" />
</xsd:sequence>
<xsd:attribute name="value" type="xsd:string">
<xsd:annotation>
@@ -1628,7 +1639,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -1669,7 +1681,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -1703,7 +1716,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -1737,7 +1751,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -1752,7 +1767,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
@@ -1761,7 +1777,8 @@
<xsd:annotation>
<xsd:documentation>
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).
</xsd:documentation>
</xsd:annotation>
@@ -1775,7 +1792,8 @@
<xsd:annotation>
<xsd:documentation>
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'.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -1882,9 +1900,11 @@ Name of the header whose value to use.
<xsd:attribute name="selector-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -2112,18 +2132,21 @@ Name of the header whose value to use.
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Specify whether the splitter method must return a non-null value. This value will be
FALSE by default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or expression) returns a NULL value.
Specify whether the splitter method must return a non-null value. This value will be
FALSE by
default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or
expression) returns a NULL value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="apply-sequence" type="xsd:boolean" use="optional">
<xsd:annotation>
<xsd:documentation>
Set this flag to false to prevent adding sequence related headers in this splitter. This
can be convenient in cases where the set sequence numbers conflict with downstream custom
aggregations.
Set this flag to false to prevent adding sequence related headers in this splitter. This
can be
convenient in cases where the set sequence numbers conflict with downstream custom
aggregations.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -2154,7 +2177,7 @@ Name of the header whose value to use.
<xsd:annotation>
<xsd:documentation>
A SpEL expression to be evaluated against the input message list as its root object.
</xsd:documentation>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
@@ -2224,11 +2247,13 @@ Name of the header whose value to use.
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
@@ -2264,8 +2289,9 @@ Name of the header whose value to use.
<xsd:attribute name="comparator" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -2405,7 +2431,7 @@ Name of the header whose value to use.
<xsd:extension base="handlerEndpointType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1" />
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="expression" type="xsd:string">
@@ -2435,7 +2461,7 @@ Name of the header whose value to use.
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.expression.ExpressionSource" />
</tool:annotation>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
@@ -2582,14 +2608,31 @@ only be one Message History writer per ApplicationContext hierarchy.
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="tracked-components" type="xsd:string" default="*">
<xsd:annotation>
<xsd:documentation>
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The list of component name patterns you want to track (e.g., tracked-components="inputChannel, out*, *Channel, *Service")
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="control-bus">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:annotation>
<xsd:complexType>
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:complexType>
</xsd:element>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:groovy="http://www.springframework.org/schema/integration/groovy"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<channel id="input">
<queue />
</channel>
<channel id="output">
<queue />
</channel>
<control-bus input-channel="input" output-channel="output">
<poller fixed-rate="100" />
</control-bus>
<beans:bean id="service" class="org.springframework.integration.config.xml.ControlBusTests$Service" />
</beans:beans>

View File

@@ -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";
}
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:groovy="http://www.springframework.org/schema/integration/groovy"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<channel id="input">
<queue />
</channel>
<channel id="output">
<queue />
</channel>
<control-bus input-channel="input" output-channel="output"/>
<poller default="true" fixed-rate="100"/>
<beans:bean id="service" class="org.springframework.integration.config.xml.ControlBusTests$Service" />
</beans:beans>

View File

@@ -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";
}
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:groovy="http://www.springframework.org/schema/integration/groovy"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<channel id="output">
<queue/>
</channel>
<control-bus input-channel="input" output-channel="output" send-timeout="100" order="1" auto-startup="true"/>
<beans:bean id="service" class="org.springframework.integration.config.xml.ControlBusTests$Service" />
</beans:beans>

View File

@@ -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";
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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<Object> {
private final Map<String, ?> map;
private final GroovyObjectCustomizer customizer;
public GroovyScriptPayloadMessageProcessor() {
this(null);
this((GroovyObjectCustomizer)null);
}
public GroovyScriptPayloadMessageProcessor(Map<String, ?> 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);

View File

@@ -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<String, ?> map;
public MapContextBindingCustomizer(Map<String, ?> 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));
}
}
}
}

View File

@@ -63,25 +63,4 @@ class MessageContextBindingCustomizer implements GroovyObjectCustomizer {
binding.setVariable("headers", this.message.getHeaders());
}
}
private static class MapContextBindingCustomizer implements GroovyObjectCustomizer {
private final Map<String, ?> map;
public MapContextBindingCustomizer(Map<String, ?> 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));
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -26,6 +26,7 @@ public class GroovyNamespaceHandler extends AbstractIntegrationNamespaceHandler
public void init() {
this.registerBeanDefinitionParser("script", new GroovyScriptParser());
this.registerBeanDefinitionParser("control-bus", new GroovyControlBusParser());
}
}

View File

@@ -1,14 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/groovy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
targetNamespace="http://www.springframework.org/schema/integration/groovy"
<xsd:schema xmlns="http://www.springframework.org/schema/integration/groovy" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration" targetNamespace="http://www.springframework.org/schema/integration/groovy"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:import namespace="http://www.springframework.org/schema/integration"
schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd" />
<xsd:import namespace="http://www.springframework.org/schema/integration" schemaLocation="http://www.springframework.org/schema/integration/spring-integration-2.0.xsd" />
<xsd:element name="script">
<xsd:annotation>
@@ -23,7 +20,8 @@
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -31,7 +29,8 @@
<xsd:annotation>
<xsd:documentation>
Refresh delay for the script contents if specified as a resource
location (defaults to never refresh).
location (defaults to never
refresh).
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -40,4 +39,34 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="control-bus">
<xsd:annotation>
<xsd:documentation>
Control bus that accepts messages in the form of Groovy scripts. The scripts should be provided as
String payloads
in incoming messages
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:element name="poller" type="integration:innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attribute name="customizer" use="optional">
<xsd:annotation>
<xsd:documentation>
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.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="org.springframework.scripting.groovy.GroovyObjectCustomizer" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:inputOutputChannelGroup" />
</xsd:complexType>
</xsd:element>
</xsd:schema>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:groovy="http://www.springframework.org/schema/integration/groovy"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/groovy
http://www.springframework.org/schema/integration/groovy/spring-integration-groovy.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<channel id="output">
<queue/>
</channel>
<groovy:control-bus input-channel="input" output-channel="output" send-timeout="100" order="1" auto-startup="true"/>
<beans:bean id="service" class="org.springframework.integration.groovy.config.GroovyControlBusTests$Service" />
</beans:beans>

View File

@@ -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";
}
}
}