1. Overview
1.概述
Activiti API is a workflow and Business Process Management system. We can define a process in it, execute it, and manipulate it in different ways using the services provided by the API. It requires JDK 7+.
Activiti API是一个工作流程和业务流程管理系统。我们可以在其中定义一个流程,执行它,并利用API提供的服务以不同的方式操作它。它需要JDK 7以上。
Development using the API can be done in any IDE, but to use the Activiti Designer, we need Eclipse.
使用 API 的开发可以在任何 IDE 中完成,但是要使用 Activiti Designer,我们需要 Eclipse。。
We can define a process in it using the BPMN 2.0 standard. There is another, less popular way – using Java classes like StartEvent, EndEvent, UserTask, SequenceFlow, etc.
我们可以使用BPMN 2.0标准在其中定义一个流程。还有一种不太流行的方式–使用Java类,如StartEvent、EndEvent、UserTask、SequenceFlow等。
If we want to run a process or access any of the services, we need to create a ProcessEngineConfiguration.
如果我们想运行一个进程或访问任何服务,我们需要创建一个ProcessEngineConfiguration。
We can get the ProcessEngine using ProcessEngineConfiguration, in some ways, which we’ll discuss further in this article. Through the ProcessEngine we can perform the Workflow and BPMN operations.
我们可以使用ProcessEngineConfiguration获得ProcessEngine,在某些方面,我们将在本文中进一步讨论。通过ProcessEngine我们可以执行工作流和BPMN操作。
2. Maven Dependencies
2.Maven的依赖性
To use this API, we need to include the Activiti dependency:
为了使用这个API,我们需要包括Activiti的依赖性。
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
</dependency>
3. Creating a ProcessEngine
3.创建一个ProcessEngine
ProcessEngine in Activiti, is typically configured using an XML file, activiti.cfg.xml. An example of this configuration file is:
Activiti中的ProcessEngine,通常使用XML文件activiti.cfg.xml进行配置。这个配置文件的一个例子是。
<beans xmlns="...">
<bean id="processEngineConfiguration" class=
"org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl"
value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
Now we can obtain the ProcessEngine using the ProcessEngines class:
现在我们可以使用ProcessEngine类获得ProcessEngines。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
This statement will look for an activiti.cfg.xml file in the classpath, and construct a ProcessEngine based on the configuration in the file.
该语句将在classpath中寻找一个activiti.cfg.xml文件,并基于该文件中的配置构建一个ProcessEngine。
The sample code for the configuration file shows that it is just a Spring-based configuration. But, this does not mean that we can only use Activiti in a Spring environment. The capabilities of Spring are just used internally to create the ProcessEngine.
配置文件的示例代码显示,它只是一个基于Spring的配置。但是,这并不意味着我们只能在Spring环境下使用Activiti。Spring的能力只是在内部用于创建ProcessEngine。
Let’s write a JUnit test case which will create the ProcessEngine using the configuration file shown above:
让我们写一个JUnit测试案例,它将使用上面显示的配置文件创建ProcessEngine。
@Test
public void givenXMLConfig_whenGetDefault_thenGotProcessEngine() {
ProcessEngine processEngine
= ProcessEngines.getDefaultProcessEngine();
assertNotNull(processEngine);
assertEquals("root", processEngine.getProcessEngineConfiguration()
.getJdbcUsername());
}
4. Activiti Process Engine API and Services
4.Activiti Process Engine API and Services
The entry point of interaction with the API is the ProcessEngine. Through the ProcessEngine, we can access various services that provide workflow/BPMN methods. The ProcessEngine and all the service objects are thread safe.
与API互动的入口是ProcessEngine。通过ProcessEngine,我们可以访问提供工作流/BPMN方法的各种服务。ProcessEngine和所有的服务对象是线程安全的。
The ProcessEngines class will scan for the activiti.cfg.xml and activiti-context.xml files. As mentioned earlier, for all the activiti.cfg.xml files, the ProcessEngine will be created in a typical way.
ProcessEngines类将扫描activiti.cfg.xml和activiti-text.xml文件。如前所述,对于所有activiti.cfg.xml文件,ProcessEngine将以典型的方式创建。
Whereas, for all the activiti-context.xml files, it will be created in the Spring way — I’ll create the Spring Application Context and will obtain the ProcessEngine from that. During the execution of a process, all the steps will be visited in the order that is defined in the BPMN file.
而对于所有的activiti-context.xml文件,它将以Spring的方式创建–我将创建Spring应用上下文并从中获取ProcessEngine。在一个流程的执行过程中,所有的步骤都将按照BPMN文件中定义的顺序被访问。
During the execution of a process, all the steps will be visited in the order that is defined in the BPMN file.
在一个流程的执行过程中,所有的步骤将按照BPMN文件中定义的顺序被访问。
4.1. Process Definition and Related Terms
4.1.过程定义和相关术语
A ProcessDefinition represents a business process. It’s used to define the structure and behavior of different steps in the process. Deploying a process definition means loading the process definition into the Activiti database.
一个ProcessDefinition代表一个业务流程。它被用来定义流程中不同步骤的结构和行为。部署一个流程定义意味着将流程定义加载到Activiti数据库中。
Process definitions are mostly defined by the BPMN 2.0 standard. It’s also possible to define them using Java code. All the terms defined in this section are available as Java classes as well.
流程定义大多由BPMN 2.0标准定义。也可以用Java代码来定义它们。本节中定义的所有术语也可以作为Java类使用。
Once we start running a process definition, it can be referred to as a process
一旦我们开始运行一个进程定义,它就可以被称为一个进程
A ProcessInstance is one execution of a ProcessDefinition.
一个ProcessInstance是一个ProcessDefinition的执行。。
A StartEvent is associated with every business process. It indicates the entry point of the process. Similarly, there is an EndEvent which indicates the end of the process. We can define conditions over these events.
一个StartEvent与每个业务流程相关联。同样地,还有一个EndEvent,它表示流程的结束。我们可以在这些事件上定义条件。
All the steps (or elements) in between the start and end are referred to as Tasks. Tasks can be of various types. The most commonly used tasks are UserTasks and ServiceTasks.
在开始和结束之间的所有步骤(或元素)被称为Tasks。任务可以是各种类型。最常用的任务是UserTasks和ServiceTasks。
UserTasks, as the name suggests, are such that they need to be carried out manually by a user.
UserTasks,顾名思义,是指需要由用户手动执行的任务。
ServiceTasks, on the other hand, are configured with a piece of code. Whenever the execution reaches them, their block of code will be executed.
另一方面,ServiceTasks是用一段代码配置的。每当执行到他们时,他们的代码块将被执行。
SequenceFlows connect the Tasks. We can define the SequenceFlows by the source and target elements that they’ll connect. Again, we can also define conditions over the SequenceFlows to create conditional paths in the process.
SequenceFlows连接Tasks。我们可以通过它们要连接的源和目标元素来定义SequenceFlows。同样,我们也可以在SequenceFlows上定义条件,在这个过程中创建条件路径。
4.2. Services
4.2.服务
We’ll discuss in brief the services provided by Activiti:
我们将简要地讨论Activiti提供的服务。
- RepositoryService helps us manipulate the deployment of process definitions. This service deals with the static data related to a process definition
- RuntimeService manages the ProcessInstances (currently running processes) as well as the process variables
- TaskService keeps track of the UserTasks. The Tasks that need to be carried out manually by a user are at the core of the Activiti API. We can create a task, claim and complete a task, manipulate the assignee of the task, etc. using this service
- FormService is an optional service. The API can be used without it, and without sacrificing any of its features. It is used to define the start form and task form in a process.
- IdentityService manages the Users and Groups
- HistoryService keeps track of the history of Activiti Engine. We can also set different history levels.
- ManagementService is related to the metadata and usually not required when creating an application
- DynamicBpmnService helps us to change anything in a process without redeploying it
5. Working With Activiti Services
5.与Activiti服务合作
To learn how we can work with different services and run a process, let’s take an example of a process for “Employee vacation request”:
为了学习我们如何与不同的服务合作并运行一个流程,让我们以 “雇员休假申请 “的流程为例。
The BPMN 2.0 file, VacationRequest.bpmn20.xml, for this process will have the start event defined as:
该流程的BPMN 2.0文件,VacationRequest.bpmn20.xml,将把开始事件定义为。
<startEvent id="startEvent" name="request"
activiti:initiator="employeeName">
<extensionElements>
<activiti:formProperty id="numberOfDays"
name="Number of days" type="long" required="true"/>
<activiti:formProperty id="startDate"
name="Vacation start date (MM-dd-yyyy)" type="date"
datePattern="MM-dd-yyyy hh:mm" required="true"/>
<activiti:formProperty id="reason" name="Reason for leave"
type="string"/>
</extensionElements>
</startEvent>
Similarly, the first user task, assigned to the user group “management”, will look like this:
同样,第一个用户任务,分配给用户组 “管理”,将看起来像这样。
<userTask id="handle_vacation_request" name=
"Handle Request for Vacation">
<documentation>${employeeName} would like to take ${numberOfDays} day(s)
of vacation (Motivation: ${reason}).</documentation>
<extensionElements>
<activiti:formProperty id="vacationApproved" name="Do you approve
this vacation request?" type="enum" required="true"/>
<activiti:formProperty id="comments" name="Comments from Manager"
type="string"/>
</extensionElements>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>management</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
With the ServiceTask, we need to define the piece of code to be executed. We have this piece of code as a Java class:
通过ServiceTask,我们需要定义要执行的那段代码。我们将这段代码作为一个Java类。
<serviceTask id="send-email-confirmation" name="Send email confirmation"
activiti:class=
"com.example.activiti.servicetasks.SendEmailServiceTask.java">
</serviceTask>
The conditional flow will be shown by adding the “conditionExpression” tag in the “sequenceFlow”:
通过在“sequenceFlow “中添加“conditionExpression”标签,将显示条件流:。
<sequenceFlow id="flow3" name="approved"
sourceRef="sid-12A577AE-5227-4918-8DE1-DC077D70967C"
targetRef="send-email-confirmation">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${vacationApproved == 'true'}]]>
</conditionExpression>
</sequenceFlow>
Here, vacationApproved is the formProperty of the UserTask shown above.
这里,vacationApproved是上面显示的UserTask的formProperty。
As we can see in the diagram, it is a very simple process. The employee makes a vacation request, providing the number of days and the start date of vacation. The request goes to the manager. They can approve/disapprove the request.
从图中我们可以看出,这是一个非常简单的过程。员工提出休假申请,提供休假天数和开始日期。该请求转给经理。他们可以批准/不批准该请求。
If approved, there is a Service task defined to send the confirmation email. If disapproved, the Employee can either select to modify and resend the request, or do nothing.
如果批准,有一个服务任务被定义为发送确认电子邮件。如果不被批准,雇员可以选择修改并重新发送请求,或者什么都不做。
Service tasks are provided with some piece of code to execute (here, as a Java class). We have given the class SendEmailServiceTask.java.
服务任务被提供了一些要执行的代码(这里是一个Java类)。我们已经给了这个类SendEmailServiceTask.java。
These types of classes should extend the JavaDelegate. Also, we need to override its execute() method, which will be performed when the process execution reaches this step.
这些类型的类应该扩展JavaDelegate。此外,我们需要覆盖其execute()方法,当流程执行到这一步时,将执行该方法。
5.1. Deploying a Process
5.1.部署一个流程
To make our process known to the Activiti Engine, we need to deploy the process. We can do it programmatically using the RepositoryService. Let’s write a JUnit test to show this:
为了让Activiti引擎知道我们的流程,我们需要部署该流程。我们可以使用RepositoryService以编程方式完成。让我们写一个JUnit测试来展示这个。
@Test
public void givenBPMN_whenDeployProcess_thenDeployed() {
ProcessEngine processEngine
= ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService
= processEngine.getRepositoryService();
repositoryService.createDeployment()
.addClasspathResource(
"org/activiti/test/vacationRequest.bpmn20.xml")
.deploy();
Long count=repositoryService.createProcessDefinitionQuery().count();
assertEquals("1", count.toString());
}
Deployment means that the engine will parse the BPMN file and convert it into something executable. Also, a record will be added to the Repository table for every deployment.
部署意味着引擎将解析BPMN文件并将其转换为可执行的东西。另外,每次部署都会有一条记录被添加到存储库表中。
Hence, afterward, we can query the Repository service to get the deployed processes; the ProcessDefinitions.
因此,之后我们可以查询Repository服务,以获得部署的进程;ProcessDefinitions。
5.2. Starting a ProcessInstance
5.2.启动一个ProcessInstance
After deploying the ProcessDefinition to Activiti Engine, we can execute the process by creating ProcessInstances. The ProcessDefinition is a blueprint, and the ProcessInstance is the runtime execution of it.
在将ProcessDefinition部署到Activiti Engine后,我们可以通过创建ProcessInstances来执行该流程。ProcessDefinition是一个蓝图,而ProcessInstance是它的运行时执行。
For a single ProcessDefinition, there can be multiple ProcessInstances.
对于一个ProcessDefinition,可以有多个ProcessInstances。
All the details related to the ProcessInstances can be accessed through the RuntimeService.
所有与ProcessInstances相关的细节都可以通过RuntimeService访问。
In our example, at the start event, we need to pass the number of vacation days, the start date, and the reason. We will use the process variables, and pass them while creating the ProcessInstance.
在我们的例子中,在开始事件中,我们需要传递休假天数、开始日期和原因。我们将使用流程变量,并在创建ProcessInstance时传递它们。
Let’s write a JUnit test case to get a better idea:
让我们写一个JUnit测试用例以获得更好的理解。
@Test
public void givenDeployedProcess_whenStartProcessInstance_thenRunning() {
//deploy the process definition
Map<String, Object> variables = new HashMap>();
variables.put("employeeName", "John");
variables.put("numberOfDays", 4);
variables.put("vacationMotivation", "I need a break!");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("vacationRequest", variables);
Long count=runtimeService.createProcessInstanceQuery().count();
assertEquals("1", count.toString());
}
The multiple instances of a single process definition will differ by the process variables.
一个流程定义的多个实例将因流程变量而不同。
There are multiple ways to start a process instance. Here, we are using the key of the process. After starting the process instance, we can get the information about it by querying the RuntimeService.
有多种方法来启动一个进程实例。这里,我们使用的是进程的键。启动进程实例后,我们可以通过查询RuntimeService来获得有关它的信息。
5.3. Completing Tasks
5.3.完成任务
When our process instance starts running, the first step is a user task, assigned to the user group “management”.
当我们的进程实例开始运行时,第一步是一个用户任务,分配给用户组“管理”。。
The user might have an inbox that would have a list of tasks to be done by them. Now, if we want to continue the process execution, the user needs to finish this task. For Activiti Engine, it’s called “completing the task”.
用户可能有一个收件箱,里面有一个有待他们完成的任务列表。现在,如果我们想继续执行流程,用户需要完成这个任务。对于Activiti Engine来说,这被称为 “完成任务”。
We can query the TaskService, to get the task object and then complete it.
我们可以查询TaskService,以获得任务对象,然后完成它。
The code we need to write for this looks like:
我们需要为此编写的代码看起来像。
@Test
public void givenProcessInstance_whenCompleteTask_thenGotNextTask() {
// deploy process and start process instance
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateGroup("management").list();
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("comments", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);
Task currentTask = taskService.createTaskQuery()
.taskName("Modify vacation request").singleResult();
assertNotNull(currentTask);
}
Note that the complete() method of TaskService also takes in the required process variables. We pass in the reply from the manager.
请注意,TaskService的complete()方法也接收了所需的过程变量。我们传入管理器的回复。
After this, the process engine will continue to the next step. Here, the next step asks the employee if the vacation request is to be re-sent or not.
在这之后,流程引擎将继续进行下一步。在这里,下一步会询问员工是否要重新发送休假申请。
So, our ProcessInstance is now waiting at this UserTask, which has the name “Modify vacation request”.
因此,我们的ProcessInstance现在正在等待这个UserTask,它的名字是“Modify vacationrequest”。
5.4. Suspending and Activating a Process
5.4.暂停和激活一个进程
We can suspend a ProcessDefinition and also a ProcessInstance. If we suspend a ProcessDefinition, we cannot create an instance of it while it is suspended. We can do this using the RepositoryService:
我们可以暂停一个ProcessDefinition,也可以暂停一个ProcessInstance。如果我们暂停一个ProcessDefinition,我们不能在它被暂停时创建它的实例。我们可以使用RepositoryService:来做到这一点。
@Test(expected = ActivitiException.class)
public void givenDeployedProcess_whenSuspend_thenNoProcessInstance() {
// deploy the process definition
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
runtimeService.startProcessInstanceByKey("vacationRequest");
}
To activate it again, we just need to call one of the repositoryService.activateProcessDefinitionXXX methods.
要再次激活它,我们只需要调用repositoryService.activateProcessDefinitionXXX方法之一。
Similarly, we can suspend a ProcessInstance, using the RuntimeService.
同样地,我们可以暂停一个ProcessInstance,使用RuntimeService。
6. Conclusion
6.结论
In this article, we saw how we could use Activiti with Java. We created a sample ProcessEngineCofiguration file, which helps us to create the ProcessEngine.
在这篇文章中,我们看到了如何用Java来使用Activiti。我们创建了一个样本ProcessEngineCofiguration文件,它帮助我们创建ProcessEngine。
Using it, we accessed various services provided by the API. These services help us to manage and keep track of ProcessDefinitions, ProcessInstances, UserTasks, etc.
使用它,我们访问了由API提供的各种服务。这些服务帮助我们管理和跟踪ProcessDefinitions、ProcessInstances、UserTasks等。
As always, the code for examples we saw in the article lies over on GitHub.
一如既往,我们在文章中看到的例子的代码位于GitHub上。