Хабрахабр

Activiti — Business process engine

Activiti framework (Java) — описание потока задач на XML (bpm) и управление этим процессом. Здесь опишу основные базовые понятия и как строить простые бизнес процессы.
Основное понятие Activiti это процесс (process) и задача (task). Процесс это все задачи связанные между собой направленными потоками и ветвлениями.
Затрону такие аспекты:

  • — Activiti в чистом виде
  • — Пользователи, Роли
  • — Подключение SpringBoot
  • — REST API
  • — Job и Delegate

Движение по потокам идет шагами от задачи к задаче, каждый такой шаг приостанавливает выполнение процесса ожидая входных данных и выполнения задачи, все промежуточные действия сохраняются в базу данных.
Где, что брать укажу ниже. Начнем с простого примера — процесс разработки программы, который состоит из написания кода и тестирования. Ниже диаграмма процесса.
image
Вот это все есть процесс, он имеет ИД, Имя и др характеристики.
image
В нем есть:
Начало процесса, две задачи «Develop» и «Test», одно ветвление (gateway) и окончание процесса. В словах все происходит так:
— загружаем описание bpm,
— стартуем процесс
— после старта сразу попадаем в задачу Develop
— после выполнения Develop она переходит в тестирование и по результату тестирования процесс завершается либо возвращается опять на разработку.
Activiti состоит из некоторого набора сервисов
Вот основные:
— RepositoryService: управляет загрузкой описания процессов
— RuntimeService: запускает процессы
— TaskService: выполняет задачи
— FormService: доступ к переменным задачи
— HistoryService: доступ к истории выполнения процесса
— IdentityService: Пользователи и Роли

Activiti в чистом виде

Но начинается все с конфигурации и файла — activiti.cfg.xml
Вот с этого

ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml");

Если не использовать свою конфигурацию, то Activiti сам развернет базу данных в памяти H2, меня это не устраивает, а вот мой любимый Oracle вполне, возможности подключения разных БД есть.
Вот моя конфигурация

activiti.cfg.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" /> <property name="jdbcDriver" value="oracle.jdbc.driver.OracleDriver" /> <property name="jdbcUsername" value="BPM" /> <property name="jdbcPassword" value="1" /> <!-- Database configurations --> <property name="databaseSchemaUpdate" value="false" /> <property name="asyncExecutorActivate" value="false" /> <!-- mail server configurations --> <property name="mailServerPort" value="5025" /> </bean>
</beans>

Меняем значения в «property name=jdbc * » и подключаем другую БД
Структура проекта

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>DemoActiviti</groupId> <artifactId>DemoActiviti</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.activiti</groupId> <version>6.0.0</version> <artifactId>activiti-spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0</version> </dependency> </dependencies> <build> <plugins> <!-- Maven Assembly Plugin --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4.1</version> <configuration> <!-- get all project dependencies --> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <!-- MainClass in mainfest make a executable jar --> <archive> <manifest> <mainClass>com.example.DemoActiviti</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <!-- bind to the packaging phase --> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <target>1.8</target> </configuration> </plugin> </plugins> </build>
</project>

0-SNAPSHOT-jar-with-dependencies.jar
jdbc driver для Oracle установил в локальный maven репозиторий
mvn install:install-file -Dfile=
-DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11. Наличие в POM плагина «maven-assembly-plugin», позволит собирать (package) запускаемый jar с зависимостями и запускать — java -jar DemoActiviti-1. 0 -Dpackaging=jar

2.

log4j

ACT=org.apache.log4j. log4j.rootLogger=WARN, ACT
log4j.appender. ACT.layout=org.apache.log4j. ConsoleAppender
log4j.appender. ACT.layout. PatternLayout
log4j.appender. ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

Каждое действие будет иметь соответствующий параметр: deploy, start, develop, test.
Скрипты для базы данных берем из
activiti-get-started
там в папке \activiti-6. Для этого процесса определим 4-ре действия: загрузка bpm, старт процесса, разработка и тестирование. 0\activiti-6. 0. 0\database\create — скрипты для создания БД
0.

Пользователи, Роли

Подготовим пользователей и роли:

Identity


