[[scripting]]
= Scripting Support

Spring Integration 2.1 added support for the https://www.jcp.org/en/jsr/detail?id=223[JSR223 Scripting for Java specification], introduced in Java version 6.
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.
For more information about JSR223, see the https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html[documentation].

You need to include this dependency into your project:

[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-scripting</artifactId>
    <version>{project-version}</version>
</dependency>
----

Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
compile "org.springframework.integration:spring-integration-scripting:{project-version}"
----
======

In addition, you need to add a script engine implementation, e.g. JRuby, Jython.

Starting with version 5.2, Spring Integration provides a Kotlin Jsr223 support.
You need to add this dependency into your project to make it working:

[tabs]
======
Maven::
+
[source, xml, subs="normal", role="primary"]
----
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-scripting-jsr223</artifactId>
    <scope>runtime</scope>
</dependency>
----

Gradle::
+
[source, groovy, subs="normal", role="secondary"]
----
runtime 'org.jetbrains.kotlin:kotlin-scripting-jsr223'
----
======

In order to use a JVM scripting language, a JSR223 implementation for that language must be included in your class path.
The https://groovy-lang.org/[Groovy] and https://www.jruby.org[JRuby] projects provide JSR233 support in their standard distributions.

IMPORTANT: Various JSR223 language implementations have been developed by third parties.
A particular implementation's compatibility with Spring Integration depends on how well it conforms to the specification and the implementer's interpretation of the specification.

TIP: If you plan to use Groovy as your scripting language, we recommended you use xref:groovy.adoc[Spring-Integration's Groovy Support] as it offers additional features specific to Groovy.
However, this section is relevant as well.

[[scripting-config]]
== Script Configuration

Depending on the complexity of your integration requirements, scripts may be provided inline as CDATA in XML configuration or as a reference to a Spring resource that contains the script.
To enable scripting support, Spring Integration defines a `ScriptExecutingMessageProcessor`, which binds the message payload to a variable named `payload` and the message headers to a `headers` variable, both accessible within the script execution context.
All you need to do is write a script that uses these variables.
The following pair of examples show sample configurations that create filters:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
@Bean
public IntegrationFlow scriptFilter() {
    return f -> f.filter(Scripts.processor("some/path/to/ruby/script/RubyFilterTests.rb"));
}
...
@Bean
public Resource scriptResource() {
	return new ByteArrayResource("headers.type == 'good'".getBytes());
}

@Bean
public IntegrationFlow scriptFilter() {
	return f -> f.filter(Scripts.processor(scriptResource()).lang("groovy"));
}
----

XML::
+
[source, xml, role="secondary"]
----
<int:filter input-channel="referencedScriptInput">
   <int-script:script location="some/path/to/ruby/script/RubyFilterTests.rb"/>
</int:filter>

<int:filter input-channel="inlineScriptInput">
     <int-script:script lang="groovy">
     <![CDATA[
     return payload == 'good'
   ]]>
  </int-script:script>
</int:filter>
----
======

As the preceding examples show, the script can be included inline or can be included by reference to a resource location (by using the `location` attribute).
Additionally, the `lang` attribute corresponds to the language name (or its JSR223 alias).

Other Spring Integration endpoint elements that support scripting include `router`, `service-activator`, `transformer`, and `splitter`.
The scripting configuration in each case would be identical to the above (besides the endpoint element).

Another useful feature of scripting support is the ability to update (reload) scripts without having to restart the application context.
To do so, specify the `refresh-check-delay` attribute on the `script` element, as the following example shows:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
Scripts.processor(...).refreshCheckDelay(5000)
}
----

XML::
+
[source, xml, role="secondary"]
----
<int-script:script location="..." refresh-check-delay="5000"/>
----
======

In the preceding example, the script location is checked for updates every 5 seconds.
If the script is updated, any invocation that occurs later than 5 seconds since the update results in running the new script.

Consider the following example:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
Scripts.processor(...).refreshCheckDelay(0)
}
----

XML::
+
[source, xml, role="secondary"]
----
<int-script:script location="..." refresh-check-delay="0"/>
----
======

In the preceding example, the context is updated with any script modifications as soon as such modification occurs, providing a simple mechanism for 'real-time' configuration.
Any negative value means the script is not reloaded after initialization of the application context.
This is the default behavior.
The following example shows a script that never updates:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
Scripts.processor(...).refreshCheckDelay(-1)
}
----

XML::
+
[source, xml, role="secondary"]
----
<int-script:script location="..." refresh-check-delay="-1"/>
----
======

