📜 ⬆️ ⬇️

"Hello World" BPMN2.0 or Activiti Introduction

In this article I want to consider an example of creating a simple application using the Activiti engine.

Activiti ” is a lightweight platform for working with business processes (Business Process Managment), adapted for business people, developers and system administrators. The platform is based on fast and reliable java-engine BPMN2 - processes . The project is open source and distributed under the Apache license. Activiti can be launched either as part of your java-application, or independently on a server, cluster or cloud. In addition, it integrates beautifully with Spring.

Since the project has excellent documentation , I will not describe its main concepts. I note only that, as mentioned above, we can use Activiti as a standalone application, but we can as an engine / library for processing business processes that are embedded in the application. Details can be found here .

Dependencies


So, let's begin. First of all, we need to create a project with Maven support and describe the following dependencies in it:
')
<!--
Activiti
-->
< dependency >
< groupId > org.activiti </ groupId >
< artifactId > activiti-engine </ artifactId >
< version > 5.1 </ version >
</ dependency >
< dependency >
< groupId > org.activiti </ groupId >
< artifactId > activiti-spring </ artifactId >
< version > 5.1 </ version >
</ dependency >


<!--
DataBase
-->
< dependency >
< groupId > com.h2database </ groupId >
< artifactId > h2 </ artifactId >
< version > 1.2.132 </ version >
</ dependency >
< dependency >
< groupId > commons-dbcp </ groupId >
< artifactId > commons-dbcp </ artifactId >
< version > 1.2.2 </ version >
</ dependency >


* This source code was highlighted with Source Code Highlighter .


The first two dependencies (the repositories for maven are described here ) connect the Activiti kernel and the set of utilities for integrating with Spring (the Spring connection is not described to save space). The second group of dependencies connects the database (in our case in-memory will be used) and the pool for working with it.

The database is needed by Activiti to store information about processes, their status, users, history, and many other things.

Buisness process


It is logical to start developing an application with a description of the business process. First of all, you should familiarize yourself with the units describing business processes offered by Activiti (more precisely, the BPMN 2.0 notation). After that, you can begin to describe your own business process. This can be done in three ways:

Despite the fact that the third point seems to be the most logical of the above, we will still use CASE technologies and draw our business process using a plug-in for Eclipse (it is important to set the plug-in settings to update the xml description after each change of the diagram, otherwise the editor begins to live his own life and completely ignore external stimuli as a user).

As a result, I got the following diagram:

diagram

ServiceTask “GenerateData” generates pseudo-random data (implemented using the GenerateDataService class that implements the JavaDelegate interface)

@Service
public class GenerateDataService implements JavaDelegate {

public void execute(DelegateExecution execution) throws Exception {
Long someData = Calendar.getInstance().getTimeInMillis() % 2;
execution.setVariable( "someData" , someData);
}
}


* This source code was highlighted with Source Code Highlighter .


depending on which either ServiceTask “SayHelloA” or ServiceTask “SayHelloB” is executed. Both of these ServiceTasks are reduced to calling the corresponding service method SayHelloService (implementation based on the UEL-expression "$ {sayHelloService.printMessageB (execution)"):

@Service
public class SayHelloService {

public void printMessageA(ActivityExecution execution) {
System. out .println( "Hello world: variant A" );
}

public void printMessageB(ActivityExecution execution) {
System. out .println( "Hello world: variant B" );
}
}


* This source code was highlighted with Source Code Highlighter .


In XML format (that is, in BPMN 2.0 format), namely it is needed by the Activiti engine for interpretation, the process is as follows:

<? xml version ="1.0" encoding ="UTF-8" ? >
< definitions xmlns ="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti ="http://activiti.org/bpmn" xmlns:bpmndi ="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc ="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi ="http://www.omg.org/spec/DD/20100524/DI" typeLanguage ="http://www.w3.org/2001/XMLSchema" expressionLanguage ="http://www.w3.org/1999/XPath" targetNamespace ="http://www.activiti.org/test" >

