Activiti Framework (Java) - Beschreibung des XML-Taskflows (bpm) und seiner Verwaltung. Hier beschreibe ich die grundlegenden Konzepte und den Aufbau einfacher Geschäftsprozesse.
Das Hauptkonzept von Activiti ist ein Prozess und eine Aufgabe. Ein Prozess sind alle Aufgaben, die durch gerichtete Abläufe und Zweige miteinander verbunden sind.
Ich werde auf solche Aspekte eingehen:
- - Activiti in seiner reinsten Form
- - Benutzer, Rollen
- - SpringBoot anschließen
- - REST-API
- - Job und Delegierter
Die Bewegung der Flüsse erfolgt schrittweise von Aufgabe zu Aufgabe. Jeder dieser Schritte unterbricht den Prozess, während auf die Eingabe und den Abschluss der Aufgabe gewartet wird. Alle Zwischenaktionen werden in der Datenbank gespeichert.
Wo, was ich nehmen soll, werde ich unten angeben. Beginnen wir mit einem einfachen Beispiel - dem Programmentwicklungsprozess, der aus dem Schreiben von Code und dem Testen besteht. Unten ist ein Prozessdiagramm.

Dies ist alles ein Prozess, es hat eine ID, einen Namen und andere Eigenschaften.

Es hat:
Der Beginn des Prozesses, zwei Aufgaben "Entwickeln" und "Testen", ein Zweig (Gateway) und das Ende des Prozesses. In Worten, alles passiert so:
- Laden der BPM-Beschreibung
- Starten Sie den Prozess
- Nach dem Start fallen wir sofort in die Entwicklungsaufgabe
- Nach der Ausführung von Develop wird es getestet und gemäß dem Testergebnis endet der Prozess oder kehrt wieder zur Entwicklung zurück.
Activiti besteht aus einer Reihe von Diensten
Hier sind die wichtigsten:
- RepositoryService: Steuert das Laden von Prozessbeschreibungen
- RuntimeService: Startet Prozesse
- TaskService: Führt Aufgaben aus
- FormService: Zugriff auf Taskvariablen
- HistoryService: Zugriff auf den Prozessverlauf
- IdentityService: Benutzer und Rollen
Activiti in seiner reinsten Form
Aber alles beginnt mit der Konfiguration und Datei - activiti.cfg.xml.
Daraus
ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createProcessEngineConfigurationFromResource("activiti.cfg.xml");
Wenn Sie Ihre Konfiguration nicht verwenden, stellt Activiti die Datenbank im H2-Speicher selbst bereit. Dies passt nicht zu mir, aber mein geliebtes Oracle ist voll. Es gibt Optionen zum Verbinden verschiedener Datenbanken.
Hier ist meine Konfiguration
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" /> <property name="databaseSchemaUpdate" value="false" /> <property name="asyncExecutorActivate" value="false" /> <property name="mailServerPort" value="5025" /> </bean> </beans>
Wir ändern die Werte in "property name = jdbc *" und verbinden eine andere Datenbank
Projektstruktur

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> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4.1</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.example.DemoActiviti</mainClass> </manifest> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <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>
Das Vorhandensein des Plugins "Maven-Assembly-Plugin" im POM ermöglicht es Ihnen, ein JAR zu erstellen (zu verpacken), das mit Abhängigkeiten gestartet und ausgeführt wird -
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar
JDBC-Treiber für Oracle im lokalen Maven-Repository installiert
mvn install:install-file -Dfile={Path/to/your/ojdbc6.jar} -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0 -Dpackaging=jar
log4j log4j.rootLogger=WARN, ACT log4j.appender.ACT=org.apache.log4j.ConsoleAppender log4j.appender.ACT.layout=org.apache.log4j.PatternLayout log4j.appender.ACT.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
Für diesen Prozess definieren wir 4 Aktionen: BPM-Laden, Prozessstart, Entwicklung und Testen. Jede Aktion hat einen entsprechenden Parameter: Bereitstellen, Starten, Entwickeln, Testen.
Wir nehmen Skripte für eine Datenbank von
Activiti-Get-Starteddort im Ordner \ activiti-6.0.0 \ activiti-6.0.0 \ database \ create - Skripte zum Erstellen einer Datenbank
Benutzer, Rollen
Bereiten Sie Benutzer und Rollen vor:
Identität 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"); } } }
Erstellen Sie Benutzer und Gruppen, Entwickler bzw. Tester.
In der Datenbank werden alle Tabellen durch die entsprechenden Dienste unterteilt und haben Präfixe
ACT_RE_ *: Repository.
ACT_RU_ *: Laufzeit.
ACT_ID_ *: Identität.
ACT_HI _ *: Geschichte
und so weiter
Nach dem Erstellen von Benutzern aus können Sie hier sehen

