diff --git a/build.gradle b/build.gradle
index 4d4e867cb2..689c86d122 100644
--- a/build.gradle
+++ b/build.gradle
@@ -408,6 +408,7 @@ project('spring-integration-scripting') {
testCompile project(":spring-integration-test")
testCompile("org.jruby:jruby:1.6.3")
testCompile("org.codehaus.groovy:groovy-all:1.7.5")
+ testCompile("org.python:jython-standalone:2.5.2")
}
}
diff --git a/spring-integration-scripting/pom.xml b/spring-integration-scripting/pom.xml
deleted file mode 100644
index 203639d858..0000000000
--- a/spring-integration-scripting/pom.xml
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
- 4.0.0
- org.springframework.integration
- spring-integration-scripting
- 2.1.0.BUILD-SNAPSHOT
- Spring Integration Scripting Support
-
-
- The Apache Software License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
-
-
-
-
-
- src/main/java
-
- **/*
-
-
- **/*.java
-
-
-
- src/main/resources
-
- **/*
-
-
-
-
-
- src/test/java
-
- **/*
-
-
- **/*.java
-
-
-
- src/test/resources
-
- **/*
-
-
-
-
-
- maven-compiler-plugin
-
- 1.5
- 1.5
-
-
-
- maven-surefire-plugin
-
-
- **/*Tests.java
-
-
- **/*Abstract*.java
-
-
-
-
-
-
-
- SpringSource External Bundle Repository
- http://repository.springsource.com/maven/bundles/external/
-
-
- SpringSource Milestone Repository
- http://maven.springframework.org/milestone/
-
-
- SpringSource Release Bundle Repository
- http://repository.springsource.com/maven/bundles/release/
-
-
- SpringSource Release Repository
- http://maven.springframework.org/release/
-
-
-
-
- org.easymock
- easymock
- 2.3
- test
-
-
- org.springframework.integration
- spring-integration-core
- 2.1.0.BUILD-SNAPSHOT
- compile
-
-
- org.springframework.integration
- spring-integration-test
- 2.1.0.BUILD-SNAPSHOT
- test
-
-
- org.easymock
- easymockclassextension
- 2.3
- test
-
-
- org.hamcrest
- hamcrest-all
- 1.1
- test
-
-
- org.springframework
- spring-test
- 3.0.6.RELEASE
- test
-
-
- org.mockito
- mockito-all
- 1.8.4
- test
-
-
- org.codehaus.groovy
- groovy-all
- 1.7.5
- test
-
-
- log4j
- log4j
- 1.2.12
- test
-
-
- cglib
- cglib-nodep
- 2.2
- test
-
-
- junit
- junit-dep
- 4.8.2
- test
-
-
- org.jruby
- jruby
- 1.6.3
- test
-
-
-
- UTF8
-
-
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptExecutor.java
index 7d260cbd18..9530901b09 100644
--- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptExecutor.java
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptExecutor.java
@@ -35,5 +35,6 @@ public interface ScriptExecutor {
* @param variables -bind variable
* @return
*/
- public abstract Object executeScript(ScriptSource scriptSource,Map variables);
+ public abstract Object executeScript(ScriptSource scriptSource,Map variables);
+
}
\ No newline at end of file
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptingException.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptingException.java
new file mode 100644
index 0000000000..59cd9aa800
--- /dev/null
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/ScriptingException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.scripting;
+
+import org.springframework.integration.MessagingException;
+
+/**
+ * @author David Turanski
+ * @since 2.1
+ */
+@SuppressWarnings("serial")
+
+public class ScriptingException extends MessagingException {
+
+ public ScriptingException(String description) {
+ super(description);
+ }
+
+
+ public ScriptingException(String description, Throwable cause) {
+ super(description, cause);
+ }
+}
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/config/jsr223/ScriptParser.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/config/jsr223/ScriptParser.java
index dd04bcc637..f5fde7a4e6 100644
--- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/config/jsr223/ScriptParser.java
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/config/jsr223/ScriptParser.java
@@ -19,7 +19,7 @@ package org.springframework.integration.scripting.config.jsr223;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.scripting.config.AbstractScriptParser;
-import org.springframework.integration.scripting.jsr223.DefaultScriptExecutor;
+import org.springframework.integration.scripting.jsr223.ScriptExecutorFactory;
import org.springframework.util.Assert;
import org.w3c.dom.Element;
@@ -47,7 +47,7 @@ public class ScriptParser extends AbstractScriptParser {
protected void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext){
String language = element.getAttribute(LANGUAGE_ATTRIBUTE);
Assert.hasLength(language, "Attribute " + LANGUAGE_ATTRIBUTE + " is required");
- builder.addConstructorArgValue(new DefaultScriptExecutor(language));
+ builder.addConstructorArgValue(ScriptExecutorFactory.getScriptExecutor(language));
}
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
new file mode 100644
index 0000000000..e3fa153e38
--- /dev/null
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/AbstractScriptExecutor.java
@@ -0,0 +1,113 @@
+/*
+ * 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.scripting.jsr223;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+
+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;
+import org.springframework.util.Assert;
+
+/**
+ * Base Class for {@link ScriptExecutor}
+ *
+ * @author David Turanski
+ * @author Mark Fisher
+ * @since 2.1
+ */
+abstract class AbstractScriptExecutor implements ScriptExecutor {
+
+ protected final Log logger = LogFactory.getLog(this.getClass());
+
+ protected final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+
+ protected final String language;
+
+ public AbstractScriptExecutor(String language) {
+ Assert.hasText(language, "language must not be empty");
+ this.language = language;
+ if (logger.isDebugEnabled()) {
+ ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(this.language);
+ if (scriptEngine == null) {
+ logger.error(invlalidLanguageMessage(this.language));
+ }
+ else {
+ logger.debug("using script engine : " + scriptEngine.getFactory().getEngineName());
+ }
+ }
+ }
+
+ public Object executeScript(ScriptSource scriptSource) {
+ return this.executeScript(scriptSource, null);
+ }
+
+ public Object executeScript(ScriptSource scriptSource, Map variables) {
+ Object result = null;
+ ScriptEngine scriptEngine = this.scriptEngineManager.getEngineByName(this.language);
+
+ if (scriptEngine == null) {
+ throw new ScriptingException(invlalidLanguageMessage(this.language));
+ }
+
+ 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);
+
+ result = postProcess(result, scriptEngine, script);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("script executed in " + (new Date().getTime() - start.getTime()) + " ms");
+ }
+ }
+
+ catch (Exception e) {
+ throw new ScriptingException(e.getMessage(), e);
+ }
+
+ return result;
+ }
+
+ /**
+ * Subclasses may implement this to provide any special handling required
+ * @param result
+ * @param scriptEngine
+ * @param script
+ * @return modified result
+ */
+ protected abstract Object postProcess(Object result, ScriptEngine scriptEngine, String script);
+
+ private static String invlalidLanguageMessage(String language) {
+ return new StringBuilder().append(ScriptEngineManager.class.getName())
+ .append(" is unable to create a script engine for language '").append(language).append("'.\n")
+ .append("This may be due to a missing language implementation or an invalid language name.").toString();
+ }
+
+}
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/DefaultScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/DefaultScriptExecutor.java
index ce23ce3f77..6cd18542b3 100644
--- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/DefaultScriptExecutor.java
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/DefaultScriptExecutor.java
@@ -12,88 +12,36 @@
*/
package org.springframework.integration.scripting.jsr223;
-import java.io.IOException;
-import java.util.Date;
-import java.util.Map;
-import java.util.Map.Entry;
-
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.springframework.integration.scripting.ScriptExecutor;
-import org.springframework.scripting.ScriptSource;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
/**
- * Executes JSR223 scripts
+ * Default implementation of the {@link ScriptExecutor}
*
* @author David Turanski
* @author Mark Fisher
* @since 2.1
*/
-public class DefaultScriptExecutor implements ScriptExecutor {
-
- private static final Log logger = LogFactory.getLog(DefaultScriptExecutor.class);
-
- static {
- if (ClassUtils.isPresent("org.jruby.embed.jsr223.JRubyEngine", System.class.getClassLoader())) {
- System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
- System.setProperty("org.jruby.embed.localcontext.scope", "threadsafe");
- }
- }
-
-
- private final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
-
- private final String language;
-
-
+ class DefaultScriptExecutor extends AbstractScriptExecutor {
/**
- * Create a DefaultScriptExceutor for the specified language name (JSR233 alias).
+ * Create a DefaultScriptExceutor for the specified language name (JSR233
+ * alias).
*/
public DefaultScriptExecutor(String language) {
- Assert.hasText(language, "language must not be empty");
- this.language = language;
- if (logger.isDebugEnabled()) {
- logger.debug("using script engine : " + scriptEngineManager.getEngineByName(language).getFactory().getEngineName());
- }
+ super(language);
}
-
- public Object executeScript(ScriptSource scriptSource) {
- return this.executeScript(scriptSource, null);
- }
-
- public Object executeScript(ScriptSource scriptSource, Map variables) {
- Object result = null;
- ScriptEngine scriptEngine = this.scriptEngineManager.getEngineByName(this.language);
- 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 (logger.isDebugEnabled()) {
- logger.debug("script executed in " + (new Date().getTime() - start.getTime()) + " ms");
- }
- }
- catch (IOException e) {
- throw new RuntimeException(e);
- }
- catch (ScriptException e) {
- throw new RuntimeException(e);
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.springframework.integration.scripting.jsr223.AbstractScriptExecutor
+ * #postProcess(java.lang.Object, javax.script.ScriptEngine,
+ * java.lang.String)
+ */
+ @Override
+ protected Object postProcess(Object result, ScriptEngine scriptEngine, String script) {
return result;
}
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/PythonScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/PythonScriptExecutor.java
new file mode 100644
index 0000000000..f4c3bc820a
--- /dev/null
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/PythonScriptExecutor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.scripting.jsr223;
+
+import javax.script.ScriptEngine;
+
+import org.springframework.integration.scripting.ScriptExecutor;
+
+/**
+ * A {@link ScriptExecutor} that implements special handling required for Python to emulate behavior similar to other JSR223 scripting languages.
+ *
+ * Script evaluation using the Jython implementation results in a null return value for normal variable expressions such as
+ * x=2. As a work around, it is necessary to get the value of 'x' explicitly following the script evaluation. This class performs
+ * simple parsing on the last line of the script to obtain the variable name, if any, and return its value.
+ *
+ * @author David Turanski
+ * @since 2.1
+ *
+ */
+ class PythonScriptExecutor extends AbstractScriptExecutor {
+ /**
+ * @param language
+ */
+ public PythonScriptExecutor() {
+ super("python");
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.scripting.jsr223.AbstractScriptExecutor#postProcess(java.lang.Object, javax.script.ScriptEngine, java.lang.String)
+ */
+ @Override
+ protected Object postProcess(Object result, ScriptEngine scriptEngine, String script) {
+ Object newResult= result;
+ if (newResult == null) {
+ String returnVariableName = PythonVariableParser.parseReturnVariable(script);
+ newResult = scriptEngine.get(returnVariableName);
+ }
+ return newResult;
+ }
+
+ public static class PythonVariableParser {
+ public static String parseReturnVariable(String script){
+ String[] lines = script.trim().split("\n");
+ String lastLine = lines[lines.length -1];
+ String[] tokens = lastLine.split("=");
+ return tokens[0].trim();
+ }
+ }
+}
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/RubyScriptExecutor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/RubyScriptExecutor.java
new file mode 100644
index 0000000000..8e7efb799e
--- /dev/null
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/RubyScriptExecutor.java
@@ -0,0 +1,35 @@
+/*
+ * 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.scripting.jsr223;
+
+import org.springframework.util.ClassUtils;
+
+
+/**
+ * @author David Turanski
+ * @since 2.1
+ *
+ */
+ class RubyScriptExecutor extends DefaultScriptExecutor {
+
+ static {
+ if (ClassUtils.isPresent("org.jruby.embed.jsr223.JRubyEngine", System.class.getClassLoader())) {
+ System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
+ System.setProperty("org.jruby.embed.localcontext.scope", "threadsafe");
+ }
+ }
+
+ public RubyScriptExecutor() {
+ super("ruby");
+ }
+}
diff --git a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/ScriptExecutingMessageProcessor.java b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/ScriptExecutingMessageProcessor.java
index ccb12ade69..b20be7cf6c 100644
--- a/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/ScriptExecutingMessageProcessor.java
+++ b/spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/ScriptExecutingMessageProcessor.java
@@ -28,7 +28,7 @@ import org.springframework.util.Assert;
*/
public class ScriptExecutingMessageProcessor extends AbstractScriptExecutingMessageProcessor