This is the fourth day of my participation in Gwen Challenge

task

User tasks

describe
  • User tasks are used to set up work that must be done by people
  • When the process executes to a user task, a new task is created and added to the task list of the allocator or group
Graphic tag
  • The user task is displayed as a normal task (rounded rectangle) with a small user icon in the upper left corner

The XML content
  • User task definition in XML: The ID attribute is required, and the name attribute is optional:
<userTask id="theTask" name="Important task" />
Copy the code
  • User tasks can set descriptions, adding documentation elements to define descriptions:
<userTask id="theTask" name="Schedule meeting" >
  <documentation>
          Schedule an engineering meeting for next week with the new hire.
  </documentation>
Copy the code
  • Description text can be obtained using standard Java methods:
task.getDescription()
Copy the code
The duration of the
  • Tasks can have a field that describes the duration of the task
  • You can use the query API to search for duration, depending on whether it is before or after the time
    • Activiti provides a node extension that sets an expression in the task definition so that the initial duration can be set when the task is created
    • The expression should be:
      • java.util.Date
      • Java.util. String(ISO8601 format),ISO8601 duration (e.g. PT50M)
      • null
  • Enter the date in the process using the format above, or calculate a time in the previous service task. Here the duration is used, which is calculated based on the current time and then added over the given time period: using “PT30M” as the duration, the task will last 30 minutes from now
<userTask id="theTask" name="Important task" activiti:dueDate="${dateVariable}"/>
Copy the code
  • The duration of the task can also be changed by TaskService or by the DelegateTask parameter passed in the TaskListener
Users are assigned
  • User tasks can be assigned directly to a user, as defined by the humanPerformer element
  • HumanPerformer definition requires a resourceAssignmentExpression to actually define the user. Currently, only formalExpressions is supported
<process . >.<userTask id='theTask' name='important task' >
    <humanPerformer>
      <resourceAssignmentExpression>
        <formalExpression>kermit</formalExpression>
      </resourceAssignmentExpression>
    </humanPerformer>
  </userTask>
Copy the code
  • Only one user can be assigned as the performer of the task
  • In Activiti, users are called performers
  • Users with performers do not appear in other people’s task lists, only in the performers’ personal task lists
  • Tasks directly assigned to users can be obtained using TaskService:
List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list();
Copy the code
  • Tasks can also be added to a person’s list of candidate tasks. The potentialOwner element is required
  • The usage is similar to the humanPerformer element in that you need to specify whether each item in the expression is a person or a group **
<process . >.<userTask id='theTask' name='important task' >
    <potentialOwner>
      <resourceAssignmentExpression>
        <formalExpression>user(kermit), group(management)</formalExpression>
      </resourceAssignmentExpression>
    </potentialOwner>
  </userTask>
Copy the code
  • Tasks defined using the potentialOwner element are available via TaskService:
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit");
Copy the code

This gets all the tasks for which Kermit is a candidate, including user(kermit) in the expression. This will also get all groups assigned to contain the member Kermit (for example, Group (Management), provided Kermit is a member of the group and uses the Activiti account component). Groups of users are acquired at run time and can be managed through IdentityService

  • If you do not explicitly specify user or group Settings, the engine defaults to group Settings
  • The following setting is the same as using group(Accountancy) :
<formalExpression>accountancy</formalExpression>
Copy the code
Activiti’s extension to task assignment
  • Setting up users and groups is cumbersome when allocation is not complex. To avoid complexity, you can use custom extensions of user tasks
  • Assignee attributes: Assign user tasks directly to designated users (exactly the same as using humanPerformer)
<userTask id="theTask" name="my task" activiti:assignee="kermit" />
Copy the code
  • The candidateUsers property: Sets the candidate for the task (exactly the same effect as using potentialOwner; there is no need to declare through user(kermit) as with potentialOwner, this property can only be used for people)
<userTask id="theTask" name="my task" activiti:candidateUsers="kermit, gonzo" />
Copy the code
  • The candidateGroups property: Sets the candidate group for the task (exactly the same as using potentialOwner; there is no need to declare through group(management) as with potentialOwner; this property can only be used for groups)
<userTask id="theTask" name="my task" activiti:candidateGroups="management, accountancy" />
Copy the code
  • CandidateUsers and candidateGroups can be set in the same user task
  • Although Activiti has an account management component and IdentityService, the account component does not detect the existence of the specified user. Activiti allows integration with other existing account management solutions
  • Implement custom allocation logic using task listeners that create events:
<userTask id="task1" name="My task" >
  <extensionElements>
    <activiti:taskListener event="create" class="org.activiti.MyAssignmentHandler" />
  </extensionElements>
</userTask>
Copy the code
  • DelegateTask is passed to the implementation of TaskListener, which sets the performer, candidate, and candidate group
public class MyAssignmentHandler implements TaskListener {

  public void notify(DelegateTask delegateTask) {
    // Execute custom identity lookups here

    // and then for example call following methods:
    delegateTask.setAssignee("kermit");
    delegateTask.addCandidateUser("fozzie");
    delegateTask.addCandidateGroup("management"); . }}Copy the code
  • When you use Spring, you use expressions to set the task listener to the Spring agent’s bean and have the listener listen for the task creation event
  • Example: The performer gets this by calling the findManagerOfEmployee method of the spring bean ldapService. The process variable EMP is passed to the bean as a parameter
<userTask id="task" name="My Task" activiti:assignee="${ldapService.findManagerForEmployee(emp)}"/>
Copy the code
  • Can be used to set candidates and candidate groups:
<userTask id="task" name="My Task" activiti:candidateUsers="${ldapService.findAllSales()}"/>
Copy the code
  • The method return type can only be String(candidate) or Collection < String >(candidate group):
public class FakeLdapService {

  public String findManagerForEmployee(String employee) {
    return "Kermit The Frog";
  }

  public List<String> findAllSales(a) {
    return Arrays.asList("kermit"."gonzo"."fozzie"); }}Copy the code

The script task

describe
  • The script task is an automatic node
  • When the process reaches the script task, the corresponding script is executed
Graphic tag
  • Script tasks are displayed as standard BPMN 2.0 tasks (rounded rectangles) with a small script icon in the upper left corner

The XML content
  • Script task definitions need to specify script and scriptFormat
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy">
  <script>
    sum = 0
    for ( i in inputArray ) {
      sum += i
    }
  </script>
</scriptTask>
Copy the code

