diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyRefreshTests.java b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyRefreshTests.java index 4d035e5ffe..bc5ceba157 100644 --- a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyRefreshTests.java +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyRefreshTests.java @@ -62,7 +62,7 @@ public class GroovyRefreshTests { super.setValue(new CycleResource()); } } - + private static class CycleResource extends AbstractResource { private int count = -1; @@ -71,12 +71,12 @@ public class GroovyRefreshTests { public String getDescription() { return "CycleResource"; } - + @Override public String getFilename() throws IllegalStateException { return "CycleResource"; } - + @Override public long lastModified() throws IOException { return -1; @@ -88,6 +88,6 @@ public class GroovyRefreshTests { } return new ByteArrayInputStream(scripts[count].getBytes()); } - + } } diff --git a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyServiceActivatorTests.java b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyServiceActivatorTests.java index 41528bc540..aefdfb6a22 100644 --- a/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyServiceActivatorTests.java +++ b/spring-integration-groovy/src/test/java/org/springframework/integration/groovy/config/GroovyServiceActivatorTests.java @@ -16,9 +16,12 @@ package org.springframework.integration.groovy.config; -import static org.junit.Assert.*; -import groovy.lang.GroovyObject; -import groovy.lang.MissingPropertyException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Date; import java.util.HashMap; @@ -44,6 +47,9 @@ import org.springframework.scripting.groovy.GroovyObjectCustomizer; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import groovy.lang.GroovyObject; +import groovy.lang.MissingPropertyException; + /** * @author Mark Fisher * @author Oleg Zhurakousky diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/DefaultScriptVariableGenerator.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/DefaultScriptVariableGenerator.java index 31f0b634c9..35cf65ed99 100644 --- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/DefaultScriptVariableGenerator.java +++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/DefaultScriptVariableGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -24,6 +24,10 @@ import org.springframework.messaging.Message; import org.springframework.util.CollectionUtils; /** + * A default {@link ScriptVariableGenerator} implementation; used by script processors. + * The result of {@link #generateScriptVariables(Message)} is a {@link Map} of any provided {@code variables} + * plus {@code payload} and {@code headers} from the {@code Message} argument. + * * @author Oleg Zhurakousky * @author Mark Fisher * @since 2.0.2 @@ -53,7 +57,7 @@ public class DefaultScriptVariableGenerator implements ScriptVariableGenerator { if (!CollectionUtils.isEmpty(this.variableMap)) { for (Map.Entry entry : this.variableMap.entrySet()) { scriptVariables.put(entry.getKey(), entry.getValue()); - } + } } return scriptVariables; } diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java index 3f928ed35b..5348100d24 100644 --- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java +++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/RefreshableResourceScriptSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2013 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. @@ -26,6 +26,7 @@ import org.springframework.scripting.support.ResourceScriptSource; /** * @author Dave Syer * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ public class RefreshableResourceScriptSource implements ScriptSource { @@ -51,7 +52,10 @@ public class RefreshableResourceScriptSource implements ScriptSource { } public String getScriptAsString() throws IOException { - this.script = source.getScriptAsString(); + if (this.script == null || this.isModified()) { + this.lastModifiedChecked.set(System.currentTimeMillis()); + this.script = source.getScriptAsString(); + } return this.script; } @@ -60,15 +64,14 @@ public class RefreshableResourceScriptSource implements ScriptSource { } public boolean isModified() { - if (this.refreshDelay < 0) { - return false; - } - long time = System.currentTimeMillis(); - if (this.refreshDelay == 0 || (time - this.lastModifiedChecked.get()) > this.refreshDelay) { - this.lastModifiedChecked.set(time); - return this.source.isModified(); - } - return false; + return this.refreshDelay >= 0 && + (System.currentTimeMillis() - this.lastModifiedChecked.get()) > this.refreshDelay && + this.source.isModified(); + } + + @Override + public String toString() { + return this.source.toString(); } } diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptVariableGenerator.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptVariableGenerator.java index 461d8eb065..8c108c08e7 100644 --- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptVariableGenerator.java +++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptVariableGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -21,6 +21,9 @@ import java.util.Map; import org.springframework.messaging.Message; /** + * Strategy interface to provide a {@link Map} of variables to the script execution context. + * Variables may be extracted from the {@link Message} argument. + * * @author Oleg Zhurakousky * @since 2.0.2 */ diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java index 061045348f..6c88dcf9f4 100644 --- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java +++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. - * + * Copyright 2002-2013 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. @@ -14,13 +14,14 @@ package org.springframework.integration.scripting.jsr223; import java.util.Date; import java.util.Map; -import java.util.Map.Entry; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; +import javax.script.SimpleBindings; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.integration.scripting.ScriptExecutor; import org.springframework.integration.scripting.ScriptingException; import org.springframework.scripting.ScriptSource; @@ -28,9 +29,10 @@ import org.springframework.util.Assert; /** * Base Class for {@link ScriptExecutor} - * + * * @author David Turanski * @author Mark Fisher + * @author Artem Bilan * @since 2.1 */ abstract class AbstractScriptExecutor implements ScriptExecutor { @@ -44,9 +46,9 @@ abstract class AbstractScriptExecutor implements ScriptExecutor { public AbstractScriptExecutor(String language) { Assert.hasText(language, "language must not be empty"); this.language = language; - + scriptEngine = new ScriptEngineManager().getEngineByName(this.language); - + if (logger.isDebugEnabled()) { if (scriptEngine == null) { @@ -66,18 +68,18 @@ abstract class AbstractScriptExecutor implements ScriptExecutor { Object result = null; try { - if (variables != null) { - for (Entry entry : variables.entrySet()) { - scriptEngine.put(entry.getKey(), entry.getValue()); - } - } String script = scriptSource.getScriptAsString(); Date start = new Date(); if (logger.isDebugEnabled()) { logger.debug("executing script: " + script); } - result = scriptEngine.eval(script); + if (variables != null) { + result = scriptEngine.eval(script, new SimpleBindings(variables)); + } + else { + result = scriptEngine.eval(script); + } result = postProcess(result, scriptEngine, script); diff --git a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests-context.xml b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests-context.xml new file mode 100644 index 0000000000..f814fef198 --- /dev/null +++ b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests.java b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests.java new file mode 100644 index 0000000000..110f83caf9 --- /dev/null +++ b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013 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.scripting.config.jsr223; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.message.GenericMessage; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.FileCopyUtils; + +/** + * @author Artem Bilan + * @since 3.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class Int3164Jsr223RefreshTests { + + private static File workDir; + + private static File scriptFile; + + @Autowired + private MessageChannel referencedScriptInput; + + @Autowired + private PollableChannel outputChannel; + + @BeforeClass + public static void start() throws IOException { + String basePath = System.getProperty("java.io.tmpdir") + File.separator + "Int3164Jsr223RefreshTests"; + workDir = new File(basePath); + shutdown(); + workDir.mkdir(); + workDir.deleteOnExit(); + scriptFile = new File(workDir, "int3164.groovy"); + scriptFile.createNewFile(); + FileCopyUtils.copy("1".getBytes(), scriptFile); + } + + @AfterClass + public static void shutdown() { + if (workDir != null && workDir.exists()) { + for (File file : workDir.listFiles()) { + file.delete(); + } + workDir.delete(); + } + } + + @Test + public void testRefreshingScript() throws Exception { + this.referencedScriptInput.send(new GenericMessage("test")); + assertEquals(1, this.outputChannel.receive(100).getPayload()); + + FileCopyUtils.copy("2".getBytes(), scriptFile); + scriptFile.setLastModified(System.currentTimeMillis() + 10000); // force refresh + + this.referencedScriptInput.send(new GenericMessage("test")); + assertEquals(2, this.outputChannel.receive(100).getPayload()); + } + +} diff --git a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests-context.xml b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests-context.xml index 6096802dcc..5a15e6293d 100644 --- a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests-context.xml +++ b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests-context.xml @@ -1,15 +1,17 @@ + http://www.springframework.org/schema/integration/scripting http://www.springframework.org/schema/integration/scripting/spring-integration-scripting.xsd + http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> @@ -19,4 +21,23 @@ ]]> + + + + + + + + + + + + + + + diff --git a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests.java b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests.java index 4d2b43b477..e82f2d81cd 100644 --- a/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests.java +++ b/spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2013 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. @@ -19,6 +19,9 @@ package org.springframework.integration.scripting.config.jsr223; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import java.util.HashSet; +import java.util.Set; + import org.junit.Test; import org.junit.runner.RunWith; @@ -26,12 +29,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Mark Fisher + * @author Artem Bilan * @since 2.0 */ @ContextConfiguration @@ -44,6 +50,12 @@ public class Jsr223TransformerTests { @Autowired private MessageChannel inlineScriptInput; + @Autowired + private MessageChannel int3162InputChannel; + + @Autowired + private PollableChannel int3162OutputChannel; + @Test public void referencedScript() { @@ -73,4 +85,22 @@ public class Jsr223TransformerTests { assertNull(replyChannel.receive(0)); } + @Test + public void testInt3162ScriptExecutorThreadSafety() { + for (int i = 0; i < 100; i++) { + this.int3162InputChannel.send(new GenericMessage(i)); + } + + Set result = new HashSet(); + + for (int i = 0; i < 100; i++) { + Message message = this.int3162OutputChannel.receive(1000); + result.add(message.getPayload()); + } + + assertEquals(100, result.size()); + + } + + } diff --git a/src/reference/docbook/amqp.xml b/src/reference/docbook/amqp.xml index 282d4ca7f4..2fc36fbd91 100644 --- a/src/reference/docbook/amqp.xml +++ b/src/reference/docbook/amqp.xml @@ -47,11 +47,11 @@ -
+
Inbound Channel Adapter A configuration sample for an AMQP Inbound Channel Adapter is shown - below with all available parameters. + below. Optional. + Optional. + + Note that when configuring an external container, you cannot use the Spring AMQP + namespace to define the container. This is because the namespace requires at least one <listener/> + element. In this environment, the listener is internal to the adapter. For this reason, you must define + the container using a normal Spring <bean/> definition, such as: + + + + +]]> + The MessageConverter to use when receiving AMQP Messages. @@ -273,7 +286,7 @@ this list can also be simple patterns to be matched against the header names (e. Outbound Channel Adapter A configuration sample for an AMQP Outbound Channel Adapter is shown - below with all available parameters. + below. Inbound Gateway A configuration sample for an AMQP Inbound Gateway is shown - below with all available parameters. + below. + + See the note in about configuring the listener-container + attribute. +
Outbound Gateway A configuration sample for an AMQP Outbound Gateway is shown - below with all available parameters. + below. <variable> sub-elements or the script-variable-generator attribute. It can also be provided when defining an inline script. + For more information regarding <variable> and script-variable-generator, see the + paragraph 'Script variable bindings' of .
diff --git a/src/reference/docbook/scripting.xml b/src/reference/docbook/scripting.xml index a2a000233b..559bd018ce 100644 --- a/src/reference/docbook/scripting.xml +++ b/src/reference/docbook/scripting.xml @@ -8,26 +8,26 @@ Scripting support With Spring Integration 2.1 we've added support for the - JSR223 Scripting for Java specification, - introduced in Java version 6. This allows you to use scripts written in any supported language including - Ruby/JRuby, Javascript and Groovy to provide the logic for various integration components similar to the way - the Spring Expression Language (SpEL) is used in Spring Integration. For more information about JSR223 please refer to the + JSR223 Scripting for Java specification, + introduced in Java version 6. This allows you to use scripts written in any supported language including + Ruby/JRuby, Javascript and Groovy to provide the logic for various integration components similar to the way + the Spring Expression Language (SpEL) is used in Spring Integration. For more information about JSR223 please refer to the documentation - Note that this feature requires Java 6 or higher. Sun developed a JSR223 reference implementation which works with + Note that this feature requires Java 6 or higher. Sun developed a JSR223 reference implementation which works with Java 5 but it is not officially supported and we have not tested it with Spring Integration. In order to use a JVM scripting language, a JSR223 implementation for that language must be included in your class path. Java 6 natively - supports Javascript. The Groovy and + supports Javascript. The Groovy and JRuby projects provide JSR233 support in their standard distribution. Other language implementations may be available or under development. Please refer to the appropriate project website for more information. - Various JSR223 language implementations have been developed by third parties. A particular implementation's compatibility - with Spring Integration depends on how well it conforms to the specification and/or the implementer's interpretation of the specification. - - If you plan to use Groovy as your scripting language, we recommended you use Spring-Integration's Groovy Support + Various JSR223 language implementations have been developed by third parties. A particular implementation's compatibility + with Spring Integration depends on how well it conforms to the specification and/or the implementer's interpretation of the specification. + + If you plan to use Groovy as your scripting language, we recommended you use Spring-Integration's Groovy Support as it offers additional features specific to Groovy. However you will find this section relevant as well. @@ -50,28 +50,30 @@ </int:filter> <int:filter input-channel="inlineScriptInput"> - <int-script:script lang="groovy"><![CDATA[ + <int-script:script lang="groovy"> + <![CDATA[ return payload == 'good' - ]]></int-script:script> + ]]> + </int-script:script> </int:filter> Here, you see that the script can be included inline - or can reference a resource location via the location attribute. Additionally the lang attribute + or can reference a resource location via the location attribute. Additionally the lang attribute corresponds to the language name (or JSR223 alias) Other Spring Integration endpoint elements which support scripting include router, service-activator, - transformer, and splitter. The scripting configuration in each case would be identical to the above + transformer, and splitter. The scripting configuration in each case would be identical to the above (besides the endpoint element). - Another useful feature of Scripting support is the ability to update (reload) scripts without - having to restart the Application Context. To accomplish this, specify the refresh-check-delay + Another useful feature of Scripting support is the ability to update (reload) scripts without + having to restart the Application Context. To accomplish this, specify the refresh-check-delay attribute on the script element: <int-script:script location="..." refresh-check-delay="5000"/> In the above example, the script location will be checked for updates every 5 seconds. If the script is updated, - any invocation that occurs later than 5 seconds since the update will result in execution of the new script. + any invocation that occurs later than 5 seconds since the update will result in execution of the new script. <int-script:script location="..." refresh-check-delay="0"/> @@ -82,12 +84,12 @@ This is the default behavior. Inline scripts can not be reloaded. <int-script:script location="..." refresh-check-delay="-1"/> - + Script variable bindings - + - Variable bindings are required to enable the script to reference variables externally provided to the script's execution context. - As we have seen, payload and headers are used as binding variables by default. You can bind additional variables + Variable bindings are required to enable the script to reference variables externally provided to the script's execution context. + As we have seen, payload and headers are used as binding variables by default. You can bind additional variables to a script via <variable> sub-elements: @@ -96,29 +98,32 @@ ]]> As shown in the above example, you can bind a script variable either to a scalar value or a Spring bean reference. Note that payload and headers will still be included as binding variables. - + - If you need more control over how variables are generated, you can implement your own Java class + If you need more control over how variables are generated, you can implement your own Java class using the ScriptVariableGenerator strategy: generateScriptVariables(Message message); - + }]]> This interface requires you to implement the method generateScriptVariables(Message). The Message argument allows you to access any data available in the Message payload and headers and the return value is - the Map of bound variables. This method will be called every time the script is executed for a Message. All you need to do is + the Map of bound variables. This method will be called every time the script is executed for a Message. All you need to do is provide an implementation of ScriptVariableGenerator and reference it with the script-variable-generator attribute: ]]> - + If a script-variable-generator is not provided, script components use + org.springframework.integration.scripting.DefaultScriptVariableGenerator, which merges + any provided <variable>s with payload and headers + variables from the Message in its generateScriptVariables(Message) method. - You cannot provide both the script-variable-generator attribute and <variable> sub-element(s) + You cannot provide both the script-variable-generator attribute and <variable> sub-element(s) as they are mutually exclusive. Also, custom variable bindings cannot be used with an inline script.