The basic document of the workflow engine for project practice! Detailed explanation of the process engine API and service of the Activiti workflow framework

The basic document of the workflow engine for project practice! Detailed explanation of the process engine API and service of the Activiti workflow framework

API and services of the process engine

  • Process Engine API (ProcessEngine API) is the most common way to deal with Activiti
  • Activiti starts from ProcessEngine. In ProcessEngine, you can get many services including workflow or BPM methods
  • ProcessEngine and service classes are both thread-safe. You can keep only one reference to them in the entire server.

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RuntimeService runtimeService = processEngine.getRuntimeService(); RepositoryService repositoryService = processEngine.getRepositoryService(); TaskService taskService = processEngine.getTaskService(); ManagementService managementService = processEngine.getManagementService(); IdentityService identityService = processEngine.getIdentityService(); HistoryService historyService = processEngine.getHistoryService(); FormService formService = processEngine.getFormService(); Copy code
ProcessEngines.getDefaultProcessEngine(): -It will initialize and create a process engine when it is called for the first time, and the same process engine will be returned when it is called later -Use the corresponding methods to create and close all process engines: ProcessEngines.init() and ProcessEngines.destroy() -ProcessEngines will scan all activiti.cfg.xml and activiti-context.xml files -For the activiti.cfg.xml file, the process engine will be constructed in the classic way of Activiti: -ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine() -For the activiti-context.xml file, the process engine will be built using the Spring method: first create a Spring environment, and then obtain the process engine through the environment -All services are stateless. This means that Activiti can be run in a multi-node cluster environment, and each node points to the same database, so there is no need to worry about which machine actually executes the front-end call. There is no problem wherever the service is executed RepositoryService -Responsible for static information -It is the first service contacted when using the Activiti engine, and provides operations for managing and controlling release packages and process definitions -Process definition is the java implementation of BPMN 2.0 process. It contains the structure and behavior of each link in a process -The release package is the packaging unit of the Activiti engine. A release package can contain multiple BPMN 2.0 xml files and other resources -Developers can freely choose to include any resources in the release package -Either a single BPMN 2.0 xml file can be placed in the release package, or the entire process and related resources can be put together -This kind of release package can be deployed through RepositoryService. Publishing a release package means uploading it to the engine, and all processes will be analyzed and resolved before being saved in the database -From this point of view, the system knows the existence of this release package, and the process contained in the release package can already be started -RepositoryService can query the release package and process definition in the engine -RepositoryService pauses or activates the release package, corresponding to all and specific process definitions. Pausing means that they can no longer perform any operations, and activation is the corresponding reverse operation -RepositoryService obtains a variety of resources, such as the files contained in the release package, and the flowchart automatically generated by the engine -RepositoryService obtains the pojo version of the process definition, which can be used to parse the process through java instead of xml RuntimeService -Responsible for starting a new instance of the process definition -Process definition defines the structure and behavior of each node of the process -A process instance is an instance of such a process definition -For each process definition, there will be many instances in execution at the same time -RuntimeService can be used to obtain and save process variables. These data are specific to a process instance and will be used by nodes in many processes -Runtimeservice can query the process instance and execute it, execute the corresponding'token' in BPMN 2.0, basically the execution points to where the process instance is currently -RuntimeService can be used when the process instance is waiting for an external trigger, and can be used to continue the process instance. The process instance can have many pause states, and the service provides a variety of methods to'trigger' the instance. After receiving an external trigger, the process instance will continue Execute down TaskService -Tasks are executed by real people in the system. It is one of the core functions of BPMN engines such as Activiti. All task-related functions are included in TaskService -In TaskService, query tasks assigned to users or groups -In TaskService, create independent running tasks, which have nothing to do with process instances -In TaskService, manually set the executor of the task, or how these users are associated with the task -In TaskService, claim and complete a task: -Claim means that a person expects to be the performer of the task, that is, the user will complete the task -Completion means "doing what the task requires", usually there are many forms of processing IdentityService -Can manage, create, update, delete, query... groups and users -When Activiti is executed, the user is not checked. Tasks can be assigned to anyone, but the engine will not check whether the user exists in the system. This is the Activiti engine and can also use external services: ldap, active directory... HistoryService -HistoryService provides all historical data of Activiti engine -When executing the process, the engine will save a lot of data according to the configuration: process instance startup time, task participants, time to complete the task, execution path of each process instance...This service mainly obtains these data through the query function FormService -FormService is an optional service, even if you don't use it, Activiti can run perfectly without any loss of functionality -FormService provides two concepts: start form and task form -The start form will be shown to the user before the process instance starts -The task form will be displayed when the user completes the task -Activiti supports setting these forms in the BPMN 2.0 process definition. This service exposes the data in a simple way, is optional, and the form does not have to be embedded in the process definition ManagementService -Basically not used in the custom environment using Activiti -ManagementService can query database tables and table metadata -ManagementService provides the function of querying and managing asynchronous operations -Activiti has many uses for asynchronous operations: timers, asynchronous operations, delayed pause, activation... Copy code

Exception strategy

  • The basic exception in Activiti is org.activiti.engine.ActivitiException, an unchecked exception
  • This exception can be thrown by the API at any time, a specific exception thrown by a specific method
/** * Called when the task is successfully executed. * @param taskId the id of the task to complete, cannot be null. * @throws ActivitiObjectNotFoundException when no task exists with the given id. */ void complete (String taskId) ; copy the code

When the id of a non-existent task is passed in, an exception will be thrown. taskId cannot be null, if null is passed in, an ActivitiIllegalArgumentException will be thrown

  • Excessive inheritance of exceptions should be avoided, subclasses are only used for specific occasions
  • The process engine and API calls do not use subclass exceptions in other occasions, and throw a normal ActivitiExceptions