The value of scriptFormat must be compatible with JSR-223(the scripting language of the Java platform). The default Javascript is included with the JDK and no additional dependencies are required. If you want to use another script engine, it must be jSR-223 engine compatible. You also need to add the corresponding JAR to your CLASspath and use the appropriate name: Groovy is often used for Activiti unit tests

  • The Groovy script engine is housed in groovy-all.jar and was part of the Groovy JAR prior to version 2.0. Use to add dependencies:
<dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>2.x.x<version>
</dependency>
Copy the code
Script variable
  • All process variables that can be accessed by the process arriving at the script task can be used in the script
<script>
    sum = 0
    for ( i in inputArray ) {
      sum += i
    }
</script>
Copy the code
  • You can also set the process variable in the script and call it directlyexecution.setVariable(“variableName”, variableValue)
    • By default, variables are not automatically saved (pre-Activiti 5.12)
    • You can automatically save any variable in a script by setting the autoStoreVariables property of a scriptTask to true
    • The best practice is not to use, but to explicitly call execution.setvariable ()
<scriptTask id="script" scriptFormat="JavaScript" activiti:autoStoreVariables="false">
Copy the code

The default parameter is false: if no parameter is set for the script task definition, all declared variables will only exist during the script execution phase

  • Set variables in the script: these names are already occupied and cannot be used as variable names – out, out:print, lang:import, context, elcontext.
<script>
    def scriptVar = "test123"
    execution.setVariable("myVar", scriptVar)
</script>
Copy the code
Script results
  • The return value of a script task can be assigned to an existing or a new process variable by specifying the name of the process variable, using the ‘Activiti :resultVariable’ attribute defined by the script task
  • Any existing process variables are overwritten by the results of script execution
  • If no variable name is specified, the return value of the script is ignored
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="juel" activiti:resultVariable="myVar">
  <script>#{echo}</script>
</scriptTask>
Copy the code

The result of the script – the value of the expression #{echo} is set to the myVar variable after the script is complete

Java Service Task

describe
  • Java service tasks are used to invoke external Java classes
Graphic tag
  • The Java service task appears as a rounded rectangle with a small gear icon in the upper left corner

The XML content
  • There are four ways to declare Java call logic:
    • Implement JavaDelegate or ActivityBehavior
    • Performs an expression that parses the proxy object
    • Call a method expression
    • Calls a value expression
  • To execute a class called during process execution, set the full class name in the Activiti: Class property:
<serviceTask id="javaService"
             name="My Java Service Task"
             activiti:class="org.activiti.MyJavaDelegate" />
Copy the code
  • Use expressions call an object, the object must follow some rules, and use the activiti: create delegateExpression attributes:
<serviceTask id="serviceTask" activiti:delegateExpression="${delegateExpressionBean}" />
Copy the code

DelegateExpressionBean is a bean that implements the JavaDelegate interface and defines the specific UEL method expression to execute in the instance’s Spring container using activiti: Expression:

<serviceTask id="javaService"
             name="My Java Service Task"
             activiti:expression="#{printer.printMessage()}" />
Copy the code

The method printMessage() calls the method named printer object

  • Pass arguments to methods in expressions:
<serviceTask id="javaService"
             name="My Java Service Task"
             activiti:expression="#{printer.printMessage(execution, myVar)}" />
Copy the code

Call the method printMessage on a printer object. The first parameter is DelegateExecution, which defaults to execution in an expression environment. The second argument passes a variable named myVar for the current process. To execute the specified UEL method expression, use activiti:expression:

<serviceTask id="javaService"
             name="My Java Service Task"
             activiti:expression="#{split.ready}" />
Copy the code

The getter method for the ready property :getReady() will be used on a bean named split. This object is resolved into the process object and objects in the Spring environment

implementation
  • To implement a class that is called in a process execution, this class needs to implement org. Activiti. Engine. Delegate. JavaDelegate interface, and provides the corresponding business logic in the execute method. When the process executes to a particular stage, the business logic defined in the method is specified and leaves the node as in the default BPMN 2.0
  • Example:
    • Create an example Java class that converts a string in a process variable to uppercase
    • This class needs to implement org. Activiti. Engine. Delegate. JavaDelegate interface, required to execute (DelegateExecution) method, contains the business logic of the engine calls
    • Process instance information: Process variables and other information that can be accessed and manipulated through the DelegateExecution interface
public class ToUppercase implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    String var = (String) execution.getVariable("input");
    var = var.toUpperCase();
    execution.setVariable("input".var); }}Copy the code
  • The class defined by serviceTask creates only one instance of a Java class
    • All process instances share the same class instance and call Execute (DelegateExecution)
    • Classes cannot use any member variables, must be thread-safe, and must simulate execution in different threads. It affects how property injection is handled
  • Classes referenced in the process definition (Activiti: Class) are not instantiated at deployment time
    • An instance of a class is created only when the process first executes to use it
    • If the class is not found, a ActivitiException is thrown
    • The reason for this is that the deployment environment (classpath, to be more exact) and the real environment are often different: when publishing a process using Ant or business archive uploaded to Activiti Explorer, the classpath does not contain the referenced classes
  • Internal implementation classes can also provide implementationsorg.activiti.engine.impl.pvm.delegate.ActivityBehaviorThe class of the interface
    • The implementation has access to more powerful ActivityExecution, which can influence the flow of the process
    • Note: This should be avoided whenever possible. Use the ActivityBehavior interface only in advanced situations where you know exactly what to do
Properties into
  • Inject data for the properties of the proxy class.The following types of injection are supported:
    • Fixed string
    • expression
  • If valid, the value is injected via setter methods of the proxy class, following Java bean naming conventions (for example, the fistName property corresponds to the setFirstName(Xxx) method)
  • If the property has no corresponding setter method, the value is injected directly into the private property
  • Some environments do not allow the SecurityManager to modify private properties and instead expose the corresponding setter method for the property to be injected
  • No matter what type of data in a process definition, injection target attribute types should be org. Activiti. Engine. Delegate. The Expression
  • Example:
    • Inject a constant into a property
    • Property injection can use the class property
    • Before you can declare the actual property injection, you need to define an XML element of extensionElements
<serviceTask id="javaService"
    name="Java service invocation"
    activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
    <extensionElements>
      <activiti:field name="text" stringValue="Hello World" />
  </extensionElements>
</serviceTask>
Copy the code

ToUpperCaseFieldInjected class has a text attribute, type is org. Activiti engine. Delegate. Expression. A call to text.getValue(execution) returns the defined string Hello World

  • You can use long text (for example, embedded email) and use the Activiti: String child element:
<serviceTask id="javaService"
    name="Java service invocation"
    activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
  <extensionElements>
    <activiti:field name="text">
        <activiti:string>
          Hello World
      </activiti:string>
    </activiti:field>
  </extensionElements>
