Home > docs > getting started > Scripting Support
Concord flows can include scripting language snippets for execution. The scripts run within the same JVM that is running Concord, and hence need to implement the Java Scripting API as defined by JSR-223. Language examples with a compliant runtimes are JavaScript, Groovy, Python, JRuby and many others.
Script engines must support Java 8.
Script languages have to be identified by setting the language explicitly or can be automatically identified based on the file extension used. They can be stored as external files and invoked from the Concord YAML file or they can be inline in the file.
Flow variables, Concord tasks and other Java methods can be accessed from the scripts due to the usage of the Java Scripting API. The script and your Concord processes essentially run within the same context on the JVM.
For most of the supported languages, flow variables can be accessed directly inside the script (without using ${} syntax):
configuration:
arguments:
myVar: "world"
flows:
default:
- script: js
body: |
print("Hello, ", myVar)
If a flow variable contains an illegal character for a chosen scripting
language, it can be accessed using a built-in execution
variable:
- script: js
body: |
var x = execution.getVariable("an-illegal-name");
print("We got", x);
To set a variable, you need to use execution#setVariable
method:
- script: js
body: |
execution.setVariable("myVar", "Hello!");
Note that not every data structure of supported scripting languages is directly compatible with the Concord runtime. The values exposed to the flow via
execution.setVariable
must be serializable in order to work correctly with forms or when the process suspends. Refer to the specific language section for more details.
Scripts can retrieve and invoke all tasks available for flows by name:
- script: js
body: |
var slack = tasks.get("slack");
slack.call(execution, "C5NUWH9S5", "Hi there!");
The number and type of arguments depend on the particular task’s method. In
this example, the script calls call
method of the SlackTask
instance.
The execution
variable is an alias for context
and automatically provided by the runtime for all supported script engines.
Scripts can be automatically retrieved from an external server:
- script: "http://localhost:8000/myScript.groovy"
The file extension in the URL must match the script engine’s
supported extensions – e.g. .groovy
for the Groovy language, .js
for JavaScript, etc.
Script can have an optional error block. It is executed when an exception occurs in the script execution:
- script: groovy
body: |
throw new RuntimeException("kaboom!")
error:
- log: "Caught an error: ${lastError.cause}"
Using external script file:
- script: "http://localhost:8000/myScript.groovy"
error:
- log: "Caught an error: ${lastError.cause}"
JavaScript support is built-in and doesn’t require any external
dependencies. It is based on the
Nashorn
engine and requires the identifier js
.
Nashorn is based on
ECMAScript, adds
numerous extensions.
including e.g. a print
command.
Using an inline script:
flows:
default:
- script: js
body: |
function doSomething(i) {
return i * 2;
}
execution.setVariable("result", doSomething(2));
- log: ${result} # will output "4"
Using an external script file:
flows:
default:
- script: test.js
- log: ${result}
// test.js
function doSomething(i) {
return i * 2;
}
execution.setVariable("result", doSomething(2));
JavaScript objects must be converted to regular Java Map
instances to be
compatible with the Concord runtime:
flows:
default:
- script: js
body: |
var x = {a: 1};
var HashMap = Java.type('java.util.HashMap');
execution.setVariable('x', new HashMap(x));
- log: "${x.a}"
Alternatively, a HashMap
instance can be used directly in the JavaScript
code.
Similarly, JavaScript arrays (lists) must be converted into compatible
Java List
objects:
var arr = [1, 2, 3];
var ArrayList = Java.type('java.util.ArrayList');
execution.setVariable('x', new ArrayList(arr));
Groovy is another compatible engine that is fully-supported in Concord. It
requires the addition of a dependency to
groovy-all and
the identifier groovy
. For versions 2.4.* and lower jar packaging is used in
projects, so the correct dependency is
e.g. mvn://org.codehaus.groovy:groovy-all:2.4.12
. Versions 2.5.0 and higher
use pom packaging, which has to be added to the dependency declaration before
the version mvn://org.codehaus.groovy:groovy-all:pom:2.5.2
.
configuration:
dependencies:
- "mvn://org.codehaus.groovy:groovy-all:pom:2.5.2"
flows:
default:
- script: groovy
body: |
def x = 2 * 3
execution.setVariable("result", x)
- log: ${result}
The following example uses some standard Java APIs to create a date value in the desired format.
- script: groovy
body: |
def dateFormat = new java.text.SimpleDateFormat('yyyy-MM-dd')
execution.setVariable("businessDate", dateFormat.format(new Date()))
- log: "Today is ${businessDate}"
Groovy’s LazyMap
are not serializable and must be converted to regular Java
Maps:
configuration:
dependencies:
- "mvn://org.codehaus.groovy:groovy-all:pom:2.5.2"
flows:
default:
- script: groovy
body: |
def x = new groovy.json.JsonSlurper().parseText('{"a": 123}') // produces a LazyMap instance
execution.setVariable('x', new java.util.HashMap(x))
- log: "${x.a}"
Python scripts can be executed using the Jython
runtime. It requires the addition of a dependency to
jython-standalone
located in the Central Repository or on another server and the identifier
python
. Any version that supports JSR-223 and Java 8 should work.
configuration:
dependencies:
- "mvn://org.python:jython-standalone:2.7.2"
flows:
default:
- script: python
body: |
x = 2 * 3;
execution.setVariable("result", x)
- log: ${result}
Note that pip
and 3rd-party modules with native dependencies are not
supported.
Python objects must be converted to regular Java List
and Map
instances to be
compatible with the Concord runtime:
flows:
default:
- script: python
body: |
from java.util import HashMap, ArrayList
aDict = {'x': 123}
aList = [1, 2, 3]
execution.setVariable('aDict', HashMap(aDict))
execution.setVariable('aList', ArrayList(aList))
- log: "${aDict}"
- log: "${aList}"
Ruby scripts can be executed using the JRuby
runtime. It requires the addition of a dependency to
jruby
located in the Central Repository or on another server and the identifier
ruby
.
configuration:
dependencies:
- "mvn://org.jruby:jruby:9.1.13.0"
flows:
default:
- script: ruby
body: |
puts "Hello!"