This is the 10th day of my participation in the More text Challenge. For more details, see more text Challenge

>>>> 😜😜😜 Making: 👉 github.com/black-ant

A. The preface

This document will open the Activiti series of documents, so the content of this article is mainly based on process use, for more clarity, we will analyze from Task to outer layer >>>

Let’s look at the Process instance creation and configuration processing later

2. Overall use

2.1 the Maven rely on

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.0.0. Beta 2</version>
</dependency>

<! -- Add the following DAO framework because you need to use the database -->

<! -- DAO -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<! -- PS: This package is primarily for building a DataSource-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Copy the code

2.2 application. Yml

PS: There is no need to create a database. In Activiti, tables are created by default.

spring:
  datasource:
    url: jdbc:mysql:/ / 127.0.0.1:3306 / activiti007? useUnicode=true&characterEncoding=utf8&serverTimezone=GMT&nullCatalogMeansCurrent=true
    username : root
    password : 123456
    driver-class-name: com.mysql.jdbc.Driver
  activiti:
    database-schema-update: true
server:
  port: 8086

Copy the code

The following table is created by default:

2.3 Preparations

Activiti is bound to the user by default. You need to perform the following configurations:

Add two users to the cache

@Configuration
public class SecurityConfiguration {

    private Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Bean
    public UserDetailsService myUserDetailsService(a) {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        logger.info("> Registering new user: " + "root" + " with the following Authorities[ 'ACTIVE' , 'ADMIN' ]");

        // Build the Group information
        List<SimpleGrantedAuthority> groupList = new ArrayList<>();
        // Note that this permission is required
        groupList.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER"));
        groupList.add(new SimpleGrantedAuthority("ADMIN"));

        // Prepare two users: Root and Admin
        inMemoryUserDetailsManager.createUser(new User("root", passwordEncoder().encode("123456"), groupList));
        inMemoryUserDetailsManager.createUser(new User("admin", passwordEncoder().encode("123456"), groupList));

        return inMemoryUserDetailsManager;
    }


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

Mock login tool class

@Component
public class SecurityUtil {

    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public void logInAs(String username) {

        UserDetails user = userDetailsService.loadUserByUsername(username);

        logger.info("> User Security Configuration (1) : simple verify whether the user exists [{}]", username);
        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }

        logger.info("-- -- -- -- -- - > user Security configuration (2), and simulation in the Security log object: {} < -- -- -- -- -- -- --", username);
        SecurityContextHolder.setContext(new SecurityContextImpl(new UsernamePasswordAuthenticationToken(user.getUsername(), "123456")));

        logger.info("-- -- -- -- -- - > user security configuration (3), set in the Activiti objects: {} < -- -- -- -- -- -- --", username); org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); }}Copy the code

2.4 A simple flow

@RestController
public class StartController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ProcessRuntime processRuntime; // The implementation process defines the related operations

    @Autowired
    private TaskRuntime taskRuntime; // Implement task-related operations

    @Autowired
    private SecurityUtil securityUtil;//SpringSecurity related utility classes

    @RequestMapping("/test")
    public String test(a) {
        logger.info("------> [Enter StartController successfully] <-------");
        return "Success !";
    }

    @GetMapping("/info")
    public String getInfd(a) {
        Page<ProcessDefinition> processDefinitionPage = processRuntime
                .processDefinitions(Pageable.of(0.10));
        logger.info("------> Number of process definitions available: [{}] <-------", processDefinitionPage.getTotalItems());
        for (ProcessDefinition pd : processDefinitionPage.getContent()) {
            logger.info("------> Process definition: [{}] <-------", pd);
        }

        return "success";
    }

    @GetMapping("/startFlow")
    public String startFlow(a) {
        securityUtil.logInAs("root");
        ProcessInstance pi = processRuntime.start(ProcessPayloadBuilder
                .start()
                // The.bpm file defined in processers
                .withProcessDefinitionKey("SimpleProcess")
                .build());// Start the process instance

        logger.info("------> Process instance ID: + [{}] <-------", pi.getId());
        return "Start the process";
    }

    @GetMapping("/selectFlow")
    public String selectFlow(a) {
        securityUtil.logInAs("root");
        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0.10));
        if (taskPage.getTotalItems() > 0) {
            taskPage.getContent().forEach(item -> {
                logger.info("------> Remaining tasks :[{}] <-------", JSONObject.toJSONString(item));
            });
        } else {
            logger.info("------> All tasks completed <-------", taskPage.getContent());
        }


        return "Query Flow:" + taskPage.getTotalItems();
    }

    @GetMapping("/doFlow")
    public String doFlowBusiness(a) {

        logger.info("------> [Entering doFlowBusiness process] <-------");
        securityUtil.logInAs("root");
        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0.10));

        logger.info("------> Task start complete <-------");

        if (taskPage.getTotalItems() > 0) {
            for (Task task : taskPage.getContent()) {

                logger.info("------> Loop processing tasks [{}] <-------", task.getName());

                // Pick up the task
                taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());

                // Execute the task
                taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            }
        }

        logger.info("------> Query task result <-------");

        Page<Task> taskPage2 = taskRuntime.tasks(Pageable.of(0.10));
        if (taskPage2.getTotalItems() > 0) {
            logger.info("------> Remaining tasks :[{}] <-------", taskPage2.getContent());
        } else {
            logger.info("------> All tasks completed <-------", taskPage2.getContent());
        }

        return "Success: Do Flow Business processing completed";
    }
    
    @GetMapping("deleteFlow")
    public String deleteFlow(a) {
        // PS: If there are multiple users, you need to switch the user
        // securityUtil.logInAs("admin");
        Page<Task> temTaskList = taskRuntime.tasks((Pageable.of(0.10)));
        temTaskList.getContent().forEach(item -> {
            try {
                logger.info("------> Step 4 item: delete Task :{} <-------", item.getId());
                taskRuntime.delete(TaskPayloadBuilder.delete().withTaskId(item.getId()).build());
            } catch (Exception e) {
                logger.error("E----> error :{} -- content :{}", e.getClass(), e.getMessage()); }});return "success"; }}Copy the code

