Review ‘Activiti 5.x Business Process Management’ by Zakir Laliwala and Irshad Mansuri

I’ve been contacted by the people of Packt Publishing to review their recent book release of the ‘Activiti 5.x Business Process Management”, written by Dr. Zakir Laliwala and Irshad Mansuri. For an open source project, books are a good thing. They indicate that a project is popular, and often people prefer books over gathering all the information from bits all over the internet. Don’t expect any insight into use cases, process modeling best practices or how to fit Activiti architecturally though, this is a hands-on description on how you get started technically with Activiti.

The book, as is clear from the cover, clearly aims to people who want to get started with Activiti and have no prior knowledge of it:

7065OS_Activiti 5.x Business Process Management Beginner's Guide

 

So, before going into more details, I’m happy to report that the book does exactly that. It introduces Activiti and its concepts gently and with a lot of screenshots of installing components but also when executing certain code. For people who are well advanced in Activiti certain things are a given, but I can imagine for people that have to start from zero these steps help to get bootstrapped quicker and with a lot less pain during learning.

In the past, I’ve read (and reviewed) books from Packt where the English wasn’t good to a point where it hindered reading it. However, the English in this book is really well-written and understandable. Absolutely no complaints here.

Some of the remarks I noted down some small remarks while reading the book:

  • The XML formatting of the process XML is often not readable due to lack of indentation and formatting. Probably a problem in general for printed books, but it’s very hard to understand the processes without structure.
  • I liked the fact that many concepts in the book were accompanied with a unit test, something I definitely support!
  • The bit about reporting had a bad screenshot, which kinda defeats the purpose of showing reporting
  • In chapter 5, it seems to hint that the activiti.cfg.xml way of configuring the engine is ‘Spring-only’, but actually this can be used outside of spring (only relying on spring-beans to have the parsing)
  • Chapter 6 about the services was really good: clearly explained and always with a short example/unit test. Also explains how to do debugging of processes. Many people will find this chapter useful
  • Chapter 7 was the same, but now for REST. Decent explanation of rest client install *and* unit testing with an http client. Nice.
  • Chapter 8 (integration with Liferay/Drools/OSGI) felt a bit ‘off’. It’s something I would have placed at the end of the book, and I definitely would have placed chapter 9 (advanced Activiti constructs) before as it fits better. Also, it doesn’t really go deep, it’s more about how Activiti is integrated with those technologies, accompanied with screenshots of the installation process. No insight into use cases or how it would be used in reality (granted, which is probably not the goal of this book).

So in summary: I cheer for everything being written about Activiti, so I really like the fact that this book was written. But more-so, I think it targets the beginners audience well. It has one goal: get a technical someone going with Activiti quickly and teaching the basics. I believe  people who are in the dark about Activiti and need to get started will be able to get going quickly with this book.

Important: Activiti 5.15 and MySQL 5.6+ users

Execute Custom queries in Activiti

(This will probably end up in the user guide of the Activiti 5.15 release, but I wanted to share it already)

The Activiti API allows for interacting with the database using a high level API. For example, for retrieving data the Query API and the Native Query API are powerful in its usage. However, for some use cases they might not be flexible enough. The following section described how a completely custom SQL statement (select, insert, updated and deletes are possible) can be executed against the Activiti data store, but completely within the configured Process Engine (and thus levering the transaction setup for example).

To define custom SQL statements, the Activiti engine leverages the capabilities of its underlying framework, MyBatis. The first thing to do when using custom SQL, is to create a MyBatis mapper class. More info can be read in the MyBatis user guide. For example, suppose that for some use case not the whole task data is needed, but only a small subset of it. A Mapper that could do this, looks as follows:

public interface MyTestMapper {

  @Select("SELECT ID_ as id, NAME_ as name, CREATE_TIME_ as createTime FROM ACT_RU_TASK")
  List<Map<String, Object>> selectTasks();

}

This mapper must be provided to the Process Engine configuration as follows:

