BPEL BluePrint 3

Fault Handling Using BPEL

Kiran Bhumana, Ryan Kennedy, Shauna Pickett-Gordon

Problem Description

Built-in support for error handling has evolved into a key feature of many programming languages. Before the advent of error-handling constructs, developers had to manually provide the framework to ensure proper execution in error situations. Now, error-handling constructs are available to support the following patterns:

Modern programming languages, including the Business Process Execution Language (BPEL), provide high-level constructs that enable error-handling patterns to be programmed easily and with minimal risk.

Since BPEL is a WSDL-based specification, it refers to these concepts as fault handling. Fault handling in BPEL provides semantics for catching various types of faults, propagating faults, and nesting fault-handling operations.

This document explains some design aspects and usage patterns of BPEL fault handling. It does not attempt an exhaustive list of fault-handling features, but focuses on the most common usage patterns.

Solution

While a BPEL process is running, it can encounter faults from partner services that are invoked or faults that originate from within the BPEL process itself. Faults that are due to partner service invocations are typically defined on the WSDL operation, whereas faults that originate from within the process are BPEL-defined standard faults and are identified in certain error situations.

BPEL defines a faultHandler construct that contains catch or catchAll constructs to enable the handling of a fault. A faultHandler's catch construct defines certain criteria for catching a fault, and the catch executes when a fault is thrown and meets those criteria. The faultHandler's catchAll construct handles any occurrence of a fault. In the example provided in this BluePrint's design details, a defined faultHandler executes a reply activity notifying the client that an item is out of stock.

A BPEL developer can isolate sets of related activities within a business process by placing them in a scope element. Using scope lets the developer control a fault's effect and any consequent action or compensation. The design also isolates other areas of business process execution, and enables better event handling and compensatory actions.

BPEL provides the faultHandler construct at three levels:

Scopes not only isolate fault handling, but also provide a nesting capability, which suggests that associated fault handlers can form a similar nested hierarchy. A nested faultHandler that can't handle a particular fault delegates fault handling to the enclosing faultHandler, until it reaches the faultHandler at the process level. At that level, either the fault is handled or the instance is terminated. The throw and rethrow constructs are defined to explicitly cause a fault to happen and propagate throughout the fault-handling chain. A nested faultHandler can rethrow a fault so that its enclosed scope has the opportunity to handle it. faultHandler that are associated with a scope are also used to undo partially completed work within the scope.

Design Details

The example shown here uses the services discussed in the first BluePrint of this series, Synchronous Web Service Interactions Using BPEL, with added emphasis on fault handling.

The following figure describes the synchronous interaction pattern between the services.

Service-Implementation
Figure 1: Stateless and Synchronous Service Implementation

In this scenario, the following fault handling patterns are demonstrated:

To achieve those fault handling patterns, the following BPEL constructs are used:

Business Logic of the Services

See the first BluePrint in this series for the essential business logic of the services that are used in this example.

Define the BPEL

This particular example demonstrates two kinds of faults: a fault that is explicitly thrown in POService, and a fault that could happen due to the consumption of InventoryService.

Fault Explicitly Thrown in BPEL Process
BPEL provides the throw construct in order to throw a fault explicitly within a BPEL process.

In this example, POService has been modified to check if the incoming purchase order's orderDescription value starts with ThrowPOServiceFault. If so, POService uses the throw construct to throw a fault that is to be handled by the process-level faultHandlers.

        <if name="Decision">
            <condition>starts-with($purchaseOrderRequest.purchaseOrder/orderDescription,                 'ThrowPOServiceFault')
            </condition>
                <sequence name="Sequence1">
                    <assign name="Assign1">
                        <copy>
                            <from>'throwing purchase order fault'</from>
                            <to part="faultInfo" variable="purchaseOrderFault"/>
                        </copy>
                    </assign>
                    <throw name="Throw"
                    faultName="pos:cannotCompleteOrder"
                    faultVariable="purchaseOrderFault"/>
                </sequence>
        </if>

faultHandlers are used in much the same way as try/catch blocks found in modern programming languages. Within the faultHandlers, one or more catch activities are defined allowing you to handle different faults that may occur within the business process.

In this scenario, POService contains a process-level fault handler with a catch activity to handle the pos:cannotCompleteOrder fault. Here, the catch clause is defined so that the faults are handled by the fault name.

<faultHandlers>
        <catch faultName="pos:cannotCompleteOrder">
            <sequence>
                <reply name="sendPurchaseOrder.Fault"
                partnerLink="POServicePLink"
                portType="pos:purchaseOrderPT"
                operation="sendPurchaseOrder"
                variable="purchaseOrderFault"
                faultName="pos:cannotCompleteOrder"/>
            </sequence>
        </catch>
...
</faultHandlers>

