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:
committed by
Artem Bilan
parent
bd62b4191d
commit
c51299b1b2
@@ -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
|
* 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
|
* 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.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineManager;
|
import javax.script.ScriptEngineManager;
|
||||||
import javax.script.SimpleBindings;
|
import javax.script.SimpleBindings;
|
||||||
@@ -33,6 +34,7 @@ import org.springframework.util.Assert;
|
|||||||
* @author David Turanski
|
* @author David Turanski
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Artem Bilan
|
* @author Artem Bilan
|
||||||
|
* @author Gary Russell
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
abstract class AbstractScriptExecutor implements ScriptExecutor {
|
abstract class AbstractScriptExecutor implements ScriptExecutor {
|
||||||
@@ -60,10 +62,12 @@ abstract class AbstractScriptExecutor implements ScriptExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object executeScript(ScriptSource scriptSource) {
|
public Object executeScript(ScriptSource scriptSource) {
|
||||||
return this.executeScript(scriptSource, null);
|
return this.executeScript(scriptSource, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) {
|
public Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) {
|
||||||
Object result = null;
|
Object result = null;
|
||||||
|
|
||||||
@@ -74,14 +78,16 @@ abstract class AbstractScriptExecutor implements ScriptExecutor {
|
|||||||
logger.debug("executing script: " + script);
|
logger.debug("executing script: " + script);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variables != null) {
|
Bindings bindings = null;
|
||||||
result = scriptEngine.eval(script, new SimpleBindings(variables));
|
if (variables != null && variables.size() > 0) {
|
||||||
|
bindings = new SimpleBindings(variables);
|
||||||
|
result = scriptEngine.eval(script, bindings);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = scriptEngine.eval(script);
|
result = scriptEngine.eval(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = postProcess(result, scriptEngine, script);
|
result = postProcess(result, scriptEngine, script, bindings);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("script executed in " + (new Date().getTime() - start.getTime()) + " ms");
|
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
|
* Subclasses may implement this to provide any special handling required
|
||||||
* @param result
|
* @param result the result.
|
||||||
* @param scriptEngine
|
* @param scriptEngine the engine.
|
||||||
* @param script
|
* @param script the script.
|
||||||
|
* @param bindings the bindings.
|
||||||
* @return modified result
|
* @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) {
|
private static String invlalidLanguageMessage(String language) {
|
||||||
return new StringBuilder().append(ScriptEngineManager.class.getName())
|
return new StringBuilder().append(ScriptEngineManager.class.getName())
|
||||||
|
|||||||
@@ -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
|
* 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
|
* the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* 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
|
* 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
|
* 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.
|
* specific language governing permissions and limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.integration.scripting.jsr223;
|
package org.springframework.integration.scripting.jsr223;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
|
|
||||||
import org.springframework.integration.scripting.ScriptExecutor;
|
import org.springframework.integration.scripting.ScriptExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of the {@link ScriptExecutor}
|
* Default implementation of the {@link ScriptExecutor}
|
||||||
*
|
*
|
||||||
* @author David Turanski
|
* @author David Turanski
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
* @author Gary Russell
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
class DefaultScriptExecutor extends AbstractScriptExecutor {
|
class DefaultScriptExecutor extends AbstractScriptExecutor {
|
||||||
@@ -32,16 +34,8 @@ import org.springframework.integration.scripting.ScriptExecutor;
|
|||||||
super(language);
|
super(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.integration.scripting.jsr223.AbstractScriptExecutor
|
|
||||||
* #postProcess(java.lang.Object, javax.script.ScriptEngine,
|
|
||||||
* java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script) {
|
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script, Bindings bindings) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* 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
|
* the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* 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
|
* 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
|
* 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.
|
* specific language governing permissions and limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.integration.scripting.jsr223;
|
package org.springframework.integration.scripting.jsr223;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
|
|
||||||
import org.springframework.integration.scripting.ScriptExecutor;
|
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.
|
* A {@link ScriptExecutor} that implements special handling required for Python to emulate behavior similar to other JSR223 scripting languages.
|
||||||
* <p>
|
* <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
|
* <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 David Turanski
|
||||||
|
* @author Gary Russell
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -32,22 +34,24 @@ import org.springframework.integration.scripting.ScriptExecutor;
|
|||||||
* @param language
|
* @param language
|
||||||
*/
|
*/
|
||||||
public PythonScriptExecutor() {
|
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
|
@Override
|
||||||
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script) {
|
protected Object postProcess(Object result, ScriptEngine scriptEngine, String script, Bindings bindings) {
|
||||||
Object newResult= result;
|
Object newResult= result;
|
||||||
if (newResult == null) {
|
if (newResult == null) {
|
||||||
String returnVariableName = PythonVariableParser.parseReturnVariable(script);
|
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;
|
return newResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PythonVariableParser {
|
public static class PythonVariableParser {
|
||||||
public static String parseReturnVariable(String script){
|
public static String parseReturnVariable(String script){
|
||||||
String[] lines = script.trim().split("\n");
|
String[] lines = script.trim().split("\n");
|
||||||
|
|||||||
@@ -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
|
* 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
|
* the License. You may obtain a copy of the License at
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
package org.springframework.integration.scripting.jsr223;
|
package org.springframework.integration.scripting.jsr223;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -20,6 +21,7 @@ import java.util.Map;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.python.core.PyTuple;
|
import org.python.core.PyTuple;
|
||||||
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.integration.scripting.ScriptExecutor;
|
import org.springframework.integration.scripting.ScriptExecutor;
|
||||||
import org.springframework.scripting.ScriptSource;
|
import org.springframework.scripting.ScriptSource;
|
||||||
@@ -28,6 +30,7 @@ import org.springframework.scripting.support.StaticScriptSource;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author David Turanski
|
* @author David Turanski
|
||||||
|
* @author Gary Russell
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PythonScriptExecutorTests {
|
public class PythonScriptExecutorTests {
|
||||||
@@ -62,12 +65,26 @@ public class PythonScriptExecutorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void test3() {
|
public void test3() {
|
||||||
ScriptSource source =
|
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);
|
Object obj = executor.executeScript(source);
|
||||||
PyTuple tuple = (PyTuple) obj;
|
PyTuple tuple = (PyTuple) obj;
|
||||||
assertEquals(1, tuple.get(0));
|
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
|
@Test
|
||||||
public void testEmbeddedVariable() {
|
public void testEmbeddedVariable() {
|
||||||
Map<String,Object> variables = new HashMap<String,Object>();
|
Map<String,Object> variables = new HashMap<String,Object>();
|
||||||
|
|||||||
Reference in New Issue
Block a user