...
<property name="customMybatisMappers">
  <set>
    <value>org.activiti.standalone.cfg.MyTestMapper</value>
  </set>
</property>
...

Notice that this is an interface. The underlying MyBatis framework will make an instance of it that can be used at runtime. Also notice that the return value of the method is not typed, but a list of maps (which corresponds to the list of rows with column values). Typing is possible with the MyBatis mappers if wanted.

To execute the query above, the managementService.executeCustomSql method must be used. This method takes in a CustomSqlExecution instance. This is a wrapper that hides the internal bits of the engine otherwise needed to make it work.

Unfortunately, Java generics make it a bit less readable than it could have been. The two generic types below are the mapper class and the return type class. However, the actual logic is simply to call the mapper method and return its results (if applicable).

CustomSqlExecution<MyTestMapper, List<Map<String, Object>>> customSqlExecution =
    new AbstractCustomSqlExecution<MyTestMapper, List<Map<String, Object>>>(MyTestMapper.class) {

  public List<Map<String, Object>> execute(MyTestMapper customMapper) {
    return customMapper.selectTasks();
  }

};

List<Map<String, Object>> results = managementService.executeCustomSql(customSqlExecution);

The Map entries in the list above will only contain id, name and create time in this case and not the full task object.

Any SQL is possible when using the approach above. Another more complex example:

  @Select({
    "SELECT task.ID_ as taskId, variable.LONG_ as variableValue FROM ACT_RU_VARIABLE variable",
    "inner join ACT_RU_TASK task on variable.TASK_ID_ = task.ID_",
    "where variable.NAME_ = #{variableName}"
  })
  List<Map<String, Object>> selectTaskWithSpecificVariable(String variableName);

Using this method, the task table will be joined with the variables table. Only where the variable has a certain name is retained, and the task id and the corresponding numerical value is returned.

This will be possible in Activiti 5.15. However, the code (and more specifically the Command implementation and the wrapper interface) can be used in any older version of Activiti.

Brazos For Activiti

As I mentioned a while ago, BP3 has become on of our enterprise partners for Activiti. The people of BP3 always said that they wanted to contribute to the open-source code of Activiti, but of course the proof of the pudding is in the eating.

We’re very enthousiast about what they’ve shown us recently: a full fledged drag and drop UI builder for Activiti forms, based on their experiences with building such a product on top IBM BPM (but now of course way cooler). I wanted to give it the exposure it deserves: see it for yourself!

And quoting Scott Francis,

we’re going to release this into the open source community on the same licensing terms as Activiti (Apache).  We think this could really elevate the way people approach building process-enabled application UIs with Activiti, and it seems like a great first contribution from BP3 to the Activiti open source community.

That’s just awesome. Stay tuned!

Advanced scripting in Activiti: Custom Configuration Injection

The scripting task is probably one of the ‘oldest’ classes in the Activiti code base, but I think it is still underused by many. The (perceived?) downsides are of course performance (interpretation vs compilation) and less support from IDE perspective.

However, the benefits (imo) outweigh this:

  • Scripts are defined in the process xml itself. No more worries about versioning and having to juggle with libs on the classpath.
  • What we’ve seen in the past is that less technical skilled people dare to try scripts. But never Java.

Anyway, what few people know or have realized is that you can do really awesome and advance stuff in scripts in Activiti. Since such a script is executed within the process engine, you have access to everything the engine is capable of. Yes … everything… which makes it both a very powerful but also (potential) dangerous thing (if you don’t know what you’re doing).

Let me walk you through such an example. I like to call it ‘custom configuration injection’ as a concept, because it effectively allows you to add custom logic at runtime which alters process execution significantly. If you have a cooler name for it, please let me know.

All code can be found on my Github Page: https://github.com/jbarrez/activiti-advanced-scripting

awesome-code-648x303

The use case

Now what is this thing I want to do. Well, I want to have a process that, when executed

  • Adds a ‘task completion event handler’ to every user task that is executed
  • This event handler must fire a custom event off to a remote URL, where potentially a event processor is doing its stuff

