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:
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.
