JBoss.org Community Documentation

- Prev
- Next

## Chapter 14. Exception Management

## 14.1. Overview

This chapter will describe how to deal with unexpected behavior in your business processes using both BPMN2 and technical mechanisms.

The first section (Introduction) will define and explain the types of exceptions that can happen or be used in a business process (Business Exceptions and Technical Exceptions).

The next section will explain Technical Exceptions: we'll go through an example that uses both BPMN2 and `WorkItemHandler` implementations in order to isolate and handle exceptions caused by a technical component. We will also explain how to modify the example to suit other use cases.

## 14.2. Introduction

What happens to a business process when something unexpected happens during the process? Most of the time, when you create and design a new process definition, you'll begin by describing the normative or desirable behaviour. However, a process definition that only describes all of the normal tasks and their execution order is incomplete.

The next step is to think about what might go wrong when the business process is run. What would happen if any of the human or technical actors in the process do not respond in unexpexected ways? Will any of the technical systems that the process interacts with return unexpected results -- or not return any results at all?

Deviations from the normative or "happy" flow of a business process are called exceptions. In some cases, exceptions might not be that unusual, such as trying to debit an empty bank account. However, some processes might contain many complex situations involving exceptions, all of which must be handled correctly.

## Note

The rest of chapter assumes that you know how to create custom `<task>` nodes and how to implement and register `WorkItemHandler` implementations. More information about these topics can be found in the Domain-specific processes chapter.

## 14.3. Business Exceptions

Business Exceptions are exceptions that are designed and managed in the BPMN2 specification of a business process. In other words, Business Exceptions are exceptions which happen at the process or workflow level, and are not related to the technical components.

Many of the elements in BPMN2 related to Business Exceptions are related to Compensation and Business Transactions. Compensation, in particular, is complexer than many other parts of the BPMN2 specfication.

Full support for compensation and business transactions is expected with the release of jBPM 6.1 or 6.2. Once that has been implemented, this section will contain more information about using those BPMN2 features with jBPM.

### 14.3.1. Business Exceptions elements in BPMN2

The following attempts to briefly describe Compensation and Business Transaction related elements in BPMN2. For more complete information about these elements and their uses, see the BPMN2 specification, Bruce Silver's book `BPMN Method and Style` or any of the other available books about the use of BPMN2.

Table 14.1. BPMN2 Exception Handling Elements