So basically, we want to fire off custom events to some remote URL whenever a task gets completed. A good use case for this could be Business Intelligence reporting/Complex event processing, eg with something like Esper.

Screen Shot 2013-07-23 at 10.03.11

The first version

The first cut of this functionality can be found at https://github.com/jbarrez/activit-advanced-scripting/blob/master/src/test/resources/org/activiti/test/my-process.bpmn20.xml. When this process is executed, the following happens:

var config = Context.getProcessEngineConfiguration();
var bpmnParser = config.getBpmnParser();

We simply fetch the current ProcessEngineConfiguration instance. We fetch the BpmnParser instance from this configuration, as we will want to change the general user task parsing for the whole engine.

Next, we build the script:

var script = "";
script = script + "importPackage(java.net);";
script = script + "importPackage(java.io);";
script = script + "var url = new URL('http://localhost:8182/echo');";
script = script + "var connection = url.openConnection();";
script = script + "connection.setRequestMethod('POST');";
script = script + "connection.setDoOutput(true);";
script = script + "var outputStream = new BufferedOutputStream(connection.getOutputStream());";
script = script + "outputStream.write(new java.lang.String(\"{'eventType':'task-complete'}\").bytes);";
script = script + "outputStream.flush();";
script = script + "connection.connect();";
script = script + "var respCode = connection.getResponseCode();";
script = script + "if (respCode != 200) ";
script = script + "println('Response code : ' + respCode);";
script = script + "outputStream.close();";
script = script + "connection.disconnect();";

This is obviously not the most efficient way to do this, but it sure shows the details of what happens. The message ‘eventType:task-complete’ is send to the localhost:8182 url through standard java.net and java.io classes.

The tricky part comes next:

var handler = new ExecuteScriptOnTaskCompleteBpmnParseHandler("JavaScript");
handler.setUserTaskCompleteScript(script);
bpmnParser.getBpmnParserHandlers().addHandler(handler);

// reset the deployment cache such that the new listener gets picked up on a new redeploy
config.getProcessDefinitionCache().clear();

Here we add a BpmnParseHandler class to the engine configuration. The parse handler will add the execution of the script defined above to every receival of the ‘task complete event’ send out by the engine. This parse handler kicks in every time a user task is parsed, which effectively adds our ‘send-event-to-remote-service’ to every user task now happening in your Activiti environment!

There is a unit test to see how this works: https://github.com/jbarrez/activiti-advanced-scripting/blob/master/src/test/java/org/activiti/test/ExecuteScriptInProcessTest.java. In the test, we setup a very simple ‘echo service’ which simply prints out whenever such an event is received. If you run it in your IDE, you’ll see something like this:

Screen Shot 2013-07-23 at 09.53.00

 

But we can do better

But we can do better. Check the following code.

var handler = new ExecuteScriptOnTaskCompleteBpmnParseHandler("JavaScript");
handler.setUserTaskCompleteScript("http://localhost:8182/scripts/task-complete.js");
handler.setExecuteScriptInJob(true);
bpmnParser.getBpmnParserHandlers().addHandler(handler);

// Update the configuration to use the correct job handler
var jobHandler = new ExecuteScriptJobHandler();
config.getJobHandlers().put(jobHandler.type,jobHandler);

This code does the same as in the previous section, ie. attaching a listener for ‘complete’ events to every user task. However, this implementation:

  • Executes the script asynchronously
  • Does not define the script in the process xml, but it is fetched from a remote url
  • Updates the job handler configuration

If you ask me, that’s pretty awesome! So this means that the actual sending of a message to the remote service is not impacting the execution performance of your process instance. Obviously, from here you can go crazy and add persistent queues and all that fanciness. And on top of that, the script is always fetched from a remote server. If you want to update the logic that is executed, simply change the script that is returned. This means you can impact process execution AT RUNTIME without touching the actual process.

