Generate Model API
Target audience: Developers
Overview
In this how-to guide you will learn how to use the model generation APIs available in the Flowable Java backend.
We use that API heavily when creating models for unit-testing purposes as an alternative to just use the JSON or XML (e.g. BPMN, CMMN or DMN) source format when generating and deploying models we need for unit testing.
Another use-case is to use that model API to generate models based on other formats like you might have from legacy systems where you want an automatic one-to-one migration from the old format to the Flowable models.
Example 1: Generate simple BPMN process model
In this simple example, we use the BPMN model API to create a very basic and simple BPMN process model to showcase how the API can be used to create deployable BPMN XML models.
public void testProcessModelGeneration() {
// create the BPMN model container and one process within (typically you will have one process
// per BPMN model, although the standard would allow multiple processes per BPMN xml file)
BpmnModel bpmnModel = new BpmnModel();
Process processModel = new Process();
processModel.setName("My Process");
processModel.setId("myProcess");
bpmnModel.addProcess(processModel);
// create and add the start event of the process
StartEvent startEvent = new StartEvent();
startEvent.setId("startEvent");
processModel.addFlowElement(startEvent);
// and the end event as well
EndEvent endEvent = new EndEvent();
endEvent.setId("endEvent");
processModel.addFlowElement(endEvent);
// create a first user task and add it to the process as an element
UserTask userTaskA = new UserTask();
userTaskA.setId("userTaskA");
userTaskA.setName("User Task A");
processModel.addFlowElement(userTaskA);
// do it again for a second user task
UserTask userTaskB = new UserTask();
userTaskB.setId("userTaskB");
userTaskB.setName("User Task B");
processModel.addFlowElement(userTaskB);
// now create a sequence flow connecting the start event and the first user task
SequenceFlow flow1 = new SequenceFlow(startEvent.getId(), userTaskA.getId());
flow1.setId("flow1");
processModel.addFlowElement(flow1);
// do the same to connect both user tasks
SequenceFlow flow2 = new SequenceFlow(userTaskA.getId(), userTaskB.getId());
flow2.setId("flow2");
processModel.addFlowElement(flow2);
// and finally, add a new sequence flow connecting the second user task with the end event
SequenceFlow flow3 = new SequenceFlow(userTaskB.getId(), endEvent.getId());
flow3.setId("flow3");
processModel.addFlowElement(flow3);
// now we can export the process model to BPMN XML
byte[] xml = new BpmnXMLConverter().convertToXML(bpmnModel);
String processModelXml = new String(xml, StandardCharsets.UTF_8);
}
And this is the BPMN XML which will be generated with the example above:
<?xml version='1.0' encoding='UTF-8'?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/test">
<process id="myProcess" name="My Process" isExecutable="true">
<startEvent id="startEvent"/>
<endEvent id="endEvent"/>
<userTask id="userTaskA" name="User Task A"/>
<userTask id="userTaskB" name="User Task B"/>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="userTaskA"/>
<sequenceFlow id="flow2" sourceRef="userTaskA" targetRef="userTaskB"/>
<sequenceFlow id="flow3" sourceRef="userTaskB" targetRef="endEvent"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess"/>
</bpmndi:BPMNDiagram>
</definitions>
You can directly deploy such a generated process model to the runtime of Flowable using this code snipped:
processEngine
.getRepositoryService()
.createDeployment()
.name("myProcess").addString("MyProcess.bpmn20.xml", new String(xml))
.deploy();
Of course, you can also directly import this generated BPMN model into Flowable Design, if you want to be able to modify the model before deploying it.
If you open the imported BPMN process model, it might look something like this:
Now you can either directly deploy and run the model, if you also defined all necessary technical details or you can use it as a starting point and further develop and define the model according your needs and requirements.
Example 2: More sophisticated BPMN process model
In this example, we are going to create another BPMN process model which has a bit more complexity, still very simple, but to just add some more elements like gateways and service tasks and even use conditional sequence flows to explain how every detail can be defined using the Java model API.
Create a process model building helper
If you look at the code we had to write in our first simple example, you might have seen that it is very verbose and not very fluent. Currently, the API is mostly used internal to reflect all the models in a Java object model used at runtime. So its main purpose is not to use it for an API-based model generation.
If you want to make use of the model API more heavily, it might make sense to write a small builder API on top to be able to easier write models using that helper building API.
Basic example of such a helper class:
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.ExternalWorkerServiceTask;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SendEventServiceTask;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
public class ProcessModelBuilder {
public static String FLOWABLE_NAMESPACE = "http://flowable.org/bpmn";
public static String FLOWABLE_NAMESPACE_PREFIX = "flowable";
protected BpmnModel bpmnModel;
protected Process processModel;
public ProcessModelBuilder(String processName, String processId) {
bpmnModel = new BpmnModel();
processModel = new Process();
processModel.setName(processName);
processModel.setId(processId);
bpmnModel.addProcess(processModel);
}
public StartEvent addStartEvent(String eventId) {
StartEvent startEvent = new StartEvent();
startEvent.setId(eventId);
processModel.addFlowElement(startEvent);
return startEvent;
}
public EndEvent addEndEvent(String eventId) {
EndEvent endEvent = new EndEvent();
endEvent.setId(eventId);
processModel.addFlowElement(endEvent);
return endEvent;
}
public UserTask addUserTask(String taskId, String taskName) {
UserTask userTask = new UserTask();
userTask.setId(taskId);
userTask.setName(taskName);
processModel.addFlowElement(userTask);
return userTask;
}
public ServiceTask addServiceTask(String taskId, String taskName) {
ServiceTask serviceTask = new ServiceTask();
serviceTask.setId(taskId);
serviceTask.setName(taskName);
processModel.addFlowElement(serviceTask);
return serviceTask;
}
public ExternalWorkerServiceTask addExternalWorkerServiceTask(String taskId, String taskName) {
ExternalWorkerServiceTask externalWorkerServiceTask = new ExternalWorkerServiceTask();
externalWorkerServiceTask.setId(taskId);
externalWorkerServiceTask.setName(taskName);
processModel.addFlowElement(externalWorkerServiceTask);
return externalWorkerServiceTask;
}
public SendEventServiceTask addSendEventServiceTask(String taskId, String taskName) {
SendEventServiceTask sendEventServiceTask = new SendEventServiceTask();
sendEventServiceTask.setId(taskId);
sendEventServiceTask.setName(taskName);
processModel.addFlowElement(sendEventServiceTask);
return sendEventServiceTask;
}
public ExclusiveGateway addExclusiveGateway(String gatewayId) {
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(gatewayId);
processModel.addFlowElement(exclusiveGateway);
return exclusiveGateway;
}
public SequenceFlow addSequenceFlow(String flowId, BaseElement source, BaseElement target) {
SequenceFlow sequenceFlow = new SequenceFlow();
sequenceFlow.setId(flowId);
sequenceFlow.setSourceRef(source.getId());
sequenceFlow.setTargetRef(target.getId());
processModel.addFlowElement(sequenceFlow);
return sequenceFlow;
}
public ExtensionElement addExtensionElement(BaseElement element, String name, String value) {
ExtensionElement extensionElement = new ExtensionElement();
extensionElement.setNamespace(FLOWABLE_NAMESPACE);
extensionElement.setNamespacePrefix(FLOWABLE_NAMESPACE_PREFIX);
extensionElement.setName(name);
extensionElement.setElementText(value);
if (element != null) {
element.addExtensionElement(extensionElement);
}
return extensionElement;
}
public byte[] getBpmnXml() {
return new BpmnXMLConverter().convertToXML(bpmnModel);
}
}
Use the builder to create a more complex model
As we have a basic helper now making the model API easier to use, we can create a new model more easily and a bit less verbose than using the basic plain model API directly.
public void testProcessModelGeneration() {
ProcessModelBuilder builder = new ProcessModelBuilder("My Process", "myProcess");
StartEvent startEvent = builder.addStartEvent("startEvent");
EndEvent endEvent = builder.addEndEvent("endEvent");
UserTask userTaskA = builder.addUserTask("userTaskA", "Task A");
UserTask userTaskB = builder.addUserTask("userTaskB", "Task B");
UserTask userTaskC = builder.addUserTask("userTaskC", "Task C");
ExclusiveGateway gatewayStart = builder.addExclusiveGateway("gatewayStart");
gatewayStart.setName("Go to B or C?");
ExclusiveGateway gatewayEnd = builder.addExclusiveGateway("gatewayEnd");
ExternalWorkerServiceTask externalTask = builder.addExternalWorkerServiceTask("externalTask", "External Worker Task");
externalTask.setTopic("externalTopic");
SendEventServiceTask sendEventTask = builder.addSendEventServiceTask("sendEvent", "Send Event");
sendEventTask.setEventType("myEventKey");
builder.addSequenceFlow("flow1", startEvent, userTaskA);
builder.addSequenceFlow("flow2", userTaskA, gatewayStart);
builder.addSequenceFlow("flow3", gatewayStart, userTaskB).setConditionExpression("${decision == 'B'}");
builder.addSequenceFlow("flow4", gatewayStart, userTaskC).setConditionExpression("${decision == 'C'}");
builder.addSequenceFlow("flow5", userTaskB, gatewayEnd);
builder.addSequenceFlow("flow6", userTaskC, sendEventTask);
builder.addSequenceFlow("flow7", sendEventTask, gatewayEnd);
builder.addSequenceFlow("flow8", gatewayEnd, externalTask);
builder.addSequenceFlow("flow9", externalTask, endEvent);
byte[] xml = builder.getBpmnXml();
String processModel = new String(xml, StandardCharsets.UTF_8);
}
The generated BPMN process model might look something like this:
<?xml version='1.0' encoding='UTF-8'?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/test">
<process id="myProcess" name="My Process" isExecutable="true">
<startEvent id="startEvent"/>
<endEvent id="endEvent"/>
<userTask id="userTaskA" name="Task A"/>
<userTask id="userTaskB" name="Task B"/>
<userTask id="userTaskC" name="Task C"/>
<exclusiveGateway id="gatewayStart" name="Go to B or C?"/>
<exclusiveGateway id="gatewayEnd"/>
<serviceTask id="externalTask" name="External Worker Task" flowable:type="external-worker" flowable:topic="externalTopic"/>
<serviceTask id="sendEvent" name="Send Event" flowable:type="send-event">
<extensionElements>
<flowable:eventType><![CDATA[myEventKey]]></flowable:eventType>
</extensionElements>
</serviceTask>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="userTaskA"/>
<sequenceFlow id="flow2" sourceRef="userTaskA" targetRef="gatewayStart"/>
<sequenceFlow id="flow3" sourceRef="gatewayStart" targetRef="userTaskB">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${decision == 'B'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="gatewayStart" targetRef="userTaskC">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${decision == 'C'}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="userTaskB" targetRef="gatewayEnd"/>
<sequenceFlow id="flow6" sourceRef="userTaskC" targetRef="sendEvent"/>
<sequenceFlow id="flow7" sourceRef="sendEvent" targetRef="gatewayEnd"/>
<sequenceFlow id="flow8" sourceRef="gatewayEnd" targetRef="externalTask"/>
<sequenceFlow id="flow9" sourceRef="externalTask" targetRef="endEvent"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess"/>
</bpmndi:BPMNDiagram>
</definitions>
If we import it into Flowable Design, it might look like this:
Fluent model builder API
If you are planning to use this API quite frequently or heavily, you might even go the extra mile and create a full fluent builder API on top of the basic model API from Flowable.
To see how this could be done, let's look at some basic functionality and you will get the picture on how to create such an API.
Generating a BPMN process model using the fluent builder API
But first, we create another simple process using the fluent model builder API, which we discuss a bit later.
public void testProcessModelGeneration() {
// start a new fluent process model builder
byte[] xml = new FluentProcessModelBuilder("My Process", "myProcess")
// add a start event for the process
.addStartEvent("startEvent")
// then directly add a sequence flow to the next element
.addSequenceFlow("flow1")
// add a user task as the target of the sequence flow and set some advanced properties
.addUserTask("userTask")
.name("User Task A")
.priority("medium")
.category("SLA B")
.dueDate("P2D")
// add sequence flow from the task to the gateway
.addSequenceFlow("flow2")
.addExclusiveGateway("startGateway")
.name("Path B or C?")
// the outbound gateway flows need conditions, so we know which path to take
.addSequenceFlow("flow3")
.condition("${outcome == 'taskB'}")
.addUserTask("userTaskB")
.name("User Task B")
.priority("high")
.category("SLA A")
.dueDate("P1D")
.addSequenceFlow("flow4")
// joining the different path' after the exclusive gateway together
.addExclusiveGateway("endGateway")
.addSequenceFlow("flow7")
.addUserTask("userTaskD")
.name("User Task D")
.addSequenceFlow("flow8")
// first path through the process is finishing with an end event
.addEndEvent("endEvent")
// now we want to add the second path and need to go back to the starting gateway
.getFlowElementBuilder("startGateway")
// second path to be added to the gateway with its condition
.addSequenceFlow("flow5")
.condition("${outcome == 'taskC'}")
// still in the second path
.addUserTask("userTaskC")
.name("User Task C")
// the outbound sequence flow of the second path needs to go into the joining end gateway
.addSequenceFlow("flow6")
.targetElement("endGateway")
// we have to get back from the element builder to the main builder in order to generate
// the process model BPMN XML
.getMainBuilder()
.getBpmnXml();
}
The generated BPMN XML will look something like this:
<?xml version='1.0' encoding='UTF-8'?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/test">
<process id="myProcess" name="My Process" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="userTask"/>
<userTask id="userTask" name="User Task A" flowable:dueDate="P2D" flowable:category="SLA B" flowable:priority="medium"/>
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="startGateway"/>
<exclusiveGateway id="startGateway" name="Path B or C?"/>
<sequenceFlow id="flow3" sourceRef="startGateway" targetRef="userTaskB">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome == 'taskB'}]]></conditionExpression>
</sequenceFlow>
<userTask id="userTaskB" name="User Task B" flowable:dueDate="P1D" flowable:category="SLA A" flowable:priority="high"/>
<sequenceFlow id="flow4" sourceRef="userTaskB" targetRef="endGateway"/>
<exclusiveGateway id="endGateway"/>
<sequenceFlow id="flow7" sourceRef="endGateway" targetRef="userTaskD"/>
<userTask id="userTaskD" name="User Task D"/>
<sequenceFlow id="flow8" sourceRef="userTaskD" targetRef="endEvent"/>
<endEvent id="endEvent"/>
<sequenceFlow id="flow5" sourceRef="startGateway" targetRef="userTaskC">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome == 'taskC'}]]></conditionExpression>
</sequenceFlow>
<userTask id="userTaskC" name="User Task C"/>
<sequenceFlow id="flow6" sourceRef="userTaskC" targetRef="endGateway"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess"/>
</bpmndi:BPMNDiagram>
</definitions>
And after importing it into Flowable Design, here it is:
Fluent builder example implementation
As we have seen in the example above, using a fluent builder API makes it very easy to create a process model.
Let's have a look on how such a fluent API can be built on top of the low-level model API in Flowable.
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
public class FluentProcessModelBuilder {
protected static Map<String, String> builderInfoMap = new HashMap<>();
protected BpmnModel bpmnModel;
protected Process processModel;
public FluentProcessModelBuilder(String processName, String processId) {
bpmnModel = new BpmnModel();
processModel = new Process();
processModel.setName(processName);
processModel.setId(processId);
bpmnModel.addProcess(processModel);
}
public StartEventBuilder addStartEvent(String eventId) {
StartEventBuilder builder = new StartEventBuilder(this, eventId);
processModel.addFlowElement(builder.getFlowElement());
return builder;
}
public EndEventBuilder addEndEvent(String eventId) {
EndEventBuilder builder = new EndEventBuilder(this, eventId);
processModel.addFlowElement(builder.getFlowElement());
return builder;
}
public UserTaskBuilder addUserTask(String taskId) {
UserTaskBuilder builder = new UserTaskBuilder(this, taskId);
processModel.addFlowElement(builder.getFlowElement());
return builder;
}
public ExclusiveGatewayBuilder addExclusiveGateway(String gatewayId) {
ExclusiveGatewayBuilder builder = new ExclusiveGatewayBuilder(this, gatewayId);
processModel.addFlowElement(builder.getFlowElement());
return builder;
}
public SequenceFlowBuilder addSequenceFlow(String flowId) {
SequenceFlowBuilder builder = new SequenceFlowBuilder(this, flowId);
processModel.addFlowElement(builder.getFlowElement());
return builder;
}
/**
* Searches for the flow element given by its id, if found, otherwise an exception is thrown.
*
* @param flowElementId the id of the element to be returned
* @return the flow element found according the provided id, never null as there is an exception
* thrown, if the element is not found
* @param <T> the expected type of the element
*/
public <T extends FlowElement> T getFlowElement(String flowElementId) {
FlowElement flowElement = processModel.getFlowElement(flowElementId, true);
if (flowElement == null) {
throw new FlowableException("Could not find flow element with id " + flowElementId);
}
return (T) flowElement;
}
/**
* Returns the builder for the already created element given by its id. This is particularly
* interesting when using the fluent API creating one path at a time and then going back to for
* instance a gateway to continue building the next path without having to store the builders
* beforehand.
*
* @param elementId the id of the element to go back to
* @return the builder for the element specified, if found otherwise an exception is thrown,
* will never return null
* @param <T> the expected type of the element (e.g. a gateway or a user task, etc)
* @param <B> the expected type of the builder wrapping the element
*/
public <T extends FlowElement, B extends AbstractFlowElementBuilder<T, B>> B getFlowElementBuilder(String elementId) {
T flowElement = getFlowElement(elementId);
if (builderInfoMap.isEmpty()) {
initializeBuilderInfoMap();
}
String builderClassName = builderInfoMap.get(flowElement.getClass().getName());
if (builderClassName == null) {
throw new FlowableException("Currently there is no builder available for flow element of class " + flowElement.getClass().getName());
}
try {
B elementBuilder = (B) Class.forName(builderClassName)
.getConstructor(FluentProcessModelBuilder.class, flowElement.getClass())
.newInstance(this, flowElement);
return elementBuilder;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
throw new FlowableException("Could not instantiate builder class " + builderClassName, e);
}
}
/**
* Generates the BPMN XML based on the defined process model and returns it as bytes (in UTF-8).
* @return the BPMN XML according the process model built so far
*/
public byte[] getBpmnXml() {
return new BpmnXMLConverter().convertToXML(bpmnModel);
}
protected void initializeBuilderInfoMap() {
builderInfoMap.put(StartEvent.class.getName(), StartEventBuilder.class.getName());
builderInfoMap.put(EndEvent.class.getName(), EndEventBuilder.class.getName());
builderInfoMap.put(UserTask.class.getName(), UserTaskBuilder.class.getName());
builderInfoMap.put(ExclusiveGateway.class.getName(), ExclusiveGatewayBuilder.class.getName());
builderInfoMap.put(SequenceFlow.class.getName(), SequenceFlowBuilder.class.getName());
}
}
The downside of this fluent API approach is to create builders wrapping each different type of a flow element accordingly to create the fluent API experience. The positive side effect is to be able to wrap or hide complexities wherever needed (e.g. a simple property could be added using the extension elements as in BPMN and hence the builder API could hide that complexity and offer a very simple property setter instead).
Here are some basic builders for the start- and end-event as well as the user task, gateway and sequence flows. To keep it small, not all properties have been wrapped, it is only meant to illustrate how such a builder can be developed.
As those builders share some common functionality, we start with an abstract element builder class:
import org.flowable.bpmn.model.FlowElement;
public abstract class AbstractFlowElementBuilder<T extends FlowElement, B extends AbstractFlowElementBuilder> {
protected final FluentProcessModelBuilder builder;
protected final T flowElement;
protected AbstractFlowElementBuilder(FluentProcessModelBuilder builder, T flowElement) {
this.builder = builder;
this.flowElement = flowElement;
}
public B id(String id) {
flowElement.setId(id);
return (B) this;
}
public B name(String name) {
flowElement.setName(name);
return (B) this;
}
/**
* Adds a sequence flow between the current flow element and offers support to connect to
* the next one or even creating the next one automatically connecting the two elements.
*
* @param flowId the id for the sequence flow
* @return the sequence flow builder, offering support to configure the flow or start and
* connect to the next one
*/
public SequenceFlowBuilder addSequenceFlow(String flowId) {
SequenceFlowBuilder flowBuilder = new SequenceFlowBuilder(builder, flowId)
.sourceElement(getFlowElement());
builder.processModel.addFlowElement(flowBuilder.getFlowElement());
return flowBuilder;
}
public T getFlowElement() {
return flowElement;
}
public <T extends FlowElement, B extends AbstractFlowElementBuilder<T, B>> B getFlowElementBuilder(String elementId) {
return builder.getFlowElementBuilder(elementId);
}
/**
* This method can be used to get back to the main builder when within an element builder,
* e.g. to finalize the process model or to start another path.
*
* @return the main builder this element builder was created with.
*/
public FluentProcessModelBuilder getMainBuilder() {
return builder;
}
}
And here is the list of the specific element builders. Like mentioned before, they don't implement all the property definition, but should highlight, how such builders can be created.
import org.flowable.bpmn.model.StartEvent;
public class StartEventBuilder extends AbstractFlowElementBuilder<StartEvent, StartEventBuilder> {
public StartEventBuilder(FluentProcessModelBuilder builder, String eventId) {
super(builder, new StartEvent());
id(eventId);
}
public StartEventBuilder(FluentProcessModelBuilder builder, StartEvent startEvent) {
super(builder, startEvent);
}
public StartEventBuilder formKey(String formKey) {
flowElement.setFormKey(formKey);
return this;
}
}
import org.flowable.bpmn.model.EndEvent;
public class EndEventBuilder extends AbstractFlowElementBuilder<EndEvent, EndEventBuilder> {
public EndEventBuilder(FluentProcessModelBuilder builder, String eventId) {
super(builder, new EndEvent());
id(eventId);
}
public EndEventBuilder(FluentProcessModelBuilder builder, EndEvent endEvent) {
super(builder, endEvent);
}
}
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SequenceFlow;
public class SequenceFlowBuilder extends AbstractFlowElementBuilder<SequenceFlow, SequenceFlowBuilder> {
public SequenceFlowBuilder(FluentProcessModelBuilder builder, String flowId) {
super(builder, new SequenceFlow());
flowElement.setId(flowId);
}
public SequenceFlowBuilder(FluentProcessModelBuilder builder, SequenceFlow flow) {
super(builder, flow);
}
public SequenceFlowBuilder condition(String conditionExpression) {
flowElement.setConditionExpression(conditionExpression);
return this;
}
public SequenceFlowBuilder sourceElement(FlowElement source) {
flowElement.setSourceRef(source.getId());
return this;
}
public SequenceFlowBuilder targetElement(FlowElement target) {
flowElement.setTargetRef(target.getId());
return this;
}
public SequenceFlowBuilder targetElement(String targetElementId) {
FlowElement element = builder.getFlowElement(targetElementId);
flowElement.setTargetRef(element.getId());
return this;
}
public UserTaskBuilder addUserTask(String taskId) {
UserTaskBuilder taskBuilder = builder.addUserTask(taskId);
targetElement(taskBuilder.getFlowElement());
return taskBuilder;
}
public ExclusiveGatewayBuilder addExclusiveGateway(String gatewayId) {
ExclusiveGatewayBuilder gatewayBuilder = builder.addExclusiveGateway(gatewayId);
targetElement(gatewayBuilder.getFlowElement());
return gatewayBuilder;
}
public EndEventBuilder addEndEvent(String eventId) {
EndEventBuilder eventBuilder = builder.addEndEvent(eventId);
targetElement(eventBuilder.getFlowElement());
return eventBuilder;
}
}
import org.flowable.bpmn.model.UserTask;
public class UserTaskBuilder extends AbstractFlowElementBuilder<UserTask, UserTaskBuilder> {
public UserTaskBuilder(FluentProcessModelBuilder builder, String taskId) {
super(builder, new UserTask());
flowElement.setId(taskId);
}
public UserTaskBuilder(FluentProcessModelBuilder builder, UserTask userTask) {
super(builder, userTask);
}
public UserTaskBuilder formKey(String formKey) {
flowElement.setFormKey(formKey);
return this;
}
public UserTaskBuilder owner(String owner) {
flowElement.setOwner(owner);
return this;
}
public UserTaskBuilder assignee(String assignee) {
flowElement.setAssignee(assignee);
return this;
}
public UserTaskBuilder category(String category) {
flowElement.setCategory(category);
return this;
}
public UserTaskBuilder priority(String priority) {
flowElement.setPriority(priority);
return this;
}
public UserTaskBuilder dueDate(String dueDate) {
flowElement.setDueDate(dueDate);
return this;
}
}
public class ExclusiveGatewayBuilder extends AbstractFlowElementBuilder<ExclusiveGateway, ExclusiveGatewayBuilder> {
public ExclusiveGatewayBuilder(FluentProcessModelBuilder builder, String gatewayId) {
super(builder, new ExclusiveGateway());
flowElement.setId(gatewayId);
}
public ExclusiveGatewayBuilder(FluentProcessModelBuilder builder, ExclusiveGateway gateway) {
super(builder, gateway);
}
}
Conclusion
This how-to should shed some light on the model APIs of Flowable, specifically for the BPMN process model. It described how you can use various ways to build models using the model API, either by just using the existing model elements directly or by having some sort of utility class on top or by creating a fluent builder API that makes model generation very readable and fluent.
The use-cases for this might be very different, be it for a automatic migration for models of an older system towards BPMN / Flowable where you would most likely use the second approach and not necessarily need a fluent API. Or for an alternative model generation on API rather than modeling with Flowable Design.
Important to mention as well: we have been looking into the BPMN process model API only in the shown examples, but of course you can also do it the very same way with CMMN case models, or even event models and channel models, as in Flowable we always follow the same patterns for the different engines.