3. Task core process

@Component
public class ActivitiTaskRuntimeService implements ApplicationRunner {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private static String taskId = "MyTask001";

    @Autowired
    private TaskRuntime taskRuntime;
    @Autowired
    private SecurityUtil securityUtil;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("-- -- -- -- -- - > [open a complete Activiti process], simulated first login a user < -- -- -- -- -- -- --");

        // PS: Activiti relies on Spring Security by default
        securityUtil.logInAs("root");

        deleteTask();

        // Create the process
        createTask();

        // Query the object
        getTaskInfo();
        selectTaskInfo();

        selectTaskInfoByUserId("admin");
        selectTaskInfoByUserId("root");

        // Execute the process
        doTask();

        // Query the process again
        getTaskInfo();
        selectTaskInfo();
    }


    public void getTaskInfo(a) {
        try {
            // Step 2: Query information about a single Task
            Task temTask = taskRuntime.task(taskId);
            logger.info("-- -- -- -- -- - > Step 2 query ID: {} - the corresponding Task: {} < -- -- -- -- -- -- --", taskId, JSONObject.toJSONString(temTask));
        } catch (NotFoundException e) {
            logger.error("E----> Current Task is completed, error :{} -- content :{}", e.getClass(), e.getMessage()); }}/** * Query the current Task case */
    public void selectTaskInfo(a) {
        // Step 2: Query information about all known tasks
        Pageable pageable = Pageable.of(0.10);
        Page<Task> temTaskList = taskRuntime.tasks(pageable);
        temTaskList.getContent().forEach(item -> {
            logger.info("-- -- -- -- -- - > Step 2-1 query series number - [] {} - the corresponding Task: {} < -- -- -- -- -- -- --", temTaskList.getTotalItems(), JSONObject.toJSONString(item));
        });

    }

    /** * Correspond to the client to query their own task */
    public void selectTaskInfoByUserId(String assignee) {
        // Step 2: Query information about all known tasks
        Pageable pageable = Pageable.of(0.10);
        Page<Task> temTaskList = taskRuntime.tasks(pageable, TaskPayloadBuilder.tasks().withAssignee(assignee).build());
        temTaskList.getContent().forEach(item -> {
            logger.info("-- -- -- -- -- - > Step 2-2 query assignee: {} series number - [] {} - the corresponding Task: {} < -- -- -- -- -- -- --", assignee, temTaskList.getTotalItems(), JSONObject.toJSONString(item));
        });

    }


    /** * Create a Task */
    public void createTask(a) {
        logger.info("------> Step 1: Create a Task to start <-------");
        CreateTaskPayload taskPayloadBuilder = TaskPayloadBuilder.create()
                .withName("First Team Task")
                .withDescription("This is something really important")
                // Set the user group of the current Task
                .withGroup("ADMIN")
                .withPriority(10)
                .build();
        Task temTask = taskRuntime.create(taskPayloadBuilder);

        logger.info("-- -- -- -- -- - > Step 1 create a second Task, note that here set the Assignee < -- -- -- -- -- -- --");
        CreateTaskPayload taskPayloadBuilderTo = TaskPayloadBuilder.create()
                .withName("Second Team Task")
                .withDescription("This is something really important hava Assignee")
                // Set the user group of the current Task
                .withGroup("ADMIN")
                .withAssignee("admin")
                .withPriority(10)
                .build();
        taskRuntime.create(taskPayloadBuilderTo);


        this.taskId = temTask.getId();
    }

    /** * Execute a Task */
    public void doTask(a) {

        logger.info("-- -- -- -- -- - > Step 3-1: declare a Task start claimed < -- -- -- -- -- -- --");
        taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(taskId).build());
        logger.info("-- -- -- -- -- - > Step 3-3: to complete a Task starts to complete < -- -- -- -- -- -- --");
        taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(taskId).build());
    }

    /** * delete Task: PS: */
    public void deleteTask(a) {
        logger.info("------> Step 4: Delete all tasks <-------");

        Pageable pageable = Pageable.of(0.10);
        Page<Task> temTaskList = taskRuntime.tasks(pageable);
        temTaskList.getContent().forEach(item -> {
            try {
                logger.info("------> Step 4 item: delete Task :{} <-------", item.getId());
                taskRuntime.delete(TaskPayloadBuilder.delete().withTaskId(item.getId()).build());
            } catch (Exception e) {
                logger.error("E----> error :{} -- content :{}", e.getClass(), e.getMessage()); }}); securityUtil.logInAs("admin");
        Pageable pageableAdmin = Pageable.of(0.10);
        Page<Task> temTaskListAdmin = taskRuntime.tasks(pageable);
        temTaskListAdmin.getContent().forEach(item -> {
            try {
                logger.info("------> Step 4 item: delete Task :{} <-------", item.getId());
                taskRuntime.delete(TaskPayloadBuilder.delete().withTaskId(item.getId()).build());
            } catch (Exception e) {
                logger.error("E----> error :{} -- content :{}", e.getClass(), e.getMessage()); }}); securityUtil.logInAs("root"); }}Copy the code

