Un guide sur Activiti avec Java

1. Vue d'ensemble

L'API Activiti est un système de gestion des flux de travail et des processus métier. Nous pouvons y définir un processus, l'exécuter et le manipuler de différentes manières en utilisant les services fournis par l'API. Il nécessite JDK 7+.

Le développement à l'aide de l'API peut être effectué dans n'importe quel IDE, mais pour utiliser Activiti Designer, nous avons besoin d'Eclipse.

Nous pouvons y définir un processus en utilisant le standard BPMN 2.0. Il existe un autre moyen moins populaire - en utilisant des classes Java comme StartEvent , EndEvent , UserTask , SequenceFlow , etc.

Si nous voulons exécuter un processus ou accéder à l'un des services, nous devons créer un ProcessEngineConfiguration .

Nous pouvons obtenir le ProcessEngine à l' aide de ProcessEngineConfiguration, d'une certaine manière, dont nous parlerons plus loin dans cet article . À traversla ProcessEngine nous pouvons effectuer les opérations de flux de travail et BPMN.

2. Dépendances de Maven

Pour utiliser cette API, nous devons inclure la dépendance Activiti:

 org.activiti activiti-engine 

3. Création d'un ProcessEngine

ProcessEngine dans Activiti est généralement configuré à l'aide d'un fichier XML, activiti.cfg.xml . Un exemple de ce fichier de configuration est:

Nous pouvons maintenant obtenir le ProcessEngine en utilisant la classe ProcessEngines :

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

Cette instruction recherchera un activiti.cfg. xml dans le classpath, et construisez un ProcessEngine en fonction de la configuration dans le fichier.

L'exemple de code pour le fichier de configuration montre qu'il s'agit simplement d'une configuration Spring. Mais cela ne signifie pas que nous ne pouvons utiliser Activiti que dans un environnement Spring. Les capacités de Spring sont simplement utilisées en interne pour créer le ProcessEngine .

Écrivons un cas de test JUnit qui créera le ProcessEngine en utilisant le fichier de configuration ci-dessus:

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

4. API et services Activiti Process Engine

Le point d'entrée d'interaction avec l'API est le ProcessEngine . Grâce au ProcessEngine, nous pouvons accéder à divers services qui fournissent des méthodes de flux de travail / BPMN. Le ProcessEngine et tous les objets de service sont thread-safe.

Tiré de //www.activiti.org/userguide/images/api.services.png

La classe ProcessEngines recherchera les fichiers activiti.cfg.xml et activiti-context.xml . Comme mentionné précédemment, pour tous les fichiers activiti.cfg.xml , le ProcessEngine sera créé de manière typique.

Alors que, pour tous les fichiers activiti-context.xml , il sera créé à la manière Spring - je créerai le contexte d'application Spring et j'obtiendrai le ProcessEngine à partir de cela. Lors de l'exécution d'un processus, toutes les étapes seront visitées dans l'ordre défini dans le fichier BPMN.

Lors de l'exécution d'un processus, toutes les étapes seront visitées dans l'ordre défini dans le fichier BPMN.

4.1. Définition de processus et termes associés

Un ProcessDefinition représente un processus métier. Il est utilisé pour définir la structure et le comportement des différentes étapes du processus. Déployer une définition de processus signifie charger la définition de processus dans la base de données Activiti.

Les définitions de processus sont principalement définies par la norme BPMN 2.0. Il est également possible de les définir à l'aide du code Java. Tous les termes définis dans cette section sont également disponibles en tant que classes Java.

Une fois que nous commençons à exécuter une définition de processus, elle peut être appelée processus

Un ProcessInstance est une exécution d'un ProcessDefinition.

Un StartEvent est associé à chaque processus métier. Il indique le point d'entrée du processus. De même, il existe un EndEvent qui indique la fin du processus. Nous pouvons définir des conditions sur ces événements.