IMPORTANT: Inline scripts can not be reloaded.

[[scripting-script-variable-bindings]]
=== Script Variable Bindings

Variable bindings are required to enable the script to reference variables externally provided to the script's execution context.
By default, `payload` and `headers` are used as binding variables.
You can bind additional variables to a script by using `<variable>` elements (or `ScriptSpec.variables()` option), as the following example shows:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
Scripts.processor("foo/bar/MyScript.py")
    .variables(Map.of("var1", "thing1", "var2", "thing2", "date", date))
}
----

XML::
+
[source, xml, role="secondary"]
----
<script:script lang="py" location="foo/bar/MyScript.py">
    <script:variable name="var1" value="thing1"/>
    <script:variable name="var2" value="thing2"/>
    <script:variable name="date" ref="date"/>
</script:script>
----
======

As shown in the preceding example, you can bind a script variable either to a scalar value or to a Spring bean reference.
Note that `payload` and `headers` are still included as binding variables.

With Spring Integration 3.0, in addition to the `variable` element, the `variables` attribute has been introduced.
This attribute and the `variable` elements are not mutually exclusive, and you can combine them within one `script` component.
However, variables must be unique, regardless of where they are defined.
Also, since Spring Integration 3.0, variable bindings are allowed for inline scripts, too, as the following example shows:

[source,xml]
----
<service-activator input-channel="input">
    <script:script lang="ruby" variables="thing1=THING1, date-ref=dateBean">
        <script:variable name="thing2" ref="thing2Bean"/>
        <script:variable name="thing3" value="thing2"/>
        <![CDATA[
            payload.foo = thing1
            payload.date = date
            payload.bar = thing2
            payload.baz = thing3
            payload
        ]]>
    </script:script>
</service-activator>
----

The preceding example shows a combination of an inline script, a `variable` element, and a `variables` attribute.
The `variables` attribute contains a comma-separated value, where each segment contains an '=' separated pair of the variable and its value.
The variable name can be suffixed with `-ref`, as in the `date-ref` variable in the preceding example.
That means that the binding variable has the name, `date`, but the value is a reference to the `dateBean` bean from the application context.
This may be useful when using property placeholder configuration or command-line arguments.

If you need more control over how variables are generated, you can implement your own Java class that uses the `ScriptVariableGenerator` strategy, which is defined by the following interface:

[source,java]
----
public interface ScriptVariableGenerator {

    Map<String, Object> generateScriptVariables(Message<?> message);

}
----

This interface requires you to implement the `generateScriptVariables(Message)` method.
The message argument lets you access any data available in the message payload and headers, and the return value is the `Map` of bound variables.
This method is called every time the script is executed for a message.
The following example shows how to provide an implementation of `ScriptVariableGenerator` and reference it with the `script-variable-generator` attribute:

[tabs]
======
Java DSL::
+
[source, java, role="primary"]
----
Scripts.processor("foo/bar/MyScript.groovy")
    .variableGenerator(new foo.bar.MyScriptVariableGenerator())
}
----

XML::
+
[source, xml, role="secondary"]
----
<int-script:script location="foo/bar/MyScript.groovy"
        script-variable-generator="variableGenerator"/>

<bean id="variableGenerator" class="foo.bar.MyScriptVariableGenerator"/>
----
======

If a `script-variable-generator` is not provided, script components use `DefaultScriptVariableGenerator`, which merges any provided `<variable>` elements with `payload` and `headers` variables from the `Message` in its `generateScriptVariables(Message)` method.

IMPORTANT: You cannot provide both the `script-variable-generator` attribute and `<variable>` element(s).
They are mutually exclusive.

[[graalmv-polyglot]]
=== GraalVM Polyglot

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].
The JSR223 engine implementation for JavaScript, removed from Java by itself, has been replaced by using this new script executor.
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.
By default, the framework sets `allowAllAccess` to `true` on the shared Polyglot `Context` which enables this interaction with host JVM:

* The creation and use of new threads.
* The access to public host classes.
* The loading of new host classes by adding entries to the class path.
* Exporting new members into the polyglot bindings.
* Unrestricted IO operations on host system.
* Passing experimental options.
* The creation and use of new sub-processes.
* The access to process environment variables.

This can be customized via overloaded `PolyglotScriptExecutor` constructor which accepts a `org.graalvm.polyglot.Context.Builder`.

To enable this JavaScript support, GraalVM with the `js` component installed has to be used or, when using a regular JVM, the `org.graalvm.sdk:graal-sdk` and `org.graalvm.js:js` dependencies must be included.