< process id ="helloWorldProcess" name ="Hello World" >
< documentation > Simple "Hello World" process </ documentation >
< startEvent id ="startevent1" name ="Start" ></ startEvent >
< serviceTask id ="servicetask1" name ="GenerateData" activiti:class ="name.krestjaninoff.activiti.hello.process.GenerateDataService" ></ serviceTask >
< endEvent id ="endevent1" name ="End" ></ endEvent >
< sequenceFlow id ="flow1" name ="" sourceRef ="startevent1" targetRef ="servicetask1" ></ sequenceFlow >
< serviceTask id ="servicetask3" name ="SayHelloB" activiti:expression ="${sayHelloService.printMessageB(execution)}" ></ serviceTask >
< serviceTask id ="servicetask4" name ="SayHelloA" activiti:expression ="${sayHelloService.printMessageA(execution)}" ></ serviceTask >
< exclusiveGateway id ="exclusivegateway2" name ="Exclusive Gateway" ></ exclusiveGateway >
< sequenceFlow id ="flow4" name ="" sourceRef ="servicetask1" targetRef ="exclusivegateway2" ></ sequenceFlow >
< sequenceFlow id ="flow5" name ="${someData != 0}" sourceRef ="exclusivegateway2" targetRef ="servicetask3" >
< conditionExpression xsi:type ="tFormalExpression" ><! [CDATA[${someData != 0}]] ></ conditionExpression >
</ sequenceFlow >
< sequenceFlow id ="flow6" name ="${someData == 0}" sourceRef ="exclusivegateway2" targetRef ="servicetask4" >
< conditionExpression xsi:type ="tFormalExpression" ><! [CDATA[${someData == 0}]] ></ conditionExpression >
</ sequenceFlow >
< exclusiveGateway id ="exclusivegateway3" name ="Exclusive Gateway" ></ exclusiveGateway >
< sequenceFlow id ="flow7" name ="" sourceRef ="servicetask3" targetRef ="exclusivegateway3" ></ sequenceFlow >
< sequenceFlow id ="flow8" name ="" sourceRef ="servicetask4" targetRef ="exclusivegateway3" ></ sequenceFlow >
< sequenceFlow id ="flow9" name ="" sourceRef ="exclusivegateway3" targetRef ="endevent1" ></ sequenceFlow >
</ process >

< bpmndi:BPMNDiagram id ="BPMNDiagram_helloWorldProcess" >
< bpmndi:BPMNPlane bpmnElement ="helloWorldProcess" id ="BPMNPlane_helloWorldProcess" >
< bpmndi:BPMNShape bpmnElement ="startevent1" id ="BPMNShape_startevent1" >
< omgdc:Bounds height ="55" width ="55" x ="273" y ="10" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="servicetask1" id ="BPMNShape_servicetask1" >
< omgdc:Bounds height ="55" width ="105" x ="248" y ="120" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="endevent1" id ="BPMNShape_endevent1" >
< omgdc:Bounds height ="55" width ="55" x ="273" y ="460" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="servicetask3" id ="BPMNShape_servicetask3" >
< omgdc:Bounds height ="55" width ="105" x ="390" y ="304" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="servicetask4" id ="BPMNShape_servicetask4" >
< omgdc:Bounds height ="55" width ="105" x ="109" y ="304" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="exclusivegateway2" id ="BPMNShape_exclusivegateway2" >
< omgdc:Bounds height ="60" width ="60" x ="270" y ="210" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNShape bpmnElement ="exclusivegateway3" id ="BPMNShape_exclusivegateway3" >
< omgdc:Bounds height ="60" width ="60" x ="270" y ="370" ></ omgdc:Bounds >
</ bpmndi:BPMNShape >
< bpmndi:BPMNEdge bpmnElement ="flow1" id ="BPMNEdge_flow1" >
< omgdi:waypoint x ="328" y ="37" ></ omgdi:waypoint >
< omgdi:waypoint x ="248" y ="147" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow4" id ="BPMNEdge_flow4" >
< omgdi:waypoint x ="353" y ="147" ></ omgdi:waypoint >
< omgdi:waypoint x ="270" y ="240" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow5" id ="BPMNEdge_flow5" >
< omgdi:waypoint x ="330" y ="240" ></ omgdi:waypoint >
< omgdi:waypoint x ="390" y ="331" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow6" id ="BPMNEdge_flow6" >
< omgdi:waypoint x ="330" y ="240" ></ omgdi:waypoint >
< omgdi:waypoint x ="109" y ="331" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow7" id ="BPMNEdge_flow7" >
< omgdi:waypoint x ="495" y ="331" ></ omgdi:waypoint >
< omgdi:waypoint x ="270" y ="400" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow8" id ="BPMNEdge_flow8" >
< omgdi:waypoint x ="214" y ="331" ></ omgdi:waypoint >
< omgdi:waypoint x ="270" y ="400" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
< bpmndi:BPMNEdge bpmnElement ="flow9" id ="BPMNEdge_flow9" >
< omgdi:waypoint x ="330" y ="400" ></ omgdi:waypoint >
< omgdi:waypoint x ="273" y ="487" ></ omgdi:waypoint >
</ bpmndi:BPMNEdge >
</ bpmndi:BPMNPlane >
</ bpmndi:BPMNDiagram >
</ definitions >