BPMN2 Element types | Description
--- | ---
Errors and Error Events | Error Events can be used to signal when a process has encountered an unexpected situation: signalling an error is often called throwing an error. Boundary Error Events in a different part of the process can then be used to catch the error and initiate a sequence of activities to handle the exception. Errors themselves can be extended with extra information that is passed from the throwing to catching event. This is done with the use of an Item Definition.
Business Transactions | A Business Transaction in BPMN2 is a subprocess which can be used with compensation. Grouping activities in a Business Transaction lets the process designer easily add exception handling to specific activities in the subprocess. Using a Business Transaction guarantees that all activities in the transaction will have completed successfully if the Business Transaction completes successfully. When a Business Transaction is interrupted or otherwise not completed successfully, there is a guarantee that all activities in the Business Transaction that have been initiated will be compensated if compensating activities are defined for those activities.
Compensation | Exception handling activities associated with the normal activies in a Business Transaction are triggered by Compensation Events. Compensation Events may only be used within Business Transactions. There are 3 types of compensation events: Intermediate (a.k.a. Boundary) (catch) events, Start (catch) events, and Intermediate or End (throw) events. Compensation Boundary (catch) events are attached to activites (e.g. tasks) that could cause an exception. They may only be attached to activites inside a Business Transaction. If a Business Transaction fails, possibly because of the failure of one of the activities inside it, then the activities associated with Boundary (catch) events will be triggered. Only one activity or node may be associated with a Compensation Boundary Event! Start (catch) events are used when defining an Compensation Event SubProcess inside a Business Transaction. Compensation Event SubProcesses are often used when a subprocess is needed to compensate for the Business Transaction as a whole (as opposed to defining compensating activities per node in the Business Transaction. This subprocess is triggered when a Business Transaction fails, just like activities attached to Compensation Boundary (catch) events. Compensation Intermediate and End events are used within Business Transactions in order to throw Compensation Events. Often, logic in the Business Transaction subprocesses will determine whether or not the Business Transaction has succeeded or failed. If the subprocess has failed, then the process will proceed to an Intermediate or End Compensation Event in order to trigger compensation for the Business Transaction subprocess.
Cancel Events | Cancel Events trigger cancellation of a Business Transaction and can thus only be used with a Business Transaction. When a Cancel Event is thrown, this indicates that the Business Transaction should be cancelled. Entities involved in the Business Transaction are then informed (via a TransactionProtocol Cancel Message) that the Business Transaction has been cancelled. Cancellation of a Business Transaction implicitly triggers compensation of the Business Transaction. See the sources mentioned above for the differences between Error Events (abortion of a process), Cancel Events (cancellation) and Compensate Events (compensation).

## 14.4. Technical Exceptions

Technical exceptions happen when a technical component of a business process acts in an unexpected way. When using Java based systems, this often results in a literal Java Exception being thrown by the system.

Technical components used in a process can fail in a way that can not be described using BPMN2. In this case, it's important to handle these exceptions in expected ways.

The following types of code might throw exceptions:

- Any code that is present in the process definition itself
- Any code that is executed during a process and is not part of jBPM
- Any code that interacts with a technical component outside of the process engine

However, those are somewhat abstract defintions. We can narrow down the places at which an exception might be thrown. Technical exceptions can occur at the following points:

1. Code present in `<scriptTask>` nodes or in the jbpm-specific `<onEntry>` and `<onExit>` elements
2. Code executed in `WorkItemHandlers` associated with `<task>` and task-type nodes

It is much easier to ensure correct exception handling for `<task>` and other task-type nodes that use `WorkItemHandler` implementations, than for code executed directly in a `<scriptTask>`.

Exceptions thrown by `<scriptTask>` can cause the process to fail in an unrecoverable fashion. While there are certain things that you can do to contain the damage, a process that has failed in this way can not be restarted or otherwise recovered. This also applies for other nodes in a process definition that contain script code in the node definition, such as the `<onEntry>` and `<onExit>` elements.

When jBPM engine does throw an exception generated by the code in a `<scriptTask>` the exception thrown is a special Java exception called the `WorkflowRuntimeException` that contains information about the process.

## Warning

Again, exceptions generated by a `<scriptTask>` node (and other nodes containing script code) will leave the process unrecoverable. In fact, often, the code that starts the process itself will end up throwing the exception generated by the business process, without returning a reference to the process instance.

For this reason, it's important to limit the scope of the code in these nodes to operations dealing with process variables. Using a `<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.

`<task>` nodes, `<serviceTask>` nodes and the rest of the `task`-type nodes are explictly meant for interacting with other systems -- not `<scriptTask>` nodes! Use `<task>`-type nodes to interact with other technical components.

### 14.4.1. Handling exceptions in `WorkItemHandler` instances

`WorkItemHandler` classes are used when your process interacts with other technical systems. For an introduction to them and how to use them in processes, please see the Domain-specific processes chapter.

While you can build exception handling into your own `WorkItemhandler` implementations, there are also two “handler decorator” classes that you can use to wrap a `WorkItemhandler` implementation.

These two wrapper classes include logic that is executed when an exception is thrown during the execution (or abortion) of a work item.

Table 14.2. Exception Handling `WorkItemHandler` wrapper classes

Decorator classes in the `org.jbpm.bpmn2.handler` package | Description
--- | ---
`SignallingTaskHandlerDecorator` | This class wraps an existing `WorkItemHandler` implementation. When the `.executeWorkItem(...)` or `.abortWorkItem(...)` methods of the original `WorkItemHandler` instance throw an exception, the `SignallingTaskHandlerDecorator` will catch the exception and signal the process instance using a configurable event type. The exception thrown will be passed as part of the event. This functionality can be used to signal an Event SubProcess defined in the process definition.
`LoggingTaskHandlerDecorator` | This class reacts to all exceptions thrown by the `.executeWorkItem(...)` or `.abortWorkItem(...)` `WorkItemHandler` methods by logging the errors. It also saves any exceptions thrown so to an internal list so that they can be retrieved later for inspection or further logging. Lastly, the content and format of the message logged upon an exception are configurable.

While the two classes described above should cover most cases involving exception handling, a Java developer with some experience with jBPM should be able to create a `WorkItemHandler` that executes custom code upon an exception.

If you do decide to write a custom `WorkItemHandler` that includes exception handling logic, keep the following checklist in mind:

1. Are you catching all possible exceptions that you want to (and no more, or less)?
2. Are you making sure to either complete or abort the work item after an exception has been caught? If not, are there mechanisms to retry the process later? Or are incomplete process instances acceptable?
3. What other actions should be taken when an exception is caught? Do you want to simply log the exception, or is it also important to interact with other technical systems? Do you want to trigger a (BPMN2) subprocess that will handle the exception?

## Important

When you use the `WorkItemManager` to signal that the work item has been completed or aborted, make sure to do that after you've sent any signals to the process instance. Depending on how you've defined your process, calling `WorkItemManager.completeWorkItem(...)` or `WorkItemManager.abortWorkItem(...)` will trigger the completion of the process instance. This is because the these methods trigger the jBPM process engine to continue the process flow.

In the next section, we'll describe an example that uses the `SignallingTaskHandlerDecorator` to signal an event subprocess when a work item handler throws an exception.

## 14.5. Technical Exception Examples

### 14.5.1. Example: service task handlers

We'll go through one example in this section, and then look quickly at how you can change it to get the behavior you want. The example involves an `<error>` event that's caught by an (Error) Event SubProcess.

When an Error Event is thrown, the containing process will be interrupted. This means that after the process flow attached to the error event has executed, the following will happen:

1. process execution will stop, and no other parts of the process will execute
2. the process instance will end up in an aborted state (instead of completed)

The example we'll go through contains an `<error>`, but at the end of the secion, we'll show how you can change the process to use a `<signal>` instead.

## Tip

The code and BPMN2 process definition shown in the next section are available in the `jbpm-examples` module. See the `org.jbpm.examples.exceptions.ExceptionHandlingErrorExample` class for the java code. The BPMN2 process definition is available in the `exceptions/ExceptionHandlingWithError.bpmn2` file in the `src/main/resources` directory of the `jbpm-examples` module.

#### 14.5.1.1. BPMN2 configuration

Let's look at the BPMN2 process definition first. Besides the definition of the process, the BPMN2 elements defined before the actual process definition are also important. Here's an image of the BPMN2 process that we'll be using in the example:

Figure 14.1.

The BPMN2 process fragment below is part of the process shown above, and contains some notes on the different BPMN2 elements.

## Note

If you're viewing this on a web browser, you may need to widen your browser window in order to see the "callout" or note numbers on the righthand side of the code.

      <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>

| This `<itemDefinition>` element defines a data structure that we then use in the `serviceInputItem` property in the process.
| This `<message>` element (1rst reference) defines a message that has a String as its content (as defined by the `<itemDefintion>` element on line above). The `<interface>` element below it refers to it (2nd reference) in order to define what type of content the service (defined by the `<interface>`) expects.
| This `<error>` element (1rst reference) defines an error for use later in the process: an Event SubProcess is defined that is triggered by this error (2nd reference). The content of the error is defined by the `<itemDefintion>` element defined below the `<error>` element.
| This `<itemDefintion>` element (1rst 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 itself, a `<property>` element (4th reference) is defined as having the content defined by the initial `<itemDefintion>`. This is helpful because it means that the Event SubProcess can then store the error it receives in that property (5th reference).

## Caution

When you're using a `<serviceTask>` to call a Java class, make sure to double check the class name in your BPMN2 definition! A small typo there can cost you time later when you're trying to figure out what went wrong.

#### 14.5.1.2. `SignallingTaskHandlerDecorator` and `WorkItemHandler` configuration

Now that BPMN2 process definition is (hopefully) a little clearer, we can look at how to set up jBPM to take advantage of the above BPMN2.

In the (BPMN2) process definition above, we define two different `<serviceTask>` activities. The `org.jbpm.bpmn2.handler.ServiceTaskHandler` class is the default task handler class used for `<serviceTask>` tasks. If you don't specify a `WorkItemHandler` implementation for a `<serviceTask>`, the `ServiceTaskHandler` class will be used.

In the code below, you'll see that we actually wrap or decorate the `ServiceTaskHandler` class with a `SignallingTaskHandlerDecorator` instance. We do this in order to define the what happens when the `ServiceTaskHandler` throws an exception.

In this case, the `ServiceTaskHandler` will throw an exception because it's configured to call the `ExceptionService.throwException` method, which throws an exception. (See the `_handlingServiceInterface` `<interface>` element in the BPMN2.)

In the code below, we also configure which (error) event is sent to the process instance by the `SignallingTaskHandlerDecorator` instance. The `SignallingTaskHandlerDecorator` does this when an exception is thrown in a task. In this case, since we've defined an `<error>` with the error code “code” in the BPMN2, we set the signal to `Error-code`.

## Important

When signalling the jBPM process engine with an event of some sort, you should keep in mind the rules for signalling process events.

- Error events can be signalled by sending an "Error-" + <the `errorCode` attribute value> value to the session.
- Signal events can be signalled by sending the name of the signal to the session.

    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();

        }

| Here we define the name of the event that will be sent to the process instance if the wrapped `WorkItemHandler` implementation throws an exception. The `eventType` string is used when instantiating the `SignallingTaskHandlerDecorator` class.
| Then we construct an instance of the `SignallingTaskHandlerDecorator` class. In this case, we simply give it the class name of the `WorkItemHandler` implementation class to instantiate, but another constructor is available that we can pass an instance of a `WorkItemHandler` implementation to (necessary if the `WorkItemHandler` implementation does not have a no-argument constructor).
| When an exception is thrown by the wrapped `WorkItemHandler`, the `SignallingTaskHandlerDecorator` saves it as a parameter in the `WorkItem` instance with a parameter name that we configure the `SignallingTaskHandlerDecorator` to give it (see the code below for the `ExceptionService`).

#### 14.5.1.3. `ExceptionService` setup and configuration

In the BPMN2 process definition above, a service interface is defined that references the `ExceptionService` class:

    <interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">

        <operation id="_handlingServiceOperation" name="handleException">

In order to fill in the blanks a little bit, the code for the `ExceptionService` class has been included below. In general, you can specify any Java class with the default or an other no-argument constructor and have it executed during a `<serviceTask>`

    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;

      }

    }

