Skip to content

Commit 6641b1f

Browse files
authored
GH-3664: Re-enable JavaScript support via GraalVM (#3911)
* GH-3664: Re-enable JavaScript support via GraalVM Fixes #3664 The JavaScript JSR223 engine has been removed from Java. Migrate JavaScript support into GraalVM Polyglot API: * Implement `PolyglotScriptExecutor` which can also support other GraalVM languages * Change the `ScriptExecutorFactory` to use a `PolyglotScriptExecutor` when it encounters JavaScript * Re-enable tests for JavaScript * Add GraalVM Polyglot support into docs * * Reinstate `@EnabledIfSystemProperty` for GraalVM JS system property
1 parent 1dd8b6b commit 6641b1f

File tree

8 files changed

+118
-9
lines changed

8 files changed

+118
-9
lines changed

build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ ext {
6565
derbyVersion = '10.14.2.0'
6666
findbugsVersion = '3.0.1'
6767
ftpServerVersion = '1.2.0'
68+
graalvmVersion = '22.2.0'
6869
greenmailVersion = '2.0.0-alpha-2'
6970
groovyVersion = '3.0.13'
7071
hamcrestVersion = '2.2'
@@ -848,9 +849,9 @@ project('spring-integration-scripting') {
848849
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-daemon-client'
849850
}
850851
optionalApi 'org.jetbrains.kotlin:kotlin-compiler-embeddable'
852+
providedImplementation "org.graalvm.sdk:graal-sdk:$graalvmVersion"
851853

852854
testImplementation "org.jruby:jruby-complete:$jrubyVersion"
853-
testImplementation "org.codehaus.groovy:groovy:$groovyVersion"
854855
testImplementation "org.codehaus.groovy:groovy-jsr223:$groovyVersion"
855856
testImplementation "org.python:jython-standalone:$jythonVersion"
856857

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.scripting;
18+
19+
import java.util.Map;
20+
21+
import org.graalvm.polyglot.Context;
22+
import org.graalvm.polyglot.Value;
23+
24+
import org.springframework.lang.Nullable;
25+
import org.springframework.scripting.ScriptSource;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* GraalVM Polyglot {@link ScriptExecutor} implementation.
30+
*
31+
* @author Artem Bilan
32+
*
33+
* @since 6.0
34+
*/
35+
public class PolyglotScriptExecutor implements ScriptExecutor {
36+
37+
private final String language;
38+
39+
private Context.Builder contextBuilder;
40+
41+
/**
42+
* Construct an executor based on the provided language id.
43+
* @param language the supported by GraalVM language id.
44+
*/
45+
public PolyglotScriptExecutor(String language) {
46+
this(language, Context.newBuilder().allowAllAccess(true));
47+
}
48+
49+
/**
50+
* Construct an executor based on the provided language id.
51+
* @param language the supported by GraalVM language id.
52+
*/
53+
public PolyglotScriptExecutor(String language, Context.Builder contextBuilder) {
54+
Assert.hasText(language, "'language' must not be empty");
55+
Assert.notNull(contextBuilder, "'contextBuilder' must not be null");
56+
this.contextBuilder = contextBuilder;
57+
this.language = language;
58+
try (Context context = this.contextBuilder.build()) {
59+
context.initialize(language);
60+
}
61+
}
62+
63+
@Override
64+
public Object executeScript(ScriptSource scriptSource, @Nullable Map<String, Object> variables) {
65+
try (Context context = this.contextBuilder.build()) {
66+
if (variables != null) {
67+
Value bindings = context.getBindings(this.language);
68+
variables.forEach(bindings::putMember);
69+
}
70+
return context.eval(this.language, scriptSource.getScriptAsString()).as(Object.class);
71+
}
72+
catch (Exception ex) {
73+
throw new ScriptingException(ex.getMessage(), ex);
74+
}
75+
}
76+
77+
}

spring-integration-scripting/src/main/java/org/springframework/integration/scripting/jsr223/ScriptExecutorFactory.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import javax.script.ScriptEngine;
2020
import javax.script.ScriptEngineManager;
2121

22+
import org.springframework.integration.scripting.PolyglotScriptExecutor;
2223
import org.springframework.integration.scripting.ScriptExecutor;
2324
import org.springframework.util.Assert;
2425

@@ -42,6 +43,9 @@ else if (language.equalsIgnoreCase("ruby") || language.equalsIgnoreCase("jruby"
4243
else if (language.equalsIgnoreCase("kotlin")) {
4344
return new KotlinScriptExecutor();
4445
}
46+
else if (language.equalsIgnoreCase("js") || language.equalsIgnoreCase("javascript")) {
47+
return new PolyglotScriptExecutor("js");
48+
}
4549
return new DefaultScriptExecutor(language);
4650
}
4751

@@ -58,6 +62,9 @@ public static String deriveLanguageFromFileExtension(String scriptLocation) {
5862
if (extension.equals("kts")) {
5963
return "kotlin";
6064
}
65+
else if (extension.equals("js")) {
66+
return "js";
67+
}
6168
ScriptEngineManager engineManager = new ScriptEngineManager();
6269
ScriptEngine engine = engineManager.getEngineByExtension(extension);
6370
Assert.state(engine != null, () -> "No suitable scripting engine found for extension '" + extension + "'");

spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223HeaderEnricherTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223InboundChannelAdapterTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2021 the original author or authors.
2+
* Copyright 2013-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-integration-scripting/src/test/java/org/springframework/integration/scripting/config/jsr223/Jsr223TransformerTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/reference/asciidoc/scripting.adoc

+21-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ Spring Integration 2.1 added support for the https://www.jcp.org/en/jsr/detail?i
55
It lets you use scripts written in any supported language (including Ruby, JRuby, Groovy and Kotlin) to provide the logic for various integration components, similar to the way the Spring Expression Language (SpEL) is used in Spring Integration.
66
For more information about JSR223, see the https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html[documentation].
77

8-
NOTE: Starting with Java 11, the Nashorn JavaScript Engine has been deprecated with possible removal in Java 15.
9-
It is recommended to reconsider it in favor of other scripting language from now on.
10-
118
You need to include this dependency into your project:
129

1310
====
@@ -32,7 +29,6 @@ In addition, you need to add a script engine implementation, e.g. JRuby, Jython.
3229
Starting with version 5.2, Spring Integration provides a Kotlin Jsr223 support.
3330
You need to add these dependencies into your project to make it working:
3431

35-
3632
====
3733
[source, xml, subs="normal", role="primary"]
3834
.Maven
@@ -279,3 +275,24 @@ If a `script-variable-generator` is not provided, script components use `Default
279275

280276
IMPORTANT: You cannot provide both the `script-variable-generator` attribute and `<variable>` element(s).
281277
They are mutually exclusive.
278+
279+
[[graalmv-polyglot]]
280+
===== GraalVM Polyglot
281+
282+
Starting with version 6.0, the framework provides a `PolyglotScriptExecutor` which is based the https://www.graalvm.org/22.2/reference-manual/embed-languages[GraalVM Polyglot API].
283+
The JSR223 engine implementation for JavaScript, removed from Java by itself, has been replaced by using this new script executor.
284+
See more information about enabling JavaScript support in GraalVM and what https://www.graalvm.org/22.2/reference-manual/js[configuration options] can be propagated via script variables.
285+
By default, the framework sets `allowAllAccess` to `true` on the shared Polyglot `Context` which enables this interaction with host JVM:
286+
287+
* The creation and use of new threads.
288+
* The access to public host classes.
289+
* The loading of new host classes by adding entries to the class path.
290+
* Exporting new members into the polyglot bindings.
291+
* Unrestricted IO operations on host system.
292+
* Passing experimental options.
293+
* The creation and use of new sub-processes.
294+
* The access to process environment variables.
295+
296+
This can be customized via overloaded `PolyglotScriptExecutor` constructor which accepts a `org.graalvm.polyglot.Context.Builder`.
297+
298+
The usage of JavaScript in the framework components remains the same, on ly the difference that now it is going to work only on GraalVM with installed `js` component.

src/reference/asciidoc/whats-new.adoc

+7
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ See <<./sftp.adoc#sftp,SFTP Adapters>> for more information.
6363
Enabling observation for timers and tracing using Micrometer is now supported.
6464
See <<./metrics.adoc#micrometer-observation,Micrometer Observation>> for more information.
6565

66+
[[x6.0-graalmv-polyglot]]
67+
==== GraalVM Polyglot Support
68+
69+
The Scripting module now provides a `PolyglotScriptExecutor` implementation based on the GraalVM Polyglot support.
70+
JavaScript support is now based on this executor since its JSR223 implementation has been removed from Java by itself.
71+
See <<./scripting.adoc#scripting,Scripting Support>> for more information.
72+
6673
[[x6.0-general]]
6774
=== General Changes
6875

0 commit comments

Comments
 (0)