3.1 TaskRuntime module

Interface in

C- TaskRuntime
public interface TaskRuntime {
  TaskRuntimeConfiguration configuration(a);
  Task task(String taskId);
  Page tasks(Pageable pageable);
  Page tasks(Pageable pageable, GetTasksPayload payload);
  Task create(CreateTaskPayload payload);
  Task claim(ClaimTaskPayload payload);
  Task release(ReleaseTaskPayload payload);
  Task complete(CompleteTaskPayload payload);
  Task update(UpdateTaskPayload payload);
  Task delete(DeleteTaskPayload payload); . }Copy the code

Methods effect

  • Task Task (String taskId) : obtains a Task by id
  • Page tasks(Pageable pageable); Obtain all tasks of the current authenticated user
  • Page Tasks (Pageable Pageable,GetTasksPayload GetTasksPayload) : Gets all the tasks that apply the filter in the Payload
  • Task Create (CreateTaskPayload CreateTaskPayload) : Creates a Task
  • Task Claim (ClaimTaskPayload ClaimTaskPayload) : Declares a Task
    • IllegalStateException is thrown if there is no authenticated user
    • IllegalStateException is thrown if the currently authenticated user is not a candidate user
    • The current method does not support impersonation and will always accept currently authenticated users
    • After the declaration, the task should be in the assigned state
  • Task Release (ReleaseTaskPayload ReleaseTaskPayload) : Releases a previously declared Task
  • Task Complete (CompleteTaskPayload CompleteTaskPayload) : Sets a variable in the payload to complete the selected Task
    • This method also checks whether the task is assigned to the currently authenticated user before it completes
    • This method returns a shallow Task object that contains the basic information needed to verify that the Task is complete
  • Task Update (UpdateTaskPayload UpdateTaskPayload) : updates the details of the Task
  • Task DELETE (DeleteTaskPayload DeleteTaskPayload) : deletes a Task

3.2 Task object

Task is the core flow object in the process. Take a look at the parameters of this object:

public interface Task extends TaskInfo {

  /** * The default priority for creating a new task */
  int DEFAULT_PRIORITY = 50;

  /** The name or title of the task */
  void setName(String name);
  
  /** Set an optional localization name for the task
  void setLocalizedName(String name);

  /** Modify the task description */
  void setDescription(String description);
  
  /** Set the optional localization description for the task
  void setLocalizedDescription(String description);

  /** Set the importance/urgency of the task */
  void setPriority(int priority);

  /** * userId of the person responsible for this task. */
  void setOwner(String owner);

  /** * specifies the userId of the person to whom this task is delegated
  void setAssignee(String assignee);

  /** The current delegate state of the task
  DelegationState getDelegationState(a);

  /** The current delegate state of this task. * /
  void setDelegationState(DelegationState delegationState);

  /** Change the due date of the task */
  void setDueDate(Date dueDate);

  /** * Change the category of the task. This is an optional field that allows tasks to be marked as belonging to a category. * /
  void setCategory(String category);

  /** ID of the parent task */
  void setParentTaskId(String parentTaskId);

