Camunda BPM supports scripting with JSR-223 compatible script engine implementations. Currently wetest the integration for Groovy, JavaScript, JRuby and Jython. To use a scripting engineit is necessary to add the corresponding jar to the classpath.

JavaScript is part of the Java Runtime (JRE) and thus available out ot the box.

We include Groovy in the pre-packaged Camunda distributions.

The following table provides an overview of the BPMN elements which support the execution ofscripts.

BPMN element Script support
Script Task Script inside a script task
Processes, Activities, Sequence Flows, Gateways and Events Script as an execution listener
User Tasks Script as a task listener
Sequence Flows Script as condition expression of a sequence flow
All Tasks, All Events, Transactions, Subprocesses and Connectors Script inside an inputOutput parameter mapping

Use Script Tasks

With a BPMN 2.0 script task you can add a script to your BPM process (for more information see theBPMN 2.0 reference.

The following process is a simple example with a Groovy script task that sums up the elements of an array.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  3. targetNamespace="http://camunda.org/example">
  4. <process id="process" isExecutable="true">
  5. <startEvent id="start"/>
  6. <sequenceFlow id="sequenceFlow1" sourceRef="start" targetRef="task"/>
  7. <scriptTask id="task" name="Groovy Script" scriptFormat="groovy">
  8. <script>
  9. <![CDATA[
  10. sum = 0
  11. for ( i in inputArray ) {
  12. sum += i
  13. }
  14. println "Sum: " + sum
  15. ]]>
  16. </script>
  17. </scriptTask>
  18. <sequenceFlow id="sequenceFlow2" sourceRef="task" targetRef="end"/>
  19. <endEvent id="end"/>
  20. </process>
  21. </definitions>

To start the process, a variable inputArray is necessary.

  1. Map<String, Object> variables = new HashMap<String, Object>();
  2. variables.put("inputArray", new Integer[]{5, 23, 42});
  3. runtimeService.startProcessInstanceByKey("process", variables);

Use Scripts as Execution Listeners

Besides Java code and expression language, Camunda BPM also supports the execution of a scriptas an execution listener. For general information about execution listeners see the correspondingsection.

To use a script as an execution listener, a camunda:script element has to be added as a childelement of the camunda:executionListener element. During script evaluation, the variable execution isavailable, which corresponds to the DelegateExecution interface.

The following example shows usage of scripts as execution listeners.

  1. <process id="process" isExecutable="true">
  2. <extensionElements>
  3. <camunda:executionListener event="start">
  4. <camunda:script scriptFormat="groovy">
  5. println "Process " + execution.eventName + "ed"
  6. </camunda:script>
  7. </camunda:executionListener>
  8. </extensionElements>
  9. <startEvent id="start">
  10. <extensionElements>
  11. <camunda:executionListener event="end">
  12. <camunda:script scriptFormat="groovy">
  13. println execution.activityId + " " + execution.eventName + "ed"
  14. </camunda:script>
  15. </camunda:executionListener>
  16. </extensionElements>
  17. </startEvent>
  18. <sequenceFlow id="flow1" startRef="start" targetRef="task">
  19. <extensionElements>
  20. <camunda:executionListener>
  21. <camunda:script scriptFormat="groovy" resource="org/camunda/bpm/transition.groovy" />
  22. </camunda:executionListener>
  23. </extensionElements>
  24. </sequenceFlow>
  25. <!--
  26. ... remaining process omitted
  27. -->
  28. </process>

Use Scripts as Task Listeners

Similar to execution listeners, task listeners can also be implemented as scripts. For generalinformation about task listeners see the correspondingsection.

To use a script as a task listener, a camunda:script element has to be added as a child element ofthe camunda:taskListener element. Inside the script, the variable task is available, which corresponds tothe DelegateTask interface.

The following example shows usage of scripts as task listeners.

  1. <userTask id="userTask">
  2. <extensionElements>
  3. <camunda:taskListener event="create">
  4. <camunda:script scriptFormat="groovy">println task.eventName</camunda:script>
  5. </camunda:taskListener>
  6. <camunda:taskListener event="assignment">
  7. <camunda:script scriptFormat="groovy" resource="org/camunda/bpm/assignemnt.groovy" />
  8. </camunda:taskListener>
  9. </extensionElements>
  10. </userTask>

Use Scripts as Conditions

As an alternative to expression language, Camunda BPM allows you to use scripts asconditionExpression of conditional sequence flows. To do that, the language attribute of theconditionExpression element has to be set to the desired scripting language. The script source codeis the text content of the element, as with expression language. Another way to specify the scriptsource code is to define an external source as described in the script source section.

The following example shows usage of scripts as conditions. The Groovy variable status is aprocess variable which is available inside the script.

  1. <sequenceFlow>
  2. <conditionExpression xsi:type="tFormalExpression" language="groovy">
  3. status == 'closed'
  4. </conditionExpression>
  5. </sequenceFlow>
  6. <sequenceFlow>
  7. <conditionExpression xsi:type="tFormalExpression" language="groovy"
  8. camunda:resource="org/camunda/bpm/condition.groovy" />
  9. </sequenceFlow>

Use Scripts as inputOutput Parameters

With the Camunda inputOutput extension element you can map an inputParameter or outputParameterwith a script. The following example process uses the Groovy script from the previous example to assignthe Groovy variable sum to the process variable x for a Java delegate.

Script Return Value

Please note that the last statement of the script is returned. This applies to Groovy, JavaScript and JRuby but not to Jython. If you want to use Jython, your script has to be a single expression like a + b or a > b where a and b are already process variables. Otherwise, the Jython scripting engine will not return a value.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  3. xmlns:camunda="http://activiti.org/bpmn"
  4. targetNamespace="http://camunda.org/example">
  5. <process id="process" isExecutable="true">
  6. <startEvent id="start"/>
  7. <sequenceFlow id="sequenceFlow1" sourceRef="start" targetRef="task"/>
  8. <serviceTask id="task" camunda:class="org.camunda.bpm.example.SumDelegate">
  9. <extensionElements>
  10. <camunda:inputOutput>
  11. <camunda:inputParameter name="x">
  12. <camunda:script scriptFormat="groovy">
  13. <![CDATA[
  14. sum = 0
  15. for ( i in inputArray ) {
  16. sum += i
  17. }
  18. sum
  19. ]]>
  20. </camunda:script>
  21. </camunda:inputParameter>
  22. </camunda:inputOutput>
  23. </extensionElements>
  24. </serviceTask>
  25. <sequenceFlow id="sequenceFlow2" sourceRef="task" targetRef="end"/>
  26. <endEvent id="end"/>
  27. </process>
  28. </definitions>

After the script has assigned a value to the sum variable, x can be used inside the Java delegatecode.

  1. public class SumDelegate implements JavaDelegate {
  2. public void execute(DelegateExecution execution) throws Exception {
  3. Integer x = (Integer) execution.getVariable("x");
  4. // do something
  5. }
  6. }

The script source code can also be loaded from an external resource in the same way as describedfor script tasks.

  1. <camunda:inputOutput>
  2. <camunda:inputParameter name="x">
  3. <camunda:script scriptFormat="groovy" resource="org/camunda/bpm/example/sum.groovy"/>
  4. </camunda:inputParameter>
  5. </camunda:inputOutput>

Script Engine Caching

Whenever the process engine reaches a point where a script has to be executed, the process engine looks for a Script Engine by a language name. The default behavior is that if it is the first request, a new Script Engine is created. If the Script Engine declares to be thread safe, it is also cached. The caching prevents the process engine from creating a new Script Engine for each request for the same script language.

By default the caching of Script Engines happens at process application level. Each process application holds an own instance of a Script Engine for a given language. This behavior can be disabled by setting the process engine configuration flag named enableFetchScriptEngineFromProcessApplication to false. Consequently, the Script Engines are cached globally at process engine level and they are shared between each process application. For further details about the process engine configuration flag enableFetchScriptEngineFromProcessApplication, please read the section about referencing process application classes.

If it is not desired to cache Script Engines in general, it can be disabled by setting the process engine configuration flag name enableScriptEngineCaching to false.

Script Compilation

Most script engines compile script source code either to a Java class or to a differentintermediary format prior to executing the script. Script engines implementing the Java Compilableinterface allow programs to retrieve and cache the script compilation. The default setting of theprocess engine is to check if a Script Engine supports the compile feature. If true and the caching of Script Engines is enabled, the script engine compiles the script and then caches the compilation result. This prevents the process engine from compiling a script source each time the same script task is executed.

By default, compilation of scripts is enabled. If you need to disable script compilation, you can set the process engine configuration flag named enableScriptCompilation to false.

Load Script Engine

If the process engine configuration flag named enableFetchScriptEngineFromProcessApplication is set to true, it is also possible to load Script Engines from the classpath of the process application. For that, the Script Engine can be packaged as a library within the process application. It is also possible to install the Script Engine globally.

In case the Script Engine module should be installed globally and JBoss is used, it is necessary to add a module dependency to the Script Engine. This can be done by adding a jboss-deployment-structure.xml to the process application, e.g.,:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <jboss-deployment-structure>
  3. <deployment>
  4. <dependencies>
  5. <module name="org.codehaus.groovy.groovy-all"
  6. services="import" />
  7. </dependencies>
  8. </deployment>
  9. </jboss-deployment-structure>

Reference Process Application Provided Classes

The script can reference to process application provided classes by importing them like in the following groovy script example.

  1. import my.process.application.CustomClass
  2. sum = new CustomClass().calculate()
  3. execution.setVariable('sum', sum)

To avoid possible class loading problems during the script execution, it is recommended to set the process engine configuration flag name enableFetchScriptEngineFromProcessApplication to true.

Be aware that the process engine flag enableFetchScriptEngineFromProcessApplication is only relevant in a shared engine scenario.

Variables Available During Script Execution

During the execution of scripts, all process variables visible in the current scope are available.They can be accessed directly by the name of the variable (i.e., sum). This does not apply forJRuby where you have to access the variable as a ruby global variable (prepend with a dollar sign,i.e., $sum)

There are also special variables:

  • execution, which is always available if the script is executed in an execution scope (e.g., in a script task) (DelegateExecution).
  • task, which is available if the script is executed in a task scope (e.g., a task listener) (DelegateTask).
  • connector, which is available if the script is executed in a connector variable scope (e.g., outputParameter of a camunda:connector) (ConnectorVariableScope).
    These variables correspond to the DelegateExecution, DelegateTask or resp. ConnectorVariableScopeinterface which means that it can be used to get and set variables or access process engine services.
  1. // get process variable
  2. sum = execution.getVariable('x')
  3. // set process variable
  4. execution.setVariable('y', x + 15)
  5. // get task service and query for task
  6. task = execution.getProcessEngineServices().getTaskService()
  7. .createTaskQuery()
  8. .taskDefinitionKey("task")
  9. .singleResult()

Accessing Process Engine Services using Scripts

Camunda’s Java API provides access to Camunda’s process engine services; these services can be accessed using Scripts:

Process Engine ServicesPublic Java API of Camunda BPM Engine

Example of creating a BPMN Message that correlates with the message key “work”:

  1. execution.getProcessEngineServices().getRuntimeService().createMessageCorrelation("work").correlateWithResult();

Printing to Console using Scripts

During the execution of scripts, it might be desired to print to the console due to logging and debugging reasons.

Here are examples on how this can be accomplished in the respective language:

  • Goovy:

    1. println 'This prints to the console'
  • Javascript:

    1. var system = java.lang.System;
    2. system.out.println('This prints to the console');

Script Source

The standard way to specify the script source code in the BPMN XML model is to add it directly tothe XML file. Nonetheless, Camunda BPM provides additional ways to specify the script source.

If you use another scripting language than Expression Language, you can also specify the scriptsource as an expression which returns the source code to be executed. This way, the source code can,for example, be contained in a process variable.

In the following example snippet the process engine will evaluate the expression ${sourceCode} inthe current context every time the element is executed.

  1. <!-- inside a script task -->
  2. <scriptTask scriptFormat="groovy">
  3. <script>${sourceCode}</script>
  4. </scriptTask>
  5. <!-- as an execution listener -->
  6. <camunda:executionListener>
  7. <camunda:script scriptFormat="groovy">${sourceCode}</camunda:script>
  8. </camunda:executionListener>
  9. <!-- as a condition expression -->
  10. <sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
  11. <conditionExpression xsi:type="tFormalExpression" language="groovy">
  12. ${sourceCode}
  13. </conditionExpression>
  14. </sequenceFlow>
  15. <!-- as an inputOutput mapping -->
  16. <camunda:inputOutput>
  17. <camunda:inputParameter name="x">
  18. <camunda:script scriptFormat="groovy">${sourceCode}</camunda:script>
  19. </camunda:inputParameter>
  20. </camunda:inputOutput>

You can also specify the attribute camunda:resource on the scriptTask and conditionExpressionelement, respectively the resource attribute on the camunda:script element. This extensionattribute specifies the location of an external resource which should be used as script source code.Optionally, the resource path can be prefixed with an URL-like scheme to specify if the resource iscontained in the deployment or classpath. The default behaviour is that the resource is part of theclasspath. This means that the first two script task elements in the following examples are equal.

  1. <!-- on a script task -->
  2. <scriptTask scriptFormat="groovy" camunda:resource="org/camunda/bpm/task.groovy"/>
  3. <scriptTask scriptFormat="groovy" camunda:resource="classpath://org/camunda/bpm/task.groovy"/>
  4. <scriptTask scriptFormat="groovy" camunda:resource="deployment://org/camunda/bpm/task.groovy"/>
  5. <!-- in an execution listener -->
  6. <camunda:executionListener>
  7. <camunda:script scriptFormat="groovy" resource="deployment://org/camunda/bpm/listener.groovy"/>
  8. </camunda:executionListener>
  9. <!-- on a conditionExpression -->
  10. <conditionExpression xsi:type="tFormalExpression" language="groovy"
  11. camunda:resource="org/camunda/bpm/condition.groovy" />
  12. <!-- in an inputParameter -->
  13. <camunda:inputParameter name="x">
  14. <camunda:script scriptFormat="groovy" resource="org/camunda/bpm/mapX.groovy" />
  15. </camunda:inputParameter>

The resource path can also be specified as an expression which is evaluated on the invocation of thescript task.

  1. <scriptTask scriptFormat="groovy" camunda:resource="${scriptPath}"/>

For more information, see thecamunda:resourcesection of the Custom Extensions chapter.

原文: https://docs.camunda.org/manual/7.9/user-guide/process-engine/scripting/