Here are some notes about creating Custom Work Item Handlers for Red Hat Process Automation Manager (RHPAM). These notes should work for jBPM/KIE BPM as well.
Relevant links:
- RHPAM 7.11 Documentation
- KIE Blog Entry
- Community Built Custom Work Item Handlers – great for copy/paste or learn-by-example
Custom Tasks and Custom Work Item Handlers allow you implement activities that do more than Script or the OOTB task types (like Email or Log or REST Request). NOTE: I think the terms “Custom Tasks” and “Custom Work Item Handlers” are essentially interchangeable…I can’t figure out why there are two ways to refer to this subject. I guess the Work Item Handler is the actual Java application (a JAR) that does the work and a “Custom Task” is what it looks like when you add the Work Item Definition (WID) and such to your project. But I’ll just keep calling them Custom Work Item Handlers, or Custom WIH, for this post.
Getting Started
Though it may be tempting to start with cloning or copying an existing Custom WIH, I found that this can create some Maven POM issues that aren’t that easy to resolve. The best way to start is to use the JBPM Maven Archetype (like a template) to create a blank project and then add your customizations or paste any borrowed code.
Here’s the Windows command line to initialize an empty project based on the Custom WIH archetype:
mvn archetype:generate -DarchetypeGroupId=org.jbpm -DarchetypeArtifactId=jbpm-workitems-archetype -DarchetypeVersion=7.52.0.Final -Dversion=1.0.0-SNAPSHOT -DgroupId=com.mygroup -DartifactId=workitem-name -DclassPrefix=workitemClass
The items in bold should be hardcoded, but if you need to, update the archetypeVersion to match whatever jBPM version you’re using (check for a RedHat version if you’re on RHPAM – just make sure you are mapped to the RedHat Maven Repo using the documentation instructions). The -Dversion, -DgroupId and -DartifactId will be the Maven GAV (group, artifact, version) data for your Custom WIH – update these to match your Custom WIH code. The GAV values will be used for dependency management in your RHPAM Project’s POM and the WID in your RHPAM Project. -DclassPrefix just puts this value at the start of the initial Class in your Custom WIH project.
Running that command will grab some files from your Maven Repo, ask a simple project initialization question, and create a directory where it is run to hold your Custom WIH project. You’ll have to press Y at one prompt to confirm the GAV parameters from the command. Then you’ll get two directories and a pom.xml file to get started:

It seems easiest to startup IntelliJ or VSCode or whatever your IDE-of-choice is and configure it to recognize the folder as a Java project with source in src > main > java and tests in src > test > java. The rest of this page will use IntelliJ but I’m not using anything IDE specific, so if you see any major discrepancies, please feel free to comment below.
There should be a single pre-generated class file for your project and that is probably where you’ll want to start you actual development. It will be something like {WorkItemClassPrefix}WorkItemHandler from the archetype generation above.
It doesn’t seem like you need to make any major changes to the pom.xml that is generated except to add any dependencies for libraries that your Custom WIH will need, like JSON or JodaTime or Mongo or whatever. See this thread about how your Custom WIH dependencies could impact your runtime.
Describe the Custom WIH with the WID
The Work Item Definition (WID) controls how your code looks to RHAPM/jBPM. You can author the WID as its own file or just use the @Wid annotation that is already provided in the class generated from the Archetype. The WID is important for controlling how your Custom WIH looks to someone in Business Central. I tried using the text file like the RHPAM docs say, but I could never get it to work, so this annotation seems fine.
Here is the @Wid for a new project from the archetype with some comments about each section.
| Custom WIH Java Class File Annotation | Notes |
@Wid(widfile="WorkitemClassDefinitions.wid", name="WorkitemClassDefinitions",Do an exciting Custom WIH"), | widfile – Maven seems to auto-generate this file which is essentially just everything from this annotation, but in its own text file. name – the name of the Custom WIH displayName – what will appear in Business Central as the name of the Custom Task defaultHandler – what a user will have to put in Project Settings > Deployments > Work item handlers. If you want to require something at this point which will end up being used in the constructor for this class, use “\” to escape the input. So for example the ExecuteSQL WIH has this value: defaultHandler = “mvel: new org.jbpm.process.workitem.executesql.ExecuteSqlWorkItemHandler(\”dataSourceName\”)”, which means you have to supply the dataSourceName when you pull this WIH into your project as a dependencies or else the WIH won’t initialize. NOTE: the defaultHandler in the @Wid section generated from the archetype is wrong, because the WIH is setup to require SampleParam and SampleParamTwo in the only Constructor, so I think those parameters should be in the Default Handler as well. documentation – not really sure how a URL is used here… category – This groups the Custom WIH on the BPMN authoring palette icon – a picture for the Task/Activity box on the BPMN diagram parameters – the Input parameters for your Custom WIH. You can use the “required” flag if you want, or leave it out so that the parameter is not required. You can also define a runtimeType like @WidParameter(name=”SampleParamThree”, runtimeType = “java.lang.Object”), which should be a fully qualified class name. This will help when a process author drops the Custom WIH into the BPMN flow. NOTE: parameters appear to be entirely optional, so if your Custom WIH doesn’t need an input you don’t have to include this section. results – the name of the object returned by the Custom WIH. You can also define the runtimeType just like SampleParamThree above. NOTE: results also appear to be entirely optional, so if your Custom WIH doesn’t need to return anything, don’t include results. In order to call the Workitem Manager completeWorkItem() method, you have to pass either null or a Map<String, Object>. The Map should have items for each Result parameter defined. So in this example, the Map<String, Object> “results” object will have an entry for “SampleResult” as the Key and, for this example, a String as the Value/Object. But it is perfectly fine to list multiple @WidReults and then use each of those @WidResults.name values as a “Key” in the Map<String, Object> result. Or not. See notes below for help with results. mavenDepends – as far as I can tell, this is the GAV for the Custom WIH, so I’m assuming that if you update the VERSION in the Custom WIH pom, you’ll want to update here as well. I guess you can also add other dependencies that you’ve added to your Custom WIH pom…? I’m not sure what happens if you do or don’t. serviceInfo – this also seems to control how the Custom WIH appears in Business Central, but will need to do some more testing. description – notice that by default this points to a variable ${description}, but it doesn’t seem to be defined so feel free to add description=”something here” up between documentation and category so the Custom WIH will get a description, but I’m not sure how this used. @WidAction(title) – will show up after the Custom WIH name in Business Central, so its more of a headline about what the Custom WIH does, like “Send email” or “Send JMS Message” or “Call SOR API…” @WidAuth – it is OK to leave authinfo=@WidAuth without using the () values. But if anything is added here, these fields will display when the Custom WIH is added as dependency to the Process Project using Settings > Custom Tasks > Install. I have no idea why this is considered “authentication”… Here’s a good example of a more complete serviceInfo section from here: |
Code the actual Custom WIH
Make sure your class constructors match the defaultHandler from the WID and contains any required values for the constructor. Or leave it entirely blank – up to you. You can also define more than one constructor as well. And you can also define any variables global to the class if you want, like String sampleParam, String sampleParamTwo from the archetype.
Once you’re ready to write the real logic, most everything goes into the executeWorkItem() method. A few pointers for the execute() method:
- Wrapping everything in a try/catch seems pretty standard.
- Just like the Archetype does, use the RequiredParameterValidator.validate(this.getClass(), workItem) to make sure any WID Parameters marked as “required=true” were actually provided.
- To get the values of the Input Parameters from the BPMN, use the parameter name as a String for the workItem.getParameter() method and then cast the result to whatever you need for your code. If you defined the Parameter as a specific object in the WID, use that Class to cast because getParameter() just seems to return a generic object.
- Side note: the workItem object has a method to get the running Process Instance ID, which might be helpful: workItem.getProcessInstanceId()
- When your code is done you’ll have to call manager.completeWorkItem(workItem.getId(),results) to close out the Task. “results” is a required parameter for the method, but it seems like you can pass “null” if your Custom WIH doesn’t need to return anything (like this).
- If your Custom WIH needs to return anything, the results parameter for completeWorkItem is always of type Map.
- Each Key (the String) in the Map should correspond to whatever you’ve put in the results/@WidResult section. So if you have a @WidResult defined as “SampleResult”, your code will have to call results.put(“SampleResult”,{a}) and then put whatever your result object is as the second parameter (replace “{a}”). If you decide to use a runtimeType in your @WidResult section, make sure the Object in the Map is that same type.
- NOTE: You can also “put” items into the results map that aren’t listed in results/@WidResult. They will still be available to the BPMN, but will have to be added and mapped manually by the BPMN author when your Custom WIH is added to the BPMN palette and the author assigns the Data Mapping.
- If your Custom WIH needs to return anything, the results parameter for completeWorkItem is always of type Map.
- I guess leave the “abortWorkItem” method alone…or maybe do something like close a connection or something in a scenario where your Custom WIH is interrupted for whatever reason…?
Feel free to write any tests and such that you want. I’ll add some more notes on this later.
Build and Deploy and Integrate
It seems easiest to run the build from the command line in the project directory instead of using the IDE, but feel free to do this whatever way you want. From the command line, this seems to work best:
mvn clean package -Dmaven.test.skip=true
This command will do a ton of stuff but most importantly it creates a JAR in /target that you’ll want to upload to Business Central. It also creates a lot of other stuff that I have no idea how it’s used, so that will be for another day.
In order to fully integrate the Custom WIH in a Process Project, it seems like you need to follow all three (+1) of these steps from here:
- ADDING THE WORK ITEM HANDLER TO BUSINESS CENTRAL AS A CUSTOM TASK
- This is where you upload the mvn clean package JAR to BC
- INSTALLING THE CUSTOM TASK IN YOUR PROJECT – Custom Tasks
- Project > Settings > Custom Tasks
- This should auto-generate the WID Asset for your process project once you click Save after Installing the Custom Task
- Project > Settings > Custom Tasks
- INSTALLING THE CUSTOM TASK IN YOUR PROJECT – Add GAV Dependency to your project’s pom.xml
- Use your Custom WIH’s GAV that is defined in the Custom WIH pom.xml.
- I’m not really sure why you have to do this because BC makes it seem like it will add the dependency automatically, but it doesn’t at this point, so make sure it gets added under Project > Settings > Dependencies and that the Custom WIH GAV shows up in the Process App’s pom.xml.
- MAYBE: You might have to create a entry in Project > Settings > Deployments > Work Item Handlers with the @Wid name and @Wid defaultHandler values from your Custom WIH. It seems like this entry shows up automatically some times but not other times…not sure how this is related but just make sure you have the Work Item Handler defined here.
NOTE: It seems to help if you add the following plugin to your Process Application Project’s pom.xml. This seems to help ensure your Project is built with any required downstream dependencies from assets like your Custom WIH.
<plugin>
<artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration></plugin>
Once you’ve saved all those Project-level changes, you should be able to see your Custom WIH in your BPMN palette. Congratulations! Drag your new Custom WIH into your BPMN flow and map it up. REMEMBER: for Data Assignments > Data Outputs, the “Name” should match a Key for whatever your Custom WIH is putting in the Map<String, Object> results parameter that is passed to completeWorkItem(). Because the Map.key will just resolve to an Object, make sure you cast before you start trying to do anything with it, because the Value for the Map.key you’re using in Data Output and Assignments > Name will just be a generic Object. You’ll want to do something like:
if (sqlResult != null) {
java.util.List<String> lines = (java.util.List) sqlResult;
}
That will get you a List<String> of the SQL Results from the ExecuteSQL Work Item Handler. You know that “Result” parameter is a List<String> because you looked at the code and saw results.put(RESULT,processResults(resultSet)); where processResults returns a List<String> lines = new ArrayList<>();
So hopefully these notes help anyone trying to develop a Custom Work Item Handler or Custom Task in RHPAM/jBPM.
Feel free to add a comment or send me a note if you have any feedback or questions.
Hopefully I’ll get around to Exception handling and such in a later post.