</serviceTask>
Copy the code
  • You can use expressions to dynamically resolve injected values at run time
  • These expressions can use process variables or Spring-defined beans.
  • Java class instances in service tasks are shared among all process instances:
    • Inject the value of the attribute to the dynamic, can be in org. Activiti. Engine. The delegate. Use value and method of Expression in the Expression
    • The DelegateExecution parameter passed to the execute method is resolved
<serviceTask id="javaService" name="Java service invocation"
  activiti:class="org.activiti.examples.bpmn.servicetask.ReverseStringsFieldInjected">

  <extensionElements>
    <activiti:field name="text1">
      <activiti:expression>${genderBean.getGenderString(gender)}</activiti:expression>
    </activiti:field>
    <activiti:field name="text2">
       <activiti:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</activiti:expression>
    </activiti:field>
  </ extensionElements>
</ serviceTask>
Copy the code
  • Example:
    • Inject the expression and use the DelegateExecution resolution passed in at the moment:
public class ReverseStringsFieldInjected implements JavaDelegate {

  private Expression text1;
  private Expression text2;

  public void execute(DelegateExecution execution) {
    String value1 = (String) text1.getValue(execution);
    execution.setVariable("var1".new StringBuffer(value1).reverse().toString());

    String value2 = (String) text2.getValue(execution);
    execution.setVariable("var2".newStringBuffer(value2).reverse().toString()); }}Copy the code
  • We can set the expression to be a property instead of a child:
    • Because Java class instances are reused, injection occurs only once, when the service task is called for the first time
    • When properties in the code change, the values are not reinjected; treat them as immutable and do not modify them
Service Task Results
  • The results returned by the service process (service tasks using expressions) can be assigned to existing or new process variables
  • Defined by specifying the service taskactiviti:resultVariableProperty to implement
    • The specified process variable is overridden by the return result of the service process
    • If no return variable name is specified, the return result is ignored
<serviceTask id="aMethodExpressionServiceTask"
    activiti:expression="#{myService.doSomething()}"
    activiti:resultVariable="myVar" />
Copy the code

The return value of the service process (the return value of calling the doSomething() method on myService, which could bea process variable or a Spring bean) is set to a process variable named myVar after the service execution is complete

Handle exceptions

When executing custom logic, it is often necessary to catch the corresponding business exception and handle it within the process

  • Throw the BPMN Errors:
    • Throw a BPMN error in the service task or script task code:
      • A special ActivitiExeption called BpmnError is thrown from JavaDelegate, scripts, expressions, and proxy expressions
      • The engine catches this exception and forwards it to the corresponding error handler: the boundary error event or error event subflow
public class ThrowBpmnErrorDelegate implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    try {
      executeBusinessLogic();
    } catch (BusinessException e) {
      throw new BpmnError("BusinessExceptionOccured"); }}}Copy the code

The construct parameter is the error code used to determine which error handler will respond to the error. This mechanism is only used for business failures and should be handled by the boundary error event or error event subprocess set in the process definition. Technical errors should use other exception types and are usually not handled in the process

  • Exception sequence flow:

The internal implementation class takes the process to another path when some exception occurs

<serviceTask id="javaService"
  name="Java service invocation"
  activiti:class="org.activiti.ThrowsExceptionBehavior">
</serviceTask>

<sequenceFlow id="no-exception" sourceRef="javaService" targetRef="theEnd" />
<sequenceFlow id="exception" sourceRef="javaService" targetRef="fixException" />
Copy the code

The service task here has two outgoing sequential flows called Exception and no-Exception. The ID of the sequential stream is used to determine the flow direction when an exception occurs

public class ThrowsExceptionBehavior implements ActivityBehavior {

  public void execute(ActivityExecution execution) throws Exception {
    String var = (String) execution.getVariable("var");

    PvmTransition transition = null;
    try {
      executeLogic(var);
      transition = execution.getActivity().findOutgoingTransition("no-exception");
    } catch (Exception e) {
      transition = execution.getActivity().findOutgoingTransition("exception"); } execution.take(transition); }}Copy the code
JavaDelegate uses the Activiti service
  • Scenarios where the Activiti service needs to be used in a Java service task: for example, when a process instance is started through RuntimeService, callActivity does not meet the requirements
  • Org. Activiti. Engine. Delegate. DelegateExecution allowed by org. Activiti. Engine. EngineServices interface direct access to these services:
public class StartProcessInstanceTestDelegate implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    RuntimeService runtimeService = execution.getEngineServices().getRuntimeService();
    runtimeService.startProcessInstanceByKey("myProcess"); }}Copy the code
  • All of the activiti service apis are available through this interface
  • All data changes that occur using these API calls are in the current transaction
  • It also works in dependency injection environments such as Spring and CDI, whether or not JTA data sources are enabled
  • Example: the following code function in accordance with the above code, this is RuntimeService is through dependency injection, rather than by org. Activiti. Engine. EngineServices interface
@Component("startProcessInstanceDelegate")
public class StartProcessInstanceTestDelegateWithInjection {

    @Autowired
    private RuntimeService runtimeService;

    public void startProcess(a) {
      runtimeService.startProcessInstanceByKey("oneTaskProcess"); }}Copy the code
  • Because the service invocation is in the current transaction, the creation or change of data is not committed to the database until the service task is completed. So API operations on database data mean that uncommitted operations are not visible from API calls to service tasks

WebService task

describe
  • A WebService task can be used to invoke an external WebService synchronously
Graphic tag
  • WebService tasks display the same as Java Service tasks (rounded rectangle with a small gear icon in the upper left corner)

The XML content
  • To use a WebService requires an import operation and type, and you can specify the WSDL for the WebService using the import tag
<import importType="http://schemas.xmlsoap.org/wsdl/"
        location="http://localhost:63081/counter? wsdl"
        namespace="http://webservice.activiti.org/" />
Copy the code

The declaration tells Activiti to import the WSDL definition, but does not create an itemDefinition and message

  • Imagine calling a method called prettyPrint, which must create message and itemDefinition for the request and response information
<message id="prettyPrintCountRequestMessage" itemRef="tns:prettyPrintCountRequestItem" />
<message id="prettyPrintCountResponseMessage" itemRef="tns:prettyPrintCountResponseItem" />

