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 the 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.
Similar to Runtime V1, flow variables can be accessed directly inside the script by the variable’s name.
configuration:
runtime: concord-v2
arguments:
myVar: "world"
flows:
default:
- script: js
body: |
print("Hello, ", myVar);
Additionally, the execution
variable has a variables()
method which returns a
Variables
object. This object includes a number of methods for interacting with flow variables.
configuration:
runtime: concord-v2
flows:
default:
- script: js
body: |
var myVar = execution.variables().getString('myString', 'world');
print("Hello, ", myVar);
To set a variable, use the execution.variables().set()
method:
configuration:
runtime: concord-v2
flows:
default:
- script: js
body: |
execution.variables().set('myVar', 'Hello, world!');
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}"
Dry-run mode is useful for testing and validating the flow logic before running it in production.
By default, script steps do not support dry-run mode. To enable a script to run in this mode,
you need to modify the script to support dry-run mode or mark script step as dry-run ready
using meta
field of the step if you are confident it is safe to run.
An example of a script step marked as dry-run ready:
flows:
myFlow:
- script: js
body: |
log.info('I'm confident that this script can be executed in dry-run mode!');
meta:
dryRunReady: true # dry-run ready marker for this step
Important: Use the
meta.dryRunReady
only if you are certain that the script is safe to run in dry-run mode
If you need to change the logic in the script depending on whether it is running in dry-run mode
or not, you can use the isDryRun
variable. isDryRun
variable is available to indicate whether
the process is running in dry-run mode:
flows:
default:
- script: js
body: |
if (isDryRun) {
log.info('running in DRY-RUN mode');
} else {
log.info('running in REGULAR mode');
}
meta:
dryRunReady: true # dry-run ready marker for this step is also needed in this case
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. For example: mvn://org.codehaus.groovy:groovy-all:pom:2.5.21
.
configuration:
dependencies:
- "mvn://org.codehaus.groovy:groovy-all:pom:2.5.21"
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.21"
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.4.2.0"
flows:
default:
- script: ruby
body: |
puts "Hello!"