Activiti is the most detailed series of articles on the web.

Activiti integration article

1. Integrate with Spring

1.1 Adding Dependencies


      
<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>com.bobo</groupId>
    <artifactId>ActivitiDemo02Spring</artifactId>
    <version>1.0 the SNAPSHOT</version>


    <properties>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-model</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-converter</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>7.0.0. Walk</version>
            <exclusions>
                <exclusion>
                    <groupId>com.github.jgraph</groupId>
                    <artifactId>jgraphx</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>7.0.0. Walk</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.7. RELEASE</version>
        </dependency>
        <! -- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <! -- log end -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>alfresco</id>
            <name>Activiti Releases</name>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
Copy the code

1.2 Adding an Integrated Configuration File

Add a Spring configuration file where you can integrate Activiti

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <! -- Data source -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti? characterEncoding=utf-8&amp;nullCatalogMeansCurrent=true&amp;serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>
    <! Workflow Engine configuration bean -->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <! -- Data source -->
        <property name="dataSource" ref="dataSource"/>
        <! Spring transaction Manager -->
        <property name="transactionManager" ref="transactionManager"/>
        <! -- Database policy flase: default value. When Activiti starts, it compares versions saved in database tables and throws an exception if there are no tables or versions do not match. True: Activiti updates all tables in the database. If the table does not exist, it is created automatically. Create_drop: Create tables when Activiti is started and drop tables when activiti is shut down (you must manually shut down the engine to drop tables). Drop-create: Drop old tables when Activiti starts and create new tables (no need to manually shut down the engine). -->
        <property name="databaseSchemaUpdate" value="drop-create"/>
    </bean>
    <! -- Process Engine -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
    <! Service -->
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>
    <! -- Process run service -->
    <bean id="runtimeService" factory-bean="processEngine"  factory-method="getRuntimeService"/>
    <! Service -->
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>
    <! Service -->
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>
    <! -- Transaction manager -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <! -- notice -- -- >
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <! -- Communication behavior -->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
    <! - section, <aop:config proxy-target-class="true"> <aop: Advisor advice-ref="txAdvice" pointcut="execution(*com.bobo.service.impl.. (..) )"/> </aop:config>-->
</beans>
Copy the code

Note the value of databaseSchemaUpdate:

Flase: default value. When Activiti starts, it compares versions saved in database tables and throws an exception if there are no tables or versions do not match. True: Activiti updates all tables in the database. If the table does not exist, it is created automatically. Create_drop: Create tables when Activiti is started and drop tables when activiti is shut down (you must manually shut down the engine to drop tables). Drop-create: Drop old tables when Activiti starts and create new tables (no need to manually shut down the engine).

1.3 Creating Test Class Tests

package com.bobo.test;

import org.activiti.engine.RepositoryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:activiti-spring.xml"})
public class ActivitiTest {

    @Autowired
    private RepositoryService repositoryService;

    @Test
    public void test01(a){ System.out.println(repositoryService); }}Copy the code

The method execution shows that the associated table structure has been created in the database, indicating a successful integration between Activiti and Spring.

2. Integration with SpringBoot

Activiti7 has been fully integrated with SpringBoot2.x since its official release

2.1 Adding dependencies

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.0.0. Beta 2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
Copy the code

2.2 Modifying the Configuration File

Configure Spring's data source
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///activiti? characterEncoding=utf-8& nullCatalogMeansCurrent=true& serverTimezone=UTC
spring.datasource.name=root
spring.datasource.password=123456
# Activiti configuration
#1.flase: default value. When Activiti starts, it compares versions saved in database tables and throws an exception if there are no tables or versions do not match
#2.true: Activiti updates all tables in the database. If the table does not exist, it is created automatically
#3. Create_drop: Create tables when Activiti is started and drop tables when activiti is shut down (you must manually shut down the engine to drop tables)
#4. Drop-create: Drop old tables when Activiti starts and then create new tables (no need to manually shut down the engine)
spring.activiti.database-schema-update=true
Activiti7 starts database history when database history is disabled by default
spring.activiti.db-history-used=true
The configurable history levels include None, Activity, Audit, and full
# None: No historical data is saved, so this is most efficient during process execution.
# Activity: A level higher than None saves the process instance and process behavior. Other data is not saved.
#audit: All process tasks and their properties are saved in addition to the data stored at the activity level. Audit is the default value for history.
#full: The highest level of historical data saving, in addition to audit level data, will save all other process related details data, including some process parameters, etc.
spring.activiti.history-level=full
By default, verify process files in the Process folder under resouces
spring.activiti.check-process-definitions=false

Copy the code

2.3 integrated SpringSecurity

Because Activiti7 is integrated with SpringBoot, by default, the SpringSecurity framework is integrated, so we need to prepare SpringSecurity configuration information

Add a utility class for SpringSecurity