<itemDefinition id="prettyPrintCountRequestItem" structureRef="counter:prettyPrintCount" />
<itemDefinition id="prettyPrintCountResponseItem" structureRef="counter:prettyPrintCountResponse" />
Copy the code
  • Before applying for a service task, you must define BPMN interfaces and operations that actually reference WebService
  • Basically, define the interface and the necessary operations. The information defined above is reused as input and output for each operation
  • Example:
    • Interface and defines the counter prettyPrintCountOperation operation:
  <interface name="Counter Interface" implementationRef="counter:Counter">
        <operation id="prettyPrintCountOperation" name="prettyPrintCount Operation"
                        implementationRef="counter:prettyPrintCount">
                <inMessageRef>tns:prettyPrintCountRequestMessage</inMessageRef>
                <outMessageRef>tns:prettyPrintCountResponseMessage</outMessageRef>
        </operation>
</interface>
Copy the code

Then you define a WebService task, implement it using WebService, and reference the WebService action

<serviceTask id="webService"
        name="Web service invocation"
        implementation="##WebService"
        operationRef="tns:prettyPrintCountOperation">
Copy the code
WebService task IO specification
  • Each WebService task can define input and output IO specifications for the task
<ioSpecification>
        <dataInput itemSubjectRef="tns:prettyPrintCountRequestItem" id="dataInputOfServiceTask" />
        <dataOutput itemSubjectRef="tns:prettyPrintCountResponseItem" id="dataOutputOfServiceTask" />
        <inputSet>
                <dataInputRefs>dataInputOfServiceTask</dataInputRefs>
        </inputSet>
        <outputSet>
                <dataOutputRefs>dataOutputOfServiceTask</dataOutputRefs>
        </outputSet>
</ioSpecification>
Copy the code
WebService task data entry association
  • There are two ways to specify a data input association:
    • Using expressions
    • Use a simplified approach
  • Use expressions to specify data input associations: We need to define the source and destination items and specify the correspondence between each item attribute:
<dataInputAssociation>
        <sourceRef>dataInputOfProcess</sourceRef>
        <targetRef>dataInputOfServiceTask</targetRef>
        <assignment>
                <from>${dataInputOfProcess.prefix}</from>
                <to>${dataInputOfServiceTask.prefix}</to>
        </assignment>
        <assignment>
                <from>${dataInputOfProcess.suffix}</from>
                <to>${dataInputOfServiceTask.suffix}</to>
        </assignment>
</dataInputAssociation>
Copy the code

Assign item prefixes and suffixes

  • The sourceRef element is the activiti variable name, and the targetRef element is an attribute defined by item:
<dataInputAssociation>
        <sourceRef>PrefixVariable</sourceRef>
        <targetRef>prefix</targetRef>
</dataInputAssociation>
<dataInputAssociation>
        <sourceRef>SuffixVariable</sourceRef>
        <targetRef>suffix</targetRef>
</dataInputAssociation>
Copy the code

The value of PrefixVariable is assigned to the prefix attribute, and the value of SuffixVariable is assigned to the suffix attribute

WebService task data output association
  • There are two ways to specify a data output association:
    • Using expressions
    • Use a simplified approach
  • Specify data output associations using expressions: You need to define destination variables and source expressions
<dataOutputAssociation>
        <targetRef>dataOutputOfProcess</targetRef>
        <transformation>${dataOutputOfServiceTask.prettyPrint}</transformation>
</dataOutputAssociation>
Copy the code

The method is exactly the same as the data input association

  • Specify data output associations in a simplified way: the sourceRef element is an attribute defined by item, and the targetRef element is the variable name of Activiti
<dataOutputAssociation>
        <sourceRef>prettyPrint</sourceRef>
        <targetRef>OutputVariable</targetRef>
</dataOutputAssociation>
Copy the code

The method is exactly the same as the data input association

Business rule task

describe
  • A business rule task is used to execute one or more rules synchronously
  • Activiti uses the Drools rules engine to execute business rules:
    • The. DRL file containing the business rules must be published with the process definition
    • The process definition contains the business rule tasks that execute these rules
    • All. DRL files used by the process must be packaged in the process BAR file
  • If you want to customize the implementation of a rule task: You want to use Drools in a different way, or use a completely different rule engine. You can use the class or expression properties on BusinessRuleTask
Graphic tag
  • The business rule task is a rounded rectangle with a small table icon in the upper left corner

The XML content
  • To execute one or more business rules in the BAR file defined by the deployment process, you need to define input and output variables:
    • For input variable definitions, you can use comma-separated process variables
    • The output variable definition contains a single variable name that saves the object returned after the execution of the business rule into the corresponding process variable
  • Note: the result variable will contain a list of objects, if you don’t specify OUTPUT variable name, the default will use org. Activiti. Engine. Rules. The OUTPUT
<process id="simpleBusinessRuleProcess">

  <startEvent id="theStart" />
  <sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" />

  <businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
      activiti:resultVariable="rulesOutput" />

  <sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" />

  <endEvent id="theEnd" />

</process>
Copy the code
  • The business rules task can also be configured to execute only some of the rules in the deployed.drL file. The comma-separated rule name is set and only rule1 and rule2 are executed:
<businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
      activiti:rules="rule1, rule2" />
Copy the code
  • Define which rules do not execute: Except for RUle1 and RUle2, all rules deployed to the same BAR file in the process definition are executed:
<businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
      activiti:rules="rule1, rule2" exclude="true" />
Copy the code
  • You can modify the implementation of BusinessRuleTask with an option:
<businessRuleTask id="businessRuleTask" activiti:class="${MyRuleServiceDelegate}" />
Copy the code

BusinessRuleTask functions the same as ServiceTask, but uses the BusinessRuleTask icon to indicate that business rules are to be executed here

The mail task

  • Activiti enhances business processes to support automated mail tasks:
    • Mail can be sent to one or more participants, including support for CC, BCC,HTML content, and so on
    • Mail tasks are not official tasks defined by the BPMN 2.0 specification; in Activiti, mail tasks are implemented using specialized service tasks
Mail Server Configuration
  • The Activiti engine sends messages through an external mail server that supports SMTP
  • In order to actually send messages, the engine knows how to access the mail server. The following configuration can be set to the activiti.cfg.xml configuration file:
attribute Whether must describe
mailServerHost no Host name of the mail server (for example, mail.mycorp.com). The default is localhost
mailServerPort is

If the default port is not used
SMTP transfer port on the mail server. The default is 25
mailServerDefaultFrom no If the user does not specify an email address to send the email, the default setting is the email address of the sender. The default [email protected]
mailServerUsername If the server needs Some mail servers require authentication to send mail. Default not set
mailServerPassword If the server needs Some mail servers require authentication to send mail. Default not set
mailServerUseSSL If the server needs Some mail servers require SSL interaction. The default is false
mailServerUseTLS If the server needs Some mail servers (such as Gmail) need to support TLS. The default is false
##### Defines a mail task
  • The mail task is a dedicated service task whose type is set to mail