There is a unit test for this at https://github.com/jbarrez/activiti-advanced-scripting/blob/master/src/test/java/org/activiti/test/ExecuteScriptWithJobTest.java

If you run this test,  you’ll see the following. Note that we host the completion script as static file called ‘task-complete.js’ on the test server.

Screen Shot 2013-07-23 at 09.50.36

In the test, you can see we have to execute the async job specifically to see the output of the test.

Caveat

On small caveat here: when the process engine reboots, the configuration will be reloaded from config file. Hence, the process from above that injects custom logic is not added. However, this can easily be done by using a ProcessEngineLifeCycleListener implementation that executes a process definition of a certain category after the engine has booted up. If you for example give all these processes ‘config-processes’ as category, they can easily be executed on bootup.

Conclusion

Scripting in BPMN 2.0 processes is a very powerful feature. It allows you to change process execution engine-wide in a matter of a few lines. Of course, all the code above can be done with Java. But the examples above use nothing more than standard BPMN 2.0 and the javascript engine that is bundled with every JDK install.

Thanks for reading. Happy coding!

NoSQL experimentations with Activiti: A (very simple) Neo4J prototype

logoI’ve got this itch for a long time now to try and see how easy it is to write a BPM engine on a graph database such as Neo4J. After all, the data model fits perfectly, as business processes are graphs and executions of those processes basically boil down to keeping pointers to where you are in that graph. It just feels as a very natural fit.

So I spend some time implementing a prototype, which you can find on https://github.com/jbarrez/activiti-neo4j

The prototype contains some unit tests which execute simple BPMN 2.0 processes. I tried to be as close as possible to the Activiti concepts of services, commands, executions, JavaDelegates, etc. Currently covered:

  • start and end event
  • user task
  • service task
  • parallel gateway
  • setting/getting variables

Much more information can be found at the readme on https://github.com/jbarrez/activiti-neo4j

I must say that working with Neo4j and its graph model for processes feel very very natural. It really simplifies stuff a lot and I can feel great potenital. Feel free to clone the code and check the code, it’s a regular maven project.

Note: we’re not moving to Neo4J with Activiti. This is just an experiment for myself. Of course, if you want to contribute you’re more than welcome!

Announcement: Enterprise Support for Activiti through BP3 and Edorasware

We often get questions (through email, forum or in person on conferences) where to go for enterprise support on Activiti. So far, we have not really been able to answer that question in an adequate way.

So we are *very* proud, happy and honored to be able to announce today that the Activiti project has selected two go-to partners who provide professional enterprise support for Activiti. Whether you’re in need for SLA backed services, expert consultancy or assistance on small to large-scale end-to-end Business Process Management projects, these are the people we trust and know you can build upon.

The partners in question are not of the least: BP3 and Edorasware. Both companies are deeply committed to the Activiti project and will have a major role to play in the vision and future of the Activiti project.

logo_bp3BP3 is a well-known company in the Business Process Management space. They operate from Austin, Texas (US), and have a vast expertise across multiple industries on deploying BPM projects. Scott Francis, the CTO of BP3 is a leading thought-leader and BPM blogger. Actually, Scott has blogged about Activiti since its first release and has followed and blogged about Activiti since then with great interest. Of course Scott is accompanied with a team of awesome people called the ‘BP3 Labs Group’ which will help driving the innovation in Activiti.

logo_edoraswareEdorasware, our other preferred go-to partner, is a prominent player in the BPM and ACM market. As a matter of fact, Edorasware was named a Gartner Cool Vendor for BPM just two weeks ago! On top of that, their core cloud and on-premise product called ‘Edoras One’ is built upon Activiti. Edorasware is a spin-off since 2010 of Mimacom and operates from Bern, Switzerland. Micha Kiener, CEO of Edorasware, and his team have been involved with the Activiti community since the first days of the project.

What does this mean for the Activiti project?

Nothing but good news, of course ;-)