Fault Thrown Due to the Consumption of Partner Service
The inventory service provider has business logic to check the availability of an item. In this BluePrint, additional behavior has been defined so that InventoryService can return a WSDL-defined fault when a requested item is not available in inventory. Within InventoryService, if the incoming purchase order's orderDescription value starts with ThrowInventoryFault, a reply activity is used to return a fault message to the consumer of the service, POService.

          <if name="checkAvailability">
            <condition>starts-with($purchaseOrder.purchaseOrder/orderDescription, 'OrderVal')</condition>
                <assign name="Case1">
                    <copy>
                        <from>true()</from>
                        <to>$inventoryStatus.inventoryPart/inventoryStatus</to>
                    </copy>
                    <copy>
                        <from>'available'</from>
                        <to>$inventoryStatus.inventoryPart/inventoryStatusMessage</to>
                    </copy>
                </assign>
            <elseif>
                <condition>starts-with($purchaseOrder.purchaseOrder/orderDescription, 'ThrowInventoryFault')</condition>
                <sequence name="Case3">
                    <assign name="Assign2">
                        <copy>
                            <from>concat('Inventory Fault thrown for PurchaseOrder ID ', $purchaseOrder.purchaseOrder/orderId, ' customer ID ', $purchaseOrder.purchaseOrder/customerId)</from>
                            <to part="faultInfo" variable="inventoryService_Fault"/>
                        </copy>
                    </assign>
                    <reply name="inventoryService.Reply_1"
                    partnerLink="inventorySevicePLink"
                    portType="invs:inventoryPortType"
                    operation="inventoryService"
                    variable="inventoryService_Fault" faultName="invs:inventoryFaultType"/>
                </sequence>
            </elseif>
            <else>
                <assign name="Case2">
                    <copy>
                        <from>false()</from>
                        <to>$inventoryStatus.inventoryPart/inventoryStatus</to>
                    </copy>
                    <copy>
                        <from>'currently out of stock'</from>
                        <to>$inventoryStatus.inventoryPart/inventoryStatusMessage</to>
                    </copy>
                </assign>
            </else>
        </if>

A invs:inventoryFaultType fault is then caught in a second catch activity defined within the POService fault handler. This particular fault is handled at the process level and causes POService to return a WSDL-defined fault to its consumer.

<faultHandlers>
...
        <catch faultName="invs:inventoryFaultType" faultVariable="inventoryServiceFault" faultMessageType="invs:inventoryFault">
            <sequence>
                <assign name="Assign1_1">
                    <copy>
                        <from variable="inventoryServiceFault"
                        part="faultInfo"/>
                        <to variable="purchaseOrderFault"
                        part="faultInfo"/>
                    </copy>
                </assign>
                <reply name="sendPurchaseOrder.Fault"
                partnerLink="POServicePLink"
                portType="pos:purchaseOrderPT"
                operation="sendPurchaseOrder"
                variable="purchaseOrderFault"
                faultName="pos:cannotCompleteOrder"/>
            </sequence>
        </catch>

...
</faultHandlers>

Since the fault handling is at the process level, the BPEL process instance is terminated after the fault is handled.

The following diagram illustrates how a process-level fault handler is used in the POService BPEL process.


Figure 2: POService BPEL Process


The next diagram shows the corresponding fault-handling activities in the InventoryService BPEL process.


Figure 3: InventoryService BPEL Process

See the first BluePrint in this series for further details on the BPEL artifacts used in each process.

Define the WSDL

The following POService WSDL operation defines a fault named cannotCompleteOrder:

    <portType name="purchaseOrderPT">
<operation name="sendPurchaseOrder">
<input name="sendPurchaseOrderRequest" message="tns:POMessage"></input>
<output name="sendPurchaseOrderReply" message="tns:POMessage"></output>
<fault name="cannotCompleteOrder" message="tns:orderFaultType"></fault>
</operation>
</portType>

The InventoryService WSDL operation also defines a fault named inventoryFaultType:

    <portType name="inventoryPortType">
<operation name="inventoryService">
<input name="purchaseOrder" message="tns:POMessage"></input>
<output name="inventoryStatus" message="tns:InventoryMessage"></output>
<fault name="inventoryFaultType" message="tns:inventoryFault"></fault>
</operation>
</portType>

See the first BluePrint in this series for further details on these WSDL artifacts.

Additional Design Details

Also consider the following when you are designing fault handling into your BPEL processes.

Input Data XML for Different Scenarios

The following input XML instance gives a valid response.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns="http://manufacturing.org/xsd/purchase"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<sendPurchaseOrder>
<purchaseOrder>
<purchaseOrder xmlns="http://manufacturing.org/xsd/purchase">
<orderId>00012343</orderId>
<customerId>9876</customerId>
<orderDescription>OrderVal_Any_Description</orderDescription>
<price></price>
</purchaseOrder>
</purchaseOrder>
</sendPurchaseOrder>
</soapenv:Body>
</soapenv:Envelope>

The next input XML instance causes a cannotCompleteOrder fault to be thrown to the client. The fault is due to the execution of a throw activity within the POService BPEL process.

<soapenv:Envelope xmlns="http://manufacturing.org/xsd/purchase" 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<sendPurchaseOrder>
<purchaseOrder>
<purchaseOrder xmlns="http://manufacturing.org/xsd/purchase">
<orderId>00012342</orderId>
<customerId>9876</customerId>
<orderDescription>ThrowPOServiceFault_Val_Any_Description</orderDescription>
<price></price>
</purchaseOrder>
</purchaseOrder>
</sendPurchaseOrder>
</soapenv:Body>
</soapenv:Envelope>

The following input XML instance causes a cannotCompleteOrder fault to be thrown to the client. This fault is due to a fault sent by the inventory service.

<soapenv:Envelope xmlns="http://manufacturing.org/xsd/purchase" 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<sendPurchaseOrder>
<purchaseOrder>
<purchaseOrder xmlns="http://manufacturing.org/xsd/purchase">
<orderId>00012341</orderId>
<customerId>9876</customerId>
<orderDescription>ThrowInventoryFault_Any_Description</orderDescription>
<price></price>
</purchaseOrder>
</purchaseOrder>
</sendPurchaseOrder>
</soapenv:Body>
</soapenv:Envelope>

References


© Sun Microsystems 2006. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.