Wir werden unsere Aufgaben in der Beschreibung den entsprechenden Gruppen (CandidateGroup) zuordnen, zum Beispiel die Aufgabe Entwickeln den Gruppenprogrammierern

Als erstes platzieren wir das Programm in der Datenbank "MyProcess.bpmn" und führen das Programm mit dem Befehl
deploy aus java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar deploy
Starten Sie als Nächstes den Startvorgang
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar start
Nach dem Delpoy- und Startvorgang werden die entsprechenden Einträge in der Datenbank angezeigt.
Repository

Laufzeit, welche Aufgabe ausgeführt wird

wem ist zugeordnet

Im Code sieht es so aus (der vollständige Code wird unten sein):
bereitstellen deployment = repositoryService.createDeployment() .addClasspathResource("processes/MyProcess.bpmn").deploy()
starten ProcessInstance myProcess = runtimeService.startProcessInstanceByKey(DEV_PROCESS);
entwickelnDanach können Sie mit der Fertigstellung der Entwicklungsaufgabe beginnen.
java -jar DemoActiviti-1.0-SNAPSHOT-jar-with-dependencies.jar develop
// tasks = taskService.createTaskQuery().taskCandidateGroup("programmers").list();
In der Entwicklungsaufgabe wird eine "Issue" -Variable definiert

Nach der Verarbeitung der Variablen mit FormService wird die Task ausgeführt
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>();

Für die Entwicklungsaufgabe werden Sie aufgefordert, eine Variable einzugeben.
In der Verlaufstabelle sehen Sie die Variablen und Werte der Aufgabe, des Prozesses

Wenn der Prozess nach dem Beenden der Entwicklungsaufgabe beendet wird, wird der Status in der Datenbank gespeichert.
Im Allgemeinen sieht die Schleife folgendermaßen aus:
Fordern Sie eine Aufgabe für einen Executor an
tasks = taskService.createTaskQuery().taskCandidateGroup("...").list();
Definition von Variablen
Map<String, Object> variables = new HashMap<String, Object>(); ... variables.put("var_1", value);
Aufgabenausführung
taskService.complete(task.getId(), variables);
Überprüfen Sie das Ende des Prozesses
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstance.getId()).singleResult(); if (processInstance != null && !processInstance.isEnded())
Nach jeder Aufgabenausführung wird der Prozess angehalten, bis eine neue Aufgabe abgeschlossen ist.
Nachdem wir Develop ausgeführt haben, fahren wir mit der Testaufgabe fort. Hier werden wir auch aufgefordert, die Variable "devResult" einzugeben - das Entwicklungsergebnis (es hat nicht richtig funktioniert, noch bevor der Test beginnt, geben Sie das Ergebnis ein), und dann wird das Ergebnis verzweigt oder endet (Ok) oder Entwicklung erneut (Nein), siehe Prozessdiagramm.