It means that we now have an answer for people asking us where to go for Activiti enterprise support, or assistance on Activiti and more general, BPM projects.

Activiti of course remains 100% open source and community driven. Both BP3 and Edorasware will become an important part and driver in the open source community.

logo_alfresco

The main sponsor of Activiti, Alfresco, will keep investing in Activiti. And obviously, Alfresco keeps providing support for the Activiti usage within Alfresco when looking for Alfresco enterprise support.

For more information, see the Enterprise support Activiti website

Five years of blogging

I recently realized that I have this blog for five years already. More precisely, on March 31 2008, I did my first post here.

It was my project lead at that moment that encouraged me to write down some stuff I had done with jBPM. He is now a real good friend of mine, and it is funny to look back how different things where five years ago. In that same period of time I got a house, got married, two kids, a dog, was hired by JBoss to work on jBPM and later joined Alfresco to build Activiti.

I remember that my first posts on this blog caused quite a stir in the company I was working back then. Something about ‘company secrets’ or something. Yeah, it was five years ago, I told you. It’s good to see they changed their attitude towards blogging if I see my ex-colleagues freely blogging now.

Over the past five years, my blog grew really nicely. Below you can see an overview of the unique visitors / month. There’s a jump in June 2009, when I joined JBoss. For a long time, I’ve been floating around a steady 5K line but the last months traffic is sure on the rise as shown by the slope on the right (8850 unique visits last month). It really amazes me that a simple blog like this can attract this amount of people. Really humbling. As such, it really motivates me to continue posting on what I’m working on from day to day.

Screen Shot 2013-04-15 at 19.22.42

In those five years, I wrote 115 posts (that’s 23 posts on average each year),  223.211 people have visited this blog, of which 128.222 unique visitors. If you think about that number of people … that’ just amazing. That are more people than the amount of inhabitants of Bruges, one of the more famous cities in Belgium. But more importantly, over those five years the average time on the blog is 2:21 minutes. So people really take their time to write down what I write! In the age of twitter that means a lot.

The image below shows where all the visits are coming from.

Screen Shot 2013-04-15 at 19.25.29The top 10 looks as follows

  1. USA (most traffic from California, New York and Texas)
  2. Germany
  3. India (most traffic from Karnataka, which is the province where Bangalore is)
  4. Belgium (yay!)
  5. China (most traffic from Beiijing, Shanghai and Shenzen)
  6. France
  7. UK
  8. Poland
  9. Italy
  10. Spain

The top 3 is no surprise, as it is where we also see most requests from in the Activiti community.

I can say nothing more than that I’m really humbled that you all take the time to read what I write down here.

Thank you. Thank all of the 128.222 of you! 

Bug on JDK 1.7.0_17 when using ScriptTask in Activiti

One of the awesome things of having a big open source community is that it makes Activiti run on platforms and configuration which we don’t have in our automated QA environment. Regardless of how many people/time/money you have, there’s always going to be a funny configuration that makes the software behave just differently.

So one of the issues discovered and reported by Brian Showers in the Activiti forum, is that the test suite doesn’t run green on JDK 1.7.0._17. And indeed,  my dev machine didn’t have the latest JDK installed nor had the QA at the time of writing.

So what is the problem? Well, in Activiti when using a scriptTask, you can write something like

<scriptTask id="myScript" scriptFormat="JavaScript">
  <script><![CDATA[
    var sum = a + b;
  ]]></script>
</scriptTask>

The variable sum will be automatically saved as a process variable (unless you set the property “activiti:autoStoreVariables=’false’” on the scriptTask). This has worked since whatever version of Activiti we introduced scripting. So it has worked for JDK 5, 6 and until recently 7. But on the latest JDK, at the time of writing 1.7.0_17, it doesn’t run. It throws an exception:

