INT-2177 - Added python support and related refactoring
This commit is contained in:
committed by
Mark Fisher
parent
1d80d34225
commit
a0e7b3cd78
@@ -35,5 +35,6 @@ public interface ScriptExecutor {
|
||||
* @param variables -bind variable
|
||||
* @return
|
||||
*/
|
||||
public abstract Object executeScript(ScriptSource scriptSource,Map<String,Object> variables);
|
||||
public abstract Object executeScript(ScriptSource scriptSource,Map<String,Object> variables);
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
logger.debug("using script engine : " + scriptEngineManager.getEngineByName(language).getFactory().getEngineName());
|
||||
for (String name:scriptEngineManager.getEngineByName(language).getFactory().getNames()){
|
||||
logger.debug("name=" + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object executeScript(ScriptSource scriptSource) {
|
||||
return this.executeScript(scriptSource, null);
|
||||
}
|
||||
|
||||
public Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) {
|
||||
Object result = null;
|
||||
ScriptEngine scriptEngine = this.scriptEngineManager.getEngineByName(this.language);
|
||||
try {
|
||||
if (variables != null) {
|
||||
for (Entry<String, Object> 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 (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (ScriptException e) {
|
||||
throw new RuntimeException(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);
|
||||
|
||||
}
|
||||
@@ -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<String, Object> variables) {
|
||||
Object result = null;
|
||||
ScriptEngine scriptEngine = this.scriptEngineManager.getEngineByName(this.language);
|
||||
try {
|
||||
if (variables != null) {
|
||||
for (Entry<String, Object> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class ScriptExecutingMessageProcessor extends AbstractScriptExecutingMessageProcessor<Object> {
|
||||
|
||||
private final DefaultScriptExecutor scriptExecutor;
|
||||
private final ScriptExecutor scriptExecutor;
|
||||
private volatile ScriptSource scriptSource;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class ScriptExecutingMessageProcessor extends AbstractScriptExecutingMess
|
||||
* @param scriptSource
|
||||
* @param scriptExecutor
|
||||
*/
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource, DefaultScriptExecutor scriptExecutor) {
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptExecutor scriptExecutor) {
|
||||
super();
|
||||
this.scriptSource = scriptSource;
|
||||
this.scriptExecutor = scriptExecutor;
|
||||
@@ -48,7 +48,7 @@ public class ScriptExecutingMessageProcessor extends AbstractScriptExecutingMess
|
||||
* @param scriptSource
|
||||
* @param scriptExecutor
|
||||
*/
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptVariableGenerator scriptVariableGenerator,DefaultScriptExecutor scriptExecutor) {
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptVariableGenerator scriptVariableGenerator, ScriptExecutor scriptExecutor) {
|
||||
super(scriptVariableGenerator);
|
||||
this.scriptSource = scriptSource;
|
||||
this.scriptExecutor = scriptExecutor;
|
||||
@@ -60,7 +60,7 @@ public class ScriptExecutingMessageProcessor extends AbstractScriptExecutingMess
|
||||
* @param scriptExecutor
|
||||
* @param variables
|
||||
*/
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource,DefaultScriptExecutor scriptExecutor,Map<String,Object> variables ) {
|
||||
public ScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptExecutor scriptExecutor,Map<String,Object> variables ) {
|
||||
super(new DefaultScriptVariableGenerator(variables));
|
||||
this.scriptSource = scriptSource;
|
||||
this.scriptExecutor = scriptExecutor;
|
||||
|
||||
@@ -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.jsr223;
|
||||
|
||||
import org.springframework.integration.scripting.ScriptExecutor;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @since 2.1
|
||||
*/
|
||||
public class ScriptExecutorFactory {
|
||||
|
||||
private ScriptExecutorFactory(){};
|
||||
public static ScriptExecutor getScriptExecutor(String language) {
|
||||
if (language.equalsIgnoreCase("python") || language.equalsIgnoreCase("jython")){
|
||||
return new PythonScriptExecutor();
|
||||
}
|
||||
else if (language.equalsIgnoreCase("ruby") || language.equalsIgnoreCase("jruby")) {
|
||||
return new RubyScriptExecutor();
|
||||
}
|
||||
return new DefaultScriptExecutor(language);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,13 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="return" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
The script variable to return as a result of the evaluation (Optional).
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
Reference in New Issue
Block a user