Ръководство за Activiti с Java

1. Общ преглед

API на Activiti е система за управление на работния процес и бизнес процесите. Можем да дефинираме процес в него, да го изпълним и да го манипулираме по различни начини, използвайки услугите, предоставяни от API. Изисква JDK 7+.

Разработката с помощта на API може да се направи във всяка IDE, но за да използваме Activiti Designer, ни трябва Eclipse.

Можем да определим процес в него, използвайки стандарта BPMN 2.0. Има и друг, по-малко популярен начин - използване на Java класове като StartEvent , EndEvent , UserTask , SequenceFlow и т.н.

Ако искаме да стартираме процес или да получим достъп до някоя от услугите, трябва да създадем ProcessEngineConfiguration .

Можем да получим ProcessEngine, използвайки ProcessEngineConfiguration, по някои начини, които ще обсъдим по-нататък в тази статия . Презна ProcessEngine можем да извършва операциите Workflow и BPMN.

2. Зависимости на Maven

За да използваме този API, трябва да включим зависимостта Activiti:

 org.activiti activiti-engine 

3. Създаване на ProcessEngine

ProcessEngine в Activiti, обикновено се конфигурира с помощта на XML файл activiti.cfg.xml . Пример за този конфигурационен файл е:

Сега можем да получим ProcessEngine, използвайки клас ProcessEngines :

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

Това изявление ще търси activiti.cfg. xml файл в пътя на класа и конструирайте ProcessEngine въз основа на конфигурацията във файла.

Примерният код за конфигурационния файл показва, че той е просто базирана на Spring конфигурация. Но това не означава, че можем да използваме Activiti само в пролетна среда. Възможностите на Spring просто се използват вътрешно за създаване на ProcessEngine .

Нека напишем JUnit тестов случай, който ще създаде ProcessEngine, използвайки конфигурационния файл, показан по-горе:

@Test public void givenXMLConfig_whenGetDefault_thenGotProcessEngine() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); assertNotNull(processEngine); assertEquals("root", processEngine.getProcessEngineConfiguration() .getJdbcUsername()); } 

4. API и услуги на Activiti Process Engine

Входната точка на взаимодействие с API е ProcessEngine . Чрез ProcessEngine можем да осъществим достъп до различни услуги, които предоставят работни потоци / BPMN методи. В ProcessEngine и всички обекти на службата са безопасни нишка.

Взето от //www.activiti.org/userguide/images/api.services.png

Класът ProcessEngines ще сканира за файлове activiti.cfg.xml и activiti-context.xml . Както бе споменато по-рано, за всички файлове activiti.cfg.xml ProcessEngine ще бъде създаден по типичен начин.

Докато за всички файлове activiti-context.xml той ще бъде създаден по пролетния начин - аз ще създам контекста на пролетното приложение и ще получа ProcessEngine от това. По време на изпълнението на даден процес всички стъпки ще бъдат посетени в реда, определен в BPMN файла.

По време на изпълнението на даден процес всички стъпки ще бъдат посетени в реда, определен в BPMN файла.

4.1. Определение на процеса и свързани термини

Определението на процеса представлява бизнес процес. Използва се за определяне на структурата и поведението на различните стъпки в процеса. Разполагането на дефиниция на процес означава зареждане на дефиницията на процеса в базата данни Activiti.

Определенията на процесите се определят предимно от стандарта BPMN 2.0. Възможно е също така да ги дефинирате с помощта на Java код. Всички термини, дефинирани в този раздел, са достъпни и като Java класове.

След като започнем да изпълняваме определение на процес, то може да бъде наречено процес

А ProcessInstance е едно изпълнение на ProcessDefinition.

А StartEvent се свързва с всеки бизнес процес. Той посочва входната точка на процеса. По същия начин има EndEvent, който указва края на процеса. Можем да определим условия за тези събития.

Всички стъпки (или елементи) между началото и края се наричат Задачи . Задачите могат да бъдат от различен тип. Най-често използваните задачи са UserTasks и ServiceTasks .

Потребителските задачи , както подсказва името, са такива, че трябва да се изпълняват ръчно от потребител.

ServiceTasks , от друга страна, са конфигурирани с парче код. Винаги, когато изпълнението достигне до тях, техният блок код ще бъде изпълнен.