org.activiti.engine.ActivitiException: couldn't find a variable type that is able to serialize sun.org.mozilla.javascript.internal.Undefined@7216df
at org.activiti.engine.impl.variable.DefaultVariableTypes.findVariableType(DefaultVariableTypes.java:62)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:359)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:292)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:279)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:254)
at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:237)
at org.activiti.engine.impl.scripting.ScriptBindings.put(ScriptBindings.java:83)
at javax.script.SimpleScriptContext.setAttribute(SimpleScriptContext.java:228)
at com.sun.script.javascript.ExternalScriptable.put(ExternalScriptable.java:182)
at sun.org.mozilla.javascript.internal.ScriptableObject.defineProperty(ScriptableObject.java:1394)
at sun.org.mozilla.javascript.internal.ScriptRuntime.initScript(ScriptRuntime.java:3239)
at sun.org.mozilla.javascript.internal.Interpreter.initFrame(Interpreter.java:2695)
at sun.org.mozilla.javascript.internal.Interpreter.interpret(Interpreter.java:844)
at sun.org.mozilla.javascript.internal.InterpretedFunction.call(InterpretedFunction.java:162)
at sun.org.mozilla.javascript.internal.ContextFactory.doTopCall(ContextFactory.java:429)
at com.sun.script.javascript.RhinoScriptEngine$1.superDoTopCall(RhinoScriptEngine.java:116)
at com.sun.script.javascript.RhinoScriptEngine$1.doTopCall(RhinoScriptEngine.java:109)
at sun.org.mozilla.javascript.internal.ScriptRuntime.doTopCall(ScriptRuntime.java:3161)
at sun.org.mozilla.javascript.internal.InterpretedFunction.exec(InterpretedFunction.java:173)
at sun.org.mozilla.javascript.internal.Context.evaluateReader(Context.java:1159)
...

So it seems that in the latest JDK 7 version, somehow the class that is used to represent a scripting variable in the JDK isn't Serializable anymore. However, if you switch your script to

<scriptTask id="myScript" scriptFormat="JavaScript">
  <script><![CDATA[
    var sum = a + b;
    execution.setVariable("sum", sum);
  ]]></script>
</scriptTask>

It just works. When I debugged, an object of the class 'Double' was passed instead of 'Undefined' in the first example. So when calling a Java object from a script, some auto-coercion/conversion is happening ... which is not happening in the first example. Unfortunately all the classes involved are internal classes (eg sun.javascript.xxxxx) so we can't just force the conversion somehow as this would break on some systems.

So what options do we have

  • The behavior we're seeing is a bug in the JDK. It is fixed in a next release.
  • The behavior is actually a fix for something broken in the past few years. Although I don't think that Oracle would break backwards compatibility in a minor JDK release.

So the current thinking is that we wait a bit until next release. In the mean time, I'l try to figure out how to report a JDK bug.

People that are depending on the auto storage of variable will have to use the workaround with execution.setVariable(name, value); for the moment. If anybody has any input regarding this issue, please let us know!

Reporting capabilities in Activiti 5.12

In the Activiti 5.12 release, we added reporting capabilities on top of the Activiti engine, demonstrating the concepts through the Activiti Explorer web application (but of course usable everywhere).

Now, don’t be fooled: since a very long time , the Activiti engine has the capability of gathering historical or audit data when you execute business processes. All this data is stored in the historical database tables and can thus be easily queried. Which means that any reporting tool such as JasperReports, Birt, Crystal Reports etc. can just use the historical tables as a datasource to produce reports in any format you’d like (Word, PDF, …) to get insight how your business is executing its business processes. I’ll probably blog such an example pretty soon.

Eating our own dogfood

But the thing where I’d like to focus on today is the web side of things: web charts/reports which can be combined into a dashboard for example. The first thing we must be able to do is to expose the historical data in a way we can use it to create these charts. But where do you put the logic (the queries and data manipulation) to generate a dataset for the chart and/or the report? Do you embed the SQL in your UI-layer? Of course not. What if multiple applications want to use the data? What if we want to store the generated dataset to get a snapshot of the data at a certain point in time.