public class DemoActiviti { private static final String DEV_PROCESS = "devProcess"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); createIdentity(processEngine, "programmer", "programmers"); createIdentity(processEngine, "tester", "testers"); } public static void createIdentity(ProcessEngine processEngine, String userName, String userGroup) { IdentityService identityService = processEngine.getIdentityService(); String userId = userName + "Id"; if (identityService.createUserQuery().userId(userId).count() == 0) { User user = identityService.newUser(userName); user.setId(userId); user.setEmail(userName + "@gmail.com"); identityService.saveUser(user); System.out.println("user created success fully"); } String groupId = userGroup + "Id"; if (identityService.createGroupQuery().groupId(groupId).count() == 0) { Group group = identityService.newGroup(userGroup); group.setName(userGroup); group.setId(groupId); identityService.saveGroup(group); System.out.println("group created success fully"); } if (identityService.createGroupQuery().groupId(groupId).list().size() > 0) { identityService.createMembership(userId, groupId); System.out.println("user to group success fully"); } }
}

Создадим пользователей и группы, разработчик и тестировщик соответственно.
В базе данных все таблицы разделены по соответствующим сервисам и имеют префиксы
ACT_RE_*: repository.
ACT_RU_*: runtime.
ACT_ID_*: identity.
ACT_HI_*:history
и пр.
После создания пользователей из можно посмотреть здесь
image
Наши задачи в описании назначим соответствующим группам (CandidateGroup), так например задачу Develop группе — programmers

image

0-SNAPSHOT-jar-with-dependencies.jar deploy
далее стартуем процесс start
java -jar DemoActiviti-1. И так первое что делаем размещаем в БД «MyProcess.bpmn», запускаем программу с командой deploy
java -jar DemoActiviti-1. 0-SNAPSHOT-jar-with-dependencies.jar start
После delpoy и start процесса в базе появятся соответствующие записи.
Репозиторий
image
Runtime, какая задача на исполнении
image
кому назначена
image
В коде это выглядит так (полный код будет ниже):
deploy
deployment = repositoryService.createDeployment()
.addClasspathResource("processes/MyProcess.bpmn").deploy()

start
ProcessInstance myProcess = runtimeService.startProcessInstanceByKey(DEV_PROCESS);
develop

0-SNAPSHOT-jar-with-dependencies.jar develop
// Задачи для разработчика
tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list();

В задаче Develop определена одна переменная «issue»
image После этого можно приступить к выполнения задачи на разработку
java -jar DemoActiviti-1.

После обработки переменных с помощью FormService, задача выполняется

