Tuesday, July 17, 2012

jBPM application example demo

This is a DEMO of what is possible to do by embedding jBPM in a Centralized System to execute workflow/business processes.

What the system "basically" does is controlling media equipment to "convert" files.

It's a Web Application that communicates with jBoss Web Server, where jBPM and Drools Guvnor are running.

jBPM was used to design and run workflow. During execution tasks are assigned to the media equipment. To interact with the network resources a SOAP layer is used and load Balancing for parallel tasks is consider. The demo has three "sections":
  • Adding service tasks to be used during business process design.
  • Business process design with jBPM Web Designer
  • Workflow execution and progress notifications.
The following list pairs demo features with the jBPM/Drool Guvnors components used during implementation:
Because the three first items were already covered in a previous post I'll start with Asynchronous handlers.

jBPM Task execution with Asynchronous handlers

There really isn't much to it. To execute domain specific tasks, those that run custom code, jBPM uses the notion of WorkItemHandler which is an interface with two methods
executeWorkItem(WorkItem arg0, WorkItemManager arg1) and abortWorkItem(WorkItem arg0, WorkItemManager arg1). To run asynchronous tasks the developer just needs to use a thread to implement the executeWorkItem method. There is one thing to keep in mind though, jBPM needs to be told when a service task has been completed through the method WorkItemManger.completeWorkItem(long workItemHandlerId,Map result). Due to this method arguments, the thread must have access to the WorkItemManager and the respective WorkItemHandler Id and needs to call it upon completion. The easiest way to do this might be extending Thread in the class that implements WorkItemHandler. Illustration code follows:


public class ActivityHandler implements WorkItemHandler {

 @Override
 public void executeWorkItem(WorkItem arg0, WorkItemManager arg1) {
    new WorkItemExecute(arg0,arg1).start();
 }
}

public class WorkItemExecuter extends Thread{

  private WorkItem workItem;
  private WorkItemManager workItemManager;
  public WorkItemExecuter(WorkItem arg0,WorkItemManager arg1)
  {
     this.workItemManager = arg1;
     this.workItem = arg0;
  }

 @Override
 public void run() {
    /* Execute the task 
     * ....*/
  
    /* When finished warn jBPM the task is completed */
    this.workItemManager.completeWorkItem(this.workItem.getId(), null);
 }
}

  

Notify user about workflow events

The notifications to the user are made by implementing the ProcessEventListener interface. Whenever events like "before trigger node" and "event completed" are fired the corresponding interface method is called and from there it's possible to notify the user about workflow evolution or request some input needed for a certain task. The workflow event architecture was something on the lines:
  • beforeNodeTriggered was used to check if the node's  "requirements" were fulfilled. For instance the two parallel activities needed some input and the Asset Explorer is called so video "assets could be injected" to the tasks.
  •  afterProcessCompleted fires the last notification informing the user that the workflow has ended.
Getting the information from the jBPM regarding workflow progress is quite easy when using the ProcessEventListener interface, what isn't as trivial is notifying the user in real time about those events due to  HTTP  request/response nature. Considering that a workflow might take hours to complete managing a connection to the client so it's possible to post notification requires some effort. If you can easily push data to your interface environment then this won't be an issue but in the html (Web Socketless) realm it's quite a nagging problem.

Share data between activities automatically

For what I could understand in order to share data between processes' tasks the user must set data association during the BPMN2.0 design process. This assures for example that WorkItems are as independent from processes as possible but on the other hand demands a user interaction that might not be very intuitive. Thus, in order to map inputs and ouputs between processes tasks automatically you can use the org.drools.definition.process.Node and org.drools.definition.process.Connection. These two classes allow you to "navigate" through the workflow and find each tasks' dependencies, so you can map the output of one task as the input of the next one. Be aware of the (WorkflowProcess) cast. Code follows:

 KnowledgeBase kbase = kbuilder.newKnowledgeBase();
 WorkflowProcess workflow = (WorkflowProcess)kbase.getProcess(processId);
 for(Node node:workflow.getNodes())
 {
  if(node instanceof WorkItemNode)
  {
   for(String key:node.getOutgoingConnections().keySet())
    markOutInputs(node.getOutgoingConnections().get(key));
  }
 }

 private void markOutInputs(List list) {
  
  for(Connection conn:list)
  {
   Node to = conn.getTo();
   /* only reachable workitemnodes will be affected */
   if(to instanceof WorkItemNode)
   {
    /* The connection ends in a WorkItemNode */
    /* handle data input mapping */
   }
   else
    for(String key:to.getOutgoingConnections().keySet())
     markOutInputs(to.getOutgoingConnections().get(key));
  }
 }