Chapter 5. Advanced Process modeling
5.1. Process modeling options
- Using one of the graphical editors
- You can use two delivered graphical editors. Process Designer is available through Business Central and Eclipse Process Designer. See JBoss BPMS User Guide for more information on how to use the editors.
- Using an XML editor
- You can use any XML or text editor to create a process specification using the BPMN2 XML schema.
- Using the Process API
- You can use the BPMS
coreAPI directly. The most important process model elements are defined in the packages org.jbpm.workflow.core and org.jbpm.workflow.core.node.
5.1.1. Process modeling using XML
- XML prolog
- The XML prolog consists of the XML declaration and DTD declaration.
- The
processelement - The
processelement defines process attributes and contains definitions of the process elements (nodes and connections). - BPMN diagram definition
- The
BPMNDiagramelement contains definitions for visualization of the Process elements in the Process Diagram.
Example 5.1. BPMN2 example file
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="Definition"
targetNamespace="http://www.jboss.org/drools"
typeLanguage="http://www.java.com/javaTypes"
expressionLanguage="http://www.mvel.org/2.0"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"Rule Task
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
xmlns:g="http://www.jboss.org/drools/flow/gpd"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:tns="http://www.jboss.org/drools">
<process processType="Private" isExecutable="true" id="com.sample.hello" name="Hello Process" >
<!-- nodes -->
<startEvent id="_1" name="Start" />
<scriptTask id="_2" name="Hello" >
<script>System.out.println("Hello World");</script>
</scriptTask>
<endEvent id="_3" name="End" >
<terminateEventDefinition/>
</endEvent>
<!-- connections -->
<sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
<sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />
</process>
<bpmndi:BPMNDiagram>
<bpmndi:BPMNPlane bpmnElement="com.sample.hello" >
<bpmndi:BPMNShape bpmnElement="_1" >
<dc:Bounds x="16" y="16" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_2" >
<dc:Bounds x="96" y="16" width="80" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" >
<dc:Bounds x="208" y="16" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_1-_2" >
<di:waypoint x="40" y="40" />
<di:waypoint x="136" y="40" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2-_3" >
<di:waypoint x="136" y="40" />
<di:waypoint x="232" y="40" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>5.1.2. Process modeling using API
- Create a Runtime Manager in your Execution Server.
- Singleton: allows sequential execution of multiple instances in the one session
- PerProcessInstance: allows you to create multiple process instances; every instance is created within its own session.
- PerRequestSession: every external interaction with a process instances causes that the process session finishes and the process instance is re-created in a new session.
- Get a runtime context and create a session in it.
- Start a Process from the underlying Knowledge Base.
- Close the Runtime Manager.
Example 5.2. Process instantiation in a session of Per Process Instance Runtime Manager
import org.kie.api.runtime.manager.RuntimeManager;
import org.kie.api.runtime.manager.RuntimeManagerFactory.Factory;
import org.kie.api.runtime.manager.RuntimeEngine;
import org.kie.api.runtime.KieSession;
...
RuntimeManager manager =
RuntimeManagerFactory.Factory.get()
.newPerProcessInstanceRuntimeManager(environment);
RuntimeEngine runtime =
manager.getRuntimeEngine(
ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();
// do something here, e.g.
ksession.startProcess(“org.jbpm.hello”);
manager.disposeRuntimeEngine(engine);
manager.close();5.1.3. Process update
5.1.3.1. Process update
Abort: any running Process instances are aborted. If necessary, you can have the Process instance restarted using the new Process definition.Transfer: any running Process instances are migrated to the new process definition: once the instance has been migrated successfully, it will continue its execution based on the updated process logic. For further information refer to Section 5.1.3.3, “Migrating a Process instance”.
Example 5.3. Process abort update
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieSessionConfiguration;
// build kbase with the replace-version-1.bpmn process
KieBase kbase = KieServices.Factory.get().newKieSessionConfiguration();
kbase.addKnowledgePackages(getProcessPackages("replace-version-1.bpmn"));
KieSession ksession = kbase.newStatefulKnowledgeSession();
try {
// start a replace-version-1.bpmn process instance
ksession.startProcess("com.sample.process", Collections.<String, Object>singletonMap("name", "process1"));
// add the replace-version-2.bpmn process and start its instance
kbase.addKnowledgePackages(getProcessPackages("replace-version-2.bpmn"));
ksession.startProcess("com.sample.process", Collections.<String, Object>singletonMap("name", "process2"));
// signal all processes in the session to continue (both instances finish)
ksession.signalEvent("continue", null);
} finally {
ksession.dispose();
}5.1.3.2. Process instance migration
5.1.3.3. Migrating a Process instance
Example 5.4. Process transfer with custom active Element mapping
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.runtime.process.WorkflowProcessInstance;
// build kbase with the replace-version-1.bpmn process
KieBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(getProcessPackages("replace-version-1.bpmn"));
KieSession ksession = kbase.newStatefulKnowledgeSession();
try {
// start two instances of the replace-version-1.bpmn process
ProcessInstance pi = ksession.startProcess("com.sample.process", Collections.<String, Object>singletonMap("name", "process1"));
ProcessInstance pi2 = ksession.startProcess("com.sample.process", Collections.<String, Object>singletonMap("name", "process2"));
// add the replace-version-3.bpmn process to the kbase and start its instance
kbase.addKnowledgePackages(getProcessPackages("replace-version-3.bpmn"));
ksession.startProcess("com.sample.process2", Collections.<String, Object>singletonMap("name", "process3"));
// upgrade: active nodes from the replace-version-1.bpmn process are mapped to the same nodes in the process
WorkflowProcessInstanceUpgrader.upgradeProcessInstance(ksession, pi.getId(), "com.sample.process2", null);
// upgrade the process using custom mapping
Map<String, Long> mapping = new HashMap<String, Long>();
mapping.put("3", 8L);
mapping.put("2", 7L);
WorkflowProcessInstanceUpgrader.upgradeProcessInstance(ksession, pi2.getId(), "com.sample.process2", mapping);
ksession.fireAllRules();
// signal all processes they may continue (all of them finish)
ksession.signalEvent("continue", null);
} finally {
ksession.dispose();
}
5.1.4. Multi-threading
5.1.4.1. Asynchronous execution
executeWorkItem() method in the work item handler that allows the Process instance to continue its execution.
Example 5.5. Example of asynchronous service handling in Java
import org.kie.api.runtime.process.WorkItem;
import org.kie.api.runtime.process.WorkItemHandler;
import org.kie.api.runtime.process.WorkItemManager;
public class MyServiceTaskHandler implements WorkItemHandler {
public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
new Thread(new Runnable() {
public void run() {
// The main thread with the parent element execution
}
}).start();
}
public void abortWorkItem(WorkItem workItem, WorkItemManager manager) {
}
}
5.1.4.2. Multiple Sessions and persistence
- Thread A has a lock on the ProcessInstanceInfo table, having just committed a change to that table.
- Thread A wants a lock on the SessionInfo table in order to commit a change.
- Thread B has the opposite situation: It has a lock on the SessionInfo table, having just committed a change.
- Thread B wants a lock on the ProcessInstanceInfo table, even though Thread A already has a lock on it.
5.1.5. Technical exceptions
- Code present directly in the process definition
- Code that is not part of the product executed during a Process
- Code that interacts with a technical component outside of the Process Engine
- Code in Element properties, such as the Script property of a
Script Taskelement or in the definitions of the interception actions, that is, theonEntryandonExitproperties - Code in
WorkItemHandlersassociated withtaskand task-type nodes
Code in Element properties
onEntry and onExit properties, Script defined for the Script Task, etc.
scriptTask to interact with a different technical component, such as a database or web service has significant risks because any exceptions thrown will corrupt or abort the Process instance.
task Elements, serviceTask Elements and other task-type Elements. Do not use the scriptTask nodes for these purposes.
Note
scriptTask causes the problem, the Process Engine usually throws the WorkflowRuntimeException with information on the Process (refer to Section 5.1.5.1.5, “Extracting information from WorkflowRuntimeException”).
Code in WorkItemHandlers
handler decorator classes (for examples and detailed information refer to Section 5.1.5.1.2, “Exception handling classes”). These classes include the logic that is executed when an exception is thrown during the execution or abortion of a work item:
- SignallingTaskHandlerDecorator
- catches the exception and signals it to the Process instance using a configurable event type when the
executeWorkItem()orabortWorkItemmethods of the originalWorkItemHandlerinstance throw an exception. The exception thrown is passed as part of the event. This functionality can be also used to signal to an Event SubProcess defined in the Process definition. - LoggingTaskHandlerDecorator
- logs error about any exceptions thrown by the
executeWorkItem()andabortWorkItem()methods. It also saves any exceptions thrown to an internal list so that they can be retrieved later for inspection or further logging. The content and format of the message logged are configurable.
- Does the implementation catch all exceptions the code could return?
- Does the implementation complete or abort the work item after an exception has been caught or uses a mechanisms to retry the process later (in some cases, incomplete process instances might be acceptable)?
- Does the implementation define any other actions that need to be taken when an exception is caught? Would it be beneficial to interact with other technical systems? Should a Sub-Process be triggered to handle the exception?
Important
5.1.5.1. Technical exception examples
5.1.5.1.1. Service Task handlers
- Execution of the Process instance stops: no other parts of the Process are executed.
- The Process instance finishes as ABORTED.