  /** Modify task tenantId */
  void setTenantId(String tenantId);

  /** Change the form key of the task */
  void setFormKey(String formKey);

  /** indicates whether the task is suspended. */
  boolean isSuspended(a);

}

Copy the code

3.3 TaskRuntimeImpl module

TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl TaskRuntimeImpl

Step 1: Triggering a Delete

In a service, a method is invoked to delete a Task

    public void deleteTask(a) {
        logger.info("------> Step 4: Delete all tasks <-------");
        // Set the current user
        securityUtil.logInAs("admin");
        Pageable pageableAdmin = Pageable.of(0.10);
        Page<Task> temTaskListAdmin = taskRuntime.tasks(pageable);
        temTaskListAdmin.getContent().forEach(item -> {
            taskRuntime.delete(TaskPayloadBuilder.delete().withTaskId(item.getId()).build());
        });

    }

Copy the code

PS: here we do a special operation -> Securityutil.loginas (“admin”);

This is because the operation of Task is divided by permission, and the corresponding personnel can only operate their own Task

Step 2: Call TaskRuntimeImpl # delete

 public Task delete(DeleteTaskPayload deleteTaskPayload) {
        // Get a Task
        Task task;
        try {
            task = task(deleteTaskPayload.getTaskId());
        } catch (IllegalStateException ex) {
            throw new IllegalStateException("T....");
        }
        
        // Obtain the Userid of the current authentication
        String authenticatedUserId = securityManager.getAuthenticatedUserId();
        // Verify that you are trying to delete a task for which you are the assignee or owner
        if ((task.getAssignee() == null|| task.getAssignee().isEmpty() || ! task.getAssignee().equals(authenticatedUserId)) && (task.getOwner() ==null|| task.getOwner().isEmpty() || ! task.getOwner().equals(authenticatedUserId))) {throw new IllegalStateException("...");
        }
        
        // Create the same Task from the original data
        TaskImpl deletedTaskData = new TaskImpl(task.getId(),
                                                task.getName(),
                                                Task.TaskStatus.DELETED);
        
        // Set Reason to cause
        if(! deleteTaskPayload.hasReason()) { deleteTaskPayload.setReason("Cancelled by " + authenticatedUserId);
        }
        
        // Run Service -> PS:0001
        taskService.deleteTask(deleteTaskPayload.getTaskId(),
                               deleteTaskPayload.getReason(),
                               true);
        return deletedTaskData;
    }


// PS:0001 Call taskService -> TaskServiceImpl
public void deleteTask(String taskId, String deleteReason, boolean cancel) {
    commandExecutor.execute(new DeleteTaskCmd(taskId, deleteReason, false, cancel));
}

Copy the code

There are two things to note:

  • C-deletetaskcmd: This object is a command object. Guess this is a command mode
  • C – commandExecutor object

Take a look at what the object does:

// commandExecutor is an interface with three methods
C- commandExecutor : 
	M- CommandConfig getDefaultConfig(a): Get the default CommandConfig, or use M- <T> T if not providedexecute(CommandConfig config, Command<T> command): Runs the M- <T> T command using the specified CommandConfigexecute(Command<T> command): Use the default CommandConfig command to run the c-commandexecutorImpl i-commandexecutor command// Let's see, what is implemented
public <T> T execute(CommandConfig config, Command<T> command) {
    // The first is the LogInterceptor
    return first.execute(config, command);
}


Copy the code

First. Execute (config, command); At this point, you actually start calling the interceptor chain

Step 3: The invocation of the interceptor chain

The generation of the interception chain is shown later, but what is being done here

// When we analyze the Delete process, we can see that it goes through an interception chain, as shown in the figure below:

- CommandContextInterceptor
- CommandInvoker
- DebugCommandInvoker
- CommandInvoker
- JtaRetryInterceptor
- JtaTransactionInterceptor
- LoggingCommandInvoker
- SpringTransactionInterceptor
- TotalExecutionTimeCommandInterceptor
- TransactionContextInterceptor
- RetryInterceptor

Copy the code

Not all of these interception chains will go, and the main ones are as follows:

  • Step 1: SpringTransactionInterceptor: control the transaction
  • Step 2: CommandContextInterceptor: ready for container
  • Step 3: TransactionContextInterceptor: build TransactionContext
  • Step 4: Prepare the DbSqlSession in CommandInvoker and execute the processing thread using executeOperation # runnable.run()
  • NeedsActiveTaskCmd Executes the TaskCmd
  • Step 6: Initiate execute with CompleteTaskCmd. (PS: The Command type is in Command mode.)
  • Step End: TaskEntityManagerImpl executes the specific DB operation

conclusion

This article has basically introduced the main process, and the following operations will be in-depth:

  • SpringConfiguration operation
  • Overall Process
  • Custom operation processing