#### 14.5.1.4. Changing the example to use a `<signal>`

In the example above, the thrown Error Event interrupts the process: no other flows or activities are executed once the Error Event has been thrown.

However, when a Signal Event is processed, the process will continue after the Signal Event SubProcess (or whatever other activities that the Signal Event triggers) has been executed. Furthermore, this implies that the the process will not end up in an aborted state, unlike a process that throws an Error Event.

In the process above, we use the `<error>` element in order to be able to use an Error Event:

      <error id="_exception" errorCode="code" structureRef="_exceptionItem"/>

When we want to use a Signal Event instead, we remove that line and use a `<signal>` element:

       <signal id="exception-signal" structureRef="_exceptionItem"/>

However, we must also change all references to the "`_exception`" `<error>` so that they now refer to the "`exception-signal`" `<signal>`.

That means that the `<errorEventDefintion>` element in the `<startEvent>`,

       <errorEventDefinition id="_X-1_ED_1" errorRef="_exception" />

must be changed to a `<signalEventDefintion>` which would like like this:

       <signalEventDefinition id="_X-1_ED_1" signalRef="exception-signal"/>

In short, we have to make the following changes to the `<startEvent>` in the Event SubProcess:

1. It will now contain a `<signalEventDefintion>` instead of a `<errorEventDefintion>`
2. The `errorRef` attribute in the `<erroEventDefintion>` is now a `signalRef` attribute in the `<signalEventDefintion>`.
3. The `id` attribute in the `signalRef` is of course now the id of the `<signal>` element. Before it was id of `<error>` element.
4. Lastly, when we signal the process in the Java code, we do not signal "`Error-code`" but simply "`exception-signal`", the `id` of the `<signal>` element.

