Edit this page on GitHub

Home > docs > getting started > Scripting Support

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 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.

Using Flow Variables

For most of the supported languages, flows variables can be accessed directly:

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.

Using Concord Tasks

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.

External scripts

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.

Error Handling

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

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));

Compatibility

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.

Groovy

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}"

Compatibility

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

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.

configuration:
  dependencies:
  - "mvn://org.python:jython-standalone:2.7.1"

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.

Compatibility

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

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!"