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:
- In this tutorial we have covered how to use the built-in REST WorkItemHandler: How to use a REST WorkItem Handler in jBPM
- Here you can find how to use the SQLExecutor WorkItemHandler: Using the ExecuteSQL Service task in jBPM Processes
Creating a custom WorkItemHandler
In order to add a custom WorkItemHandler you need to perform the following steps:
- Add a Work Item Definition (WID)
- Map the WIP with the actual Handler class in the kie-deployment-descriptor.xml
- 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:
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:
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