Introdução ao Activiti com Spring

Introdução ao Activiti com Spring

1. Visão geral

Simplificando,Activiti is a workflow and Business Process Management platform.

Podemos começar rapidamente criando umProcessEngineConfiguration (normalmente baseado em um arquivo de configuração). A partir disso, podemos obter umProcessEngine - e por meio deProcessEngine, podemos executar operações de fluxo de trabalho e BPM.

A API fornece vários serviços que podem ser usados ​​para acessar e gerenciar processos. Esses serviços podem nos fornecer informações sobre o histórico de processos, o que está em execução no momento e os processos que estão implantados, mas ainda não estão em execução.

Os serviços também podem ser usados ​​para definir a estrutura do processo e manipular o estado do processo, ou seja, executar, suspender, cancelar etc.

Se você é novo na API, verifique nossoIntroduction to Activiti API with Java. Neste artigo, discutiremos como podemos configurar a API Activiti em um aplicativo Spring Boot.

2. Configuração com Spring Boot

Vamos ver como podemos configurar o Activiti como um aplicativo Spring Boot Maven e começar a usá-lo.

2.1. Configuração inicial

Como sempre, precisamos adicionar a dependência do maven:


    org.activiti
    activiti-spring-boot-starter-basic

A última versão estável da API pode ser encontradahere. Funciona com o Spring Boot até a v1.5.4. Ainda não funciona com a v2.0.0.M1.

Também podemos gerar um projeto Spring Boot usandohttps://start.spring.ioe selecionar Activiti como uma dependência.

Apenas adicionando esta dependência e a anotação@EnableAutoConfiguration ao aplicativo Spring Boot, ele fará a configuração inicial:

  • Criar fonte de dados (a API requer um banco de dados para criarProcessEngine)

  • Crie e exponha o beanProcessEngine

  • Criar e expor os beans de serviços Activiti

  • Criar o executor de tarefas do Spring

2.2. Criação e execução de um processo

Vamos construir um exemplo de criação e execução de um processo de negócios.

Para definir um processo, precisaremos criar um arquivo BPMN. Para isso, podemos usarhttps://activiti.alfresco.com/activiti-app/editor para criar uma definição de processo.

Em seguida, basta baixar o arquivo BPMN. Precisamos colocar este arquivo na pastasrc/main/resources/processes. Por padrão, o Spring Boot procurará nesta pasta para implantar a definição de processo.

Criaremos um processo de demonstração contendo as tarefas de um usuário:

image

O responsável pela tarefa do usuário é definido como o Iniciador do processo. O arquivo BPMN para esta definição de processo é semelhante a:

 
     
     
     
     
     
     
     
     
     
     

Agora, vamos criar um controlador REST para lidar com as solicitações para iniciar este processo:

@Autowired
private RuntimeService runtimeService;

@GetMapping("/start-process")
public String startProcess() {

    runtimeService.startProcessInstanceByKey("my-process");
    return "Process started. Number of currently running"
      + "process instances = "
      + runtimeService.createProcessInstanceQuery().count();
}

Aqui,runtimeService.startProcessInstanceByKey(“my-process”) inicia a execução do processo cuja chave é“my-process”. runtimeService.createProcessInstanceQuery().count() nos dará o número de instâncias do processo.

Cada vez que atingirmos o caminho“/start-process”, um novoProcessInstance será criado e veremos um incremento na contagem dos processos em execução no momento.

Um caso de teste JUnit mostra esse comportamento:

@Test
public void givenProcess_whenStartProcess_thenIncreaseInProcessInstanceCount()
  throws Exception {

    String responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();

    assertEquals("Process started. Number of currently running"
      + " process instances = 1", responseBody);

    responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();

    assertEquals("Process started. Number of currently running"
      + " process instances = 2", responseBody);

    responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();

    assertEquals("Process started. Number of currently running"
      + " process instances = 3", responseBody);
}

3. Brincando com Processos

Agora que temos um processo em execução no Activiti usando Spring Boot, vamos estender o exemplo acima para demonstrar como podemos acessar e manipular o processo.

