From 01b470f0c799e7222e9ad55cc9cce076b6dbbf76 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 8 Oct 2013 18:43:53 -0400 Subject: [PATCH 1/5] Fix CR Newlines:ExpressionEvaluatingMessageHandler Lines terminated with CR instead of LF --- .../ExpressionEvaluatingMessageHandler.java | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageHandler.java index 4fa5ca6f3e..23645ab9de 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageHandler.java @@ -1 +1,66 @@ -/* * 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 * * 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.handler; import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.util.Assert; /** * A {@link org.springframework.integration.core.MessageHandler} that evaluates * the provided {@link Expression} expecting a void return. * * @author Artem Bilan * @see MethodInvokingMessageHandler * @since 2.1 */ public class ExpressionEvaluatingMessageHandler extends AbstractMessageHandler { private volatile ExpressionEvaluatingMessageProcessor processor; private volatile String componentType; public ExpressionEvaluatingMessageHandler(Expression expression) { Assert.notNull(expression, "Expression must not be null"); this.processor = new ExpressionEvaluatingMessageProcessor( expression, Void.class); } public void setComponentType(String componentType) { this.componentType = componentType; } @Override public String getComponentType() { return this.componentType; } @Override protected void onInit() throws Exception { this.processor.setBeanFactory(getBeanFactory()); } @Override protected void handleMessageInternal(Message message) throws Exception { this.processor.processMessage(message); } } \ No newline at end of file +/* + + * 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 + * + * 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.handler; + +import org.springframework.expression.Expression; +import org.springframework.integration.Message; +import org.springframework.util.Assert; + +/** + * A {@link org.springframework.integration.core.MessageHandler} that evaluates + * the provided {@link Expression} expecting a void return. + * + * @author Artem Bilan + * @see MethodInvokingMessageHandler + * @since 2.1 + */ +public class ExpressionEvaluatingMessageHandler extends AbstractMessageHandler { + + private volatile ExpressionEvaluatingMessageProcessor processor; + + private volatile String componentType; + + + public ExpressionEvaluatingMessageHandler(Expression expression) { + Assert.notNull(expression, "Expression must not be null"); + this.processor = new ExpressionEvaluatingMessageProcessor( + expression, Void.class); + } + + + public void setComponentType(String componentType) { + this.componentType = componentType; + } + + @Override + public String getComponentType() { + return this.componentType; + } + + @Override + protected void onInit() throws Exception { + this.processor.setBeanFactory(getBeanFactory()); + } + + @Override + protected void handleMessageInternal(Message message) throws Exception { + this.processor.processMessage(message); + } + +} + From 0f3ac4d867e33b5af8079fe289bac46026c1a2aa Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 9 Oct 2013 14:45:22 +0300 Subject: [PATCH 2/5] INT-3126: Document DefaultScriptVariableGenerator JIRA: https://jira.springsource.org/browse/INT-3126 --- .../DefaultScriptVariableGenerator.java | 8 ++- .../scripting/ScriptVariableGenerator.java | 5 +- src/reference/docbook/groovy.xml | 2 + src/reference/docbook/scripting.xml | 61 ++++++++++--------- 4 files changed, 45 insertions(+), 31 deletions(-) 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 744cefdec4..e5db4586d5 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.integration.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/ScriptVariableGenerator.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptVariableGenerator.java index 7cb5b4e113..fb7e0db38b 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.integration.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/src/reference/docbook/groovy.xml b/src/reference/docbook/groovy.xml index 7a9adbefb4..4845df307a 100644 --- a/src/reference/docbook/groovy.xml +++ b/src/reference/docbook/groovy.xml @@ -57,6 +57,8 @@ Setting a custom GroovyObjectCustomizer is not mutually exclusive with <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. From 90d5a2fb98b12866f283e48bf18ab98a56c235b3 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 10 Oct 2013 18:00:04 -0400 Subject: [PATCH 3/5] Doc Polishing for the AMQP Support Documentation for configuring an external listener container in the inbound endpoints. --- src/reference/docbook/amqp.xml | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) 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. Date: Sun, 6 Oct 2013 17:25:05 +0300 Subject: [PATCH 4/5] INT-3164: Fix Scripting Refresh Previously, there was no logic to check, if a JSR223 script was modified should be refreshed from the external resource. * Fix `RefreshableResourceScriptSource` to take care of refreshing on call `isModified()` * Add `scriptSource.isModified()` before getting script text * Introduce `ScriptSourceFactoryBean` to avoid I/O operations, when there is need to refresh script resource - `refreshDelay < 0` JIRA: https://jira.springsource.org/browse/INT-3164 INT-3164: change the refresh mutation logic Remove factory bean - no longer needed. --- .../groovy/config/GroovyRefreshTests.java | 8 +- .../config/GroovyServiceActivatorTests.java | 11 ++- .../RefreshableResourceScriptSource.java | 25 +++--- .../jsr223/AbstractScriptExecutor.java | 12 +-- .../Int3164Jsr223RefreshTests-context.xml | 20 +++++ .../jsr223/Int3164Jsr223RefreshTests.java | 89 +++++++++++++++++++ 6 files changed, 140 insertions(+), 25 deletions(-) create mode 100644 spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests-context.xml create mode 100644 spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Int3164Jsr223RefreshTests.java 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 48e162977c..8eb6a3f77f 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 06bfcfd1cf..c7bc6fd674 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,12 +16,12 @@ package org.springframework.integration.groovy.config; +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.*; - -import groovy.lang.GroovyObject; -import groovy.lang.MissingPropertyException; +import static org.junit.Assert.fail; import java.util.Date; import java.util.HashMap; @@ -47,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/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/jsr223/AbstractScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java index 061045348f..84293b719c 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. - * + * * 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. @@ -28,7 +28,7 @@ import org.springframework.util.Assert; /** * Base Class for {@link ScriptExecutor} - * + * * @author David Turanski * @author Mark Fisher * @since 2.1 @@ -44,9 +44,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) { 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()); + } + +} From 7b2ba3e0a342f3fb833722a747a2cc5f8f36664f Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 2 Oct 2013 20:09:33 +0300 Subject: [PATCH 5/5] INT-3162 Fix AbstractScriptExecutor Thread-Safety JIRA: https://jira.springsource.org/browse/INT-3162 --- .../jsr223/AbstractScriptExecutor.java | 18 +++++----- .../jsr223/Jsr223TransformerTests-context.xml | 33 +++++++++++++++---- .../config/jsr223/Jsr223TransformerTests.java | 32 +++++++++++++++++- 3 files changed, 68 insertions(+), 15 deletions(-) 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 84293b719c..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,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. You may obtain a copy of the License at @@ -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; @@ -31,6 +32,7 @@ import org.springframework.util.Assert; * * @author David Turanski * @author Mark Fisher + * @author Artem Bilan * @since 2.1 */ abstract class AbstractScriptExecutor implements ScriptExecutor { @@ -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/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 d1d6f5e4cd..1511e3f721 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.integration.Message; import org.springframework.integration.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()); + + } + + }