INT-2491: Scripting: Variables Improvements

JIRA: https://jira.springsource.org/browse/INT-2491

* Add support variables for 'inline' scripts
* Add convenient attribute `variables` for script tags
* Introduce `BeanFactoryFallbackBinding` for groovy scripts.

INT-2491: Polishing

* Add check for duplication of provided variables
* Tests and Docs

Polishing
This commit is contained in:
Artem Bilan
2013-11-18 17:28:29 +02:00
committed by Gary Russell
parent a7cabc53b0
commit 87798b055f
14 changed files with 327 additions and 143 deletions

View File

@@ -16,12 +16,21 @@
package org.springframework.integration.groovy;
import groovy.lang.Binding;
import groovy.lang.GString;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.lang.MissingPropertyException;
import groovy.lang.Script;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.scripting.AbstractScriptExecutingMessageProcessor;
@@ -32,13 +41,6 @@ import org.springframework.scripting.groovy.GroovyObjectCustomizer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import groovy.lang.Binding;
import groovy.lang.GString;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.lang.Script;
/**
* The {@link org.springframework.integration.handler.MessageProcessor} implementation
* to evaluate Groovy scripts.
@@ -136,12 +138,9 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
try {
GroovyObject goo = (GroovyObject) this.scriptClass.newInstance();
GroovyObjectCustomizer groovyObjectCustomizer = this.customizerDecorator;
if (variables != null) {
// Override empty Script.Binding with new one with 'variables'
groovyObjectCustomizer = new BindingOverwriteGroovyObjectCustomizerDecorator(new Binding(variables));
((VariableBindingGroovyObjectCustomizerDecorator) groovyObjectCustomizer).setCustomizer(this.customizerDecorator);
}
VariableBindingGroovyObjectCustomizerDecorator groovyObjectCustomizer =
new BindingOverwriteGroovyObjectCustomizerDecorator(new BeanFactoryFallbackBinding(variables));
groovyObjectCustomizer.setCustomizer(this.customizerDecorator);
if (goo instanceof Script) {
// Allow metaclass and other customization.
@@ -164,4 +163,34 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
}
}
private class BeanFactoryFallbackBinding extends Binding {
private BeanFactoryFallbackBinding(Map<?, ?> variables) {
super(variables);
}
@Override
public Object getVariable(String name) {
try {
return super.getVariable(name);
}
catch (MissingPropertyException e) {
// Original {@link Binding} doesn't have 'variable' for the given 'name'.
// Try to resolve it as 'bean' from the given <code>beanFactory</code>.
}
if (GroovyScriptExecutingMessageProcessor.this.beanFactory == null) {
throw new MissingPropertyException(name, this.getClass());
}
try {
return GroovyScriptExecutingMessageProcessor.this.beanFactory.getBean(name);
}
catch (NoSuchBeanDefinitionException e) {
throw new MissingPropertyException(name, this.getClass(), e);
}
}
}
}

View File

@@ -10,30 +10,38 @@
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<service-activator input-channel="referencedScriptInput">
<groovy:script location="org/springframework/integration/groovy/config/GroovyServiceActivatorTests.groovy"
<groovy:script location="org/springframework/integration/groovy/config/GroovyServiceActivatorTests.groovy"
customizer="groovyCustomizer">
<groovy:variable name="foo" value="foo"/>
<groovy:variable name="bar" value="bar"/>
<groovy:variable name="date" ref="date"/>
</groovy:script>
</service-activator>
<service-activator input-channel="withScriptVariableGenerator">
<groovy:script location="org/springframework/integration/groovy/config/GroovyServiceActivatorTests.groovy"
script-variable-generator="scriptVarSource" customizer="groovyCustomizer"/>
</service-activator>
<beans:bean id="groovyCustomizer"
<beans:bean id="groovyCustomizer"
class="org.springframework.integration.groovy.config.GroovyServiceActivatorTests.MyGroovyCustomizer"/>
<beans:bean id="scriptVarSource"
<beans:bean id="scriptVarSource"
class="org.springframework.integration.groovy.config.GroovyServiceActivatorTests.SampleScriptVariSource"/>
<service-activator input-channel="inlineScriptInput">
<groovy:script customizer="groovyCustomizer">
<groovy:script customizer="groovyCustomizer" variables="date-ref=date">
<![CDATA[
return "inline-$payload"
return "inline-$payload : ${date.format('dd.mm.yyyy')}"
]]>
</groovy:script>
</service-activator>
<service-activator input-channel="scriptWithoutVariablesInput">
<groovy:script>
<![CDATA[
return "withoutVariables-$payload : ${date.format('dd.mm.yyyy')}"
]]>
</groovy:script>
</service-activator>

View File

@@ -1,20 +0,0 @@
<?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">
<service-activator input-channel="inlineScriptInput">
<groovy:script>
<![CDATA[
return "inline-$payload-" + "$foo" + " - " + bar + " - " + date
]]>
<groovy:variable name="foo" value="foo"/>
<groovy:variable name="bar" value="bar"/>
</groovy:script>
</service-activator>
</beans:beans>

View File

@@ -23,6 +23,8 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -38,6 +40,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.message.GenericMessage;
@@ -73,6 +76,9 @@ public class GroovyServiceActivatorTests {
@Autowired
private MessageChannel invalidInlineScript;
@Autowired
private MessageChannel scriptWithoutVariablesInput;
@Autowired
private MyGroovyCustomizer groovyCustomizer;
@@ -134,13 +140,38 @@ public class GroovyServiceActivatorTests {
Message<?> message = MessageBuilder.withPayload("test-" + i).setReplyChannel(replyChannel).build();
this.inlineScriptInput.send(message);
}
assertEquals("inline-test-1", replyChannel.receive(0).getPayload());
assertEquals("inline-test-2", replyChannel.receive(0).getPayload());
assertEquals("inline-test-3", replyChannel.receive(0).getPayload());
DateFormat format = new SimpleDateFormat("dd.mm.yyyy");
String now = format.format(new Date());
assertEquals("inline-test-1 : " + now, replyChannel.receive(0).getPayload());
assertEquals("inline-test-2 : " + now, replyChannel.receive(0).getPayload());
assertEquals("inline-test-3 : " + now, replyChannel.receive(0).getPayload());
assertNull(replyChannel.receive(0));
assertTrue(groovyCustomizer.executed);
}
@Test
public void testScriptWithoutVariables() throws Exception{
PollableChannel replyChannel = new QueueChannel();
for (int i = 1; i <= 3; i++) {
Message<?> message = MessageBuilder.withPayload("test-" + i).setReplyChannel(replyChannel).build();
this.scriptWithoutVariablesInput.send(message);
}
DateFormat format = new SimpleDateFormat("dd.mm.yyyy");
String now = format.format(new Date());
assertEquals("withoutVariables-test-1 : " + now, replyChannel.receive(0).getPayload());
assertEquals("withoutVariables-test-2 : " + now, replyChannel.receive(0).getPayload());
assertEquals("withoutVariables-test-3 : " + now, replyChannel.receive(0).getPayload());
assertNull(replyChannel.receive(0));
}
//INT-2399
@Test(expected = MessageHandlingException.class)
public void invalidInlineScript() throws Exception {
@@ -158,11 +189,6 @@ public class GroovyServiceActivatorTests {
}
@Test(expected=BeanDefinitionParsingException.class)
public void inlineScriptAndVariables() throws Exception{
new ClassPathXmlApplicationContext("GroovyServiceActivatorTests-fail-context.xml", this.getClass());
}
@Test(expected=BeanDefinitionParsingException.class)
public void variablesAndScriptVariableGenerator() throws Exception{
new ClassPathXmlApplicationContext("GroovyServiceActivatorTests-fail-withgenerator-context.xml", this.getClass());