GH-9507: Migrate Python support to GraalVM Polyglot

Fixes: #9507
Issue link: https://github.com/spring-projects/spring-integration/issues/9507

* Deprecate `PythonScriptExecutor` in favor of `PolyglotScriptExecutor` with a `python` as language
* Add handling for `PolyglotWrapper` return type of the script evaluation
* Rework `DeriveLanguageFromExtensionTests.testParseLanguage()` to the `@ParameterizedTest`
This commit is contained in:
Artem Bilan
2024-09-24 17:19:43 -04:00
parent 8082f3c3f9
commit ec31a5bed8
12 changed files with 101 additions and 116 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2024 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.
@@ -16,6 +16,8 @@
package org.springframework.integration.scripting;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.graalvm.polyglot.Context;
@@ -36,7 +38,7 @@ public class PolyglotScriptExecutor implements ScriptExecutor {
private final String language;
private Context.Builder contextBuilder;
private final Context.Builder contextBuilder;
/**
* Construct an executor based on the provided language id.
@@ -67,11 +69,28 @@ public class PolyglotScriptExecutor implements ScriptExecutor {
Value bindings = context.getBindings(this.language);
variables.forEach(bindings::putMember);
}
return context.eval(this.language, scriptSource.getScriptAsString()).as(Object.class);
String scriptAsString = scriptSource.getScriptAsString();
Object result = context.eval(this.language, scriptAsString).as(Object.class);
// We have to copy all the expected PolyglotWrapper instances before context is closed.
if (result instanceof Map<?, ?> map) {
String returnVariable = parseReturnVariable(scriptAsString);
result = map.get(returnVariable);
}
if (result instanceof List<?> list) {
result = new ArrayList<>(list);
}
return result;
}
catch (Exception ex) {
throw new ScriptingException(ex.getMessage(), ex);
}
}
private 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();
}
}

View File

@@ -32,9 +32,14 @@ import javax.script.ScriptEngine;
*
* @author David Turanski
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.1
*
* @deprecated in favor of {@link org.springframework.integration.scripting.PolyglotScriptExecutor}
* with a {@code python} language argument.
*/
@Deprecated(forRemoval = true, since = "6.4")
public class PythonScriptExecutor extends AbstractScriptExecutor {
public PythonScriptExecutor() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -35,7 +35,7 @@ public final class ScriptExecutorFactory {
public static ScriptExecutor getScriptExecutor(String language) {
if (language.equalsIgnoreCase("python") || language.equalsIgnoreCase("jython")) {
return new PythonScriptExecutor();
return new PolyglotScriptExecutor("python");
}
else if (language.equalsIgnoreCase("ruby") || language.equalsIgnoreCase("jruby")) {
return new RubyScriptExecutor();
@@ -56,11 +56,16 @@ public final class ScriptExecutorFactory {
int index = scriptLocation.lastIndexOf('.') + 1;
Assert.state(index > 0, () -> "Unable to determine language for script '" + scriptLocation + "'");
String extension = scriptLocation.substring(index);
if (extension.equals("kts")) {
return "kotlin";
}
else if (extension.equals("js")) {
return "js";
switch (extension) {
case "kts" -> {
return "kotlin";
}
case "js" -> {
return "js";
}
case "py" -> {
return "python";
}
}
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByExtension(extension);