Camunda Team Blog

The new camunda BPMN model API

Written by Sebastian Menski on , under Execution category.
As already mentioned in our last alpha release blog post we are currently working on a BPMN model API to parse, create, edit and write BPMN 2.0 XML files. Last Friday we opened our GitHub repository and have since then created a userguide and some quickstarts.

The BPMN model API offers you a simple and straightforward way to interact with an BPMN 2.0 model. It enables you to gather information from an existing model, edit and extend them. Of course you can also create completely new ones.

Technically the model API is based on a DOM layer which is accessed and manipulated by a general XML API developed by us. On top of this, the BPMN model API encapsulates the interaction with the XML API. The BPMN model API as well as the XML API does not hold any state, which means only the DOM layer has the current model state. This enables us to read information which we currently are not aware of without the risk of losing such information. It also allows non-intrusive editing of BPMN Models, conserving custom formatting and source code comments.

In addition, if your BPMN 2.0 model contains custom elements or attributes they are always accessible even if they are not exposed through the high-level BPMN model Api. Although our BPMN model API cannot be aware of these custom extensions you can obtain and modify them easily with the generic XML API. Another benefit of this concept is that we can already show you the current state despite the fact that the current BPMN model API not yet implements all BPMN 2.0 elements. But the already supported elements are enough to create rather complex processes.

Just check it out!


Show me some code please

Okey after all this promises I want to show you some code examples so you can get a first impression what already is possible with the BPMN model API.

Lets start with a simple example.

A process with a start event, user task and end event
The following code creates this process with the plain BPMN model API and saves it to a new file.
public void testCreateProcess() {
  BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
  Definitions definitions = modelInstance.newInstance(Definitions.class);
  definitions.setTargetNamespace("http://camunda.org/examples");
  modelInstance.setDefinitions(definitions);

  Process process = modelInstance.newInstance(Process.class);
  definitions.addChildElement(process);

  StartEvent startEvent = modelInstance.newInstance(StartEvent.class);
  startEvent.setId("start");
  process.addChildElement(startEvent);

  UserTask userTask = modelInstance.newInstance(UserTask.class);
  userTask.setId("task");
  userTask.setName("User Task");
  process.addChildElement(userTask);

  SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
  sequenceFlow.setId("flow1");
  process.addChildElement(sequenceFlow);
  connect(sequenceFlow, startEvent, userTask);

  EndEvent endEvent = modelInstance.newInstance(EndEvent.class);
  endEvent.setId("end");
  process.addChildElement(endEvent);

  sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
  sequenceFlow.setId("flow2");
  process.addChildElement(sequenceFlow);
  connect(sequenceFlow, userTask, endEvent);

  Bpmn.writeModelToFile(new File("target/new-process.bpmn"), modelInstance);
}

public void connect(SequenceFlow flow, FlowNode from, FlowNode to) {
  flow.setSource(from);
  from.getOutgoing().add(flow);
  flow.setTarget(to);
  to.getIncoming().add(flow);
}
This code generates the following BPMN 2.0 XML file.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<definitions targetNamespace="http://camunda.org/examples" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
  <process>
    <startEvent id="start">
      <outgoing>flow1</outgoing>
    </startEvent>
    <userTask id="task" name="User Task">
      <incoming>flow1</incoming>
      <outgoing>flow2</outgoing>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="start" targetRef="task"/>
    <endEvent id="end">
      <incoming>flow2</incoming>
    </endEvent>
    <sequenceFlow id="flow2" sourceRef="task" targetRef="end"/>
  </process>
</definitions>

Fluent model builder API

Okey I know you say this looks very verbose and becomes easily hundred lines of code to create a complex task like this one.

Although the BPMN model API gives you the possibility to modify the process definition in every aspect there should be an easier way to create process models like the above. And as you might expect here comes another great feature of the BPMN model API: our fluent model builder API. A simple way to create basic BPMN processes with the following BPMN elements:
  • process
  • start event
  • exclusive gateway
  • parallel gateway
  • script task
  • service task
  • user task
  • end event
So lets create this process fully deployable on our camunda BPM platform in less then 50 lines of code. Please have a look at the complete code in our quickstart.
BpmnModelInstance modelInstance = Bpmn.createProcess()
    .name("BPMN API Invoice Process")
    .executable()
  .startEvent()
    .name("Invoice received")
    .formKey("embedded:app:forms/start-form.html")
  .userTask()
    .name("Assign Approver")
    .formKey("embedded:app:forms/assign-approver.html")
    .assignee("demo")
  .userTask()
    .id("approveInvoice")
    .name("Approve Invoice")
    .formKey("embedded:app:forms/approve-invoice.html")
    .assignee("${approver}")
  .exclusiveGateway()
      .name("Invoice approved?")
      .gatewayDirection(GatewayDirection.Diverging)
    .condition("yes", "${approved}")
    .userTask()
      .name("Prepare Bank Transfer")
      .formKey("embedded:app:forms/prepare-bank-transfer.html")
      .candidateGroups("accounting")
    .serviceTask()
      .name("Archive Invoice")
      .className("org.camunda.bpm.example.invoice.service.ArchiveInvoiceService")
    .endEvent()
      .name("Invoice processed")
  .moveToLastGateway()
    .condition("no", "${!approved}")
    .userTask()
      .name("Review Invoice")
      .formKey("embedded:app:forms/review-invoice.html" )
      .assignee("demo")
    .exclusiveGateway()
        .name("Review successful?")
        .gatewayDirection(GatewayDirection.Diverging)
      .condition("no", "${!clarified}")
      .endEvent()
        .name("Invoice not processed")
    .moveToLastGateway()
      .condition("yes", "${clarified}")
      .connectTo("approveInvoice")
  .done();
To learn how to use the fluent builder API  have a look at our userguide. There you can find examples how to extend an existing process. For example to add a new execution path to a gateway or add new elements between two existing elements.

Well, what more can I say then to check it out and and give us feedback!