Dynamic Process Creation and Deployment in 100 ...

We recently released the 5.12 version of Activiti and it’s packed with a lot of new features and improvements. As of the 5.11 version, it’s possible to build BPMN 2.0 processes using a POJO-model. In the latest release, we embrace that POJO-model even more and use it in the core of Activiti as a means of retrieving and deploying process-definitions (on top of the existing deployment formats) using the API.

Combined with other features of Activiti this allows us to build a process, deploy it, start it, test it and retrieve the process definition diagram in under 100 lines of code. By leveraging the new activiti-bpmn-autolayout module, the process elements can be automatically layout, getting the graphical information (BPMN-DI) for free.

The code

I started off with an Activiti unit-test template, which uses a default Activiti-engine running on an in-memory H2 database. The code is written as a simple unit-test, using the built-in JUnit 4 support to have a fully initialized engine and API ready to use when the test starts to run. Full version of the code below can be found on Github.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Test
public void testDynamicDeploy ( ) throws Exception {
   // 1. Build up the model from scratch
   BpmnModel model = new BpmnModel ( ) ;
   Process process = new Process ( ) ;
   model . addProcess ( process ) ;
   process . setId ( "my-process" ) ;
 
   process . addFlowElement ( createStartEvent ( ) ) ;
   process . addFlowElement ( createUserTask ( "task1" , "First task" , "fred" ) ) ;
   process . addFlowElement ( createUserTask ( "task2" , "Second task" , "john" ) ) ;
   process . addFlowElement ( createEndEvent ( ) ) ;
 
   process . addFlowElement ( createSequenceFlow ( "start" , "task1" ) ) ;
   process . addFlowElement ( createSequenceFlow ( "task1" , "task2" ) ) ;
   process . addFlowElement ( createSequenceFlow ( "task2" , "end" ) ) ;
 
   // 2. Generate graphical information
   new BpmnAutoLayout ( model ) . execute ( ) ;
 
   // 3. Deploy the process to the engine
   Deployment deployment = activitiRule . getRepositoryService ( ) . createDeployment ( )
     . addBpmnModel ( "dynamic-model.bpmn" , model ) . name ( "Dynamic process deployment" )
     . deploy ( ) ;
 
   // 4. Start a process instance
   ProcessInstance processInstance = activitiRule . getRuntimeService ( )
     . startProcessInstanceByKey ( "my-process" ) ;
 
   // 5. Check if task is available
   List tasks = activitiRule . getTaskService ( ) . createTaskQuery ( )
     . processInstanceId ( processInstance . getId ( ) ) . list ( ) ;
 
   Assert . assertEquals ( 1 , tasks . size ( ) ) ;
   Assert . assertEquals ( "First task" , tasks . get ( 0 ) . getName ( ) ) ;
   Assert . assertEquals ( "fred" , tasks . get ( 0 ) . getAssignee ( ) ) ;
 
   // 6. Save process diagram to a file  
   InputStream processDiagram = activitiRule . getRepositoryService ( )
     . getProcessDiagram ( processInstance . getProcessDefinitionId ( ) ) ;
   FileUtils . copyInputStreamToFile ( processDiagram , new File ( "target/diagram.png" ) ) ;
 
   // 7. Save resulting BPMN xml to a file
   InputStream processBpmn = activitiRule . getRepositoryService ( )
     . getResourceAsStream ( deployment . getId ( ) , "dynamic-model.bpmn" ) ;
   FileUtils . copyInputStreamToFile ( processBpmn ,
     new File ( "target/process.bpmn20.xml" ) ) ;
}
 
protected UserTask createUserTask ( String id , String name , String assignee ) {
   UserTask userTask = new UserTask ( ) ;
   userTask . setName ( name ) ;
   userTask . setId ( id ) ;
   userTask . setAssignee ( assignee ) ;
   return userTask ;
}
 
protected SequenceFlow createSequenceFlow ( String from , String to ) {
   SequenceFlow flow = new SequenceFlow ( ) ;
   flow . setSourceRef ( from ) ;
   flow . setTargetRef ( to ) ;
   return flow ;
}
 
protected StartEvent createStartEvent ( ) {
   StartEvent startEvent = new StartEvent ( ) ;
   startEvent . setId ( "start" ) ;
   return startEvent ;
}
 
protected EndEvent createEndEvent ( ) {
   EndEvent endEvent = new EndEvent ( ) ;
   endEvent . setId ( "end" ) ;
   return endEvent ;
}
  1. Using the BPMN-model, we create a simple process containing a start-event, 2 usertaks, an end-event and the nessecairy flows connecting them.
  2. We use the BpmnAutoLayout class, found in the activiti-bpmn-autolayout module, to make sure all processes in the BpmnModel have a graphical representation defined.
  3. Using the new addBpmnModel(…) method on DeploymentBuilder, we make sure out created process gets deployed in the engine.
  4. We start a new instance of our process by using the process-key we defined in our process.
  5. Fetch all waiting tasks for the started process and check if the task’s name and assignee are correct.
  6. To check what the process actually looks like, we save the diagram-image (created based on the BPMN-DI information):Process-definition diagram
  7. Finally, save the BPMN 2.0 xml representation of this process. This allows us to, for example, further refine the process in other modeling tools like Activiti Designer.

Possibilities

For demonstration purposes I created a relatively simple process, but you can imagine the potential if you consider that the POJO-model allows you to use all supported BPMN 2.0 constructs as well as all Activiti-specific extentions.

Using this approach you can create processes at runtime without the need for a design-tool or having to juggle around with XML. It can be used, for example, to create a process-model based on your own “intermediate model” or “building-blocks”, hiding complexity to end-users without sacrificing the richness of the BPMN language.

This entry was posted in  activiti,  bpm,  java on  11 March, 2013.

你可能感兴趣的:(Dynamic Process Creation and Deployment in 100 ...)