Search

JSON Handling in Camunda BPM & Flowable

Most companies that use a workflow product to automate their business processes have a need to process data in JSON - JavaScript Object Notation - format. Although JSON is used in ECMAScript/JavaScript for the representation of objects, its plain-text and yet compact representation of data has resulted in its almost ubiquitous use across many different platforms and products here in 2020. We'll look at the different mechanisms that Camunda BPM and Flowable provide for reading, writing and storing JSON within their popular, open-source workflow products.


First, a quick introduction to Camunda and Flowable. They are head-to-head competitors in the niche open-source workflow market, both offering powerful and flexible open-source workflow engines that can help users execute very large volumes of process instances much more efficiently than the more mainstream workflow software options. Interestingly, they're related, both having been forked from Alfresco's Activiti project, and both have provided meaningful and significant enhancements to the original codebase to support increasingly more complex use cases within their respective products. Both distribute Apache-licensed versions, and both offer additional, premium features in paid versions.


As a quick note, since each JSON object can be represented simply as a string/literal, it's possible to use JSON in its "stringified" form in both platforms. However, this approach quickly fails many users, as (a) long JSON strings quickly exceed the storage capabilities for those strings in both platforms and (b) many platforms - including Java, the platform on which both of these tools are built - lack native support for JSON.


Using JSON in Camunda


Let's look at how Camunda enables the reading, writing and storage of JSON objects within its Camunda BPM product first. Camunda's approach is to use its own Spin project, which is designed to allow for easier reading and writing of JSON objects and to be usable in any Java project, not just within Camunda BPM. However, when used within Camunda BPM, Spin provides a native way in which the process engine will recognize JSON objects, thereby allowing for the storage of very large JSON objects (more than 4,000 characters by default). To use Spin to create a JSON string in Camunda BPM using JavaScript, the following code can be used:



var SpinValues = Java.type('org.camunda.spin.plugin.variable.SpinValues');

var sierra = {};
sierra.displacement = 6.2;
sierra.engineType = 'V8';
sierra.forcedInduction = false;
sierra.horsepower = 420;
sierra.year = 2018;
sierra.make = 'GMC';
sierra.model = 'Sierra';

execution.setVariable('sierra', SpinValues.jsonValue(JSON.stringify(sierra)).create());


In the code above, the line that reads execution.setVariable('sierra', SpinValues.jsonValue(JSON.stringify(sierra)).create()) both converts the JavaScript object to a JacksonJsonNode and adds the variable to the list of process instance variables. Since the variable is of type JacksonJsonNode, the variable is automatically recognized as a Spin JSON variable. This causes the variable to be stored in the ACT_GE_BYTEARRAY table as a large object. This large object is called different things in different databases; for example, in H2, it's called a LONGVARBINARY.


It is also possible to create the JSON object from scratch using Spin; here's a facsimile of the above using Java and Spin to create the JSON object instead of using JavaScript and Spin:



SpinJsonNode sierra = Spin.JSON("{}");
sierra.prop("displacement", 6.2);
sierra.prop("engineType", "V8");
sierra.prop("forcedInduction", false);
sierra.prop("horsepower", 420);
sierra.prop("year", 2018);
sierra.prop("make", "GMC");
sierra.prop("model", "Sierra");

execution.setVariable("sierra", sierra);


To retrieve the variable and introspect it, we would simply need to use an execution.getVariable('sierra') call and then use the returned JacksonJsonNode instance to read the desired data, like this (in JavaScript):



var sierra = execution.getVariable('sierra');
var make = sierra.prop('make').stringValue();


Using JSON in Flowable


Now, let's look at Flowable and their approach. Unlike Camunda, Flowable has chosen to natively support the popular Jackson library for the parsing, creation and storage of JSON objects. This native* support of Jackson means that Java developers can use a library with which they're familiar and which has much more broad-based support in the market. To wit, if a JsonNode object - which has a rich set of subclasses - is stored as a variable in Flowable, it's automatically recognized as a JSON object and is stored either as a regular string or in the ACT_GE_BYTEARRAY table. The data is stored as a regular string only if its length is less than the maximum number of allowed characters in the field where it's stored (TEXT_). (You may have noticed that both engines use the same table for the storage of larger objects; this is a result of their common ancestry.)


Here's how you could create the sierra object and store it as a process instance variable in Flowable:



var JsonNodeFactory = Java.type('com.fasterxml.jackson.databind.node.JsonNodeFactory');

var sierra = {};
sierra.displacement = 6.2;
sierra.engineType = 'V8';
sierra.forcedInduction = false;
sierra.horsepower = 420;
sierra.year = 2018;
sierra.make = 'GMC';
sierra.model = 'Sierra';

execution.setVariable('sierra', JsonNodeFactory.instance.textNode(JSON.stringify(sierra)));


As we did with Camunda BPM, in this first example, we've created the JSON object using the native JavaScript capabilities for the same. To follow the same pattern, here's an example - in Java - of how you could create the object using Jackson and then store it within Flowable:



ObjectNode sierra = JsonNodeFactory.instance.objectNode();

sierra.put("displacement", 6.2);
sierra.put("engineType", "V8");
sierra.put("forcedInduction", false);
sierra.put("horsepower", 420);
sierra.put("year", 2018);
sierra.put("make", "GMC");
sierra.put("model", "Sierra");

execution.setVariable("sierra", sierra);


To retrieve the variable and introspect it, we would use the execution.getVariable('sierra') code and could then use the native methods available within Jackson to introspect the JSON object:



var sierra = execution.getVariable('sierra');
var make = JSON.parse(sierra.textValue()).make;


In both cases, the sierra variables can then be accessed in expressions, scripts or in Java classes as desired or as necessary given your business requirements.


Closing Thoughts


Both Spin/Camunda and Jackson/Flowable provide easy ways to read, write and access JSON objects in your process models, and neither is cumbersome for even an intermediate-level developer to use. But which is better?


We prefer Flowable's approach, as it leverages the more intuitive, more flexible and more ubiquitous Jackson utility, enabling Java developers to use something with which they're already familiar. Moreover, as developers, we like that Flowable stores the JSON in the database in plain text rather than in binary form if possible, making it easier to read that value. In this case, advantage Flowable.


If you have any additional questions or need help with your use of JSON in either Camunda BPM or Flowable, please don't hesitate to reach out to us at info@summit58.co.


* - Camunda uses Jackson "behind the scenes" in Spin but has chosen to extend Jackson instead of using it directly. Flowable uses Jackson natively/directly.

1,016 views3 comments

Recent Posts

See All