ActivitiWrongDbException: Thrown when the Activiti engine finds that the database version number and the engine version number are inconsistent ActivitiOptimisticLockingException: thrown when concurrent methods are performed on the same data and optimistic locking occurs ActivitiClassLoadingException: When the class that needs to be loaded cannot be found or an error occurs when loading the class-JavaDelegate, TaskListener ActivitiObjectNotFoundException: Thrown when the corresponding request or operation does not exist ActivitiIllegalArgumentException: This exception indicates that an illegal parameter was passed in when calling the Activiti API. It may be an illegal value in the engine configuration, or an illegal value was provided, or an illegal value used in the process definition. ActivitiTaskAlreadyClaimedException: When the task has been claimed, it will be thrown when calling taskService.claim(...) Copy code

Query API

  • There are two ways to query data in the Activiti process engine:
    • Query API
    • Native query
  • Query API: Query API provides a completely type-safe API, you can customize query conditions and precise sorting conditions, all conditions are combined with AND
List<Task> tasks = taskService.createTaskQuery() .taskAssignee( "kermit" ) .processVariableValueEquals( "orderId" , "0815" ) .orderByDueDate().asc() .list(); Copy code
  • Native query:
    • When you need a more powerful query: use OR conditions or conditions that can be implemented using the query API.
    • You can write your own SQL query. The return type is determined by the query object you use, and the data will be mapped to the correct object: task, process instance, execution...
    • The query works on the database, you must use the table names and column names defined in the database, and you must understand the internal data structure
    • When using native query, the table name can be obtained through the API, which can minimize the dependence on the database
List<Task> tasks = taskService.createNativeTaskQuery() .sql( "SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}" ) .parameter( " taskName " , "gonzoTask" ) .list(); long count = taskService.createNativeTaskQuery() .sql( "SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, " + managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_" ) .count(); Copy code

expression

  • Activiti uses UEL to process expressions. UEL is the unified expression language, which is part of the EE6 specification. In order to support all the functions of the latest UEL in all operating environments, use a modified version of JUEL
  • Expressions can be used in many scenarios:
    • Java service tasks
    • Execution listener
    • Task listener
    • Conditional flow
  • Although there are two expressions: value expressions and method expressions, Activiti has abstracted them, so both can be used in scenarios where expressions are required.
  • Value expression: parsed as a value, default
${myVar} ${myBean.myProperty} Copy code

All process variables can be used, and all spring beans (in the spring environment) can also be used in expressions

  • Method expression: call a method, with or without parameters
${printer.print()} ${myBean.addNewOrder( 'orderName' )} ${myBean.doSomething(myVar, execution)} Copy code

When calling a method with no parameters, remember to add empty parentheses after the method name to distinguish between the value expression and the passed parameters can be strings or expressions, and they will be automatically parsed

  • These expressions support parsing primitive types:
    • bean
    • list
    • Array
    • map
    • Include comparison
  • In the process instance, some default objects can be used in expressions:
    • execution: DelegateExecution, providing additional information for out-of- office execution
    • task: DelegateTask, which provides additional information about the current task , only valid for the expression of the task listener
    • authenticatedUserId: id. ,

  • , :
  • Activiti java ,
  • Activiti JUnit 3 4
    • JUnit 3 , org.activiti.engine.test.ActivitiTestCase. ProcessEngine ,
    • setup() , classpath activiti.cfg.xml
    • , getConfigurationResource()
    • , ,
  • ActivitiTestCase, org.activiti.engine.test.Deployment . , , testClassName.testMethod.bpmn20.xml , . , , , ...Deployment
public class MyBusinessProcessTest extends ActivitiTestCase { @Deployment public void testSimpleProcess() { runtimeService.startProcessInstanceByKey("simpleProcess"); Task task = taskService.createTaskQuery().singleResult(); assertEquals("My Task", task.getName()); taskService.complete(task.getId()); assertEquals(0, runtimeService.createProcessInstanceQuery().count()); } }
  • JUnit 4
    • org.activiti.engine.test.ActivitiRule. , getter
    • Rule org.activiti.engine.test.Deployment
    • classpath , , ,
public class MyBusinessProcessTest { @Rule public ActivitiRule activitiRule = new ActivitiRule(); @Test @Deployment public void ruleUsageExample() { RuntimeService runtimeService = activitiRule.getRuntimeService(); runtimeService.startProcessInstanceByKey("ruleUsage"); TaskService taskService = activitiRule.getTaskService(); Task task = taskService.createTaskQuery().singleResult(); assertEquals("My Task", task.getName()); taskService.complete(task.getId()); assertEquals(0, runtimeService.createProcessInstanceQuery().count()); } }

  • H2 , Activiti :
  • :

  • , , [ ] [ ], , ,

  • Activiti , [ ] ( , [ ]-[ ]-[ ], [ ] [ ],org.h2.tools.Server.createWebServer("-web").start()

  • , . [ ]

  • Activiti , ,

Web

  • ProcessEngine ,
  • web , ,
  • ServletContextListener Servlet :
public class ProcessEnginesServletContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent servletContextEvent) { ProcessEngines.init(); } public void contextDestroyed(ServletContextEvent servletContextEvent) { ProcessEngines.destroy(); } }

The contextInitialized method will execute ProcessEngines.init() This will find the activiti.cfg.xml file under the classpath, and create a ProcessEngine based on the configuration file (for example, multiple jars contain configuration files) If the classpath contains multiple configuration files, confirm They have different names

  • When you need to use the process engine, you can pass
ProcessEngines.getDefaultProcessEngine() Copy code

or

ProcessEngines.getProcessEngine( "myName" ); Copy code
  • The contextDestroyed method in ContextListener will execute ProcessEngines.destroy() . This will close all initialized process engines