for (Task task : tasks) { System.out.println("Task:" + task.getTaskDefinitionKey() + ", id=" + task.getId()); FormData formData = formService.getTaskFormData(task.getId()); Map<String, Object> variables = new HashMap<String, Object>(); // переменные задачи for (FormProperty formProperty : formData.getFormProperties()) { System.out.println("Enter varName <" + formProperty.getName() +">:"); String value = scanner.nextLine(); variables.put(formProperty.getId(), value); } // выполняем задачу taskService.complete(task.getId(), variables); System.out.println("Task complete success:" + task.getTaskDefinitionKey()); }

image
Для задачи Develop будет предложено ввести переменную.
В историчной таблице можно увидеть переменные и значения задачи, процесса
image
Таким образом процесс после задачи Develop остановится на ней, состояние будет сохранено в базе.
В общем цикл выглядит так:
Запросить задачу на исполнителя
tasks = taskService.createTaskQuery().taskCandidateGroup("...").list();
Определение переменных
Map<String, Object> variables = new HashMap<String, Object>();
...
variables.put("var_1", value);

Исполнение задачи
taskService.complete(task.getId(), variables);
Проверка окончания процесса
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
if (processInstance != null && !processInstance.isEnded())

После каждого выполнения задачи, процесс приостанавливается, до выполнения новой задачи.
Так после выполнения Develop, перейдем к задаче Test, тут тоже будет предложено ввести переменную «devResult» — результат разработки (получилось не совсем корректно, еще до начала Test, вводим результат), а далее по результату будет будет ветвление или окончание (Ok) или опять на разработку (No), см. схему процесса.
image
В этом случае на разработку, и.т.д. Если теперь запросить задачи на разработчика, то они будут, а на тестирование — нет.

Код программы


package com.example; import org.activiti.engine.*;
import org.activiti.engine.form.FormData;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils; import java.util.*; public class DemoActiviti { private static final String DEV_PROCESS = "devProcess"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); String mode = StringUtils.EMPTY; if (args.length > 0) { mode = args[0]; } System.out.println("Processes mode: " + mode); Deployment deployment; if ("deploy".equals(mode)) { deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcess.bpmn").deploy(); System.out.println("deploy process success"); System.exit(0); } else { List<Deployment> myProcesses = repositoryService.createDeploymentQuery() .processDefinitionKey(DEV_PROCESS).list(); deployment = myProcesses.get(myProcesses.size()-1); System.out.println("get process success:" + deployment.getId()); } // RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance; if ("start".equals(mode)){ ProcessInstance myProcess = runtimeService.startProcessInstanceByKey(DEV_PROCESS); System.out.println("start process success:" + myProcess.getName() +", id="+ myProcess.getId()); System.exit(0); } processInstance = runtimeService.createProcessInstanceQuery().deploymentId(deployment.getId()).singleResult(); TaskService taskService = processEngine.getTaskService(); FormService formService = processEngine.getFormService(); List<Task> tasks = new ArrayList<>(); if ("develop".equals(mode)) { System.out.println("develop mode"); // получить задачи для разработчика tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list(); if (tasks.isEmpty()) { System.out.println("Задач на разработку нет"); System.exit(0); } } if ("test".equals(mode)) { System.out.println("test mode"); // получить задачи для тестирования tasks = taskService.createTaskQuery().taskCandidateGroup("testers").list(); if (tasks.isEmpty()) { System.out.println("Задач на тестирование нет"); System.exit(0); } } Scanner scanner = new Scanner(System.in); if (processInstance != null && !processInstance.isEnded()) { System.out.println("tasks count: [" + tasks.size() + "]"); for (Task task : tasks) { System.out.println("Task:" + task.getTaskDefinitionKey() + ", id=" + task.getId()); FormData formData = formService.getTaskFormData(task.getId()); Map<String, Object> variables = new HashMap<String, Object>(); // переменные задачи for (FormProperty formProperty : formData.getFormProperties()) { System.out.println("Enter varName <" + formProperty.getName() +">:"); String value = scanner.nextLine(); variables.put(formProperty.getId(), value); } // выполняем задачу taskService.complete(task.getId(), variables); System.out.println("Task complete success:" + task.getTaskDefinitionKey()); } // Re-query the process instance, making sure the latest state is available //processInstance = runtimeService.createProcessInstanceQuery() // .processInstanceId(processInstance.getId()).singleResult(); } } }

Подключение SpringBoot

Модифицируем проект с использованием Spring
Добавляем в POM зависимости

POM с SpringBoot

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter-basic</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <version>6.0.0</version> <artifactId>activiti-spring-boot-starter-integration</artifactId> </dependency> ....

DemoActiviti класс теперь стал таким

DemoActiviti - SpringBootApplication


@SpringBootApplication
@ImportResource("classpath:activiti.cfg.xml")
public class DemoActiviti { public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); SpringApplication.run(DemoActiviti.class, args); }
}

Я использую смешанную модель — когда часть бинов описываются в xml конфигурации (@ImportResource(«classpath:activiti.cfg.xml»)), а другая определяется через аннотации.

activiti.cfg.xml - spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> <property name="driverClass" value="oracle.jdbc.driver.OracleDriver" /> <property name="username" value="BPM" /> <property name="password" value="1" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> <property name="databaseSchemaUpdate" value="true" /> <property name="asyncExecutorActivate" value="false" /> </bean> </beans>

Теперь за конфигурацию отвечает Spring, это видно
bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"

Добавим для SpringBoot стандартную обработку командной строки, в виде компоненты

CommandLine


@Component
public class CommandLine implements CommandLineRunner { @Autowired private DemoService demoService; public void run(String... args) { if ("test".equals(args[0])) { demoService.startTest(); } else if ("develop".equals(args[0])) { demoService.startDevelop(); } }
}

Который обработает все те команды, я не буду их все реализовывать, там все просто, покажу две: test и develop. И добавим сервис для их обработки