Toutes les étapes (ou éléments) entre le début et la fin sont appelées tâches . Les tâches peuvent être de différents types. Les tâches les plus couramment utilisées sont les UserTasks et ServiceTasks .

Les UserTasks , comme son nom l'indique, sont telles qu'elles doivent être exécutées manuellement par un utilisateur.

ServiceTasks , d'autre part, sont configurés avec un morceau de code. Chaque fois que l'exécution les atteint, leur bloc de code sera exécuté.

SequenceFlows connecte les tâches . Nous pouvons définir les SequenceFlows par les éléments source et cible qu'ils vont connecter. Encore une fois, nous pouvons également définir des conditions sur les SequenceFlows pour créer des chemins conditionnels dans le processus.

4.2. Prestations de service

Nous discuterons brièvement des services fournis par Activiti:

  • RepositoryService nous aide à manipuler le déploiement des définitions de processus. Ce service traite les données statiques liées à une définition de processus
  • RuntimeService gère les ProcessInstances (processus en cours d'exécution) ainsi que les variables de processus
  • TaskService assure le suivi des UserTasks . Les tâches qui doivent être effectuées manuellement par un utilisateur sont au cœur de l'API Activiti. Nous pouvons créer une tâche, réclamer et terminer une tâche, manipuler le destinataire de la tâche, etc. en utilisant ce service
  • FormService est un service facultatif. L'API peut être utilisée sans elle et sans sacrifier aucune de ses fonctionnalités. Il est utilisé pour définir le formulaire de démarrage et le formulaire de tâche dans un processus.
  • IdentityService gère les utilisateurs et les groupes
  • HistoryService garde une trace de l'historique d'Activiti Engine. Nous pouvons également définir différents niveaux d'histoire.
  • ManagementService est lié aux métadonnées et n'est généralement pas requis lors de la création d'une application
  • DynamicBpmnService nous aide à changer quoi que ce soit dans un processus sans le redéployer

5. Utilisation des services Activiti

Pour savoir comment nous pouvons travailler avec différents services et exécuter un processus, prenons un exemple de processus pour «Demande de vacances des employés»:

Le fichier BPMN 2.0, VacationRequest.bpmn20.xml , pour ce processus aura l'événement de démarrage défini comme:

De même, la première tâche utilisateur, affectée au groupe d'utilisateurs «gestion», ressemblera à ceci:

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

Avec la ServiceTask, nous devons définir le morceau de code à exécuter. Nous avons ce morceau de code en tant que classe Java:

Le flux conditionnel sera affiché en ajoutant la balise "conditionExpression" dans le "sequenceFlow":

Ici, vacationApproved est la propriété formProperty de la UserTask illustrée ci-dessus.

Comme nous pouvons le voir sur le diagramme, c'est un processus très simple. L'employé fait une demande de vacances en indiquant le nombre de jours et la date de début des vacances. La demande est adressée au responsable. Ils peuvent approuver / désapprouver la demande.

Si elle est approuvée, une tâche de service est définie pour envoyer l'e-mail de confirmation. S'il est refusé, l'employé peut choisir de modifier et renvoyer la demande, ou ne rien faire.

Les tâches de service sont fournies avec un morceau de code à exécuter (ici, en tant que classe Java). Nous avons donné la classe 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"); } 

Pour l'activer à nouveau, il suffit d'appeler l'une des méthodes repositoryService.activateProcessDefinitionXXX .

De même, nous pouvons suspendre un ProcessInstance, en utilisant le RuntimeService.

6. Conclusion

Dans cet article, nous avons vu comment nous pourrions utiliser Activiti avec Java. Nous avons créé un exemple de fichier ProcessEngineCofiguration , qui nous aide à créer le ProcessEngine .

En l'utilisant, nous avons accédé à divers services fournis par l'API. Ces services nous aident à gérer et à suivre les ProcessDefinitions , ProcessInstances , UserTasks , etc.

Comme toujours, le code des exemples que nous avons vus dans l'article se trouve sur GitHub.