Figure 5.1. Process with an exception handling Event Sub-Process
Parts of the BPMN2 definition of the example Process relevant for exception handling
<itemDefinition id="_stringItem" structureRef="java.lang.String"/><message id="_message" itemRef="_stringItem"/>
<interface id="_serviceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService"> <operation id="_serviceOperation" name="throwException"> <inMessageRef>_message</inMessageRef>
</operation> </interface> <error id="_exception" errorCode="code" structureRef="_exceptionItem"/>
<itemDefinition id="_exceptionItem" structureRef="org.kie.api.runtime.process.WorkItem"/>
<message id="_exceptionMessage" itemRef="_exceptionItem"/>
<interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService"> <operation id="_handlingServiceOperation" name="handleException"> <inMessageRef>_exceptionMessage</inMessageRef>
</operation> </interface> <process id="ProcessWithExceptionHandlingError" name="Service Process" isExecutable="true" processType="Private"> <!-- properties --> <property id="serviceInputItem" itemSubjectRef="_stringItem"/>
<property id="exceptionInputItem" itemSubjectRef="_exceptionItem"/>
<!-- main process --> <startEvent id="_1" name="Start" /> <serviceTask id="_2" name="Throw Exception" implementation="Other" operationRef="_serviceOperation"> <!-- rest of the serviceTask element and process definition... --> <subProcess id="_X" name="Exception Handler" triggeredByEvent="true" > <startEvent id="_X-1" name="subStart"> <dataOutput id="_X-1_Output" name="event"/> <dataOutputAssociation> <sourceRef>_X-1_Output</sourceRef> <targetRef>exceptionInputItem</targetRef>
</dataOutputAssociation> <errorEventDefinition id="_X-1_ED_1" errorRef="_exception" />
</startEvent> <!-- rest of the subprocess definition... --> </subProcess> </process>
|
The itemDefinition element defines a data structure used in the serviceInputItem property of the Process.
|
|
The message element (1st reference) defines a message that contains the String defined by the itemDefinition element on the line above. The interface element below then refers to the itemDefinition element (2nd reference) in order to define what type of content the service (defined by the interface) expects.
|
|
The error element (1st reference) defines an error that is used to trigger the Event SubProcess of the Process. The content of the error is defined by the itemDefintion element defined below the error element.
|
|
This itemDefinition element (1st reference) defines an item that contains a WorkItem instance. The message element (2nd reference) then defines a message that uses this item definition to define its content. The interface element below that refers to the message definition (3rd reference) in order to define the type of content that the service expects.
In the Process element itself, a property element (4th reference) that contains the initial itemDefinition. This allows the Event SubProcess to store the error it receives in that property (5th reference).
|
5.1.5.1.2. Exception handling classes
serviceTask tasks use the org.jbpm.bpmn2.handler.ServiceTaskHandler class as its task handler class unless the serviceTask defines a custom WorkItemHandler implementation.
SignallingTaskHandlerDecorator instance.
Important
- Error events are signaled by sending an
Error-errorCode attribute valuevalue to the session. - Signal events are signaled by sending the name of the signal to the session.
- If you wanted to send an error event to a Boundary Catch Error Event, the error type should be of the format:
"Error-" + $AttachedNodeID + "-" + $ERROR_CODE. For example,Error-SubProcess_1-888would be a valid error type.However, this is NOT a recommended practice because sending the signal this way bypasses parts of the boundary error event functionality and it relies on internal implementation details that might be changed in the future. For a way to programmatically trigger a boundary error event when an Exception is thrown inWorkItemHandlersee this KnowledgeBase article.
Example 5.6. Using SignallingTaskHandlerDecorator
ServiceTaskHandler calls the ExceptionService.throwException() method to throw an exception (refer to the _handlingServiceInterface interface element in the BPMN2).
SignallingTaskHandlerDecorator that wraps the ServiceTaskHandler sends to the Process instance the error with the set error code.
import java.util.HashMap;
import java.util.Map;
import org.jbpm.bpmn2.handler.ServiceTaskHandler;
import org.jbpm.bpmn2.handler.SignallingTaskHandlerDecorator;
import org.jbpm.examples.exceptions.service.ExceptionService;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;
public class ExceptionHandlingErrorExample {
public static final void main(String[] args) {
runExample();
}
public static ProcessInstance runExample() {
KieSession ksession = createKieSession();
String eventType = "Error-code";
SignallingTaskHandlerDecorator signallingTaskWrapper
= new SignallingTaskHandlerDecorator(ServiceTaskHandler.class, eventType);
signallingTaskWrapper.setWorkItemExceptionParameterName(ExceptionService.exceptionParameterName);
ksession.getWorkItemManager().registerWorkItemHandler("Service Task", signallingTaskWrapper);
Map<String, Object> params = new HashMap<String, Object>();
params.put("serviceInputItem", "Input to Original Service");
ProcessInstance processInstance = ksession.startProcess("ProcessWithExceptionHandlingError", params);
return processInstance;
}
private static KieSession createKieSession() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ExceptionHandlingWithError.bpmn2"), ResourceType.BPMN2);
KieBase kbase = kbuilder.newKnowledgeBase();
return kbase.newKieSession();
}
|
Definition of the Error-code event to be sent to the process instance when the wrapped WorkItemHandler implementation throws an exception
|
|
Construction of the SignallingTaskHandlerDecorator class instance with the WorkItemHandler implementation and eventType as parameters
Note that a SignallingTaskHandlerDecorator class constructor that takes an instance of a WorkItemHandler implementation as its parameter is also available. This constructor is useful if the WorkItemHandler implementation does not allow a no-argument constructor.
|
|
Registering the WorItemHandler with the session
When an exception is thrown by the wrapped WorkItemHandler, the SignallingTaskHandlerDecorator saves it as a parameter in the WorkItem instance with a parameter name configured in the SignallingTaskHandlerDecorator (see the code below for the ExceptionService).
|
5.1.5.1.3. Exception service
ExceptionService class as follows:
<interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService"> <operation id="_handlingServiceOperation" name="handleException">
ExceptionService class to provide the exception handling abilities. The class is implemented as follows:
import org.kie.api.runtime.process.WorkItem;
...
public class ExceptionService {
public static String exceptionParameterName = "my.exception.parameter.name";
public void handleException(WorkItem workItem) {
System.out.println( "Handling exception caused by work item '" + workItem.getName() + "' (id: " + workItem.getId() + ")");
Map<String, Object> params = workItem.getParameters();
Throwable throwable = (Throwable) params.get(exceptionParameterName);
throwable.printStackTrace();
}
public String throwException(String message) {
throw new RuntimeException("Service failed with input: " + message );
}
public static void setExceptionParameterName(String exceptionParam) {
exceptionParameterName = exceptionParam;
}
}serviceTask.
5.1.5.1.4. Handling errors with Signals
Error event occurs during Process execution and the execution is interrupted immediately: no other Flows or Activities are executed.
Signal event as the Process execution continues after the Signal is processed (that is, after the Signal Event SubProcess or another Activities that the Signal triggered, finish their execution). Also, the Process execution finished successfully, not in an aborted state, which is the case if an Error is used.
error element which is then used to throw the Error:
<error id="_exception" errorCode="code" structureRef="_exceptionItem"/>
- Remove the line defining the
errorelement and define a<signal>element:<signal id="exception-signal" structureRef="_exceptionItem"/>
- Make sure to change all references from the "
_exception"<error>to the "exception-signal"<signal>.Change the<errorEventDefinition>element in the<startEvent>,<errorEventDefinition id="_X-1_ED_1" errorRef="_exception" />
to a<signalEventDefinition>:<signalEventDefinition id="_X-1_ED_1" signalRef="exception-signal"/>
5.1.5.1.5. Extracting information from WorkflowRuntimeException
scriptTask element that causes an exception, you can extract the information from the WorkflowRuntimeException as it is the wrapper of the scriptTask.
WorkflowRuntimeException instance stores the information outlined in Table 5.1, “Information in WorkflowRuntimeException instances”. Values of all fields listed can be obtained using the standard get* methods.
Table 5.1. Information in WorkflowRuntimeException instances
| Field name | Type | Description |
|---|---|---|
processInstanceId | long |
The id of the
ProcessInstance instance in which the exception occurred
Note that the
ProcessInstance may not exist anymore or be available in the database if using persistence.
|
processId | String | The id of the process definition that was used to start the process (that is, "ExceptionScriptTask" in
ksession.startProcess("ExceptionScriptTask");
) |
nodeId | long | The value of the (BPMN2) id attribute of the node that threw the exception |
nodeName | String | The value of the (BPMN2) name attribute of the node that threw the exception |
variables | Map<String, Object> | The map containing the variables in the process instance (experimental) |
message | String | The short message with information on the exception |
cause | Throwable | The original exception that was thrown |
WorkflowRuntimeException exception instance.
import org.jbpm.workflow.instance.WorkflowRuntimeException;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;
public class ScriptTaskExceptionExample {
public static final void main(String[] args) {
runExample();
}
public static void runExample() {
KieSession ksession = createKieSession();
Map<String, Object> params = new HashMap<String, Object>();
String varName = "var1";
params.put( varName , "valueOne" );
try {
ProcessInstance processInstance = ksession.startProcess("ExceptionScriptTask", params);
} catch( WorkflowRuntimeException wfre ) {
String msg = "An exception happened in "
+ "process instance [" + wfre.getProcessInstanceId()
+ "] of process [" + wfre.getProcessId()
+ "] in node [id: " + wfre.getNodeId()
+ ", name: " + wfre.getNodeName()
+ "] and variable " + varName + " had the value [" + wfre.getVariables().get(varName)
+ "]";
System.out.println(msg);
}
}
private static KieSession createKieSession() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ScriptTaskException.bpmn2"), ResourceType.BPMN2);
KieBase kbase = kbuilder.newKnowledgeBase();
return kbase.newKieSession();
}
}
<interface id="_serviceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
<operation id="_serviceOperation" name="throwException">
<inMessageRef>_message</inMessageRef>
<itemDefinition id="_exceptionItem" structureRef="org.kie.api.runtime.process.WorkItem"/>
<message id="_exceptionMessage" itemRef="_exceptionItem"/>
Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.