3.1. Obtenha a lista deTasks para um determinadoProcessInstance

Temos duas tarefas de usuárioA eB. Quando iniciamos um processo, ele espera que a primeira tarefaA seja concluída e, em seguida, executa a tarefaB. Vamos criar um método manipulador que aceita solicitações para visualizar as tarefas relacionadas a um determinadoprocessInstance.

Os objetos, comoTask, não podem ser enviados como uma resposta diretamente e, portanto, precisamos criar um objeto personalizado e converterTask em nosso objeto personalizado. Vamos chamar essa classe deTaskRepresentation:

class TaskRepresentation {
    private String id;
    private String name;
    private String processInstanceId;

    // standard constructors
}

O método manipulador será semelhante a:

@GetMapping("/get-tasks/{processInstanceId}")
public List getTasks(
  @PathVariable String processInstanceId) {

    List usertasks = taskService.createTaskQuery()
      .processInstanceId(processInstanceId)
      .list();

    return usertasks.stream()
      .map(task -> new TaskRepresentation(
        task.getId(), task.getName(), task.getProcessInstanceId()))
      .collect(Collectors.toList());
}

Aqui,taskService.createTaskQuery().processInstanceId(processInstanceId).list() usaTaskServicee nos dá a lista de tarefas relacionadas aoprocessInstanceId fornecido. Podemos ver que quando começarmos a executar o processo que criamos, obteremos a tarefaA fazendo uma solicitação ao método que acabamos de definir:

@Test
public void givenProcess_whenProcessInstance_thenReceivedRunningTask()
  throws Exception {

    this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn()
      .getResponse();
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()
      .orderByProcessInstanceId()
      .desc()
      .list()
      .get(0);
    String responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/get-tasks/" + pi.getId()))
      .andReturn()
      .getResponse()
      .getContentAsString();

    ObjectMapper mapper = new ObjectMapper();
    List tasks = Arrays.asList(mapper
      .readValue(responseBody, TaskRepresentation[].class));

    assertEquals(1, tasks.size());
    assertEquals("A", tasks.get(0).getName());
}

3.2. Concluindo umTask

Agora, veremos o que acontece quando concluímos a tarefaA. Criamos um método manipulador que manipulará as solicitações para completar a tarefaA para oprocessInstance fornecido:

@GetMapping("/complete-task-A/{processInstanceId}")
public void completeTaskA(@PathVariable String processInstanceId) {
    Task task = taskService.createTaskQuery()
      .processInstanceId(processInstanceId)
      .singleResult();
    taskService.complete(task.getId());
}

taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult() cria uma consulta no serviço de tarefa e nos dá a tarefa deprocessInstance fornecido. Este é oUserTask A. A próxima linhataskService.complete(task.getId) completa esta tarefa. Portanto, agora o processo chegou ao fim eRuntimeService não contém nenhumProcessInstances. Podemos ver isso usando o caso de teste JUnit:

@Test
public void givenProcess_whenCompleteTaskA_thenNoProcessInstance()
  throws Exception {

    this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn()
      .getResponse();
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()
      .orderByProcessInstanceId()
      .desc()
      .list()
      .get(0);
    this.mockMvc.perform(MockMvcRequestBuilders.get("/complete-task-A/" + pi.getId()))
      .andReturn()
      .getResponse()
      .getContentAsString();
    List list = runtimeService.createProcessInstanceQuery().list();
    assertEquals(0, list.size());
}

É assim que podemos usar os serviços da Activiti para trabalhar com processos.

4. Conclusão

Neste artigo, analisamos a visão geral do uso da API Activiti com Spring Boot.. Mais informações sobre a API podem ser encontradas emuser guide. Também vimos como criar um processo e executar várias operações nele usando os serviços da Activiti.

Spring Boot facilita o uso, pois não precisamos nos preocupar em criar o banco de dados, implantar os processos ou criarProcessEngine.

Lembre-se de que a integração do Activiti ao Spring Boot ainda está em fase experimental e ainda não é suportada pelo Spring Boot 2.

Como sempre, a implementação de todos os exemplos que vimos pode ser encontradaover on GitHub.