### 14.5.2. Example: logging exceptions thrown by bad `<scriptTask>` nodes

In this section, we'll briefly describe what's possible when dealing with `<scriptTask>` nodes that throw exceptions, and then quickly go through an example (also available in the `jbpm-examples` module) that illustrates this.

#### 14.5.2.1. Introduction

If you're reading this, then you probably already have problem: you're either expecting to run into this problem because there are scripts in your process definition that might throw an exception, or you're already running a process instance with scripts that are causing a problem.

Unfortunately, if you're running into this problem, then there is not much you can do. The only thing that you can do is retrieve more information about exactly what's causing the problem. Luckily, when a `<scriptTask>` node causes an exception, it's wrapped in a `WorkflowRuntimeException`.

What type of information is available? The `WorkflowRuntimeException` instance will contain the information outlined in the following table. All of the fields listed are available via the normal `get*` methods.

Table 14.3. Information contained in `WorkflowRuntimeException` instances.

Field name | Type | Description
--- | --- | ---
`processInstanceId` | `long` | The id of the `ProcessInstance` instance in which the exception occurred. This `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 (i.e. "`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 indicating what went wrong.
`cause` | `Throwable` | The original exception that was thrown.

#### 14.5.2.2. Example: Exceptions thrown by a `<scriptTask>`.

The following code illustrates how to extract extra information from a process instance that throws a `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();

        }

    }

- PrevChapter 13. Domain-specific processes
- Top of page
- Front page
- NextChapter 15. Flexible Processes