<serviceTask id="sendMail" activiti:type="mail">
Copy the code
  • Mail tasks are configured through property injection. All of these properties are available using EL expressions that can be parsed during process execution. The following properties can be set:
attribute Whether must describe
to is The recipient of a message. Multiple recipients can be separated by commas
from no The address of the sender. If no, the default address is used
subject no The subject of the email
cc no You can use commas to separate multiple recipients
bcc no Benders. Use commas to separate multiple recipients
charset no You can modify the character set of messages, which is mandatory for many non-English languages
html no HTML as the content of the message
text no The content of the message., used when the message needs to use raw text (not rich text). Can be used with HTML for mail clients that do not support rich text. The client will degrade to a text-only display
htmlVar no Use the corresponding process variable as the content of the E-mail. The difference with HTML is that the expression contained in the content is replaced before the Mail task is sent
textVar no Use the corresponding process variables as the plain text content of the E-mail. The difference with text is that the expression contained in the content is replaced before the Mail task is sent
ignoreException no Whether to ignore ActivitiException when processing an email fails. The default value is false
exceptionVariableName no When setting ignoreException=true to handle email without throwing exceptions, you can specify a variable name to store failure information
# # # # #
  • Examples of mail tasks:
<serviceTask id="sendMail" activiti:type="mail">
  <extensionElements>
    <activiti:field name="from" stringValue="[email protected]" />
    <activiti:field name="to" expression="${recipient}" />
    <activiti:field name="subject" expression="Your order ${orderId} has been shipped" />
    <activiti:field name="html">
      <activiti:expression><! [CDATA[ <html> <body> Hello ${male ? 'Mr.' : 'Mrs.' } ${recipientName},<br/><br/> As of ${now}, your order has been <b>processed and shipped</b>.<br/><br/> Kind regards,<br/> TheCompany. </body> </html> ]]></activiti:expression>
    </activiti:field>
  </extensionElements>
</serviceTask>
Copy the code

The Mule task

  • Mule tasks send messages to Mule to enhance Activiti’s integration capabilities
  • Mule tasks are not official tasks defined by the BPMN 2.0 specification, and Mule tasks are implemented in Activiti using specialized service tasks
Defining Mule Tasks
  • The Mule task is a dedicated service task whose type is set to Mule
<serviceTask id="sendMule" activiti:type="mule">
Copy the code
  • Mule tasks are configured through property injection. Properties use EL expressions that can be parsed during process execution
attribute Whether must describe
endpointUrl is Mule terminal that needs to be called
language is Use a language that resolves the properties of load expressions
payloadExpression is As an expression for message load
resultVariable no The name of the variable that will hold the result of the call
# # # # #
  • Examples of Mule task use:
 <extensionElements>
    <activiti:field name="endpointUrl">
      <activiti:string>vm://in</activiti:string>
    </activiti:field>
    <activiti:field name="language">
      <activiti:string>juel</activiti:string>
    </activiti:field>
    <activiti:field name="payloadExpression">
      <activiti:string>"hi"</activiti:string>
    </activiti:field>
    <activiti:field name="resultVariable">
      <activiti:string>theVariable</activiti:string>
    </activiti:field>
  </extensionElements>
Copy the code

Camel task

  • Camel tasks can send and receive messages from Camel to enhance activiti integration
  • Camel tasks are not official tasks defined by the BPMN 2.0 specification, but are implemented by dedicated service tasks
  • To use the Camel task feature, include Activiti Camel in your project
Defining the Camel task
  • Camel task is a dedicated service task whose type is set to Camel
<serviceTask id="sendCamel" activiti:type="camel">
Copy the code
  • The process definition only needs to define the Camel type in the service task
  • The integration logic is proxyed to the Camel container
  • By default, the Activiti engine looks for the camelContext bean in the Spring container. CamelContext defines the routing rules loaded by the Camel container
  • Routing rules can either be loaded from a specified Java package or defined directly through the Spring configuration
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
  <packageScan>
    <package>org.activiti.camel.route</package>
  </packageScan>
</camelContext>
Copy the code
  • Define multiple Camel environment beans and use different bean names. You can override the definition of CamelTask
<serviceTask id="serviceTask1" activiti:type="camel">
        <extensionElements>
                <activiti:field name="camelContext" stringValue="customCamelContext" />
        </extensionElements>
</serviceTask>
Copy the code
Camel calls
  • To activate a specific Camel route:
  • A Spring environment is required. The SimpleCamelCallRoute class file is placed in the packageScan scan directory
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
        <packageScan>
                <package>org.activiti.camel.examples.simpleCamelCall</package>
        </packageScan>
</camelContext>
Copy the code
  • Definition of a route:
public class SimpleCamelCallRoute extends RouteBuilder {

  @Override
  public void configure(a) throws Exception {

          from("activiti:SimpleCamelCallProcess:simpleCall").to("log: org.activiti.camel.examples.SimpleCamelCall"); }}Copy the code

This rule is used to print the message body

  • The format of a terminal consists of three parts:
    • Terminal URL: References the Activiti terminal
    • SimpleCamelCallProcess: the process name
    • SimpleCall: Camel service in the process
  • With the rules configured, Camel can be used. The workflow is as follows:
<process id="SimpleCamelCallProcess">
        <startEvent id="start"/>
        <sequenceFlow id="flow1" sourceRef="start" targetRef="simpleCall"/>
                
        <serviceTask id="simpleCall" activiti:type="camel"/>
                
        <sequenceFlow id="flow2" sourceRef="simpleCall" targetRef="end"/>
        <endEvent id="end"/>
</process>
Copy the code

In the serviceTask section, note that the type of service is Camel and the target rule is called simpleCall. This matches the Activiti terminal above. After the process is initialized, you will see an empty log

Table tennis instance
  • Camel and Activiti need to interact to send and receive data to Camel
  • Send a string, send the message in the variable to Camel,Camel does some processing, and return the result:
@Deployment
public void testPingPong(a) {
  Map<String, Object> variables = new HashMap<String, Object>();

  variables.put("input"."Hello");
  Map<String, String> outputMap = new HashMap<String, String>();
  variables.put("outputMap", outputMap);

  runtimeService.startProcessInstanceByKey("PingPongProcess", variables);
  assertEquals(1, outputMap.size());
  assertNotNull(outputMap.get("outputValue"));
  assertEquals("Hello World", outputMap.get("outputValue"));
}
Copy the code
  • The input variable is the actual input for Camel’s rule, and outputMap records the result returned by Camel