When we thought about this problem we first though about it in the traditional way. A new service with reporting capabilities, probably using some sort of DSL to define the dataset generation which are stored in some kind of data store. Anyway, a whole new things and concepts to learn and master. Not to mention extra implementation and maintenance.

But then it hit us. Everything we needed is already available in the Activiti engine. If we use a process to define the logic to create the dataset for the report, we can leverage all the facilities of the engine. The only requirement for this process is that it generates the JSON data which follows a fixed format. Some benefits

  • The process has straight access to the internals of the Activiti engine. It has direct access to the database used by the engine.
  • The dataset that is generated can be stored in the historical tables of Activiti if wanted. So we have a ‘save report data’ mechanism for free.
  • The job executor can be used as for any other process. This means that you can asynchronously generate the process or only execute certain steps asynchronously. It also means you can use timers, eg. to generate the report data on certain points in time.
  • Creating a new report can be done with known tools and known concepts. Also, no new concepts, services or applications are needed. Deploying or uploading a new report is the same as deploying a new process. Generating a report is the same as running a process instance.
  • It allows to use the BPMN 2.0 constructs. This means that all things like parallel steps, do branching based on data or even request user input during the generation are possible out-of-the-box.

Screen Shot 2013-03-22 at 13.22.49

A dash of Javascript

Since the generation of the dataset is done by a process, everything possible in a process can be used. So you can use Java delegate classes or whatever you fancy.

But since the kool kids nowadays are using Javascript, we added some example process to the demo data of Activiti Explorer that use the scripting functionality in the engine. The nice thing about Javascript is that JSON is native to it, and creating a JSON object is really easy. As said above, the only requirement for such a process is that it must generate the JSON dataset following the predefined format.

Screen Shot 2013-03-22 at 10.43.20

For example to generate an overview of all past process instances we could have a process 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:activiti="http://activiti.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="activiti-report">

<process id="process-instance-overview-report" name="Process Instance Overview" isExecutable="true">

 <startEvent id="startevent1" name="Start" />
 <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="generateDataset" />

 <scriptTask id="generateDataset" name="Execute script" scriptFormat="JavaScript" activiti:autoStoreVariables="false">
 <script><![CDATA[

  importPackage(java.sql);
  importPackage(java.lang);
  importPackage(org.activiti.explorer.reporting);

  var result = ReportingUtil.executeSelectSqlQuery("SELECT PD.NAME_, PD.VERSION_ , count(*) FROM ACT_HI_PROCINST PI
       inner join ACT_RE_PROCDEF PD on PI.PROC_DEF_ID_ = PD.ID_ group by PROC_DEF_ID_");

  var reportData = {};
  reportData.datasets = [];
  
  // Native json usage
  var dataset = {};
  dataset.type = "pieChart";
  dataset.description = "Process instance overview (" + new java.util.Date() + ")";
  dataset.data = {};

  while (result.next()) { // process results one row at a time
    var name = result.getString(1);
    var version = result.getLong(2)
    var count = result.getLong(3);
    dataset.data[name + " (v" + version + ")"] = count;
  }
  reportData.datasets.push(dataset);

  // Storing the json as process variable
  execution.setVariable("reportData", new java.lang.String(JSON.stringify(reportData)).getBytes("UTF-8"));
 ]]></script>
 </scriptTask>
 <sequenceFlow id="flow3" sourceRef="generateDataset" targetRef="theEnd" />

 <endEvent id="theEnd" />

 </process></em>

</definitions>

The script is pretty easy to understand. All it does is querying the database, creating a json object with the data and storing it as process variable. Since it is stored as a variable, it is basically a snapshot of the dataset. So at any time in the future you can just fetch the json data and look at the report as it was at that point in time.

The json produced by the process above can then easily be used to generate charts, as demonstrated by the Activiti Explorer application:

Screen Shot 2013-03-22 at 13.30.45

 

It is also very easy to see how easy it is to create a dashboard app with the same approach. But that’ll be for a next blogpost.

Thanks for reading!