Eine Anleitung zu Activiti mit Java

1. Übersicht

Activiti API ist ein Workflow- und Business Process Management-System. Wir können einen Prozess darin definieren, ausführen und auf verschiedene Weise mithilfe der von der API bereitgestellten Dienste bearbeiten. Es erfordert JDK 7+.

Die Entwicklung mithilfe der API kann in jeder IDE erfolgen. Für die Verwendung des Activiti-Designers benötigen wir jedoch Eclipse.

Wir können einen Prozess darin mithilfe des BPMN 2.0-Standards definieren. Es gibt einen anderen, weniger beliebten Weg - die Verwendung von Java-Klassen wie StartEvent , EndEvent , UserTask , SequenceFlow usw.

Wenn wir einen Prozess ausführen oder auf einen der Dienste zugreifen möchten, müssen wir eine ProcessEngineConfiguration erstellen .

Wir können die ProcessEngine auf verschiedene Weise mit ProcessEngineConfiguration abrufen, worauf wir in diesem Artikel näher eingehen werden . DurchMit der ProcessEngine können wir die Workflow- und BPMN-Operationen ausführen.

2. Maven-Abhängigkeiten

Um diese API verwenden zu können, müssen wir die Activiti-Abhängigkeit einschließen:

 org.activiti activiti-engine 

3. Erstellen einer ProcessEngine

ProcessEngine in Activiti wird normalerweise mithilfe der XML-Datei activiti.cfg.xml konfiguriert . Ein Beispiel für diese Konfigurationsdatei ist:

Jetzt können wir die ProcessEngine mit der ProcessEngines- Klasse erhalten:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

Diese Anweisung sucht nach einer activiti.cfg. XML-Datei im Klassenpfad und erstellen Sie eine ProcessEngine basierend auf der Konfiguration in der Datei.

Der Beispielcode für die Konfigurationsdatei zeigt, dass es sich nur um eine Spring-basierte Konfiguration handelt. Dies bedeutet jedoch nicht, dass wir Activiti nur in einer Spring-Umgebung verwenden können. Die Funktionen von Spring werden nur intern zum Erstellen der ProcessEngine verwendet .

Schreiben wir einen JUnit-Testfall, der die ProcessEngine mithilfe der oben gezeigten Konfigurationsdatei erstellt:

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

4. Activiti Process Engine API und Services

Der Einstiegspunkt für die Interaktion mit der API ist die ProcessEngine . Über die ProcessEngine können wir auf verschiedene Dienste zugreifen, die Workflow- / BPMN-Methoden bereitstellen. Die ProcessEngine und alle Serviceobjekte sind threadsicher.

Entnommen aus //www.activiti.org/userguide/images/api.services.png

Die ProcessEngines- Klasse sucht nach den Dateien activiti.cfg.xml und activiti-context.xml . Wie bereits erwähnt , wird die ProcessEngine für alle Dateien activiti.cfg.xml auf typische Weise erstellt.

Während für alle Dateien von activiti-context.xml die Spring-Methode erstellt wird, erstelle ich den Spring-Anwendungskontext und erhalte daraus die ProcessEngine . Während der Ausführung eines Prozesses werden alle Schritte in der Reihenfolge aufgerufen, die in der BPMN-Datei definiert ist.

Während der Ausführung eines Prozesses werden alle Schritte in der Reihenfolge aufgerufen, die in der BPMN-Datei definiert ist.

4.1. Prozessdefinition und verwandte Begriffe

Eine ProcessDefinition repräsentiert einen Geschäftsprozess. Es wird verwendet, um die Struktur und das Verhalten verschiedener Schritte im Prozess zu definieren. Das Bereitstellen einer Prozessdefinition bedeutet das Laden der Prozessdefinition in die Activiti-Datenbank.

Prozessdefinitionen werden meist durch den BPMN 2.0-Standard definiert. Es ist auch möglich, sie mit Java-Code zu definieren. Alle in diesem Abschnitt definierten Begriffe sind auch als Java-Klassen verfügbar.

Sobald wir eine Prozessdefinition ausführen, kann sie als Prozess bezeichnet werden

Eine ProcessInstance ist eine Ausführung einer ProcessDefinition.

Jedem Geschäftsprozess ist ein StartEvent zugeordnet. Es zeigt den Einstiegspunkt des Prozesses an. Ebenso gibt es ein EndEvent, das das Ende des Prozesses angibt. Wir können Bedingungen für diese Ereignisse definieren.

Alle Schritte (oder Elemente) zwischen Start und Ende werden als Aufgaben bezeichnet . Es gibt verschiedene Arten von Aufgaben . Die am häufigsten verwendeten Aufgaben sind UserTasks und ServiceTasks .

