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));
  }
 }


  

8 comments:

  1. Hi Luis, great post!
    I have just one comment about the WorkItemHandler which is creating a thread and starting it, you should avoid doing that and allow an external component to resume your process when the external task is over. I wouldn't recommend coding the work item in that way, because if you execute 1000 process instances you will get 1000 threads created.
    The rest of the article really rocks man! keep up the good work!

    ReplyDelete
    Replies
    1. Nice recommendation...but how exactly would be the best way to pause the process and resume it externally ? I mean if I do something like .wait() I'll have the same problem of keeping resources located to the thread.
      Many thanks

      Delete
    2. Hi Luis,
      I tried the asynchronous workitemhandler example above and it doesn't work. The workitemmanager doesn't persist in the thread. Any other ideas?

      Delete
    3. Hi Tim,
      Sorry but I didn't fully understand what you meant.
      Do you mean that when you pass the workitemmanager through the Thread constructor and then persist it in a private atribute, the value is not defined at the time of the thread execution?

      Delete
  2. Hi Luis, can upload source code of this example?!

    Thanks

    ReplyDelete
    Replies
    1. The example that I've shown here belongs to a rather complex and extensive web application developed during my internship at a great company called MOG Technologies. Because it uses proprietary software I can't release the code.
      But I'm more than glade to help with any questions regarding the use of jbpm

      Delete
  3. Hello Luis, I must mention that your approach to asynchronous handlers does not work. Notice that the WorkItemHandler is a singleton (after all you have to instantiate it and register it by hand). If you extend Thread and call start inside the executeWorkItem method as you have done, when executeWorkItemHandler is called again, it is being called on the same instance of the WorkItemHandler, which calls start on the same thread, leading to a java.lang.IllegalThreadStateException being thrown.

    The right way to do it, as far as I can tell, is to have a separate class that has a WorkItem and a WorkItem handler as private fields, which extends Thread. Then you can put all the task logic inside the run() method and everything works nice and dandy. Let me know what you think,

    Best,
    -Leo

    ReplyDelete
    Replies
    1. The approach you have suggest is in fact how I implemented but to keep things simple I wrote on the blog a different version where you correctly found a mistake.
      The code has been edited.

      If I got the issue correctly calling a new Thread from another class that implements WorkItemHandler during the executeWorkItem should resolve it. The Thread has access to the workItemManger and the WorkItem in order to warn the execution has ended.

      Delete