<process id="PingPongProcess">
  <startEvent id="start"/>
  <sequenceFlow id="flow1" sourceRef="start" targetRef="ping"/>
  <serviceTask id="ping" activiti:type="camel"/>
  <sequenceFlow id="flow2" sourceRef="ping" targetRef="saveOutput"/>
  <serviceTask id="saveOutput"  activiti:class="org.activiti.camel.examples.pingPong.SaveOutput" />
  <sequenceFlow id="flow3" sourceRef="saveOutput" targetRef="end"/>
  <endEvent id="end"/>
</process>
Copy the code
  • SaveOuput, the serviceTask, saves the value of the Output variable from the context to an OutputMap
  • The method by which variables are submitted to Camel is controlled by CamelBehavior. Configure a desired Camel operation:
<serviceTask id="serviceTask1" activiti:type="camel">
  <extensionElements>
    <activiti:field name="camelBehaviorClass" stringValue="org.activiti.camel.impl.CamelBehaviorCamelBodyImpl" />
  </extensionElements>
</serviceTask>
Copy the code
  • If specified a behavior, will use org. Activiti. Camel. Impl. CamelBehaviorDefaultImpl. This action copies the variable into the Camel attribute with the same name
  • On return, regardless of the behavior selected, if the Camel message body is a map, each element is copied into a variable. Otherwise, the entire object is copied to the variable named camelBody
@Override
public void configure(a) throws Exception {
  from("activiti:PingPongProcess:ping").transform().simple("${property.input} World");
} 
Copy the code

In this rule, the string world is appended to the input property, and the result is written to the body of the message. At this point, the camelBody variable in the javaServiceTask can be checked, copied to the outputMap, and checked in TestCase

  • In all Camel rules that are launched, the process instance ID is copied to camel’s property named PROCESS_ID_PROPERTY, which can then be used to associate the process instance with camel rules or directly in Camel rules
  • Three different Camel behaviors can be used in Activiti: overrides can be implemented by specifying them in the rule URL
from("activiti:asyncCamelProcess:serviceTaskAsync2? copyVariablesToProperties=true").
Copy the code

How the Activiti variable is passed to Camel:

behavior URL describe
CamelBehaviorDefaultImpl copyVariablesToProperties Copy the Activiti variable into the Camel property
CamelBehaviorCamelBodyImpl copyCamelBodyToBody Copy only the variable named “camelBody”Activiti into Camel’s message body
CamelBehaviorBodyAsMapImpl copyVariablesToBodyAsMap Copy all of Activiti’s variables into a map for Camel’s message body
How Camel’s variable is returned to Activiti can only be configured in the rule URL:
URL describe
The default If the Camel message body is a map, copy each element as an Activiti variable. Otherwise, use the entire Camel message body as the camelBody variable for Activiti
copyVariablesFromProperties Copy the Camel attribute with the same name as the Activiti variable
copyCamelBodyToBodyAsString As the default, but if camel message body is not a Map, it is first converted to a string and then set to camelBody
copyVariablesFromHeader In addition, copy the Camel header into the Activiti variable with the same name
##### Example of asynchronous ping-pong
  • For synchronized ping-pong instances, the process will not stop until Camel rule returns
  • In some cases, you need the Activiti workflow to continue running, using camelServiceTask’s asynchronous capabilities
  • This is enabled by setting the Async property of camelServiceTask
<serviceTask id="serviceAsyncPing" activiti:type="camel" activiti:async="true"/>
Copy the code

Camel rules are executed asynchronously by Activiti’s jobExecutor When a queue is defined in Camel rules, the Activiti process continues to run Camel rules while camelServiceTask executes in a fully asynchronous manner

  • You can use a receiveTask to wait for the return value of camelServiceTask, and the process instance will wait to receive a signal from Camel:
<receiveTask id="receiveAsyncPing" name="Wait State" />
Copy the code
  • In Camel, you can send a signal to the process instance via the corresponding Activiti terminal:
from("activiti:asyncPingProcess:serviceAsyncPing").to("activiti:asyncPingProcess:receiveAsyncPing");
Copy the code
  • In the Activiti terminal, will be usedThe colonThree separate parts:
    • Constant string activiti
    • The name of the process
    • Receiving task name
Instantiate the workflow in Camel rules
  • Typically, the Activiti workflow starts first and then the Camel rule starts in the process
  • Starting a workflow in an already started Camel rule triggers a receiveTask
  • Very similar, except for the last part. Example rules are as follows:
from("direct:start").to("activiti:camelProcess");
Copy the code

The URL has two parts:

  • Constant string activiti
  • Name of the process

The process has been deployed and is ready to start

Manual tasks

describe
  • Manual tasks define tasks outside the BPMN engine
  • Indicates that the work needs to be done by someone, and the engine doesn’t need to know about it and has no corresponding system or UI interface
  • For the BPMN engine, a manual task is a straight-through activity that the process automatically moves down once it reaches it
Graphic tag
  • Manual tasks are displayed as normal tasks (rounded rectangles) with a small hand-shaped icon in the upper left corner

The XML content
<manualTask id="myManualTask" name="Call client for more information" />
Copy the code

Java Receive task

describe
  • The receive task is a simple task that waits for the corresponding message to arrive
  • When the process reaches the receiving task, the process state is saved to storage. Means that the process will wait in this wait state until the engine receives a specific message that triggers the process to continue through the receive task
Graphic tag
  • The receive task is displayed as a task (rounded rectangle) with a small message marker in the upper right corner. The message is white (black icon indicates send semantics)

The XML content
<receiveTask id="waitState" name="wait" /> 
Copy the code
  • To continue execution in the process instance where the receiving task is waiting, call runtimeService. Signal (executionId), passing the ID of the process on the receiving task:
ProcessInstance pi = runtimeService.startProcessInstanceByKey("receiveTask");
Execution execution = runtimeService.createExecutionQuery()
  .processInstanceId(pi.getId())
  .activityId("waitState")
  .singleResult();
assertNotNull(execution);

runtimeService.signal(execution.getId());
Copy the code

Shell task

describe
  • Shell tasks can execute Shell scripts and commands
  • Shell tasks are not official tasks defined by the BPMN 2.0 specification and do not have corresponding ICONS
Defining Shell Tasks
  • A Shell task is a dedicated service task whose type is set to Shell
<serviceTask id="shellEcho" activiti:type="shell">
Copy the code
  • Shell tasks are configured using property injection, and all properties can contain EL expressions that are resolved during process execution