* This source code was highlighted with Source Code Highlighter .


Settings


Now, in order for us to start the process using Activiti, we need to configure and initialize the core elements of the Activity core . Since we are using Spring, for us the configuration will be reduced to the configuration of the corresponding bean:

< beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:context ="http://www.springframework.org/schema/context"
xmlns:tx ="http://www.springframework.org/schema/tx"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://www.springframework.org/schema/beans www.springframework.org/schema/beans/spring-beans.xsd
www.springframework.org/schema/context www.springframework.org/schema/context/spring-context-3.0.xsd
www.springframework.org/schema/tx www.springframework.org/schema/tx/spring-tx-3.0.xsd"

>
<!-- Configuration -->
< context:property-placeholder location ="classpath*:*.properties" />

<!-- Annotation based configuration -->
< context:annotation-config />
< context:component-scan base-package ="name.krestjaninoff" />


<!-- Data -->
< bean id ="dataSource"
class ="org.apache.commons.dbcp.BasicDataSource" destroy-method ="close" >
< property name ="driverClassName" value ="org.h2.Driver" />
< property name ="url" value ="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
< property name ="username" value ="sa" />
< property name ="password" value ="" />
</ bean >

< bean id ="transactionManager"
class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name ="dataSource" ref ="dataSource" />
</ bean >
< tx:annotation-driven transaction-manager ="transactionManager" />


<!--
Activiti
-->
< bean id ="processEngineConfiguration" class ="org.activiti.spring.SpringProcessEngineConfiguration" >
< property name ="databaseType" value ="h2" />
< property name ="dataSource" ref ="dataSource" />
< property name ="transactionManager" ref ="transactionManager" />
< property name ="databaseSchemaUpdate" value ="true" />
< property name ="jobExecutorActivate" value ="false" />
< property name ="deploymentResources" value ="classpath*:/process/*.bpmn20.xml" />
</ bean >

< bean id ="processEngine" class ="org.activiti.spring.ProcessEngineFactoryBean" >
< property name ="processEngineConfiguration" ref ="processEngineConfiguration" />
</ bean >

< bean id ="repositoryService" factory-bean ="processEngine"
factory-method ="getRepositoryService" />
< bean id ="runtimeService" factory-bean ="processEngine"
factory-method ="getRuntimeService" />
< bean id ="taskService" factory-bean ="processEngine"
factory-method ="getTaskService" />
< bean id ="historyService" factory-bean ="processEngine"
factory-method ="getHistoryService" />
< bean id ="managementService" factory-bean ="processEngine"
factory-method ="getManagementService" />
</ beans >


* This source code was highlighted with Source Code Highlighter .


It is important to note the deploymentResources property of the processEngineConfiguration bean — it tells the configurator where the files describing our processes are located. Each time the application starts, it is necessary to “deploy” (deploy) these processes to the Activiti engine. More deployment is described here .

Application launch


And so, the main work has already been done. Now, to launch the application, we will only have to create the Spring context and start the necessary process:

public class Main {
public static void main( String [] args) {

// Create Spring context
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext( "applicationContext.xml" );

// Start process
RuntimeService runtimeService = (RuntimeService) applicationContext.
getBean( "runtimeService" );
runtimeService.startProcessInstanceByKey( "helloWorldProcess" );
}
}


* This source code was highlighted with Source Code Highlighter .


Example


The code for the project described above is available on GitHub .

Source: https://habr.com/ru/post/113658/


All Articles