UserTasks sind , wie der Name schon sagt, so, dass sie von einem Benutzer manuell ausgeführt werden müssen.

ServiceTasks hingegen werden mit einem Code konfiguriert. Immer wenn die Ausführung sie erreicht, wird ihr Codeblock ausgeführt.

SequenceFlows verbinden die Aufgaben . Wir können die SequenceFlows anhand der Quell- und Zielelemente definieren, die sie verbinden. Auch hier können wir Bedingungen über die SequenceFlows definieren , um bedingte Pfade im Prozess zu erstellen.

4.2. Dienstleistungen

Wir werden die von Activiti angebotenen Dienstleistungen kurz besprechen:

  • RepositoryService hilft uns bei der Bearbeitung der Bereitstellung von Prozessdefinitionen. Dieser Service behandelt die statischen Daten einer Prozessdefinition
  • RuntimeService verwaltet die ProcessInstances sowie die Prozessvariablen (derzeit laufende Prozesse)
  • TaskService verfolgt die UserTasks . Die Aufgaben , die von einem Benutzer manuell ausgeführt werden müssen, bilden den Kern der Activiti-API. Mit diesem Service können wir eine Aufgabe erstellen, eine Aufgabe beanspruchen und abschließen, den Verantwortlichen der Aufgabe manipulieren usw.
  • FormService ist ein optionaler Dienst. Die API kann ohne sie und ohne Einbußen bei ihren Funktionen verwendet werden. Es wird verwendet, um das Startformular und das Aufgabenformular in einem Prozess zu definieren.
  • IdentityService verwaltet die Benutzer und Gruppen
  • HistoryService verfolgt den Verlauf von Activiti Engine. Wir können auch verschiedene Verlaufsebenen festlegen.
  • ManagementService bezieht sich auf die Metadaten und ist normalerweise beim Erstellen einer Anwendung nicht erforderlich
  • DynamicBpmnService hilft uns, Änderungen an einem Prozess vorzunehmen , ohne ihn erneut bereitzustellen

5. Arbeiten mit Activiti Services

Um zu erfahren, wie wir mit verschiedenen Diensten arbeiten und einen Prozess ausführen können, nehmen wir ein Beispiel für einen Prozess für "Mitarbeiterurlaubsanfrage":

In der BPMN 2.0-Datei VacationRequest.bpmn20.xml für diesen Prozess ist das Startereignis wie folgt definiert:

In ähnlicher Weise sieht die erste Benutzeraufgabe, die der Benutzergruppe "Verwaltung" zugewiesen ist, folgendermaßen aus:

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

Mit der ServiceTask müssen wir den auszuführenden Code definieren. Wir haben diesen Code als Java-Klasse:

Der bedingte Ablauf wird durch Hinzufügen des Tags "conditionExpression" im "sequenceFlow" angezeigt :

Hier vacationApproved ist die formProperty des Usertask oben gezeigt.

Wie wir im Diagramm sehen können, ist dies ein sehr einfacher Vorgang. Der Mitarbeiter stellt eine Urlaubsanfrage und gibt die Anzahl der Tage und das Startdatum des Urlaubs an. Die Anfrage geht an den Manager. Sie können die Anfrage genehmigen / ablehnen.

Wenn genehmigt, ist eine Serviceaufgabe definiert, um die Bestätigungs-E-Mail zu senden. Wenn dies abgelehnt wird, kann der Mitarbeiter entweder auswählen, ob die Anforderung geändert und erneut gesendet werden soll, oder nichts unternehmen.

Serviceaufgaben werden mit einem Code zum Ausführen bereitgestellt (hier als Java-Klasse). Wir haben die Klasse SendEmailServiceTask.java angegeben.

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"); } 

Um es erneut zu aktivieren, müssen Sie nur eine der Methoden repositoryService.activateProcessDefinitionXXX aufrufen .

In ähnlicher Weise können wir eine suspendieren ProcessInstance, mit der RuntimeService.

6. Fazit

In diesem Artikel haben wir gesehen, wie wir Activiti mit Java verwenden können. Wir haben eine ProcessEngineCofiguration- Beispieldatei erstellt, mit deren Hilfe wir die ProcessEngine erstellen können .

Damit haben wir auf verschiedene Dienste zugegriffen, die von der API bereitgestellt werden. Diese Dienste helfen uns zu verwalten und zu verfolgen ProcessDefinitions , ProcessInstances , benutzerdefinierte Aufgabe , usw.

Wie immer liegt der Code für Beispiele, die wir im Artikel gesehen haben, auf GitHub.