attribute Whether must type describe The default value
command is String Shell command executed
arg0-5 no String Parameters 0 to 5
wait no true/false Whether to wait until the shell process ends true
redirectError no true/false Print the standard error to the standard stream false
cleanEnv no true/false The Shell does not inherit the current environment false
outputVariable no String Save the variable name of the output No output is recorded
errorCodeVariable no String The variable name that contains the resulting error code Error levels will not be registered
directory no String The default directory for the Shell process In the current directory
##### Application Example
  • Run the shell script CMD /c echo EchoTest, wait until it ends, and save the output to the resultVar:
<serviceTask id="shellEcho" activiti:type="shell" >
  <extensionElements>
    <activiti:field name="command" stringValue="cmd" />
    <activiti:field name="arg1" stringValue="/c" />
    <activiti:field name="arg2" stringValue="echo" />
    <activiti:field name="arg3" stringValue="EchoTest" />
    <activiti:field name="wait" stringValue="true" />
    <activiti:field name="outputVariable" stringValue="resultVar" />
  </extensionElements>
</serviceTask>           
Copy the code

Execution listener

  • Execution listeners can execute external Java code or execute expressions when an event occurs in a process definition
  • Execution listeners docaptureThe events are:
    • Start and end of a process instance
    • Select a line
    • Start and end of a node
    • The start and end of the gateway
    • The beginning and end of an intermediate event
    • Start Time End or end Event start
  • The following process definition defines three process listeners:
 <process id="executionListenersProcess">

    <extensionElements>
      <activiti:executionListener class="org.activiti.examples.bpmn.executionlistener.ExampleExecutionListenerOne" event="start" />
    </extensionElements>

    <startEvent id="theStart" />
    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

    <userTask id="firstTask" />
    <sequenceFlow sourceRef="firstTask" targetRef="secondTask">
    <extensionElements>
      <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleExecutionListenerTwo" />
    </extensionElements>
    </sequenceFlow>

    <userTask id="secondTask" >
    <extensionElements>
      <activiti:executionListener expression="${myPojo.myMethod(execution.event)}" event="end" />
    </extensionElements>
    </userTask>
    <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" />

    <userTask id="thirdTask" />
    <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" />

    <endEvent id="theEnd" />

  </process>
Copy the code
  • The first process listener listens for the start of the process. The listener is an external Java classes (for example ExampleExecutionListenerOne), you need to implement org. Activiti. Engine. Delegate. ExecutionListener interface. (end), when the event occurs, invokes the notify (ExecutionListenerExecution execution) methods:
public class ExampleExecutionListenerOne implements ExecutionListener {

  public void notify(ExecutionListenerExecution execution) throws Exception {
    execution.setVariable("variableSetInExecutionListener"."firstValue");
    execution.setVariable("eventReceived", execution.getEventName()); }}Copy the code

Can also use the org. Activiti. Engine. Delegate. JavaDelegate interface proxy class (the proxy class can be reuse in the structure, such as serviceTask agent)

  • The second process listener is invoked when the wire executes. Note that the listener element cannot define an event, because a wire can only trigger a Take event, and the event attribute of a listener defined for the wire is ignored
  • The third process listener is called at the end of node secondTask. Use expression instead of class to execute or invoke when an event is raised
<activiti:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />
Copy the code

Process variables can be handled and used. The process implementation object has a property that holds the eventName, which is obtained using execution.eventName in the method

  • Process listeners also support delegateExpression, same as service tasks
<activiti:executionListener event="start" delegateExpression="${myExecutionListenerBean}" />
Copy the code
  • Script process listener org. Activiti. Engine. Impl. BPMN. Listener. ScriptExecutionListener can execute a script for a process to monitor event
<activiti:executionListener event="start" class="org.activiti.engine.impl.bpmn.listener.ScriptExecutionListener" >
  <activiti:field name="script">
    <activiti:string>
      def bar = "BAR";  // local variable
      foo = "FOO"; // pushes variable to execution context
      execution.setVariable("var1", "test"); // test access to execution instance
      bar // implicit return value
    </activiti:string>
  </activiti:field>
  <activiti:field name="language" stringValue="groovy" />
  <activiti:field name="resultVariable" stringValue="myVar" />
<activiti:executionListener>
Copy the code
Property injection for the process listener
  • When the process listener is configured, the class attribute can be used for property injection. This is the same as using service task property injection
  • Example of a process using a process listener with property injection:
<process id="executionListenersProcess">
    <extensionElements>
      <activiti:executionListener class="org.activiti.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener" event="start">
        <activiti:field name="fixedValue" stringValue="Yes, I am " />
        <activiti:field name="dynamicValue" expression="${myVar}" />
      </activiti:executionListener>
    </extensionElements>

    <startEvent id="theStart" />
    <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

    <userTask id="firstTask" />
    <sequenceFlow sourceRef="firstTask" targetRef="theEnd" />

    <endEvent id="theEnd" />
  </process>     
Copy the code
public class ExampleFieldInjectedExecutionListener implements ExecutionListener {

  private Expression fixedValue;

  private Expression dynamicValue;

  public void notify(ExecutionListenerExecution execution) throws Exception {
    execution.setVariable("var", fixedValue.getValue(execution).toString() + dynamicValue.getValue(execution).toString()); }}Copy the code

ExampleFieldInjectedExecutionListener series class attributes of the two injection (a is fixed, is a dynamic) and save them into the process variables var

@Deployment(resources = {"org/activiti/examples/bpmn/executionListener/ExecutionListenersFieldInjectionProcess.bpmn20.xml"})
public void testExecutionListenerFieldInjection(a) {
  Map<String, Object> variables = new HashMap<String, Object>();
  variables.put("myVar"."listening!");

  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("executionListenersProcess", variables);

  Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var");
  assertNotNull(varSetByListener);
  assertTrue(varSetByListener instanceof String);

  // Result is a concatenation of fixed injected field and injected expression
  assertEquals("Yes, I am listening!", varSetByListener);
}
Copy the code

Task listener

  • Task listeners can execute custom Java logic or expressions when a corresponding task-related event occurs
  • Task listeners can only be added to user tasks in the process definition. You must define it in a child element of BPMN 2.0 extensionElements and use the Activiti namespace because task listeners are unique to Activiti
<userTask id="myTask" name="My Task" >
  <extensionElements>
    <activiti:taskListener event="create" class="org.activiti.MyTaskCreateListener" />
  </extensionElements>
