How to create a custom WorkItem Handler in jBPM

In this second tutorial about jBPM WorkItemHandler we will discuss how to create a custom WorkItemHandler which can be used to plug into your BPMN process some complex Java logic.

A WorkItemHandler is a class that implements the org.kie.runtime.instance.WorkItemHandler interface. There are several built-in WorkItemHandler available in jBPM, for example:

Creating a custom WorkItemHandler

In order to add a custom WorkItemHandler you need to perform the following steps:

  1. Add a Work Item Definition (WID)
  2. Map the WIP with the actual Handler class in the kie-deployment-descriptor.xml
  3. Code the WorkItemHandler class

Let’s start from the first step.

The WID file is a mapping between user interactions with jBPM and the data that is passed to the work item handler.

jBPM tries to locate a *.wid file in two locations by default:

  • Within the project’s top-level global/ directory using the default name WorkDefinitions.wid file
  • Within the project’s src/main/resources/ directory.

As an example, we will add to a KJAR project a file CustomWorkDefinitions.wid under src/main/resources/ directory:

import org.drools.core.process.core.datatype.impl.type.StringDataType;

[
 [
 "name" : "HelloWIH",
 "parameters" : [
 "Message" : new StringDataType(),
 ],
 "displayName" : "HelloWIH",
 "icon" : "icon-info.gif"
 ]
]

Next, let’s define which Handler maps the “HelloWIH” in the kie-deployment-descriptor.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <persistence-unit>org.jbpm.domain</persistence-unit>
  <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit>
  <audit-mode>JPA</audit-mode>
  <persistence-mode>JPA</persistence-mode>
  <runtime-strategy>PER_PROCESS_INSTANCE</runtime-strategy>
  <marshalling-strategies/>
  <event-listeners/>
  <task-event-listeners/>
  <globals/>
  <work-item-handlers>
    <work-item-handler>
      <resolver>mvel</resolver>
      <identifier>new com.company.service.HelloItemHandler()</identifier>
      <parameters/>
      <name>HelloWIH</name>
    </work-item-handler>
  </work-item-handlers>
  <environment-entries/>
  <configurations/>
  <required-roles/>
  <remoteable-classes/>
  <limit-serialization-classes>true</limit-serialization-classes>
</deployment-descriptor>

Great, now if you check the Eclipse BPM palette, you will see that the HelloWIH Task is included in the Custom Tasks panel:

jbpm custom WorkItemHandler tutorial

Let’s add this Custom Task to our Driving License BPM process, so that we will notify to our Handler when the Process starts, passing as argument the “name” process variable:

How to inject variables into the WorkItemHandler?

In order to inject variables into the WorkItemHandler, select the I/O Settings of the Custom Task, we will set the Task name and we also include “name” as input and output:

jbpm custom WorkItemHandler tutorial

Then, within your WorkItemHandler, you will be able to collect the “name” variable with:

Object name = workItem.getParameter("name");

Let’s see the full code of the WorkItemHandler in the next section.

Coding the WorkItemHandler

By implementing the WorkItemHandler interface, you provide the necessary logic to execute the work item and handle its completion. The interface defines several methods that need to be implemented, including:

  • executeWorkItem: This is a callback method which fires when you trigger a WorkItem in your Process. It contains the implementation logic for executing the work item, such as performing an action or calling an external service.
  • abortWorkItem: This callback method executes when a work item needs to be aborted or canceled before it completes. It allows for cleanup or interruption of the work item’s execution.

Here is our custom WorkItemHandler implementation:

package com.company.service;

import org.drools.core.process.instance.WorkItemHandler;
import org.kie.api.runtime.process.WorkItem;
import org.kie.api.runtime.process.WorkItemManager;
public class HelloItemHandler implements WorkItemHandler {

	@Override
	public void abortWorkItem(WorkItem workItem, WorkItemManager manager) {
	    manager.abortWorkItem(workItem.getId());
	}

	@Override
	public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
	    
	    Object name = workItem.getParameter("name");
	    System.out.println("Hi " + name);

	    manager.completeWorkItem(workItem.getId(), null);
	}
}

The WorkItemHandler does nothing fancy, it just prints the value of the “name” parameter that has been passed by the process.

This process variable will be added during the start up of the process instance, as you can check from the Spring Boot Application class:

@SpringBootApplication
@RestController
public class Application  {

    @Autowired
    private ProcessService processService;
    @Autowired
    private RuntimeDataService runtimeDataService;
    @Autowired
    private UserTaskService userTaskService;
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @GetMapping("/hello")
    public ResponseEntity<String> sayHello(@RequestParam Integer age) throws Exception {


        // Provided as an example. Not actually needed by our process.
        Map<String, Object> vars = new HashMap<>();
        vars.put("name", "John Smith");

        Long processInstanceId = processService.startProcess("business-application-kjar-1_0-SNAPSHOT", "com.mastertheboss.LicenseDemo", vars);

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("age", age);

        List<TaskSummary> taskSummaries = runtimeDataService.getTasksAssignedAsPotentialOwner("john", new QueryFilter());

        taskSummaries.forEach(s->{
            Status status = taskSummaries.get(0).getStatus();
            if ( status == Status.Ready )
                userTaskService.claim(s.getId(), "john");
            userTaskService.start(s.getId(), "john");
            userTaskService.complete(s.getId(), "john", params);
        });

        return ResponseEntity.status(HttpStatus.CREATED).body("Task completed!");
    }

}

Depoying and running the custom WorkItemHandler

Start by installing on your local Maven repository the kjar project

$ cd business-application-kjar
$ mvn clean install

Then build, install and run the Spring Boot application service:

$ cd business-application-service
$ mvn clean install spring-boot:run

The application will start. You can now trigger the REST endpoint with a value for “age”, as required by the process:

$ curl http://localhost:8090/hello?age=14

You will see from your application logs that the process has started, the Custom Handler has been triggered and the Task has been completed:

Welcome to license check 
Hi John Smith
Enter you age John Smith
Thanks
Rejected

The source code for this example is available here: https://github.com/fmarchioni/mastertheboss/tree/master/jbpm/custom-wih

Found the article helpful? if so please follow us on Socials