package com.bobo.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class SecurityUtil {
    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    @Qualifier("myUserDetailsService")
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {
        UserDetails user = userDetailsService.loadUserByUsername(username);

        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }
        logger.info("> Logged in as: " + username);

        SecurityContextHolder.setContext(
                new SecurityContextImpl(
                        new Authentication() {
                            @Override
                            public Collection<? extends GrantedAuthority> getAuthorities() {
                                return user.getAuthorities();
                            }

                            @Override
                            public Object getCredentials(a) {
                                return user.getPassword();
                            }

                            @Override
                            public Object getDetails(a) {
                                return user;
                            }

                            @Override
                            public Object getPrincipal(a) {
                                return user;
                            }

                            @Override
                            public boolean isAuthenticated(a) {
                                return true;
                            }

                            @Override
                            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {}@Override
                            public String getName(a) {
                                returnuser.getUsername(); }})); org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); }}Copy the code

This class can be found in the Example provided by Activiti7.

Add a SpringSecurity profile

package com.bobo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
public class SpringSecurityConfiguration {
    private Logger logger = LoggerFactory.getLogger(SpringSecurityConfiguration.class);
    @Bean
    public UserDetailsService myUserDetailsService(a) {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        // Add the user here, and the task owner used in the later process needs to be added here
        String[][] usersGroupsAndRoles = {
                {"jack"."password"."ROLE_ACTIVITI_USER"."GROUP_activitiTeam"},
                {"rose"."password"."ROLE_ACTIVITI_USER"."GROUP_activitiTeam"},
                {"tom"."password"."ROLE_ACTIVITI_USER"."GROUP_activitiTeam"},
                {"other"."password"."ROLE_ACTIVITI_USER"."GROUP_otherTeam"},
                {"system"."password"."ROLE_ACTIVITI_USER"},
                {"admin"."password"."ROLE_ACTIVITI_ADMIN"}};for (String[] user : usersGroupsAndRoles) {
            List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
            inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
                    authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }

        return inMemoryUserDetailsManager;
    }
    @Bean
    public PasswordEncoder passwordEncoder(a) {
        return newBCryptPasswordEncoder(); }}Copy the code

2.4 Creating a BPMN File

Create a simple BPMN file and set the user groups for the task, CandidateGroups, CandidateGroups, and the contents of the task should be the same as the user group names specified in the configuration file for SpringSecurity. You can fill in activitTeam or otherTeam. The advantage of this is that any user in a group can pick up a task when they are unsure who is responsible for the taskProcesses can be automatically deployed in Activiti7 by creating a new directory, Processes, in the Resources directory to hold BPMN files

2.5 Unit Tests

package com.bobo;

import com.bobo.utils.SecurityUtil;
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.model.payloads.ClaimTaskPayload;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.engine.RepositoryService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ActSpringbootApplicationTests {

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private TaskRuntime taskRuntime;

    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private RepositoryService repositoryService;

    @Test
    void contextLoads(a) {
        System.out.println(taskRuntime);
    }

    /** * Query flow definition */
    @Test
    public void test02(a){
        securityUtil.logInAs("system");
        Page<ProcessDefinition> processDefinitionPage =
                processRuntime.processDefinitions(Pageable.of(0.10));
        System.out.println("Number of process definitions available :" + processDefinitionPage.getTotalItems());
        for (ProcessDefinition processDefinition : processDefinitionPage.getContent()) {
            System.out.println("Process Definition:"+ processDefinition); }}/** * Deployment process */
    @Test
    public void test03(a){
        repositoryService.createDeployment()
                .addClasspathResource("processes/my-evection.bpmn")
                .addClasspathResource("processes/my-evection.png")
                .name("Travel Application form")
                .deploy();
    }

    /** * Start the process instance */
    @Test
    public void test04(a){
        securityUtil.logInAs("system");
        ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("my-evection")
                .build()
        );
        System.out.println("Process instance ID :" + processInstance.getId());
    }

    /** * Task query, pick and complete operation */
    @Test
    public void test05(a){
        securityUtil.logInAs("jack");
        Page<Task> tasks = taskRuntime.tasks(Pageable.of(0.10));
        if(tasks ! =null && tasks.getTotalItems() > 0) {for (Task task : tasks.getContent()) {
                // Pick task
                taskRuntime.claim(TaskPayloadBuilder
                        .claim()
                        .withTaskId(task.getId())
                        .build()
                );
                System.out.println("Mission:" + task);
                taskRuntime.complete(TaskPayloadBuilder
                        .complete()
                        .withTaskId(task.getId())
                        .build()
                );
            }
        }
        Page<Task> taskPage2 = taskRuntime.tasks(Pageable.of(0.10));
        if(taskPage2 .getTotalItems() > 0){
            System.out.println("Mission:"+ taskPage2.getContent()); }}}Copy the code

Here’s the whole Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 Activiti7 V_V