11. Using (bpmn 2.0) workflows

The framework comes with a module to use bpmn type workflows, which utilizes an sqlalchemy model to store all of it’s metadata and state information.

To design workflows we use the bpmn2 modeler from eclipse (https://www.eclipse.org/bpmn2-modeler/) and some of the properties available in it.

Documentation about BPMN 2.0 modeling is available from different resources, including the standard documentation of the object management group (http://www.bpmn.org/)

Include workflow model and deploy

We start with adding a module file containing only an import of the workflow system, this adds the workflow to our project.

model/valuea/wfsample/workflow.py
1from valuea_framework.workflow import *

Next we run a regular deployment to add the workflow schema and objects to our system.

1cd <your project directory>
2deploymodel --rdbms .

Sample use-case

To demonstrate our workflow features, we will create a simple one which will loop for a couple of times, output the sequence and exits when done.

Our example is modelled in eclipse and counts from x to 9 using a script task which binds to a service, the steps within the workflow are detailed below.

../../_images/workflow_test_valuea_loop_bpmn.png
  1. Start the workflow, this is only a marker to teach the engine where to start. Our workflow will except a parameter x here, which defines the start of the process.

  2. Inclusive gateway, continue workflow from one of the incoming source arrows.

  3. The script task to run, which excepts x and returns x + 1

  4. Exclusive gateway, which evaluates the outgoing flows, where x < 10 returns to 2 and x >= 10 goes to 5

  5. End event, which marks the workflow ready when reached.

Note

The returning flow from [4] to [2] has a special attribute set to signal the workflow engine about the possibility of going back. This attribute (condition language “roundtrip”) has two different goals. It helps [2] to evaluate the steps that need to be finished before it can proceed (steps in the future can be ignored) and helps [4] to predict future work.

To import the workflow in our database, we can use the import procedure and install it into our project directory (which contains a valid config). Given the example bpmn file, we could import the file as follows:

1cd <your project directory>
2python import_bpmn_wf.py <location_to>/test_valuea_loop.bpmn

Most of the bpmn workflow uses the standard (available) types and settings, but we do need some simple mechanisme to instruct tasks what they should do. We support some different field types for this purpose in the property section of the task.

  • xs:parameter, which supports basic mapping features, like a->b (input a is named b)

  • xs:constant maps static values to the input of a service step, like value->b to match the string “value” to b

  • xs:service binds a service to it. The parameter tag

../../_images/workflow_test_valuea_loop_props_3_bpmn.png

Add “plus_one” service

Our workflow consists of one simple task which should be executed until the variable x has reached (but not including) 10.

As usual we first start with a simple service, which in this case prints x and adds one upon calling execute().

services/valuea/samples/plusone.py
 1import valuea_framework.broker.Service
 2
 3
 4class Service(valuea_framework.broker.Service.BaseService):
 5    def __init__(self, *args, **kwargs):
 6        super(Service, self).__init__(*args, **kwargs)
 7
 8    def execute(self):
 9        print (self.get_message(True).x)
10        return {'x': self.get_message(True).x+1}
11

Next we validate our input data, so we can safely assume the variable x being an integer value.

services/valuea/samples/plusone.json
 1{
 2	"$schema": "http://json-schema.org/draft-04/schema#",
 3	"description": "plusone",
 4	"properties": {
 5		"x": {
 6			"type": "integer"
 7		}
 8	},
 9	"required": [
10		"x"
11	]
12}

Putting it all together

To test it locally we will request a database session, like our RelationalService would do internally, using that session we will search for the workflow defined in the sample use-case “test_valuea_loop.bpmn” and execute it using run().

run_wf_loop.py
1import valuea_framework.connectors.rdbms
2from model.valuea.wfsample.workflow import Workflow
3rdbms_session = valuea_framework.connectors.rdbms.ORM().session()
4wf = rdbms_session.query(Workflow).filter(Workflow.name == 'test_valuea_loop.bpmn').first()
5if wf:
6    wfi = wf.create_instance(x=0)
7    wfi.run()
8else:
9    print ("workflow test_valuea_loop.bpmn not loaded")

The following output should be shown:

0
1
2
3
4
5
6
7
8
9

Note

The run() without parameters will keep the workflow running as long there’s no movement or an end status is reached. Starting with run(single_pass=True) will only execute one pass and exit.

Cleanup

Workflow states are saved after execution, in case you want to delete all finished instances of the workflow in our example, you can easily do so with a few lines of codes.

run_wf_cleanup.py
 1import valuea_framework.connectors.rdbms
 2from model.valuea.wfsample.workflow import Workflow, WorkflowInstance
 3
 4rdbms_session = valuea_framework.connectors.rdbms.ORM().session()
 5wf = rdbms_session.query(Workflow).filter(Workflow.name == 'test_valuea_loop.bpmn').first()
 6
 7if wf:
 8    for wfi in rdbms_session.query(WorkflowInstance)\
 9            .filter(WorkflowInstance.status == 'FIN')\
10            .filter(WorkflowInstance.workflow_id == Workflow.id):
11        rdbms_session.delete(wfi)
12    rdbms_session.commit()

The code above finds our workflow, then queries all finished instances and deletes them one by one (in cascading mode).

Next steps

You can easily use workflows in regular services or scripts.