</userTask>
Copy the code
  • Task listeners support the following properties:
    • The event (choice) : The type of task to which the task listener will be invoked
      • Create: Triggered after a task is created and all attributes are set
      • Assignment: Assignment is triggered when some people are assigned. When the process reaches userTask, the Assignment event occurs before the Create event (when we get the CREATE time, we want to get all the attributes of the task, including the executor).
      • Complete: Fired when the task is complete and has not been deleted from the run data
      • Delete: Occurs only before the task is deleted, and is executed when the completeTask completes normally
    • class: The proxy class that must be invoked,Class to implementorg.activiti.engine.delegate.TaskListenerinterface
       public class MyTaskCreateListener implements TaskListener {
      
      public void notify(DelegateTask delegateTask) {
      // Custom logic goes here}}Copy the code

    You can useProperties intoPassing process variables or execution to the proxy class Instances of the proxy class are created at deployment time, and all process instances share the same instance

    • Expression: Specifies the expression to be executed when an event occurs. Cannot be used together with the class attribute.Can put theDelegateTaskThe object and eventName (task.eventname) are passed as parameters to the calling object
      <activiti:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
      Copy the code
    • delegateExpression:Specify an expression that parses an implementationTaskListenerInterface object, consistent with the service task
       <activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
      Copy the code
  • Script task listener org. Activiti. Engine. Impl. BPMN. Listener. ScriptTaskListener can execute the script for task listener event
<activiti:taskListener event="complete" class="org.activiti.engine.impl.bpmn.listener.ScriptTaskListener" >
  <activiti:field name="script">
    <activiti:string>
      def bar = "BAR";  // local variable
      foo = "FOO"; // pushes variable to execution context
      task.setOwner("kermit"); // test access to task instance
      bar // implicit return value
    </activiti:string>
  </activiti:field>
  <activiti:field name="language" stringValue="groovy" />
  <activiti:field name="resultVariable" stringValue="myVar" />
<activiti:taskListener>
Copy the code

Multi-instance (loop)

describe
  • Multi-instance nodes are a way of defining repeating links in a business process
  • A multiinstance is the same as a loop: it can execute a link or even a complete subprocess for each element, based on a given collection, either sequentially or concurrently
  • Multi-instance is the addition of additional attribute definitions (hence the ‘multi-instance feature’) to an ordinary node so that the runtime points are executed multiple times
  • Nodes that can be set as multi-instance nodes:
    • User Task
    • Script Task
    • Java Service Task
    • WebService Task
    • Business Rule Task
    • Email Task
    • Manual Task
    • Receive Task
    • (Embedded) sub-process
    • Call Activity [Invoke subprocess]
  • Multiple instances cannot be set for gateways and events
  • Each parent process provides the following variables when creating branches for each instance:
    • NrOfInstances: Number of instances
    • NrOfActiveInstances: Number of active instances that have not yet been completed. Sequential execution of multiple instances, always 1
    • NrOfCompletedInstances: The number of completed instances
  • These variables are obtained through the execution.getVariable(Xx) method
  • Each branch created will have branch-level local variables(For example, other instances are not visible and are not saved to the process instance level):
    • LoopCounter – The looped index value for a particular instance
    • Change the variable name of loopCounter using activiti’s elementIndexVariable property
Graphic tag
  • For multi-instance nodes, three short lines appear at the bottom of the node. Three vertical lines indicate that instances are executed in parallel. Three lines indicate sequential execution

The XML content

  • To a node set, for instance node must set a multiInstanceLoopCharacteristics child elements of an XML element
<multiInstanceLoopCharacteristics isSequential="false|true">.</multiInstanceLoopCharacteristics>
Copy the code

The isSequential property indicates whether a node executes sequentially or in parallel

  • The number of instances is counted once the node is entered:
    • One way is to use loopCardinality child elements

Can use loopCardinality child elements directly to specify a number of XML < multiInstanceLoopCharacteristics isSequential = “false | true” > < loopCardinality > 5 < / loopCardinality > < / multiInstanceLoopCharacteristics > can use loopCardinality XML child elements results for integer expressions


${nrOfOrders-nrOfCancellations}

  • Another way is throughloopDataInputRefChild element that sets the name of a process variable of type collection. For each element in the collection, an instance is created. Also throughinputDataItemThe child element specifies the collection
    <userTask id="miTasks" name="My Task ${loopCounter}" activiti:assignee="${assignee}">
    	<multiInstanceLoopCharacteristics isSequential="false">
    		 <loopDataInputRef>assigneeList</loopDataInputRef>
             <inputDataItem name="assignee" />
        </multiInstanceLoopCharacteristics>
    </userTask>
    Copy the code

    Assuming thatassigneeListVariables containing these values [Kermit, Gonzo, foziee] are created at the same time as three user tasks. Each branch will have a use nameassigneeThe process variable contains the corresponding element in the collection, which is used to set the assignment of the user task

  • loopDataInputRefandinputDataItemFaults:
    • Names are hard to remember
    • As defined by the BPMN 2.0 format, expressions cannot be included
  • In activiti can set up a collection and in multiInstanceCharacteristics elementVariable
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="true"
     activiti:collection="${myService.resolveUsersForTask()}" activiti:elementVariable="assignee" >
  </multiInstanceLoopCharacteristics>
</userTask>
Copy the code
  • A multi-instance node does not end until all instances are complete
  • You can specify an expression to be executed at the end of each instance. If the expression returns true, all other instances are destroyed and the multi-instance node terminates. The process continues. The expression must be defined in the completionCondition child element
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false"
     activiti:collection="assigneeList" activiti:elementVariable="assignee" >
    <completionCondition>The ${nrOfCompletedInstances/nrOfInstances > = 0.6}</completionCondition>
  </multiInstanceLoopCharacteristics>
</userTask>
Copy the code

Each element of the assigneeList collection creates a parallel instance, and when 60% of the tasks are complete, the other tasks are deleted and the process continues

Boundary events and multiple instances
  • A multiinstance is a normal node that can use boundary events on edges
  • For interrupt-type boundary events, all active instances are destroyed when the event is captured

All instances of the subprocess are destroyed when the timer is triggered, regardless of how many instances there are and regardless of whether the internal nodes are not completed

Compensation processor

describe
  • If a node is used to compensate for another node’s services, it can be declared as a compensation handler
  • The compensation handler does not contain a normal stream and only executes when a compensation event is triggered
  • The compensation handler cannot contain incoming and outgoing sequential streams
  • The compensation handler must use direct correlation to assign a compensation boundary event
Graphic tag
  • The node is the compensation handler, and the compensation event icon is displayed in the bottom center area
  • Compensation handler graphic example: A service task that attaches a compensation boundary event and assigns a compensation handler. Notice the compensation handler icon shown in the bottom middle area of the Cancel Hotel Reservation service task

The XML content
  • Set isForCompensation to true to declare the node as the compensation handler:
<serviceTask id="undoBookHotel" isForCompensation="true" activiti:class="...">
</serviceTask>
Copy the code