DemoService


@Service
public class DemoService { @Autowired private TaskService taskService; @Autowired private FormService formService; public void startTest() { List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("testers").list(); if (tasks.isEmpty()) { System.out.println("Задач на тестирование нет"); return; } processTasks(tasks); } public void startDevelop() { List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("develop").list(); if (tasks.isEmpty()) { System.out.println("Задач на разработку нет"); return; } processTasks(tasks); } private void processTasks(List<Task> tasks) { Scanner scanner = new Scanner(System.in); for (Task task : tasks) { ...... тут как и ранее, выше }

В компоненте CommandLine Autowir-им сервис DemoService, а в нем уже подготовленные Spring сервисы Activiti
@Autowired
private TaskService taskService;

Собираем, запускаем как и ранее из командной строки.
Если хотим использовать выполнение задач из Web, то подключаем REST API

REST API

SpringBoot по умолчанию предоставит embedded Tomcat сервер, а далее дело техники.
В POM, к тому что есть, добавляем spring web зависимость

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

CommandLine компонент удаляем, теперь все будет поступать через URL по HTTP. Добавляем RestController

RestController


@RestController
public class DemoRestController { @Autowired private DemoService demoService; @RequestMapping(value="/test", method= RequestMethod.GET, produces= {MediaType.APPLICATION_JSON_VALUE}) public List<String> startTest(@RequestParam String devResult) { List<String> strings = demoService.startTest(devResult); return strings; } @RequestMapping(value="/develop", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE) public List<String> startDevelop(@RequestParam String issue) { List<String> strings = demoService.startDevelop(issue); return strings; } @RequestMapping(value="/start", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE) public List<String> startProcess() { List<String> strings = demoService.startDevProcess(); return strings; }
}

Те же команды выполняем, немного изменил ответы сервиса DemoService-а, который Autowire-тся в контроллере.

DemoService


@Service
public class DemoService { @Autowired private TaskService taskService; @Autowired private FormService formService; @Autowired private RuntimeService runtimeService; public List<String> startTest(String devResult) { List<String> results = new ArrayList<>(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("testers").list(); if (tasks.isEmpty()) { results.add("The tasks for testing are not"); return results; } Object issue = runtimeService.getVariables(tasks.get(0).getProcessInstanceId()).get("issue"); processTasks(tasks, devResult); results.add("Task N " + issue + " - tested, result=" + devResult); return results; } public List<String> startDevelop(String issue) { List<String> results = new ArrayList<>(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list(); if (tasks.isEmpty()) { results.add("There are no development tasks"); return results; } processTasks(tasks, issue); Object mIssue = runtimeService.getVariables(tasks.get(0).getProcessInstanceId()).get("issue"); results.add("Task N " + mIssue + " - taken in the develop"); return results; } public List<String> startDevProcess() { List<String> results = new ArrayList<>(); ProcessInstance myProcess = runtimeService.startProcessInstanceByKey("devProcess"); results.add("The process is started #"+myProcess.getId()); return results; } private void processTasks(List<Task> tasks, String param) { for (Task task : tasks) { FormData formData = formService.getTaskFormData(task.getId()); Map<String, Object> variables = new HashMap<>(); // переменные задачи for (FormProperty formProperty : formData.getFormProperties()) { variables.put(formProperty.getId(), param); } // выполняем задачу taskService.complete(task.getId(), variables); } }
}

тестируем с использованием curl, вот результат:
image
Порт для Tomcat я изменил на 8081 в application.properties
server.port=8081

Activiti Job

В Activiti много конструкций, так например запуск задач по расписанию это «TimerStartEvent». Для того что бы Job начал исполняться в конфинге надо указать
property name="asyncExecutorActivate" value="true" (см. activiti.cfg.xml), тогда java процесс останется запущенным и будет проверять расписание и запускать задачи.
Вернусь к начальному проекту, где используется Activiti в чистом виде.
В DemoActiviti классе оставлю поддержку только двух команд: deploy и start Сделаю новый процесс
image
После старта процесса он перейдет к таймеру который по расписанию будет запускать задачу «Develop». Расписание у таймера будет — запуск каждые 10 сек., cron выражение — «0/10 * * * * ?».
image
Сделаем deploy нового процесса как и ранее, затем стартуем процесс (start). Все — задача выполняется каждые 10 сек.
В качестве задачи выбрана компонента Activiti — ServiceTask, у которого можно указать в качестве реализации Java class
image

класс DemoDelegate


public class DemoDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) { Date now = new Date(); execution.setVariable("issue", now.toString()); System.out.println("job start="+now); }
}

В таблице в базе (select * from ACT_RU_TIMER_JOB t) можно видеть
image
активность Job-а, в поле DUEDATE_ будет время следующего запуска.
В истории исполнения будет фиксироваться переменная «issue» из Delegate
select * from ACT_HI_VARINST t
image

код для DemoActiviti c Job


public class DemoActiviti { private static final String DEV_PROCESS = "devProcessJob"; public static void main(String[] args) { Locale.setDefault(Locale.ENGLISH); ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml"); ProcessEngine processEngine = cfg.buildProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); String mode = StringUtils.EMPTY; if (args.length > 0) { mode = args[0]; } System.out.println("Processes mode: " + mode); Deployment deployment; if ("deploy".equals(mode)) { deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcessJob.bpmn").deploy(); System.out.println("deploy process success"); System.exit(0); } else { List<Deployment> myProcesses = repositoryService.createDeploymentQuery() .processDefinitionKey(DEV_PROCESS).list(); deployment = myProcesses.get(myProcesses.size()-1); System.out.println("get process success:" + deployment.getId()); } // RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance; if ("start".equals(mode)){ ProcessInstance myProcess = runtimeService.startProcessInstanceByKey(DEV_PROCESS); System.out.println("start process success:" + myProcess.getName() +", id="+ myProcess.getId()); } }
}

За бортом осталось еще многое: События, Listener, JPA и др., возможно к ним еще вернусь.

Материалы
Activiti
Eclipse Designer

devProcess bpmn

<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="devProcess" name="Dev process" isExecutable="true"> <startEvent id="startevent1" name="Start" activiti:initiator="programmerId"></startEvent> <userTask id="develop" name="Develop" activiti:candidateGroups="programmers"> <extensionElements> <activiti:formProperty id="issue" name="issue" type="string" required="true"></activiti:formProperty> </extensionElements> </userTask> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="develop"></sequenceFlow> <userTask id="test" name="Test" activiti:candidateGroups="testers"> <extensionElements> <activiti:formProperty id="devResult" name="devResult" type="string" default="No" required="true"></activiti:formProperty> </extensionElements> </userTask> <sequenceFlow id="flow2" sourceRef="develop" targetRef="test"></sequenceFlow> <exclusiveGateway id="gateway" name="Exclusive Gateway" default="flowNo"></exclusiveGateway> <sequenceFlow id="flow3" sourceRef="test" targetRef="gateway"></sequenceFlow> <sequenceFlow id="flowOk" name="Ok" sourceRef="gateway" targetRef="endevent1"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${devResult == "Ok"}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="flowNo" name="No" sourceRef="gateway" targetRef="develop"></sequenceFlow> <endEvent id="endevent1" name="End"></endEvent> </process> </definitions>

devProcessJob bpmn

<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="devProcessJob" name="Dev process Job" isExecutable="true"> <startEvent id="startevent" name="Start" activiti:initiator="programmerId"></startEvent> <sequenceFlow id="flow1" sourceRef="startevent" targetRef="timerstartevent"></sequenceFlow> <endEvent id="endevent" name="End"></endEvent> <startEvent id="timerstartevent" name="Timer start"> <extensionElements> <activiti:formProperty id="issue" name="issue" type="string"></activiti:formProperty> </extensionElements> <timerEventDefinition> <timeCycle>0/10 * * * * ?</timeCycle> </timerEventDefinition> </startEvent> <sequenceFlow id="flow2" sourceRef="timerstartevent" targetRef="servicetask1"></sequenceFlow> <sequenceFlow id="flow3" sourceRef="servicetask1" targetRef="endevent"></sequenceFlow> <serviceTask id="servicetask1" name="Develop" activiti:class="com.example.DemoDelegate"></serviceTask> </process> </definitions>

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»