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:
Within the I/O Settings of the Custom Task, we will set the Task name and we also include “name” as input and output:
Great, we are done with our Custom Handler configuration. Lets’s code it.
Coding the WorkItemHandler
We will now switch to our Web application. In addition to the Spring Boot application class, we will include the following WorkItemHandler:
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 availabe here: https://github.com/fmarchioni/mastertheboss/tree/master/jbpm/custom-wih