INT-3751: Fix Python Scripting

JIRA: https://jira.spring.io/browse/INT-3751

Result was null when `executeScript()` was invoked with non-null variables.

When variable are provided, the result variable is stored in the provided bindings.
This commit is contained in:
Gary Russell
2015-06-22 14:36:20 -04:00
committed by Artem Bilan
parent bd62b4191d
commit c51299b1b2
4 changed files with 61 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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
@@ -15,6 +15,7 @@ package org.springframework.integration.scripting.jsr223;
import java.util.Date;
import java.util.Map;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;
@@ -33,6 +34,7 @@ import org.springframework.util.Assert;
* @author David Turanski
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @since 2.1
*/
abstract class AbstractScriptExecutor implements ScriptExecutor {
@@ -60,10 +62,12 @@ abstract class AbstractScriptExecutor implements ScriptExecutor {
}
}
@Override
public Object executeScript(ScriptSource scriptSource) {
return this.executeScript(scriptSource, null);
}
@Override
public Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) {
Object result = null;
@@ -74,14 +78,16 @@ abstract class AbstractScriptExecutor implements ScriptExecutor {
logger.debug("executing script: " + script);
}
if (variables != null) {
result = scriptEngine.eval(script, new SimpleBindings(variables));
Bindings bindings = null;
if (variables != null && variables.size() > 0) {
bindings = new SimpleBindings(variables);
result = scriptEngine.eval(script, bindings);
}
else {
result = scriptEngine.eval(script);
}
result = postProcess(result, scriptEngine, script);
result = postProcess(result, scriptEngine, script, bindings);
if (logger.isDebugEnabled()) {
logger.debug("script executed in " + (new Date().getTime() - start.getTime()) + " ms");
@@ -97,12 +103,13 @@ abstract class AbstractScriptExecutor implements ScriptExecutor {
/**
* Subclasses may implement this to provide any special handling required
* @param result
* @param scriptEngine
* @param script
* @param result the result.
* @param scriptEngine the engine.
* @param script the script.
* @param bindings the bindings.
* @return modified result
*/
protected abstract Object postProcess(Object result, ScriptEngine scriptEngine, String script);
protected abstract Object postProcess(Object result, ScriptEngine scriptEngine, String script, Bindings bindings);
private static String invlalidLanguageMessage(String language) {
return new StringBuilder().append(ScriptEngineManager.class.getName())

View File

@@ -1,26 +1,28 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Copyright 2002-2015 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.Bindings;
import javax.script.ScriptEngine;
import org.springframework.integration.scripting.ScriptExecutor;
/**
* Default implementation of the {@link ScriptExecutor}
*
*
* @author David Turanski
* @author Mark Fisher
* @author Gary Russell
* @since 2.1
*/
class DefaultScriptExecutor extends AbstractScriptExecutor {
@@ -32,16 +34,8 @@ import org.springframework.integration.scripting.ScriptExecutor;
super(language);
}
/*
* (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) {
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script, Bindings bindings) {
return result;
}

View File

@@ -1,17 +1,18 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Copyright 2002-2015 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.Bindings;
import javax.script.ScriptEngine;
import org.springframework.integration.scripting.ScriptExecutor;
@@ -19,11 +20,12 @@ 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.
* <p>
* Script evaluation using the Jython implementation results in a <code>null</code> return value for normal variable expressions such as
* Script evaluation using the Jython implementation results in a <code>null</code> return value for normal variable expressions such as
* <code>x=2</code>. 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.
*
* simple parsing on the last line of the script to obtain the variable name, if any, and return its value.
*
* @author David Turanski
* @author Gary Russell
* @since 2.1
*
*/
@@ -32,22 +34,24 @@ import org.springframework.integration.scripting.ScriptExecutor;
* @param language
*/
public PythonScriptExecutor() {
super("python");
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) {
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script, Bindings bindings) {
Object newResult= result;
if (newResult == null) {
String returnVariableName = PythonVariableParser.parseReturnVariable(script);
newResult = scriptEngine.get(returnVariableName);
if (bindings != null) {
newResult = bindings.get(returnVariableName);
}
if (newResult == null) {
newResult = scriptEngine.get(returnVariableName);
}
}
return newResult;
}
public static class PythonVariableParser {
public static String parseReturnVariable(String script){
String[] lines = script.trim().split("\n");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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
@@ -13,6 +13,7 @@
package org.springframework.integration.scripting.jsr223;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.HashMap;
import java.util.Map;
@@ -20,6 +21,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.python.core.PyTuple;
import org.springframework.core.io.ClassPathResource;
import org.springframework.integration.scripting.ScriptExecutor;
import org.springframework.scripting.ScriptSource;
@@ -28,6 +30,7 @@ import org.springframework.scripting.support.StaticScriptSource;
/**
* @author David Turanski
* @author Gary Russell
*
*/
public class PythonScriptExecutorTests {
@@ -62,12 +65,26 @@ public class PythonScriptExecutorTests {
@Test
public void test3() {
ScriptSource source =
new ResourceScriptSource(new ClassPathResource("/org/springframework/integration/scripting/jsr223/test3.py"));
new ResourceScriptSource(
new ClassPathResource("/org/springframework/integration/scripting/jsr223/test3.py"));
Object obj = executor.executeScript(source);
PyTuple tuple = (PyTuple) obj;
assertEquals(1, tuple.get(0));
}
@Test
public void test3WithVariables() {
ScriptSource source =
new ResourceScriptSource(
new ClassPathResource("/org/springframework/integration/scripting/jsr223/test3.py"));
HashMap<String, Object> variables = new HashMap<String, Object>();
variables.put("foo", "bar");
Object obj = executor.executeScript(source, variables);
PyTuple tuple = (PyTuple) obj;
assertNotNull(tuple);
assertEquals(1, tuple.get(0));
}
@Test
public void testEmbeddedVariable() {
Map<String,Object> variables = new HashMap<String,Object>();