Skip to main content

Thrown Fault with Compensation Handler

Thrown Fault with Compensation Handler

In business process management, it is often necessary to reverse some segment of logic. This convention is known as “compensation.” The ruling principle is that if the business process does something, it must be able to undo it. That is, if a failure occurs, the business process must be able to compensate by undoing the action that failed. You need to be able to unroll all of the actions from that failure point back to the beginning, as if the problem action never occurred. BPL enables this with a mechanism called a compensation handler.

BPL <compensationhandler> blocks are somewhat like subroutines, but they do not provide a generalized subroutine mechanism. You can “call” them, but only from <faulthandler> blocks, and only within the same <scope> as the <compensationhandler> block. The <compensate> element invokes a <compensationhandler> block by specifying its name as a target. Extra quotes are not needed for this syntax:

<compensate target="general"/>

Compensation handlers are only useful if you can undo the actions already performed. For example, if you transfer money into the wrong account, you can transfer it back again, but there are some actions that cannot be neatly undone. You must plan compensation handlers accordingly, and also organize them according to how far you want to roll things back.

Suppose you have the following BPL:

Complex BPL diagram with compensationhandler element in scope element

This BPL business process does the following:

  1. The Context tab (not shown) defines a property called MyBalance and sets its value to 100.

  2. The first <trace> element generates the message before scope balance is, followed by the value of MyBalance.

  3. The <scope> element starts the scope.

  4. The next <trace> element generates the message before debit.

  5. The <assign> element decrements MyBalance by 1.

  6. The next <trace> element generates the message after debit.

  7. The <throw> element throws a specific, named fault ("BuyersRegret").

  8. Control now goes to the <faulthandlers>. A <catch> exists whose fault value is "BuyersRegret", so control goes there.

    If we drill down into this <catch> element, we see the following:

    BPL diagram with the following shapes in order: start, trace, trace, compensate, trace, end

  9. Within this <catch>, the first <trace> element generates the message in catch faulthandler for 'BuyersRegret'.

  10. Within this <catch>, the second <trace> element generates the message before restore balance is, followed by the current value of MyBalance.

  11. The <compensate> element is used. For this element, target is a <compensationhandler> whose name is RestoreBalance. Within this <compensationhandler> block:

    • A <trace> statement outputs the message “Restoring Balance”

    • An <assign> statement increments MyBalance by 1.

    Note:

    It is not possible to reverse the order of <compensationhandlers> and <faulthandlers>. If both blocks are provided, <compensationhandlers> must appear first and <faulthandlers> second.

  12. The next <trace> element generates the message after restore balance is, followed by the current value of MyBalance.

  13. The <scope> ends.

  14. The last <trace> element generates the message after scope balance is, followed by the current value of MyBalance.

Event Log Entries

The corresponding Event Log entries look like this:

Event log with several entries

XData for This BPL

This BPL is defined by the following XData block:

XData BPL
{
<process language='objectscript'
         request='Test.Scope.Request'
         response='Test.Scope.Response' >
  <context>
    <property name="MyBalance" type="%Library.Integer" initialexpression='100'/>
  </context>
  <sequence>
    <trace value='"before scope balance is "_context.MyBalance'/>
    <scope>
      <trace value='"before debit"'/>
      <assign property='context.MyBalance' value='context.MyBalance-1'/>
      <trace value='"after debit"'/>
      <throw fault='"BuyersRegret"'/>
      <compensationhandlers>
        <compensationhandler name="RestoreBalance">
          <trace value='"Restoring Balance"'/>
          <assign property='context.MyBalance' value='context.MyBalance+1'/>
        </compensationhandler>
      </compensationhandlers>
      <faulthandlers>
        <catch fault='"BuyersRegret"'>
          <trace value='"In catch faulthandler for &apos;BuyersRegret&apos;"'/>
          <trace value='"before restore balance is "_context.MyBalance'/>
          <compensate target="RestoreBalance"/>
          <trace value='"after restore balance is "_context.MyBalance'/>
        </catch>
        <catchall>
          <trace value='"in catchall faulthandler"'/>
          <trace value=
            '"%LastError "_
            $System.Status.GetErrorCodes(..%Context.%LastError)_
            " : "_
            $System.Status.GetOneStatusText(..%Context.%LastError)'
            />
          <trace value='"%LastFault "_..%Context.%LastFault'/>
        </catchall>
      </faulthandlers>
    </scope>
    <trace value='"after scope balance is "_context.MyBalance'/>
  </sequence>
</process>
}
FeedbackOpens in a new tab