In diesem Fall ist die Entwicklung usw. Wenn Sie jetzt Aufgaben für den Entwickler anfordern, werden diese ausgeführt, aber zum Testen - nein.
Programmcode 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()); }
Verbinden Sie SpringBoot
Wir modifizieren das Projekt mit Spring
Fügen Sie Abhängigkeiten zu POM hinzu
POM mit SpringBoot <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.1.RELEASE</version> <relativePath/> </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> ....
Die DemoActiviti-Klasse ist jetzt so geworden
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); } }
Ich verwende ein gemischtes Modell - wenn ein Teil der Beans in der XML-Konfiguration (@ImportResource ("classpath: activiti.cfg.xml")) beschrieben ist und der andere durch Anmerkungen definiert wird.
activiti.cfg.xml - Frühling <?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>
Jetzt ist Spring für die Konfiguration verantwortlich, wie man sehen kann
bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"
Fügen Sie die Standardbefehlszeilenverarbeitung für SpringBoot als Komponente hinzu
Befehlszeile @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(); } } }
Was all diese Befehle verarbeiten wird, ich werde sie nicht alle implementieren, alles ist dort einfach, ich werde zwei zeigen: testen und entwickeln. Und fügen Sie einen Service hinzu, um sie zu verarbeiten
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) { ...... , }
In der CommandLine-Komponente Autowir verfügen sie über den DemoService-Dienst und darin über die bereits vorbereiteten Spring Activiti-Dienste
@Autowired private TaskService taskService;
Wir sammeln, laufen wie zuvor über die Kommandozeile.
Wenn Sie die Ausführung von Aufgaben aus dem Web verwenden möchten, verbinden Sie die REST-API.
REST-API
SpringBoot stellt standardmäßig einen eingebetteten Tomcat-Server und dann eine technische Angelegenheit zur Verfügung.
Fügen Sie in POM zu dem, was es ist, die Spring-Web-Abhängigkeit hinzu
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Wir entfernen die CommandLine-Komponente, jetzt kommt alles über die URL über HTTP. RestController hinzufügen:
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; } }
Wir führen dieselben Befehle aus und haben die Antworten des DemoService-Dienstes, den Autowire in der Steuerung hat, geringfügig geändert.
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<>();
Testen mit Curl, hier ist das Ergebnis:

Ich habe den Port für Tomcat in application.properties auf 8081 geändert
server.port = 8081
Activiti Job
Activiti hat viele Designs, zum Beispiel ist das Starten geplanter Aufgaben "TimerStartEvent". Damit Job in begrenztem Umfang ausgeführt werden kann, müssen Sie angeben
property name="asyncExecutorActivate" value="true"
(siehe activiti.cfg.xml), dann läuft der Java-Prozess weiter und überprüft den Zeitplan und führt Aufgaben aus.
Ich werde zum ersten Projekt zurückkehren, in dem Activiti in seiner reinsten Form verwendet wird.
In der DemoActiviti-Klasse werde ich nur zwei Befehle unterstützen: Bereitstellen und Starten Ich werde einen neuen Prozess erstellen

Nach dem Start des Prozesses geht er zum Timer, der gemäß dem Zeitplan die Aufgabe „Entwickeln“ startet. Der Timer hat einen Zeitplan - Start alle 10 Sekunden, Cron-Ausdruck - "0/10 * * * * *?".

Lassen Sie uns den neuen Prozess wie zuvor bereitstellen und dann den Prozess starten (Start). Alle - Die Aufgabe wird alle 10 Sekunden ausgeführt.
Als Aufgabe wird die Activiti-Komponente ausgewählt - ServiceTask, aus der Sie als Java-Klassenimplementierung angeben können

Klasse 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); } }
In der Tabelle in der Datenbank (wählen Sie * aus ACT_RU_TIMER_JOB t) können Sie sehen

Jobaktivität, im Feld DUEDATE_ gibt es eine Zeit für den nächsten Start.
Die "Issue" -Variable des Delegaten wird im Ausführungsverlauf aufgezeichnet
select * from ACT_HI_VARINST t

Code für 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()); }
Es bleibt noch viel zurück: Events, Listener, JPA usw., vielleicht werde ich darauf zurückkommen.
Material
ActivitiEclipse DesignerdevProcess 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>