SequenceFlows свързват задачите . Можем да определим SequenceFflow от елементите източник и цел, които те ще свържат. Отново можем да дефинираме условия и над SequenceFlow, за да създадем условни пътеки в процеса.

4.2. Услуги

Ще обсъдим накратко услугите, предоставяни от Activiti:

  • RepositoryService ни помага да манипулираме внедряването на дефиниции на процеси. Тази услуга се занимава със статични данни, свързани с дефиниция на процес
  • RuntimeService управлява ProcessInsistance (текущите процеси), както и променливите на процеса
  • TaskService следи UserTasks . На Задачи , които трябва да се извършва ръчно от потребителя, са в основата на API Activiti. Можем да създадем задача, да заявим и изпълним задача, да манипулираме правомощица на задачата и т.н., използвайки тази услуга
  • FormService е незадължителна услуга. API може да се използва без него и без да се жертва някоя от неговите функции. Използва се за определяне на стартовата форма и формуляра на задачата в даден процес.
  • IdentityService управлява потребителите и групите
  • HistoryService проследява историята на Activiti Engine. Също така можем да зададем различни нива на историята.
  • ManagementService е свързан с метаданните и обикновено не се изисква при създаване на приложение
  • DynamicBpmnService ни помага да променим нещо в даден процес, без да го преразпределяме

5. Работа с услугите на Activiti

За да научим как можем да работим с различни услуги и да стартираме процес, нека вземем пример за процес за „Искане за ваканция на служител“:

Файлът BPMN 2.0, VacationRequest.bpmn20.xml , за този процес ще има начално събитие, дефинирано като:

По същия начин първата потребителска задача, възложена на потребителската група „управление“, ще изглежда така:

 ${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${reason}).       management   

С ServiceTask трябва да дефинираме частта от кода, която да бъде изпълнена. Имаме тази част от кода като Java клас:

Условният поток ще бъде показан чрез добавяне на тага “conditionExpression” в “sequenceFlow”:

Тук vacationApproved е formProperty на UserTask показан по-горе.

Както можем да видим на диаграмата, това е много прост процес. Служителят отправя искане за ваканция, като посочва броя дни и началната дата на ваканцията. Искането отива при мениджъра. Те могат да одобрят / неодобрят искането.

Ако бъде одобрена, има определена задача за обслужване, която да изпрати имейл за потвърждение. Ако не бъде одобрен, Служителят може да избере да модифицира и изпрати повторно заявката, или да не предприеме нищо.

Задачите за обслужване са снабдени с част от кода за изпълнение (тук като 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.

5.1. Deploying a Process

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:

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

Hence, afterward, we can query the Repository service to get the deployed processes; the ProcessDefinitions.

5.2. Starting a 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.

For a single ProcessDefinition, there can be multiple ProcessInstances.

All the details related to the ProcessInstances can be accessed through the 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.

Let's write a JUnit test case to get a better idea:

@Test public void givenDeployedProcess_whenStartProcessInstance_thenRunning() { //deploy the process definition Map 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.

5.3. Completing Tasks

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

We can query the TaskService, to get the task object and then complete it.

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 tasks = taskService.createTaskQuery() .taskCandidateGroup("management").list(); Task task = tasks.get(0); Map 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.

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

5.4. Suspending and Activating a Process

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:

@Test(expected = ActivitiException.class) public void givenDeployedProcess_whenSuspend_thenNoProcessInstance() { // deploy the process definition repositoryService.suspendProcessDefinitionByKey("vacationRequest"); runtimeService.startProcessInstanceByKey("vacationRequest"); } 

За да го активираме отново, просто трябва да извикаме един от методите repositoryService.activateProcessDefinitionXXX .

По подобен начин можем да спрем ProcessInstance, използвайки RuntimeService.

6. Заключение

В тази статия видяхме как можем да използваме Activiti с Java. Създадохме примерен файл ProcessEngineCofiguration , който ни помага да създадем ProcessEngine .

Използвайки го, имахме достъп до различни услуги, предоставяни от API. Тези услуги ни помагат да управляваме и следим ProcessDefinitions , ProcessInsistance , UserTasks и др.

Както винаги, кодът за примери, които видяхме в статията, лежи върху GitHub.