INT-1727 moved AbstractScriptExecutingMessageProcessor to the 'scripting' package and refactored executeScript() to accept a variable Map instead of the Message. The base class now manages the scriptVariableGenerator property.

This commit is contained in:
Mark Fisher
2011-02-04 11:56:19 -05:00
parent 8c50f02555
commit 4c669ec205
10 changed files with 106 additions and 79 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -11,10 +11,13 @@
* specific language governing permissions and limitations under the License.
*/
package org.springframework.integration.handler;
package org.springframework.integration.scripting;
import java.util.Map;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.scripting.ScriptSource;
/**
@@ -25,12 +28,27 @@ import org.springframework.scripting.ScriptSource;
*/
public abstract class AbstractScriptExecutingMessageProcessor<T> implements MessageProcessor<T> {
private final ScriptVariableGenerator scriptVariableGenerator;
protected AbstractScriptExecutingMessageProcessor() {
this.scriptVariableGenerator = new DefaultScriptVariableGenerator();
}
protected AbstractScriptExecutingMessageProcessor(ScriptVariableGenerator scriptVariableGenerator) {
this.scriptVariableGenerator = (scriptVariableGenerator != null) ? scriptVariableGenerator
: new DefaultScriptVariableGenerator();
}
/**
* Executes the script and returns the result.
*/
public final T processMessage(Message<?> message) {
try {
return this.executeScript(getScriptSource(message), message);
ScriptSource source = this.getScriptSource(message);
Map<String, Object> variables = this.scriptVariableGenerator.generateScriptVariables(message);
return this.executeScript(source, variables);
}
catch (Exception e) {
throw new MessageHandlingException(message, "failed to execute script", e);
@@ -39,8 +57,8 @@ public abstract class AbstractScriptExecutingMessageProcessor<T> implements Mess
/**
* Subclasses must implement this method to create a script source, optionally using the message to locate or
* create the script.
* Subclasses must implement this method to create a script source,
* optionally using the message to locate or create the script.
*
* @param message the message being processed
* @return a ScriptSource to use to create a script
@@ -48,9 +66,9 @@ public abstract class AbstractScriptExecutingMessageProcessor<T> implements Mess
protected abstract ScriptSource getScriptSource(Message<?> message);
/**
* Subclasses must implement this method. In doing so, the execution context for the script should be populated with
* the Message's 'payload' and 'headers' as variables.
* Subclasses must implement this method. In doing so, the execution context
* for the script should be populated with the provided script variables.
*/
protected abstract T executeScript(ScriptSource scriptSource, Message<?> message) throws Exception;
protected abstract T executeScript(ScriptSource scriptSource, Map<String, Object> variables) throws Exception;
}

View File

@@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.springframework.integration.groovy;
package org.springframework.integration.scripting;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -27,13 +28,13 @@ import org.springframework.util.CollectionUtils;
* @author Mark Fisher
* @since 2.0.2
*/
class DefaultScriptVariableGenerator implements ScriptVariableGenerator {
public class DefaultScriptVariableGenerator implements ScriptVariableGenerator {
private final Map<String, Object> variableMap;
public DefaultScriptVariableGenerator(){
this(null);
public DefaultScriptVariableGenerator() {
this.variableMap = Collections.<String, Object>emptyMap();
}
public DefaultScriptVariableGenerator(Map<String, Object> variableMap) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.groovy;
package org.springframework.integration.scripting;
import java.util.Map;

View File

@@ -15,12 +15,16 @@ package org.springframework.integration.groovy;
import groovy.lang.GString;
import java.util.Map;
import org.springframework.integration.Message;
import org.springframework.integration.handler.AbstractScriptExecutingMessageProcessor;
import org.springframework.integration.scripting.AbstractScriptExecutingMessageProcessor;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.groovy.GroovyScriptFactory;
import org.springframework.scripting.support.StaticScriptSource;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* @author Dave Syer
@@ -30,11 +34,18 @@ import org.springframework.util.Assert;
*/
public class GroovyCommandMessageProcessor extends AbstractScriptExecutingMessageProcessor<Object> {
private final ScriptVariableGenerator scriptVariableGenerator;
/**
* Creates a GroovyCommandMessageProcessor that will use the DefaultScriptVariableGenerator.
*/
public GroovyCommandMessageProcessor() {
super();
}
/**
* Creates a GroovyCommandMessageProcessor that will use the provided ScriptVariableGenerator.
*/
public GroovyCommandMessageProcessor(ScriptVariableGenerator scriptVariableGenerator) {
this.scriptVariableGenerator = scriptVariableGenerator;
super(scriptVariableGenerator);
}
@@ -47,12 +58,12 @@ public class GroovyCommandMessageProcessor extends AbstractScriptExecutingMessag
}
@Override
protected Object executeScript(ScriptSource scriptSource, Message<?> message) throws Exception {
protected Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) throws Exception {
Assert.notNull(scriptSource, "scriptSource must not be null");
VariableBindingGroovyObjectCustomizer customizer = new VariableBindingGroovyObjectCustomizer();
GroovyScriptFactory factory = new GroovyScriptFactory(this.getClass().getSimpleName(), customizer);
if (this.scriptVariableGenerator != null) {
customizer.setVariables(this.scriptVariableGenerator.generateScriptVariables(message));
if (!CollectionUtils.isEmpty(variables)) {
customizer.setVariables(variables);
}
Object result = factory.getScriptedObject(scriptSource, null);
return (result instanceof GString) ? result.toString() : result;

View File

@@ -21,7 +21,8 @@ import groovy.lang.GString;
import java.util.Map;
import org.springframework.integration.Message;
import org.springframework.integration.handler.AbstractScriptExecutingMessageProcessor;
import org.springframework.integration.scripting.AbstractScriptExecutingMessageProcessor;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.groovy.GroovyScriptFactory;
import org.springframework.util.Assert;
@@ -41,20 +42,17 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
private volatile ScriptSource scriptSource;
private final ScriptVariableGenerator scriptVariableGenerator;
/**
* Create a processor for the given {@link ScriptSource}.
*/
public GroovyScriptExecutingMessageProcessor(ScriptSource scriptSource) {
this(scriptSource, new DefaultScriptVariableGenerator());
this(scriptSource, null);
}
public GroovyScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptVariableGenerator scriptVariableGenerator) {
super(scriptVariableGenerator);
this.scriptSource = scriptSource;
this.scriptVariableGenerator = (scriptVariableGenerator != null) ? scriptVariableGenerator
: new DefaultScriptVariableGenerator();
this.scriptFactory = new GroovyScriptFactory(this.getClass().getSimpleName(), this.customizer);
}
@@ -65,12 +63,11 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
}
@Override
protected Object executeScript(ScriptSource scriptSource, Message<?> message) throws Exception {
protected Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) throws Exception {
Assert.notNull(scriptSource, "scriptSource must not be null");
Map<String, Object> scriptVariables = this.scriptVariableGenerator.generateScriptVariables(message);
synchronized (this) {
if (!CollectionUtils.isEmpty(scriptVariables)) {
this.customizer.setVariables(scriptVariables);
if (!CollectionUtils.isEmpty(variables)) {
this.customizer.setVariables(variables);
}
Object result = this.scriptFactory.getScriptedObject(scriptSource, null);
return (result instanceof GString) ? result.toString() : result;

View File

@@ -24,8 +24,8 @@ import org.springframework.integration.Message;
import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.groovy.GroovyCommandMessageProcessor;
import org.springframework.integration.groovy.ScriptVariableGenerator;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.CustomizableThreadCreator;
@@ -46,8 +46,8 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
@Override
protected MessageHandler createHandler() {
ManagedBeansScriptVariableSource scriptVariableSource = new ManagedBeansScriptVariableSource(this.getBeanFactory());
GroovyCommandMessageProcessor processor = new GroovyCommandMessageProcessor(scriptVariableSource);
ManagedBeansScriptVariableGenerator scriptVariableGenerator = new ManagedBeansScriptVariableGenerator(this.getBeanFactory());
GroovyCommandMessageProcessor processor = new GroovyCommandMessageProcessor(scriptVariableGenerator);
return this.configureHandler(new ServiceActivatingHandler(processor));
}
@@ -59,11 +59,11 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
}
private static class ManagedBeansScriptVariableSource implements ScriptVariableGenerator {
private static class ManagedBeansScriptVariableGenerator implements ScriptVariableGenerator {
private final ListableBeanFactory beanFactory;
public ManagedBeansScriptVariableSource(BeanFactory beanFactory) {
public ManagedBeansScriptVariableGenerator(BeanFactory beanFactory) {
this.beanFactory = (beanFactory instanceof ListableBeanFactory) ? (ListableBeanFactory) beanFactory : null;
}
@@ -73,10 +73,10 @@ public class GroovyControlBusFactoryBean extends AbstractSimpleMessageHandlerFac
if (this.beanFactory != null) {
for (String name : this.beanFactory.getBeanDefinitionNames()) {
Object bean = this.beanFactory.getBean(name);
if (bean instanceof Lifecycle ||
bean instanceof CustomizableThreadCreator ||
(AnnotationUtils.findAnnotation(bean.getClass(), ManagedResource.class) != null)) {
variables.put(name, bean);
if (bean instanceof Lifecycle ||
bean instanceof CustomizableThreadCreator ||
(AnnotationUtils.findAnnotation(bean.getClass(), ManagedResource.class) != null)) {
variables.put(name, bean);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ public class GroovyScriptParser extends AbstractSingleBeanDefinitionParser {
private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay";
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.groovy.GroovyScriptExecutingMessageProcessor";
@@ -57,32 +58,30 @@ public class GroovyScriptParser extends AbstractSingleBeanDefinitionParser {
parserContext.getReaderContext().error("Either the 'location' attribute or inline script text must be provided, but not both.", element);
return;
}
List<Element> variableElements = DomUtils.getChildElementsByTagName(element, "variable");
String scriptVariableSourceName = element.getAttribute("script-variable-generator");
if (StringUtils.hasText(scriptText) && (variableElements.size() > 0 || StringUtils.hasText(scriptVariableSourceName))){
parserContext.getReaderContext().error("Variable bindings or custom ScriptVariabelSource are not allowed when using inline groovy script. " +
String scriptVariableGeneratorName = element.getAttribute("script-variable-generator");
if (StringUtils.hasText(scriptText) && (variableElements.size() > 0 || StringUtils.hasText(scriptVariableGeneratorName))) {
parserContext.getReaderContext().error("Variable bindings or custom ScriptVariableGenerator are not allowed when using an inline groovy script. " +
"Specify location of the script via 'location' attribute instead", element);
return;
}
if (StringUtils.hasText(scriptVariableSourceName) && variableElements.size() > 0){
parserContext.getReaderContext().error("'script-variable-generator' and 'variable' sub-element are mutualy exclusive. Must use one or the other.", element);
if (StringUtils.hasText(scriptVariableGeneratorName) && variableElements.size() > 0){
parserContext.getReaderContext().error("'script-variable-generator' and 'variable' sub-elements are mutualy exclusive.", element);
return;
}
if (StringUtils.hasText(scriptLocation)){
if (StringUtils.hasText(scriptLocation)) {
builder.addConstructorArgValue(this.resolveScriptLocation(element, parserContext.getReaderContext(), scriptLocation));
}
else {
builder.addConstructorArgValue(new StaticScriptSource(scriptText, "groovy.lang.Script"));
}
if (!StringUtils.hasText(scriptVariableSourceName)){
BeanDefinitionBuilder scriptVariableSourceBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.integration.groovy.DefaultScriptVariableGenerator");
if (!StringUtils.hasText(scriptVariableGeneratorName)) {
BeanDefinitionBuilder scriptVariableGeneratorBuilder =
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.integration.scripting.DefaultScriptVariableGenerator");
ManagedMap<String, Object> variableMap = new ManagedMap<String, Object>();
for (Element childElement : variableElements) {
String variableName = childElement.getAttribute("name");
@@ -93,20 +92,20 @@ public class GroovyScriptParser extends AbstractSingleBeanDefinitionParser {
" is required for element " +
IntegrationNamespaceUtils.createElementDescription(element) + ".", element);
}
if (StringUtils.hasText(variableValue)){
if (StringUtils.hasText(variableValue)) {
variableMap.put(variableName, variableValue);
}
else {
variableMap.put(variableName, new RuntimeBeanReference(variableRef));
}
}
if (!CollectionUtils.isEmpty(variableMap)){
scriptVariableSourceBuilder.addConstructorArgValue(variableMap);
if (!CollectionUtils.isEmpty(variableMap)) {
scriptVariableGeneratorBuilder.addConstructorArgValue(variableMap);
}
scriptVariableSourceName =
BeanDefinitionReaderUtils.registerWithGeneratedName(scriptVariableSourceBuilder.getBeanDefinition(), parserContext.getRegistry());
scriptVariableGeneratorName = BeanDefinitionReaderUtils.registerWithGeneratedName(
scriptVariableGeneratorBuilder.getBeanDefinition(), parserContext.getRegistry());
}
builder.addConstructorArgReference(scriptVariableSourceName);
builder.addConstructorArgReference(scriptVariableGeneratorName);
}
private Object resolveScriptLocation(Element element, XmlReaderContext readerContext, String scriptLocation) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,11 +28,11 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.core.io.AbstractResource;
import org.springframework.integration.Message;
import org.springframework.integration.groovy.config.RefreshableResourceScriptSource;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;
@@ -59,20 +59,20 @@ public class GroovyScriptExecutingMessageProcessorTests {
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar"+count).build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new ResourceScriptSource(resource);
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, new DefaultScriptVariableGenerator());
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource);
Object result = processor.processMessage(message);
assertEquals("payload is foo, header is bar"+count, result.toString());
}
@Test
public void testSimpleExecutionWithScriptVariableSource() throws Exception {
public void testSimpleExecutionWithScriptVariableGenerator() throws Exception {
int count = countHolder.getAndIncrement();
String script = "return \"payload is $payload, header is $headers.testHeader and date is $date\"";
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar"+count).build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new ResourceScriptSource(resource);
Object result = null;
class CustomScriptVariableSource implements ScriptVariableGenerator {
class CustomScriptVariableGenerator implements ScriptVariableGenerator {
public Map<String, Object> generateScriptVariables(Message<?> message) {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("date", System.nanoTime());
@@ -82,10 +82,10 @@ public class GroovyScriptExecutingMessageProcessorTests {
}
}
for (int i = 0; i < 5; i++) {
ScriptVariableGenerator scriptVariableSource = new CustomScriptVariableSource();
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, scriptVariableSource);
ScriptVariableGenerator scriptVariableGenerator = new CustomScriptVariableGenerator();
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, scriptVariableGenerator);
Object newResult = processor.processMessage(message);
assertFalse(newResult.equals(result)); // make sure that we get different nanotime verifying that resolveScriptVariables() is invoked
assertFalse(newResult.equals(result)); // make sure that we get different nanotime verifying that generateScriptVariables() is invoked
result = newResult;
}
}
@@ -106,7 +106,7 @@ public class GroovyScriptExecutingMessageProcessorTests {
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar").build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new ResourceScriptSource(resource);
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, new DefaultScriptVariableGenerator());
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource);
Thread.sleep(20L);
resource.setScript("return \"payload is $payload\"");
Object result = processor.processMessage(message);
@@ -119,7 +119,7 @@ public class GroovyScriptExecutingMessageProcessorTests {
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar").build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new RefreshableResourceScriptSource(resource, 1000L);
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, new DefaultScriptVariableGenerator());
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource);
// should be the original script
Object result = processor.processMessage(message);
assertEquals("payload is foo, header is bar", result.toString());
@@ -143,7 +143,7 @@ public class GroovyScriptExecutingMessageProcessorTests {
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar").build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new RefreshableResourceScriptSource(resource, -1L);
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, new DefaultScriptVariableGenerator());
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource);
// process with the first script
Object result = processor.processMessage(message);
assertEquals("payload is foo, header is bar", result.toString());
@@ -160,7 +160,7 @@ public class GroovyScriptExecutingMessageProcessorTests {
Message<?> message = MessageBuilder.withPayload("foo").setHeader("testHeader", "bar").build();
TestResource resource = new TestResource(script, "simpleTest");
ScriptSource scriptSource = new RefreshableResourceScriptSource(resource, 0);
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource, new DefaultScriptVariableGenerator());
MessageProcessor<Object> processor = new GroovyScriptExecutingMessageProcessor(scriptSource);
// process with the first script
Object result = processor.processMessage(message);
assertEquals("payload is foo, header is bar", result.toString());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -20,9 +20,10 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.scripting.DefaultScriptVariableGenerator;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.annotation.Repeat;
@@ -44,14 +45,14 @@ public class GroovyScriptPayloadMessageProcessorTests {
public void testSimpleExecution() throws Exception {
int count = countHolder.getAndIncrement();
Message<?> message = MessageBuilder.withPayload("headers.foo" + count).setHeader("foo" + count, "bar").build();
processor = new GroovyCommandMessageProcessor(new DefaultScriptVariableGenerator());
processor = new GroovyCommandMessageProcessor();
Object result = processor.processMessage(message);
assertEquals("bar", result.toString());
}
@Test
public void testDoubleExecutionWithNewScript() throws Exception {
processor = new GroovyCommandMessageProcessor(new DefaultScriptVariableGenerator());
processor = new GroovyCommandMessageProcessor();
Message<?> message = MessageBuilder.withPayload("headers.foo").setHeader("foo", "bar").build();
Object result = processor.processMessage(message);
assertEquals("bar", result.toString());
@@ -64,9 +65,9 @@ public class GroovyScriptPayloadMessageProcessorTests {
public void testSimpleExecutionWithContext() throws Exception {
Message<?> message = MessageBuilder.withPayload("\"spam is $spam foo is $headers.foo\"")
.setHeader("foo", "bar").build();
ScriptVariableGenerator scriptVariableSource =
new DefaultScriptVariableGenerator(Collections.singletonMap("spam",(Object)"bucket"));
MessageProcessor<Object> processor = new GroovyCommandMessageProcessor(scriptVariableSource);
ScriptVariableGenerator scriptVariableGenerator =
new DefaultScriptVariableGenerator(Collections.singletonMap("spam",(Object)"bucket"));
MessageProcessor<Object> processor = new GroovyCommandMessageProcessor(scriptVariableGenerator);
Object result = processor.processMessage(message);
assertEquals("spam is bucket foo is bar", result.toString());
}

View File

@@ -34,7 +34,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.groovy.ScriptVariableGenerator;
import org.springframework.integration.scripting.ScriptVariableGenerator;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;