Business Process Execution Language for Web Services (BPEL)


BPEL is a specification jointly authored by IBM, Microsoft, and BEA that formed part of a trio of specifications which also included application Web service coordination and transactions to support the development of reliable, inter-enterprise workflows. The central tenet of BPEL is the ability to capture both the constituent activities of a workflow and relationships between partners involved in that workflow in a platform-neutral format (XML), and support the highest possible level of interoperability by basing the technology on commonly adopted Web services standards. BPEL is grounded on the WSDL 1.1 specification, where stateless Web service interfaces are augmented with state management and message correlating features which in turn are consumed within workflow processes. This is not dissimilar in its approach to WSCL (as discussed in Chapter 5), which extends WSDL to provide conversation support for Web services.

In this section we work with the BPEL 1.0 standard since it is well known and has a reasonable level of tool support, though BPEL 1.1 has recently been released at the time of writing. The differences between the 1.0 and 1.1 versions are small enough that anyone with a good grasp of BPEL 1.0 will find BPEL 1.1 a straightforward extension.


The BPEL Stack

The BPEL model is built on a number of layers, with each layer building on the facilities of the previous. Figure 6-5 shows the fundamental components of the BPEL architecture, which consists of the following:

  • A means of capturing enterprise interdependencies with partners and associated service links (the "type" of the partner based on its WSDL portType) and service references (a typed "pointer") to an active partner at runtime.

  • Message correlation layer that ties together messages and specific workflow instances, based on messages properties which expose parts of a message as a unique "key."

  • State management features to maintain, update and interrogate parts of the process state as a workflow progresses.

  • Scopes where individual activities (workflow stages) are composed to form actual algorithmic workflows.

Figure 6-5. BPEL logical view.

graphics/06fig05.gif

This workflow stack is ultimately supported by an execution engine (there are several available from companies like IBM, Microsoft, and Collaxa). The engine interprets the enterprise interdependencies in order to bind to partners' remote Web services, and also executes the activities that drive those Web services. It is also a useful mental framework to bear in mind while we drill down into BPEL and develop our sample BPEL workflow. Over the next sections we begin to fill in that abstract framework with concrete examples from BPEL, starting with the algorithmic components of a workflow its activities.

Activities

In order to execute a process, we must have some means of describing the algorithmic behavior of that process. We need to understand the features that the workflow language provides to manipulate data, iterate, call external functions, and so on, and how to compose these primitives into meaningful workflows. To support this, the BPEL specification defines a number of types of fundamental activities that are the building blocks of algorithms which it classifies as basic and structured types. Basic activities deal with state management, communication, and exception handling, while structured activities deal with process control-flow issues. We further classify the activities[2] as shown in Table 6-1:

[2] In all examples after this point, all BPEL-specific XML elements will be prefixed with the XML prefix bpws which is associated with the namespace http://schemas.xmlsoap.org/ws/2002/07/business-process/.

Table 6-1. Activity Types in BPEL

Structuring Activates

Communication Activities

  • <sequence>

  • <flow>

  • <switch>

  • <pick>

  • <while>

  • <scope>

  • <receive>

  • <reply>

  • <invoke>

Miscellaneous Activities

Exception Handling Activates

  • <assign>

  • <wait>

  • <empty>

  • <throw>

  • <terminate>

  • <compensate>

In the following sections, we take each of the groups of activities from Table 6-1 and examine the syntax and semantics of them.

Structuring Activities

The BPEL structuring activities manage control flow dependencies in a workflow. They are responsible for serializing and parallelizing activities (sequence, flow), choosing from alternative paths in a workflow (switch, pick), iterating parts of a workflow (while), and declaring activity scopes (scope).

Sequence

Imposing ordering on activities with a sequence is straightforward. Using a sequence activity, the order of activity is simply the order in which the enclosed activities appear (this is lexical order). For instance, the workflow that undertakes the reservation process for a hotel will first receive a sales call, check availability, take a security deposit, and confirm the order in strict sequence. The BPEL process representation for this is shown in Figure 6-6.

Figure 6-6. The sequence activity.
 <bpws:sequence>   <!-- Wait for reservation call -->   <bpws:invoke ... />   <!-- Check availability -->   <bpws:invoke ... />   <!-- Take deposit -->   <bpws:invoke ... />   <!-- Confirm reservation -->   <bpws:invoke ... /> </bpws:sequence> 
Flow

Similarly, parallelizing activities that have no dependencies is achieved by enclosing the parallel activities within a flow element. For example, the same customer's computing system that initiated the hotel reservation sequence may also have been organizing flights and car rental simultaneously. If we assume these activities are to some degree independent, we can execute them in parallel and develop the flow activity shown in Figure 6-7.

Figure 6-7. The canonical form of flow activities.
 <bpws:flow>   <! Book hotel -->   <bpws:invoke partner="Hotelier" ... />   <!-- Hire care -->   <bpws:invoke partner="CarRentalAgency" ... />   <!-- Book flight -->   <bpws:invoke partner="AirlineReservationDesk" ... /> </bpws:flow> 

However, activities within a flow may not be completely parallel and may require some degree of synchronization. In BPEL, this is achieved with links which establish control relationships, or synchronization dependencies, between activities nested within a flow and other activities outside of the flow.

The only caveats to using link declarations to create dependencies between activities are that a link cannot cross the boundary of a while activity, that links which cross fault-handling activities must have their sources and never their targets within the fault handler, and that links should never form cycles since this causes deadlock.


At the beginning of the execution of a flow activity, all links are inactive and only those activities with no dependencies (those that are not declared as the target of any links) can execute. This is shown in Figure 6-8 where only Activity A and Activity B are executing from the start of the workflow, as they are the only activities without dependencies.

Figure 6-8. Initial execution stages of a flow activity.

graphics/06fig06.jpg

As time goes by, the execution of activities completes and on completion each activity evaluates its transitionCondition to determine the status of its (previously inactive) outgoing links. This results in each link being given a positive or negative state, which it carries forward to its target activity.

Once all incoming links to an activity are active (that is, they have been assigned either a positive or negative state), the activity's joinCondition is evaluated. A joinCondition is a guard that each activity nested within a flow possesses which allows conditional execution within a flow. Where a joinCondition isn't explicitly specified for an activity, the default value of a joinCondition is the result of performing a logical OR operation on the status of all incoming links into an activity.

The effect of both transitionCondition and joinCondition are shown in Figure 6-9, where we see that the transitionCondition for the links from Activity A and Activity B to Activity C has evaluated to true, and as such both links have been activated and have both been set to positive. Based on its links from Activity A and Activity B, Activity C's joinCondition has evaluated to true, and so Activity C has become active. The link from Activity A to Activity D has evaluated to false, but since the link from Activity C to Activity D has not yet become active, Activity D has not executed its joinCondition and remains inactive.

Figure 6-9. transitionCondition and joinCondition guards.

graphics/06fig07.jpg

The final stage is to execute, or not, Activity D and Activity E. If we imagine a scenario like that shown in Figure 6-10, where we do not use the default joinCondition and instead mandate a logical AND for Activity D, then since the link between Activity C and Activity D is set to positive, Activity D will not execute. If we assume no transition condition has been specified for Activity E, we see that Activity E will execute since the default joinCondition (logical OR of all incoming links) is present even for activities with only a single incoming link.

Figure 6-10. Evaluating joinCondition guards.

graphics/06fig08.jpg

The fact that not all activities within a flow have executed may or may not be a problem. For instance, if it is critical that all activities in the flow shown in Figure 6-10 complete, then it is important that an error caused when Activity D cannot execute is propagated to the parent of the flow activity. On the other hand it may be the case in a highly parallel workflow that control flow will be largely determined by the network of links that constrain execution, and that failure to execute whole branches of activities within a flow is not necessarily a fault but a natural by-product of the design of the workflow. A case in point here are the numerous branches of a switch activity which are not executed, yet whose dormancy does not indicate a fault.

Whether or not to flag joinCondition failures as a fault to the scope of the flow is determined by the setting of the suppressJoinFailure attribute in the opening element of the flow. Where join failures are a natural by-product of the parallel-by-default workflow design, the suppressJoinFailure attribute should be set to "yes" which then ensures that unexecuted paths through the workflow do not cause the flow activity to throw a fault. Conversely, where all activities within a flow need to be executed and links provide synchronization not choices, the suppressJoinFailure attribute is set to "no" which means that any joinCondition that evaluates to false will cause a joinFailure (a standard BPEL fault) to be thrown, and the parent scope can instigate appropriate error-handling activities.

To illustrate the use of links in a real business case, imagine the scenario, as shown in Figure 6-11, where a more fully formed workflow includes managerial authorization for the business trip and expense claims in addition to the (parallel) booking of hotel, flights, and rental car.

Figure 6-11. A Parallelized Booking Workflow.

graphics/06fig09.jpg

The workflow shown in Figure 6-11 has distinct authorization, booking, and expense phases where each of the booking and expense claim activities are potentially parallel. This parallelism is constrained by the addition of synchronization dependencies. The corresponding BPEL code is shown in Figure 6-12 where the flow activity is used encourages parallel activity during the booking phase, while the links are used to constrain that activity to the extent that the logical ordering of the task authorize, book, expense is maintained.

Figure 6-12. A typical flow activity with links describing dependencies.
 <bpws:sequence>   <bpws:invoke name="ManagementInvocation"     partner="Boss">     ...   </bpws:invoke>   <bpws:flow>     <bpws:links>       <bpws:link name="HotelExpenses"/>       <bpws:link name="CarExpenses"/>       <bpws:link name="FlightExpenses"/>     </bpws:links>     <bpws:flow>       <!-- Book hotel -->       <bpws:sequence>         <bpws:invoke name="HotelInvocation"           partner="Hotelier">           ...         </bpws:invoke>         <bpws:receive name="HotelResponse" ... >           <bpws:source link="HotelExpenses"             transitionCondition="bpws:getContainerProperty                    ("HotelBookings", "costPerNight") < 300"/>         </bpws:receive>       </bpws:sequence>       <!-- Hire care -->       <bpws:invoke name="CarHireInvocation"         partner="CarRentalAgency">         <bpws:source link="CarExpenses"/>         ...       </bpws:invoke>       <!-- Book flight -->       <bpws:invoke name="AirlineInvocation"         partner="AirlineReservationDesk">         <bpws:source link="FlightExpenses"/>         ...       </bpws:invoke>     <!-- Claim expenses -->     <bpws:invoke name="ExpenseClaimInvocation"       partner="Cashier"       joinCondition="bpws:getLinkStatus("HotelExpenses") AND                      bpws:getLinkStatus("CarExpenses") AND                      bpws:getLinkStatus("FlightExpenses")">       <bpws:target link="HotelExpenses"/>       <bpws:target link="CarExpenses"/>       <bpws:target link="FlightExpenses"/>       ...     </bpws:invoke>   </bpws:flow> </bpws:sequence> 

Figure 6-12 shows the skeleton of a typical flow activity. In this flow, we see four invocation activities which are ordered according to dependencies described by links where the booking activities are parallelized, while the expense claim invocation depends on the outcome of previous booking invocations, as per Figure 6-11. These dependencies are encoded into the workflow as follows:

  • A set of links for the flow is declared.

  • Each activity within the flow that is involved in a dependency with another activity declares either a source element or a target element depending on whether they are the source of a dependency or the dependent activity.

  • Each source and target element names a link to bind its dependency.

For instance, in Figure 6-12 we see that the AirlineInvocation invoke activity is the source of a dependency described by the FlightExpenses link: <bpws:source link="FlightExpenses"/>. The ExpenseClaimInvocation invoke activity is declared as the target of the FlightExpenses link and thus is dependent on the AirlineInvocation activity (along with some other links) as shown by the declaration: <bpws:target link="FlightExpenses"/>.

However, there is more to Figure 6-12 than simple dependencies described by links. We have also specified a number of guards in the form of transitionCondition and joinCondition expressions as part of the HotelInvocation and ExpenseClaimInvocation activities. Specifically, we have added a transitionCondition to the HotelInvocation activity so that only if the price of the hotel is less than $300 per night will the HotelExpenses link be set to a positive state. In those cases where the chosen hotel is charging more than $300 per night for accommodation, this link will be set to carry a negative token. Thus at the end of the HotelInvocation activity, the following expression is evaluated: transitionCondition="bpws:getContainerProperty ("HotelBookings", "costPerNight") < 300" to determine whether the outgoing link should be set to positive or negative.

Expressions including bpws:getContainerProperty are explained fully in later sections. For now just assume that they work on state contained within a workflow process, but don't worry too much about exactly where that state is held.


The importance of the HotelExpenses link becomes apparent when we consider the joinCondition of the ExpenseClaimInvocation activity. Instead of using the default logical OR on each incoming link, this joinCondition performs a logical AND on the status of each incoming link and will, therefore, only allow the ExpenseClaimInvocation activity to execute if each link carries a positive token that is only once all preceding activities have completed their execution and each transitionCondition has been evaluated to true, like this:

 joinCondition="bpws:getLinkStatus("HotelExpenses") AND bpws:getLinkStatus("CarExpenses") AND bpws:getLinkStatus("FlightExpenses")> 

If a joinCondition fails, we essentially have two options. Usually in workflows like this one where all paths through a parallel execution have to be taken, we would set the suppressJoinFailure attribute of the surrounding scope to be "no" so that any joinCondition that evaluates to false will cause a joinFault to be thrown. This joinFault can then be handled by the parent scope, which might perform compensation or other error handling. On the other hand, and specifically in those parallel-by-default cases where it is quite normal for activities not to execute because of runtime conditions, we might want to ignore any thrown joinFault faults as a by-product of inherently parallel execution. In those cases, we set the suppressJoinFailure attribute of the surrounding scope to "yes".

Switch

The switch activity allows branches in a workflow to be navigated, based on conditions defined in case elements, or to follow a specific branch in the case where no such conditions have been met. Thus the BPEL switch activity is similar in both name and nature to the C, C++, Java, and C# switch statement.

A simple switch activity is presented in Figure 6-13, where based on the cost of an air ticket (less than $200, less than $400, or otherwise) different activities are executed to authorize the payment, where presumably as the cost increases so does the level of scrutiny of each check increase.

Figure 6-13. A switch activity.
 <bpws:switch>   <bpws:case condition="bpws:getContainerProperty(finances,                                          flightCost) < 200" >     <!-- budget bracket authorization -->   </bpws:case>   <bpws:case condition="bpws:getContainerProperty(finances,                                          flightCost) < 400" >     <!-- intermediate bracket authorization -->   </bpws:case>   <bpws:otherwise>     <!-- higher bracket authorization -->   </bpws:otherwise> </bpws:switch> 
Pick

The pick activity is akin to an asynchronous event-driven switch activity. A pick activity declares events that it will trigger on, and corresponding activities to execute once those events occur. Events can take the form of either the receipt of a message or a time-based event including both duration (time relative from now) and deadline (fixed future time).

It is also possible for a pick activity to provide the entry point into a workflow. In this case the pick activity acts very much like a receive activity (which we discuss in the next section), with the key difference that it can initiate a workflow based on a number of messages rather than the single message that receive supports. Like the receive activity, when a pick is used to instantiate a workflow it is annotated with a createInstance="yes" attribute. If this attribute is specified, alarms (of either form) are not permitted.

Every pick activity contains at least one onMessage event. The onMessage events dictate when the activity becomes active that is, when a matching message is received. Additionally a pick activity can contain a time-based alarm, either time relative from the time the pick activity is first encountered or an absolute value in the future. This is particularly useful when we need to timeout after an event has transpired (or failed to transpire) in our workflow.

An example pick activity is shown in Figure 6-14, where the activity waits on a response message from the airline booking system via its onMessage declaration. However, where that response is not been quick enough we specify a three-day limit on the response with an XML Schema duration value a timeout is triggered through its onAlarm declaration and some fault handling logic is executed. Note that in this case we specify a timeout relative to the activation of the pick activity using the for attribute. Had we wanted to use a fixed future deadline (perhaps where all flights must be booked by close of business on Friday), then we would have used the until form in conjunction with an XML Schema dateTime value.[3]

[3] For a useful quick reference to the forms of possible dates, times, and durations available in XML Schema, see http://www.xml.dvint.com/docs/SchemaDataTypesQR-2.pdf.

Figure 6-14. A pick activity.
 <bpws:pick createInstance="no">   <bpws:onMessage partner="AirlineReservationDesk"     portType="ent:BookingPortType"     operation="bookingResponse" container="BookingResponses">     <bpws:correlations>        <bpws:correlation set="BookingsCorrelationSet"           initiation="no"/>     </bpws:correlations>     <!-- Continue with booking -->   </bpws:onMessage>   <bpws:onAlarm for="P0Y0M3DT0H0M">     <!-- Airline not responded, deal with problem -->   </bpws:onAlarm> </bpws:pick> 
While

Activities are iterated by nesting them within a while activity. In BPEL, the while activity evaluates a condition expressed in XPath 1.0 and if that expression evaluates to true, another iteration of the enclosed activities is executed. We cover BPEL expressions later in this chapter, but for now think of them as something akin to SQL statements for BPEL.

In Figure 6-15 we see a simple while activity that allows flights to be booked only if the number of flights already booked in the current month is less than some threshold figure (in this case we have chosen 20 as being a reasonable value). If this condition evaluates to true, then further bookings will be possible, otherwise the booking activities will not be executed and presumably some further authorization will be sought.

Figure 6-15. A while activity.
 <bpws:while condition=  "bpws:getContainerProperty(monthlyFlightDetails,noOfFlights)   < 20">   <!-- flight booking activities --> </bpws:while> 
Scope

The final structuring activity is scope. A scope is a means of explicitly packaging activities together providing activity context such that they can share common error handling and compensation routines. The full structure for a scope is shown in Figure 6-16 and consists of a set of optional fault handlers, a single optional compensation handler and the primary activity of the scope that defines its behavior. In the absence of a scope declaration the scope of an activity is itself, with the exception of correlation sets and containers, which exist implicitly at global scope.

Figure 6-16. BPEL scope structure.

graphics/06fig10.jpg

In the absence of a scope declaration, each activity is implicitly associated with its own scope which shares the same name and delimiters as the activity. An example scope is shown in Figure 6-17, where the booking process for an individual ticket is shown enclosed in a scope.

The normal behavior for the scope shown in Figure 6-17 is for the booking activity near the bottom of the example to be executed and for flight tickets to be reserved. However, this scope declares a number of exception handlers with catch activities that allow a variety of faults that might occur while booking tickets to be rectified before they cause further problems. For instance, these catch activities deal with such matters as a flight number being incorrectly specified, a flight already being fully booked, or a fault in the payment method used to purchase the tickets. We will assume that these fault handlers are able to correct any problems so that the scope can complete normally. The catchAll handler is a little different in that it handles any faults other than those that are explicitly handled by the preceding catch activities. Since the nature of the fault is unknown, the designer of this scope has decided that the safest thing to do would be to compensate the inner scopes, by calling the logic held in their compensationHandler activities to restore the system to the same state (or a logically equivalent state) as it was before the top-level scope executed. The skeleton for a compensationHandler is shown in Figure 6-18.

Figure 6-17. A scope activity.
 <bpws:scope containerAccessSerializable="no">   <bpws:faultHandlers>     <bpws:catch faultName="FlightFullFault" ... />     <bpws:catch faultName="UnknownFlightFault" ... />     <bpws:catch faultName="UnauthorizedPaymentFault" ... />     <bpws:catchAll>       <bpws:compensate/>     </bpws:catchAll>   </bpws:faultHandlers>   <!-- Booking activity --> </bpws:scope> 
Figure 6-18. A compensationHandler.
 <bpws:process>   <!-- Process definition omitted for brevity -->   <bpws:compensationHandler>     <!-- Application specific compensation -->   </bpws:compensationHandler> </bpws:process> 

Compensation handlers are a fundamental component of BPEL workflows to support reliable, long-lived business processes. During the execution of a workflow, data in the various systems that the workflow encompasses changes. Normally such data is held in mission-critical enterprise databases, queues and so forth which have ACID transactional properties to ensure data integrity.[4] This leads to the possible situation whereby a number of valid commits to such databases could have been made during the course of a workflow, but where the overall workflow might fail, leaving its work partially completed. In such situations the reversal of partial work cannot rely on backward error recovery mechanisms (rollback) supported by the databases since the updates to the database will have been long since committed. Instead we are forced to compensate at the application level by performing the logical inverse of each scope that was executed as part of our workflow, from the most recently executed scope back to the earliest executed scope. This model is known as a saga, and is the only default compensation model supported by BPEL.

[4] See Chapter 7 for a discussion of transactions and ACID properties.

In addition to the saga model, BPEL supports the creation of user-defined compensation patterns for situations where the default saga model is unsuitable, which forms part of the discussion on error handling activities in the next section.


The general concept of a saga is shown in Figure 6-19 whereby each activity A1, A2, A3, and A4, have corresponding compensating activities C1, C2, C3, and C4. In the case where an activity faults and invokes its compensation handler Cn, then compensation handlers Cn-1, Cn-2, Cn-3 and so on back to C1 (which is the compensation handler for the activity A1) must be invoked.

Figure 6-19. Sagas.

graphics/06fig11.jpg

Where fault handlers provide alternative forward execution paths through a scope, compensation handlers, when invoked, undo the work performed by a scope. Since a compensationHandler for a specific scope reverses that scope's work, the handler can potentially be as complex and intricate as the scope's normal original activity.

A compensationHandler can be set to compensate an entire business process after its normal completion (instead of individual scopes). If an entire process is to be compensated, the enableInstanceCompensation attribute of the compensationHander is set to "yes" to indicate its process, as opposed to scope, affiliation.


When a compensationHandler is executed, either through the explicit use of a compensate activity or where it is instigated by the underlying BPEL engine in response to some external stimulus, it operates on a snapshot of the state of the workflow process at the time of execution. This means that an executing compensationHandler does not update the state of the executing process, though it can manipulate its own snapshot data. This is both a blessing and a curse, since the nature of the environment within which compensating activities are executed is often difficult to predict in advance, and it may be shortsighted to assume that compensating and concurrent normal activities should be oblivious to one another. Fortunately, the BPEL specification notes this problem and proclaims that future versions will allow compensating activities to take account of the current state of live workflow activities and vice versa.

In the absence of a compensationHandler for a scope, the scope implicitly contains the default compensationHandler, and the presence of this compensationHandler ensures that the compensation handlers in any nested scopes are activated. Thus, when a top-level scope is compensated, all contained scopes are also compensated, starting with the most recently executed. Each of those scopes' nested scopes will also be compensated starting with the most recently executed, and the same will happen over again until all nested scopes have been compensated, in the order of those most recently executed first.

For example, if a particularly disastrous fault developed during the execution of a workflow instance that changed a user's password on all systems the user has access to (a somewhat crude version of centralized computer management), we might have no recourse other than to compensate that workflow, thus reversing all of its work including changing any altered passwords back to their original form so the user is not locked out of any individual system.

A compensation handler is limited to the particular workflow instance within which it was invoked. Given the possibility or indeed likelihood that workflows will be composed from workflows and span enterprises, a means of consistently undoing related work across enterprise boundaries is required. Typically this kind of situation is handled with a distributed transaction protocol whereby activities across enterprises can be associated with the same logical unit of work (a transaction) and every scope in that transaction will undo any work (compensate) if any of the other scopes in that transaction find that they must compensate.

The BPEL specification suggests WS-Transaction (see Chapter 7) as the protocol of choice for managing transactions that support the interactions of workflow instances running within different enterprise systems. In short, the WS-Transaction protocol is used both as the means of grouping distributed scopes together into a single logical unit of work and as the dissemination of the outcome of that unit of work whether all scopes completed successfully or whether they need to be compensated.


Exception Handling Activities

Following our discussion on scope, it is useful at this point to elaborate further on the basic syntax and semantics of the error handling activities. There are three fundamental error handling activities supported by BPEL throw, terminate, and compensate, where throw is used to flag an error, and terminate and compensate are reactions to errors thrown by a workflow instance or by the underlying process engine.

Throw

The throw activity is simple to understand, since it bears close resemblance to the throw keyword in the Java programming language. To illustrate, we can borrow from a simple Web services-based login and authentication system that uses throw to indicate that a login attempt failed, as we see in Figure 6-20.

Figure 6-20. A throw activity.
 <bpws:throw faultName="login:LoginFailure"             faultContainer="LoginFailureContainer"/> 

In this case the execution of the throw activity populates a container being a place where BPEL workflows store state, which for now we can assume are akin to variables in a programming language with fault data so that the workflow process dealing with the login can create the corresponding login failure message and send it to the user, though in those cases where fault data is not required, then no container need be specified.

Catch

Handling faults also follows a similar pattern to common programming languages such as C++, Java, and C#. A catch declaration is used to handle specific faults occurring within a scope, and a catchAll declaration within the activity's scope faultHandlers declarations will handle any faults not handled by a more specific catch at the same scope.

The catch and catchAll activities are exemplified in Figure 6-21, where specific faults LoginFailure and LoginServerNotRespondingFailure are handled explicitly. If, however, another kind of fault arises that cannot be handled by these two explicitly typed fault handlers, then the fault propagates to the catchAll activity, which in this case has been written to cease the process using the <terminate/> activity.

Figure 6-21. BPEL catch and catchall fault handlers and terminate activity.
 <bpws:faultHandlers>   <bpws:catch faultName="LoginFailure">     <!-- Handle the fault -->   </bpws:catch>   <bpws:catch faultName="LoginServerNotRespondingFailure">     <!-- Handle the fault -->   </bpws:catch>   <bpws:catchAll>     <!-- Unexpected fault, shutdown -->     <bpws:terminate/>   </bpws:catchAll> </bpws:faultHandlers> 
Terminate

Terminating a workflow is just as simple as throwing a fault. When executed, the terminate activity unconditionally stops any running activities as soon as possible, without executing any compensating or fault-handling behavior. In our example shown in Figure 6-21, the terminate activity is used to close the workflow instance in the event of an unhandled fault.

Compensate

The final fault handling activity type, compensate, is more subtle and powerful than simple faults and termination activities, and builds on the compensationHandler activity associated with a scope to provide application-controlled error handling. The compensate activity is used to invoke compensation on a scope that has already completed its execution without faulting, and can only be invoked from within a fault handler or another compensation handler, as shown in Figure 6-22.

Figure 6-22. Possible compensate activities.
 <!-- Execute default compensation handler --> <bpws:compensate/> <!-- Execute named compensation handler --> <bpws:compensate scope="RemoveUserCredentials"/> 

When a workflow instance has explicitly initiated is own application-controlled compensation, there are two possible approaches that it might choose. The first is to use the default compensationHandler as invoked by the <compensate/> form, in which the scope name is omitted. A default compensationHandler may be empty, in which case invoking it through a <bpws:compensate/> activity simply causes all inner scopes to execute their default compensation handlers as per the saga model and shown on the left in Figure 6-23. It is also possible to construct the default compensationHandler to perform additional work, such as updating workflow state or sending notifications to external Web services prior to performing default compensation for inner scopes if necessary.

Figure 6-23. Default and explicitly named compensate activities.

graphics/06fig12.gif

The second approach is to explicitly call the compensation handlers for specific activities rather than follow the default reverse order of completion. Parameterized compensate activities describe the compensation path entirely by the scopes that each executed compensate activity references, as shown on the right in Figure 6-23. This allows for more intricate patterns of compensation than the simple recursive pattern used by the default compensate activity, and allows workflow designers to build smart compensation strategies into their workflows that minimize the required work and disruption in the event of compensation being initiated.

There are caveats that must be borne in mind when using compensation. First, if a scope being explicitly compensated by name was executed within a loop, the compensation handlers for the successive iterations of that loop are executed in reverse order; and second, the default compensate activity cannot be mixed with named compensation activities within a single scope: <compensate/> and <compensate scope="some-scope"/> is illegal if the named scope is an inner scope of the default compensate activity you can compensate all inner scopes, or selected inner scopes but trying to mix both is wrong.


Communication Activities

The set of communication activities deals with the sending and receiving of messages so that a workflow process instance can communicate with other Web services hosting other resources, including other workflow process instances. BPEL provides three activities invoke, receive, and reply each of which handles a different type of interaction between partners in a workflow. Partners are discussed in-depth in later sections. However, it suffices for now to understand that a partner is effectively mapping to a WSDL portType description of a physical partner's Web service.

Invoke

The invoke activity allows a workflow instance to call a synchronous or asynchronous operation on a remote Web service. An asynchronous one-way operation is the simplest form of invoke since it only requires a single input container to send messages. To illustrate, examine the example shown in Figure 6-24 where a request for additional cable TV channels is sent from a set-top box to a cable operator's system. In this example the invoke activity calls the addChannel operation from the ChannelManagementPortType portType exposed by its CableOperator partner, sending a message from the RequestedChannels container to request additions to the subscriber's package. The subscription update is sent asynchronously under the assumption that the cable company will have some non-interactive batch-mode process for updating all subscriber's hardware periodically to make the best use of network resources.

Figure 6-24. An asynchronous invoke activity.
 <bpws:invoke partner="CableOperator"       portType="co:ChannelManagementPortType"       operation="addChannel"       inputContainer="RequestedChannels"/> 

A synchronous invocation is slightly more complicated since it also needs to deal with any response messages or faults that invoking an operation may produce. A cable operator may accept synchronous invocations for queries from set-top boxes for customers wanting to know their subscription details.

A set-top box would invoke that service as shown in Figure 6-25, where the invoke activity requires both an inputContainer from which to extract the request message and an outputContainer to deposit the response message from the cable operator's service.

Figure 6-25. A aynchronous invoke activity.
 <bpws:invoke partner="CableOperator"       portType="co:ChannelManagementPortType"       operation="getSubscriptionStatus"       inputContainer="SubscriberDetails"       outputContainer="PackageDetails"/> 
Receive

Web service operations are exposed to the outside world by a receive activity. The receive activity is the workflow entity onto which a WSDL operation maps. It specifies the partner it expects that will invoke the corresponding operation, and portType and operation that it expects the partner to invoke. In Figure 6-26 we show the receive activity that the cable operator exposes as its addChannel operation (the operation invoked by customers in Figure 6-24). The cable operator uses a container called addChannelOrders to hold incoming AddChannelMessage messages from customers. When the receive activity is activated by the arrival of an AddChannelMessage from a customer, a new instance of the cable operator's channel adding workflow is created and executed.

A receive activity is blocking and does not allow the workflow graph that it precedes to progress until the messages that it requires have been received. Furthermore, it may be the case (as it is in Figure 6-26) that a receive activity initiates a workflow process instance on receipt of a message. In such cases (as in that of Figure 6-26), the activity is annotated with the createInstance="yes" attribute to indicate that it is the initial process of the workflow. In this case it is not allowed for any other type of activity to precede the receive activity.

In those cases where one of a set of messages, whose order of arrival cannot be predicted, needs to be received before a workflow instance can execute, it is permissible to have multiple receive activities declared with createInstance="yes" attributes. The BPEL implementation will determine which of the arriving messages actually triggers the instantiation of the workflow, usually based on a first-come, first-served rule. Messages arriving later will simply be routed to an existing workflow instance and will not cause the creation of a new instance where the message can be correlated with an existing instance.

Figure 6-26. A receive activity.
 <bpws:containers>   <bpws:container name="addChannelOrders"        messageType="co:AddChannelMessage"/> </bpws:containers> <bpws:receive partner="customer"      portType="co:ChannelManagementPortType"      operation="addChannel"      container="addChannelRequest">   <!-- Add a channel --> </bpws:receive> 

BPEL does not support the declaration of multiple receive activities for the same partner, portType, and operation since it is the sum of these three components which provides a unique key matching the receive activity to its WSDL counterparts. Similarly, this constraint cannot be circumvented by declaring a pick activity since a pick effectively is a receive, albeit one with useful timing features.

Reply

A reply activity is used for sending synchronous responses to messages received through a receive activity. Correlation between a receive and a reply is handled by the underlying BPEL implementation.

To draw to a close this description of communication activities, we present the cable company's synchronous response to the customer's request for subscription details from Figure 6-25. This is shown in Figure 6-27, where the customer partner is sent a message from the CustomerSubscriptionDetails container (which contains messages whose contents are the details of subscriptions), as a result of invoking the getSubscriptionStatus operation.

Figure 6-27. A reply activity.
 <bpws:reply partner="customer"      portType="co:ChannelManagementPortType"      operation="getSubscriptionStatus"      container="CustomerSubscriptionDetails"/> 
Miscellaneous Activities

The final three activities available to the BPEL workflow developer are the assign, wait, and empty activities. These activities encapsulate data-handling, unconditional timing, and no-op behavior respectively.

Assign

The assign activity is used to copy data (messages, parts of messages and service references) between containers. Assign activities follow one of several patterns, depending on what is being assigned, but in all forms type compatibility between the source and destination of the assignment must be maintained. Thus valid assignments can occur where both the from and to elements reference containers hold the same message types; or where both endpoints of an assignment are the same (XPath-friendly) XML Schema types, or compatible sub-types. Any deviation from these three forms will cause the underlying workflow engine to throw a fault which our process will have to handle.

A typical example of assignment is where (some of) the contents of one message are copied into another. This assignment is shown in Figure 6-28, where the address details of a customer, as supplied by that customer as part of a purchase order, are copied to a dispatch note prior to shipping the customer's order.

It is noteworthy here that where the contents of a container are only partly initialized through assignments, the container cannot be used as the input container to any invoke activities. Before a container can be used in this way, it must have all of its fields filled in, either through piecemeal assignments or through the assignment of a whole message from another identical container.


Figure 6-28. An assign activity.
 <bpws:assign>   <bpws:copy>     <bpws:from container="PurchaseOrders"          part="customerAddress"/>     <bpws:to container="dispatchNotes"          part="customerAddress"/>   </bpws:copy> </bpws:assign> 

Of course other forms of assignment are used on our workflow, and the BPEL specification offers a useful taxonomy of possible assignment styles as shown in Figure 6-29. However, with the caveat is that the opaque form of the from element is only available in abstract processes, where the data contained in the opaque section is populated by some agent in the workflow system's back end.[5]

[5] The remainder of this section is based on the assignment section of the BPEL specification. See: http://www-106.ibm.com/developerworks/library/ws-bpel/#Assignment.

Figure 6-29. Permitted assignment styles in BPEL.
 <from container="ncname" part="ncname"?       query="queryString"?/> <from partner="ncname"       serviceReference="myRole|partnerRole"/> <from container="ncname" property="qname"/> <from expression="general-expr"/> <from> ... literal value ... </from> <from opaque="yes"> <to container="ncname" part="ncname"? query="queryString"?/> <to container="ncname"/> <to container="ncname" property="qname"/> 

In all variants the container attribute provides the name of a container, and the part attribute provides the name of a part within that container. In the first from and to variants, the value of the query attribute is a query string (expressed in XPath 1.0 by default), which identifies a single value within a source or target container part.

The second from and to variants allow dynamic manipulation of the service references associated with partners, where the value of the partner attribute matches the name of a partner declared in the process. In the case of from elements, the role must be specified to highlight whether the assignment is passing on its own role or that of a partner. The value myRole indicates that the service reference of the process with respect to that partner is the source, while the value partnerRole means that the partner's service reference is the source. For the to element, the assignment is only possible to the partnerRole, hence there is no need to specify the role. The type of the value used in partner-style from and to elements is always a service reference. This style of assignment permits endpoint details for services to be propagated between partners something akin to the passing of a pointer-to-pointer as an argument to a function in C.

The third form of the from and to elements allow explicit manipulation of message properties, while the fourth from variant allows assignment from a literal or computed value.

The fifth from element allows a literal value to be given as the source value to assign to a destination. The type of the literal value has to match that of the destination specified by the to element, and type-checking may be strengthened by the optional inclusion of an xsi:type attribute to make explicit the types being assigned (this is useful when, for instance, the string "123" is to be assigned where that string may be equally and validly interpreted as a number).

The sixth from pattern (<from opaque="yes">) allows an opaque value to be assigned based on non-deterministic choice a value produced by a private back-end system. The value held in the from element must be capable of being mapped onto XML Schema string-compatible type to preserve compatibility with XPath 1.0.

Wait

The wait activity introduces an unconditional pause into a workflow, where that pause may be relative (i.e., pause for a fixed amount of time) or absolute (pause until a certain date and time). It is, in some respects, similar to a pick activity though it lacks the message-oriented aspects and is much simpler to declare. There are also fewer subtleties to trap the unwary workflow designer.

A typical use for this feature is shown in Figure 6-30, where an enterprise's inventory process waits for 24 hours (as defined by the string serialized XML Schema dateTime type: PT24H) hours in between runs.

Figure 6-30. A wait activity.
 <bpws:while condition="true">   <!-- XML Schema string form 24 hours -->   <bpws:wait for="PT24H"/>   <!-- Inventory process --> </bpws:while> 
Empty

The final activity type in BPEL is the <empty/> activity that does nothing. A typical use for the empty activity is where a specific fault is to be suppressed, like an empty catch block in programming languages that supports structured exception handling. This is shown in Figure 6-31 where an InvalidPostcode fault is suppressed on the basis that it is likely, though more troublesome, to correctly dispatch the order to the customer without a correct postcode.

Figure 6-31. An empty activity.
 <bpws:catch faultName="wm:InvalidPostcode">   <bpws:empty/> </bpws:catch> 

We have now covered all of the activities supported by BPEL and touched on a couple of other features like state management and message correlation. However, before we can dive into creating full workflows from scratch, we will need a little more understanding of the features that BPEL offers for capturing the static relationships between enterprises and workflow instances. These are introduced in the sections that follow.

Service Linking, Partners, and Service References

In order to create workflows that span enterprises, we must build into our workflows knowledge of how those enterprises are interrelated. BPEL provides a means of capturing the roles played by business partners in a Web services-based workflow through Service Linking, Partners, and Service References whose relationships are shown in Figure 6-32.

Figure 6-32. Abstract and concrete enterprise relationship.

graphics/06fig13.jpg

Service links are the most abstract form of relation supported in BPEL and serve to link two parties by specifying the roles of each party and the (abstract) interface that each provides. The serviceLinkType definitions can either be part of a service's WSDL interface, or it can be defined separately and referenced by the WSDL. Embedding this definition directly in a WSDL description leverages WSDL's extensibility mechanism, allowing serviceLinkType elements to become a direct child of the wsdl:definitions element. This style has certain advantages over separating the WSDL and serviceLinkType definitions insofar as it allows serviceLinkType definitions to share WSDL namespaces, and more importantly its import mechanism for importing wsdl:portType elements.[6]

[6] See BPEL specification, http://www-106.ibm.com/developerworks/library/ws-bpel/.

The actual content of a serviceLinkType is straightforward. It defines a service link usually between two services, qualified by the targetNamespace of the WSDL document, and then exposes that relationship as two roles to the rest of the workflow. In some instances, a serviceLinkType may specify a single role, which indicates that the workflow is willing to bind to any other service without placing any requirements on that service.

In Figure 6-33, two sample serviceLinkType elements are defined. The first defines a link between a WidgetSeller and a WidgetBuyer service. Thus when a WidgetBuyerSellerLinkType is used in a workflow, it will implicitly associate a WidgetSellerPortType with a WidgetBuyerPortType and enforce the appropriate operation and message constraints. The second defines an InquiryLinkType, which is used to model the link between the widget manufacturer and a third-party making widget-related enquiries. Note that in this case, there is only one role specified, WidgetAuthority, which indicates that the widget manufacturing service is willing to link to any other service without placing any further constraints the interface exposed by that service.

Figure 6-33. A serviceLink relation.
 <slnk:serviceLinkType name="WidgetBuyerSellerLinkType"    xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/">   <slnk:role name="WidgetSeller">     <slnk:portType name="WidgetSellerPortType"/>   </slnk:role>   <slnk:role name="WidgetBuyer">     <slnk:portType name="WidgetBuyerPortType"/>   </slnk:role> </slnk:serviceLinkType> <slnk:serviceLinkType name="WidgetInquiryLinkType"    xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/">   <slnk:role name="WidgetAuthority">     <slnk:portType name="WidgetInquirerPortType"/>   </slnk:role> </slnk:serviceLinkType> 

A BPEL partner refines a serviceLinkType declaration by defining the roles played by actual partners at the endpoints of the relationship. A partner is declared within the workflow script itself (as opposed to the WSDL interface) because it forms part of the behavior of that workflow.[7] A sample partner declaration for a user authentication system is presented in Figure 6-34.

[7] Partnerships only make sense within the scope of the workflow where business partners interact (and indeed where partners may be located as part of the computation of a workflow). Conversely, since serviceLinkType declarations specify the required interface and role of partner services binding to the current service, they are part of the static interface of the services and are correspondingly located within the WSDL interface.

Figure 6-34. Partner declarations.
 <bpws:partners      xmlns:sl="http://widgets.example.org/serviceLinkTypes">   <bpws:partner name="customer"        serviceLinkType="sl:WidgetBuyerSellerLinkType"        myRole="WidgetSeller"        partnerRole="WidgetBuyer"/>   <bpws:partner name="enquirer"        serviceLinkType="sl:WidgetInquiryLinkType"        myRole="WidgetAuthority" /> </bpws:partners> 

The root element for partner declarations within a BPEL workflow is partners. Inside that element we have individual partner declarations that specify the role of our enterprise and its partners on a per serviceLinkType basis. Of the two partners defined in Figure 6-34, customer specifies roles for both ends of the corresponding serviceLinkType declaration, in preparation for the bilateral exchanges that purchasing widgets necessitates. However, while inquiring about widgets, the manufacturer is not particularly fussy about who binds to and uses it, and so the partner declaration is unilateral. It specifies only the myRole attribute as WidgetAuthority, which confirms that entities placing inquiries effectively play no role in this area.

At this point we have effectively determined the types of service we are interacting with (through serviceLinkType declarations) and the roles of the enterprises that are going to provide the necessary functionality (through partner declarations). The final step in concretizing our business interrelationships is to specify the network locations of our partners so we can discover and consume their Web services.

Of course physical network address locations change over time (and indeed sometimes change quite rapidly) and WSDL has a means of supporting this through a separation of portType (abstract network interface) and port (physical, protocol-bound interface on the network), which are mapped through bindings and later exposed as services. The consumer of a service must understand the portType section of a WSDL document before it can consume a service, though the binding to an actual port can be delayed right up until a client needs to invoke that service at runtime. The information needed to create the runtime binding can be accessed in a number of ways, including out-of-band communication between users and directory services like UDDI. The point is, given the distinction between abstract and concrete in WSDL, BPEL has to have a means of bridging the same gap between abstract partner declarations and exchanging messages over the network with real services at runtime. This is addressed by ServiceReference elements, which are part of a workflow and act as typed references to a specific service. ServiceReferences allow consuming services to bind abstractly defined partners to physical network endpoints and communicate to expose those endpoints (with other useful data such as object or process ids) to workflow activities.

Figure 6-35. A minimal ServiceReference declaration.
 <sref:serviceReference       xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"       xmlns:ws="http://widgets.example.org/ ">   <sref:service name="ws:WidgetService"/> </sref:serviceReference> 

Figure 6-35 shows a minimal ServiceReference declaration where the service provided by a particular partner is statically embedded. In this case the wsdl:service element defined in a service's WSDL interface is used to create a "Web pointer" that can be used within the activities of a single workflow and passed among collaborating services as part of their message exchanges.

However, the real potency of ServiceReference comes to light when we dynamically compute or discover the endpoint or business process instance that we want to communicate with. We can thus augment the minimal ServiceReference shown in Figure 6-35 with specific instance information, such as the ws:existingCustomer shown in Figure 6-36.

Figure 6-36. Identifying process/object instances with ServiceReference.
 <sref:serviceReference       xmlns:sref="http://schemas.xmlsoap.org/ws/2002/07/service-reference/"       xmlns:ws="http://widgets.example.org/ ">   <sref:service name="ws:WidgetService"/>   <sref:referenceProperties>     <sref:property name="ws:existingCustomer">          Johnny.Widget@widget-u-like.com     </sref:property>  </sref:referenceProperties> </sref:serviceReference> 

The ServiceReference shown in Figure 6-36 has additional information held by property elements within the referenceProperties element that identifies a specific resource managed by a Web service. In BPEL, that resource is likely to be an instance of a workflow. However, it may be a process or object identifier, or other identifier that has significance to both ends of the interaction. It is important to understand that while the computational aspects of BPEL provide the ability to obtain and utilize such properties, BPEL does not place any semantics on them. That is, any information appearing inside a property element is opaque to any BPEL infrastructure and is understood only by the endpoints sending and receiving the ServiceReference. Although the sender or recipient may be BPEL workflows, those workflows must have been designed explicitly to understand such information BPEL cannot second-guess its semantics.

Fortunately, the reference data is always associated with globally named properties. It is safe to say that the recipient, understanding the global properties, will also understand the ServiceReference. However, for us mere humans to understand the implications of this, we have to understand what a BPEL property is.

Message Properties and Property Aliases

Once we have captured the relationships between our enterprise and its partners, we can begin to exchange messages through the conduits defined by those relationships. Indeed, every business already engages in this kind of activity, exchanging purchase orders, invoices, goods (with their associated documents), and even payments. It should not come as a surprise that the next item in our BPEL investigation concerns itself with messages.

Whether we are dealing with an invoice or a dispatch note, there is often a field or set of fields within that note that can be used to unambiguously differentiate that note from piles of other similar looking ones. For instance, an invoice number is usually used in correspondence in preference to the date and address of the sender of the invoice since it is both simpler and more likely to resolve in a unique result. This notion of "distinguished" data is supported in BPEL through message properties. Simply put, a message property is a unique name (within the workflow) which has a specific type from XML Schema (e.g., xs:postiveInteger) and whose name has significance to the workflow (e.g., invoice number). An example is shown in Figure 6-37.

Figure 6-37. Message property examples.
 <bpws:property name="invoice-number"      type="ins:InvoiceNumberType"/> <bpws:property name="phone" type="xs:postiveInteger"/> <bpws:property name="website" type="xs:anyURI"/> <bpws:property name="email" type="ens:EmailAddressType"/> 

Having a friendly name and type information for our property is somewhat akin to having object references in traditional programming languages. However, just as object references need to be bound to objects before we can use them, we need to bind properties to values before workflow scripts can access those values. In BPEL we have a way of binding typed-friendly names to values that we can use within our workflows, called property aliases. A property alias is used to bind the value of a property to the value of an element in a message using an XPath query to expose elements from those messages. For instance, we may be interested in the invoice number from a purchase order and wish to expose that value to the workflow as per Figure 6-38.

Figure 6-38. Defining a propertyAlias.
 <bpws:propertyAlias propertyName="tns:InvoiceNumber"    messageType="tns:PurchaseOrderMessage" part="invoice"    query="/invoice-number"/> 

Figure 6-38 shows how we can bind properties to values through propertyAlias declarations. The attributes in the element declare the property name that we are binding to (InvoiceNumber), the message (PurchaseOrderMessage) and specific message part (invoice) where the value that we wish to bind to is located. The final step to complete the binding is to specify the XPath query (specified in the query attribute) that returns the value from the specified message part. In Figure 6-38 this is calculated by the expression /invoice-number, which evaluates the contents of the first invoice-number element from the root context where the context is provided by the preceding messageType and part attributes. Now when PurchaseOrderMessage messages are processed, the property InvoiceNumber will be assigned the value of the corresponding invoice-number in the message, or conversely may be used to assign such a value to the invoice-number element, just as an object reference can be used in an object-oriented programming language.

Correlating Messages

Through message properties and their corresponding property aliases we are able to manipulate and inspect the contents of messages. However, we also need to be able to relate incoming and outgoing messages to each other and to specific business process instances. While in a typical Web services environment, messages are generally delivered to a particular WDSL port. In BPEL we not only need to deliver messages to the correct port, but to the correct instance of the business process behind that port. Given WSDL's stateless model, developers usually contrive their own methods of accessing resources "behind the port," usually by adding resources identifiers (like GUIDs) into messages, or by encoding the resource into a URI. However, to save developers the burden of having to create ad hoc (and non-interoperable) message routing schemes, BPEL provides message correlation features to support such routing in a generic and reusable fashion.

The requirement for a BPEL workflow to correlate messages for itself is lessened if the messaging infrastructure for an application is built using some sophisticated conversational transport protocol that can handle message correlation at a lower level. However, in a Web services environment this is generally not the case (at least not currently) and so application-level constructs are still needed. Application-level correlation can, however, be significantly reduced in cases where it is known that more sophisticated transport protocols will be used to transfer network messages. In such cases, correlation declarations in BPEL can be abbreviated to those activities that establish conversations.


The generic routing mechanism supported by BPEL is based on a declarative mechanism called correlation sets. To use a correlation set, the developer defines a number (>= 1) of message properties which, when taken together, form a unique key that can be used to distinguish that message from all other instances of that message from other process instances. Once correlation sets are declared, the BPEL implementation then looks after matching this unique key to a particular process instance.

In order to declare a correlation set for the message, we first need to decide what it is about the contents of one instance of that message that differentiates it from another. Or more precisely, which subset of the message properties declared for a message can uniquely identify a message? It is this set of message properties that forms the correlation set.

The first step in setting up correlation for a workflow is to ensure that sufficient message properties are declared to allow the unique identification of messages. For a hypothetical message in the cable TV domain, we expose some of the content of the AddChannelMessage message through the following message properties and bind them through their corresponding aliases, as demonstrated in Figure 6-39.

Figure 6-39. The AddChannelMessage message properties and aliases.
 <bpws:property name="customer-id" type="co:CustomerIDType"/> <bpws:property name="channel" type="co:ChannelIDType"/> <bpws:propertyAlias propertyName="tns:customer-id"      messageType="tns:AddChannelMessage"      part="uid"/> <bpws:propertyAlias propertyName="tns:channel"      messageType="tns:AddChannelMessage"      part="order" query="/new-channel"/> 

Figure 6-39 declares two properties, customer-id and channel, which are sufficient to uniquely identify an individual workflow process instance. These properties are bound to elements from the AddChannelMessage through propertyAlias declarations expose the values held by individual messages in their uid and new-channel elements respectively. Where the customer-id property is bound to the uid part of the AddChannelMessage, while the channel property is bound to the contents of the new-channel element of the order part of the AddChannel message through the addition of the XPath query "/new-channel".

The correlation set that uses the property elements declared in Figure 6-39 is shown in Figure 6-40. References to this correlation set may be used whenever there is interaction between the customer and the cable operator's Web services. This enables routing to the correct process instances since it ties together a number of message properties (in this case uid and new-channel) into a unique key. It is then a simple matter for the workflow system to look up the instance associated with this unique key and deal with message routing accordingly.

Figure 6-40. Correlation set declaration.
 <bpws:correlationSets>   <bpws:correlationSet properties="tns:uid                                    tns:new-channel"        name="AddChannelCorrelationSet"/> </bpws:correlationSets> 
Correlating Web Services Invocations

Correlation sets can be used by both senders and receivers of messages, though the way they are used differs depending on whether a message is being sent or received. To make use of a correlation set for a particular Web service invocation, Correlation declarations are added to the invoke, receive, and reply activities that handle synchronous and asynchronous Web service invocations. In the case where a message is used to initialize a correlation set, the activity is called an initiator, where in the case where an activity uses a pre-initialized correlation set it is said to be a follower. It is important to note that whether a message is being sent or received is an orthogonal issue as to how correlation is used since both senders and receivers can be initiators and followers.

Figure 6-41 shows four distinct possibilities for correlated asynchronous communication between Web services, which are the outgoing initiator, outgoing follower, incoming initiator, and incoming follower. We can see each of these roles in a simple cable television scenario as shown in Figure 6-42 and in the subsequent BPEL examples. Note that the following examples are abbreviated versions of the full BPEL syntax that focus specifically on the role of correlation sets during invocation. WSDL, BPEL and other pieces of XML are presented together for convenience and would be factored into different locations (e.g., WSDL file, BPEL script file) in an actual deployment.

Figure 6-41. Initiators and followers of correlation sets.

graphics/06fig14.gif

Figure 6-42. Purchasing a Pay Per View event for cable television subscribers.

graphics/06fig15.jpg

Figure 6-42 shows a simplified cable TV scenario where a customer (via a set-top box) orders a pay-per-view event. At the system level this translates into running a set-top box workflow that invokes the cable operator's Web services with the request for the pay-per-view event, and then waits for the response from the cable operator before displaying a confirmation message.

The first step in this process is where the set-top box invokes the cable company's Web service to request the pay-per-view event. This is shown in Figure 6-43.

Figure 6-43. Initializing a Correlation Set within an invoke activity.
 WSDL <!-- Message --> <wsdl:message name="PayPerViewRequestMessage">   <wsdl:part name="customer" type="co:CustomerID"/>   <wsdl:part name="event" type="co:EventID" /> </wsdl:message> <!-- Message properties --> <bpws:property name="eventProperty" type="co:EventID"/> <!-- Property Aliases -->   <bpws:propertyAlias propertyName="co:eventProperty"     messageType="co:PayPerViewRequestMessage" part="event"   /> BPEL Script <!-- Correlation Set --> <bpws:correlationSets>   <bpws:correlationSet properties="co:eventProperty"      name="PayPerViewEventCorrelationSet"/> </bpws:correlationSets> <!-- Invocation --> <bpws:invoke partner="CableOperator"     portType="co:CableOperatorPortType"     operation="requestPayPerViewEvent"     inputContainer="PayPerViewRequests">   <bpws:correlations>     <bpws:correlation set="PayPerViewEventCorrelationSet"         initiation="yes"/>   </bpws:correlations>     </bpws:invoke> 

The invocation shown in Figure 6-43 sends a simple message consisting of the customer ID and the event ID to the cable operator to request the particular pay-per-view event. At the same time that the invocation occurs, the correlation set PayPerViewEventCorrelationSet is initialized and so the value of the event ID is stored. Once a correlation set has been initialized, whenever messages are sent for which there is a propertyAlias specified, the value stored in the correlation set will be inserted into that message. The message then wings its way over the network to the cable operator's systems, where it is consumed by the receive activity shown in Figure 6-44.

Figure 6-44. Initializing a correlation set within an receive activity.
 WSDL <!-- Message properties --> <bpws:property name="customerProperty" type="co:CustomerID"/> <bpws:property name="eventProperty" type="co:EventID"/> <!-- Property Aliases -->   <bpws:propertyAlias propertyName="co:customerProperty"     messageType="co:PayPerViewRequestMessage" part="customer"   />   <bpws:propertyAlias propertyName="co:eventProperty"     messageType="co:PayPerViewRequestMessage" part="event"   /> BPEL Script <!-- Correlation Set --> <bpws:correlationSets>   <bpws:correlationSet properties="co:customerProperty                                    co:eventProperty"      name="PayPerViewEventCorrelationSet"/> </bpws:correlationSets> <!-- Receive message --> <bpws:receive partner="SetTopBox"     portType="co:CableOperatorPortType"     operation="requestPayPerViewEvent"     container="IncomingPayPerViewRequests"     createInstance="yes">   <bpws:correlations>     <bpws:correlation set="PayPerViewRequestsCorrelationSet"         initiation="yes"/>   </bpws:correlations> </bpws:recieve> 

The receive activity in Figure 6-44 consumes the message produced by the invoke activity in Figure 6-43 and creates a new workflow instance on the cable company's workflow system. Once the workflow is instantiated, the PayPerViewRequestsCorrelationSet correlation set is initialized and the verification and billing aspects of the workflow proceed.

At some point later when the cable company is satisfied that it can source the event to the customer, and that the customer has paid for those rights, the workflow instance at the cable company finishes by invoking an operation on the customer's set-top box with a message that informs the customer that the pay-per-view transaction has been completed, as we see in Figure 6-45.

Figure 6-45. Following a Correlation Set within an invoke activity.
 WSDL <!-- Message --> <wsdl:message name="PayPerViewResponseMessage">   <wsdl:part name="customer" type="co:CustomerID"/>   <wsdl:part name="event" type="co:EventID" />   <wsdl:part name="authorized" type="xs:boolean"/> </wsdl:message> <!-- Property Aliases -->   <bpws:propertyAlias propertyName="co:customerProperty"     messageType="co:PayPerViewResponseMessage"     part="customer"   />   <bpws:propertyAlias propertyName="co:eventProperty"     messageType="co:PayPerViewResponseMessage" part="event"   /> BPEL Script <!-- Invocation --> <bpws:invoke partner="SetTopBox"     portType="co:SetTopBoxPortType"     operation="payPerViewEventOutcome"     inputContainer="PayPerViewResponses">   <bpws:correlations>     <bpws:correlation set="PayPerViewEventCorrelationSet"         initiation="no"/>   </bpws:correlations> </bpws:invoke> 

The invoke activity in Figure 6-45 constitutes the last part of the workflow that was originally started in Figure 6-44, and as such the necessary containers and correlation sets are in scope. Of particular interest is the PayPerViewEventCorrelationSet correlation set that populates the outgoing message with the correct customer and event ID details for this workflow that is, the customer who made the original request and the event originally requested. The only difference with the way in which the cable operator's system responds compared to the set-top box's original request is that the response message has an additional part which declares whether the purchase of the event was successful (as defined in the PayPerViewResponseMessage message). This message then makes its way over the network to the customer's set-top box where it is received via a receive activity as shown in Figure 6-46.

In this final receive activity, the message has been routed to the correct workflow instance on the set-top box via the PayPerViewEventCorrelationSet correlation set and the contents of the incoming message (which are bound to the message through the corresponding propertyAlias declarations). Note that the correlation plays a particularly important role here since the customer may have requested many pay-per-view events. It is important to get the correct acknowledgement back to the right process, otherwise the customer will not know which events have been bought and which purchases have failed.

Figure 6-46. Following a correlation set within an receive activity.
 <!-- Property Aliases -->   <bpws:propertyAlias propertyName="co:customerProperty"     messageType="co:PayPerViewResponseMessage"     part="customer"   />   <bpws:propertyAlias propertyName="co:eventProperty"     messageType="co:PayPerViewResponseMessage" part="event"   /> BPEL Script <!-- Receive message --> <bpws:receive partner="SetTopBox"     portType="co:SetTopBoxPortType"     operation="payPerViewEventOutcome"     container="IncomingPayPerViewResponses"     createInstance="no">   <bpws:correlations>     <bpws:correlation set="PayPerViewEventCorrelationSet"         initiation="no"/>   </bpws:correlations> </bpws:recieve> 

Up until this point, we have only considered the correlation of asynchronous services. Synchronous services may also require correlation, though because synchronous requests and responses are implicitly correlated at the transport level, there is less work to do. However, since the messages exchanged across a synchronous transport form part of a larger workflow, their contents may well be correlated to other messages, whether they are synchronous or not. So, we do not avoid understanding the role of a message simply because it travels over HTTP! The standard pattern for correlating synchronously exchanged messages is shown in Figure 6-47, where we see a simple stock check request being sent to a widget manufacturer.

Figure 6-47. Synchronous message correlation.
 <bpws:invoke partner="widget-manufacturer"      portType="StockCheckPortType"      operation="checkStockAvailability"      inputContainer="OutOfStockWidgets"      outputContainer="StockCheckedWidgets">   <bpws:correlations>     <bpws:correlation          set="StockedWidgetsCorrelationSet"          pattern="out-in" initiation="yes"/>   </bpws:correlations>   <bpws:catch faultName="bw:PaymentUnauthorized"        faultContainer="UnauthorizedPaymentResponses">     <bpws:empty/>   </bpws:catch> </bpws:invoke> 

The correlation pattern used when communicating synchronously is a little different from the asynchronous version. In Figure 6-47, we have declared a correlation using the StockedWidgetsCorrelationSet. This specifies that the interaction is synchronous using the attribute pattern="out-in" and is an initiator since the invoke activity populates the values of the correlation set and initializes the values of messages stored in containers once the synchronous operation returns.

In this case the same criteria are used to correlate the outgoing request and later response, since we know that that both messages contain the manufacturers' standard widget codes in identically named elements. However, in the general case, we need to consider the two correlations separately and use one for the pattern="out" leg of the message exchange and a different correlation with pattern="in" for the return leg where the format of the request and response messages are different.

The final point of interest for correlation sets in general is that a single set may be valid for multiple different messages. For example, we may have a number of different messages in a workflow which, although different in structure, element names, attribute names, and so forth, may contain semantically and type-equivalent data.

In Figure 6-48, three propertyAlias declarations each bind a different element from a different message onto the same message property, wm:widgetID. Then whenever one of the particular type of messages is in-scope, the propertyAlias is automatically bound to the right data element in that message and the corresponding messageProperty is set. The construction of correlation sets is thus simplified since the set can be reduced to a single property, albeit a single property which has multiple potential propertyAlias bindings, as opposed to the canonical form, which would use multiple properties each with its own propertyAlias.

Figure 6-48. Binding a single property to the contents of multiple messages.
 <bpws:propertyAlias propertyName="wm:widgetID"   messageType="wm:PurchaseOrderMessage"   part="WidgetID"/> <bpws:propertyAlias propertyName="wm:widgetID"   messageType="stock:StockCheckRequestMessage"   part="PartNumber"/> <bpws:propertyAlias propertyName="wm:widgetID"   messageType="stock:StockCheckResponseMessage"   part="PartNumber"/> 

Containers and Data Handling

When dealing with Web services-based workflows, we encounter a significant philosophical difference between the two technologies. Workflows are inherently stateful applications, whereas Web services are inherently stateless. Of course, many Web services do actually maintain state between invocations, but do so in a proprietary manner in databases, files, statically allocated program variables and so forth, all of which requires programmer effort and is likely to be inaccessible to the business analyst. BPEL has abstracted these proprietary approaches and replaced them with a generic state management facility based on containers.

A BPEL container is a typed data structure that stores messages associated with a workflow instance. The underlying notion of containers is that in a workflow, the state of the application is simply a function of the messages that have been exchanged. Containers begin their lives uninitialized, and are populated over time by the arrival of messages or computations being executed which populate them.

A container cannot be used in an invocation operation until it is fully initialized, which means that a container cannot be used until all of the properties associated with the message type of the container have themselves been initialized.


In Figure 6-49 we present a simple container declaration that could be used by a cable operator to store requests for package updates. This container is used for holding incoming channel addition requests from customers while our workflow process goes about the business updating the customer's billing details and set-top box software accordingly.

Figure 6-49. Declaring a container to hold channel addition messages.
 <bpws:containers>   <bpws:container name="AddChannelRequests"        messageType="co:AddChannelMessage"/>   <!-- Other containers ommitted for brevity --> </bpws:containers> 

Declaring a container is straightforward. It consists of a name for the container that is unique within the scope of the workflow process definition, and the type of message as defined in the corresponding WSDL document. For this example the WSDL interface identified by the co prefix resolves to http://cableoperator.example.org/wsdl, which is the address at which our fictitious cable operator resides.

Generally, the messages stored in containers are those same messages that are exchanged with partners. However, there is nothing preventing a programmer from concocting a message type purely to store local variables during computations. Such messages are never exchanged with partners, and are usually declared inline with their associated containers, where true message containers reference their message types from published WSDL interfaces. For example, a workflow designer might need to count the number of customer channel addition requests before launching the batch process that updates the software on those customers' set-top boxes. In that case a container may be constructed to hold counter "messages," as shown in Figure 6-50.

Figure 6-50. Using containers to hold temporary variables.
 <bpws:container name="UpdateCounter">    <bpws:message name="CounterMessage">       <wsdl:part name="counter" type="xs:int"/>    </bpws:message> </bpws:container> 

Of course having containers to hold state is only useful if we have ways of accessing and manipulating that state. BPEL supports expressions for computing state and assignments for updating state, both of which are based on XPath.

BPEL Expressions

The default language for expressions in BPEL is XPath 1.0. Though the specification allows other query languages to be used, the specific reference and use of XPath 1.0 is likely to sway most implementations to also adopt XPath as the de facto query language for BPEL. This means that calculations, of some relatively basic form constrained by XPath's own abilities, can be performed as part of an activity. Such calculations may be trivial, such as incrementing an order number, or may be more complex like calculating a service reference based on the result of interrogating a UDDI service. Generally speaking, BPEL supports four kinds of expressions:

  1. Boolean XPath EXPR production where the evaluation results in a Boolean expression. Such expressions are used to manage process control flow.

  2. Deadline XPath EXPR production where the evaluation results in a string that is compatible with the format of either XML Schema date or dateTime types. Such expressions are used in timed wait and pick activities with a fixed deadline.

  3. Duration XPath EXPR production where the evaluation results in a string that is compatible with the format XML Schema duration. These expressions are used in timed wait situations with a relative deadline.

  4. General XPath EXPR production where any of the XPath types (string, number, or Boolean) can be the result. Such expressions are used for assignment and, depending on the type of the result, may use different operators. Numbers use the <, <=, =, !=, >=, > operators, while integer arithmetic is supported through the +, -, *, / operators. For strings, XPath only supports the equality and inequality operators =, !=.

In addition to the standard XPath 1.0 functions, BPEL also declares three additional XPath functions to enable XPath expressions to access process-specific data (such as elements of messages held in containers). The first of these functions, getContainerData, is shown in Figure 6-51.

Figure 6-51. XPath function referencing data based on container, part and location.
 bpws:getContainerData ('containerName', 'partName',                                               'locationPath'?) 

The function shown in Figure 6-51 extracts values held in containers. The first parameter for the function specifies the name of the container in which the data is stored. The second parameter is the part of the message in which the required data is stored, and the optional third parameter is the path within the message where the required data is held. Where the third parameter is not present, the function will return the whole message part element, though the BPEL specification points out that this function is strictly not allowed in abstract processes.

The function shown in Figure 6-52 also retrieves data from containers, but instead of being based on message parts and paths like the function shown in Figure 6-51, it uses properties to reference data elements stored in a particular container. The first parameter for the function specifies the name of the container in which the data is stored, and the second parameter is the name of a global property for a message, given as a QName. The return value of this function is the value of the data item referenced by the propertyName parameter.

Figure 6-52. XPath function referencing data based on container and property.
 bpws:getContainerProperty ('containerName', 'propertyName') 

The final BPEL XPath function is shown in Figure 6-53. This function returns either true or false depending on the status of the link passed as a parameter into the function. If the link status is positive then the function returns true, otherwise it returns false. This function is different from the others that BPEL declared insofar as its argument has to be specified statically that is, it must be the name of a link that appears within a flow activity within the statically defined process and absolutely is not allowed to be computed at runtime.

Figure 6-53. XPath function for interrogating links.
 bpws:getLinkStatus ('linkName') 

In addition to extracting data from containers, in the course of a business process we are likely to need to update data held in containers by assigning values to them. This type of activity occurs when we are building up messages to send out to partners or performing calculations based on process variables. The assign activity is used to copy data between containers, update existing data, and construct new data items and insert them into a container as part of a logically atomic operation. The assign activity can be applied equally well to messages, properties and literals, and can even be applied to copy service references to and from partner links.

Workflow Example: Online Shop

Having seen the array of features that BPEL provides, it is now time for us to apply some of those features to building a real workflow. For our example, we shall consider the abstract process of a simple online shop selling toasters, whose computing systems interact with those of its customers and financial partner in a simple arrangement, as shown in Figure 6-54.

Figure 6-54. Online toaster store partners and interactions.

graphics/06fig16.jpg

Our toaster store is a simple e-business. It has dealings with a number of customers and a bank that provides necessary financial services for processing customer purchases. Additionally our store keeps a small inventory of toasters to be able to respond quickly to any customer, or to keep afloat in times of toaster shortage. However, the core value process of this business is the one that enables customers to purchase toasters and have them delivered to the comfort of their own homes. This involves a number of activities to be coordinated in order to fulfill the customer's requirements, including inventory, payment, and shipping issues. A pictorial representation of this workflow is given in Figure 6-55, Figure 6-56, and Figure 6-57.

Figure 6-55. The Toaster-Purchase process.

graphics/06fig17.gif

Figure 6-56. Payment authorization sub-process.

graphics/06fig18.gif

Figure 6-57. Stock allocation and shipping subprocess.

graphics/06fig19.gif

Figure 6-55 shows a simple top-level view of the workflow that consists of payment authorization that verifies the financial robustness of the particular customer. Then the payment authorization activity is followed by the stock picking activity. If for some reason the requested item is not available for sale, the customer is informed of the delay or the fact that the order has to be cancelled. At this point the customer is also given the opportunity to cancel the order if the delay is deemed unacceptable. The customer may be informed of further delays, and throughout the process has the opportunity to cancel. The final stage in this workflow is the shipping, which is where the packaged goods are posted to the customer.[8]

[8] At this point we should emphasize that this workflow is purely a vehicle through which we will exemplify BPEL, and is not a production-quality workflow design (which is outside the scope of this book). We make no apologies for the naïve design of the workflow since this enables us to explore BPEL without becoming entangled in the intricacies of a particular workflow problem domain.

The payment authorization process is further elaborated on in Figure 6-56. In the normal case, the bank authorizes the credit card payment and the workflow continues to the next stage. If, however, the credit card authorization fails, the customer is informed that the payment has been rejected and the workflow terminates immediately.

Figure 6-57 shows the stock picking and shipping parts of the workflow. The stock picking process is straightforward when the item is in stock since that item is simply sent on to the dispatch part of the process for delivery to the customer. If, however, the item is not in stock, things get more interesting. The part of the workflow where stock is obtained from a supplier is executed, and the customer is informed of the delay while a supplier is located to source the item. While the item is being tracked down, the customer will receive a delay notification, in response to which the customer is free to cancel the order up until the time that the goods are obtained and dispatched. If the customer decides to cancel the order, it results in the workflow being rolled back, and the customer's credit card being credited. If eventually stock is located, then the workflow progresses to its dispatch phase and the items are posted to the customer, who is informed by the arrival of a delivery notification generated by the workflow instance when the item leaves the store's premises.

Having understood this simple workflow scenario, we can now begin to codify the application protocol that supports the interactions between the parties. There is a shared vocabulary defined by the online toaster store that forms the basis for this protocol as captured in its WSDL schema shown in Figure 6-58.

Figure 6-58. The online toaster store schema.
 <wsdl:definitions   targetNamespace="http://toaster.example.org/definitions"   xmlns:tns="http://toaster.example.org/definitions"   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:xs="http://www.w3.org/2001/XMLSchema">   <wsdl:types>     <xs:schema elementFormDefault="qualified"      attributeFormDefault="unqualified">       <xs:complexType name="ToasterType">         <xs:sequence>           <xs:element name="name" type="xs:string"/>           <xs:element name="manufacturer" type="xs:string"/>           <xs:element name="cost">             <xs:simpleType>               <xs:restriction base="xs:double">                 <xs:fractionDigits value="2"/>               </xs:restriction>             </xs:simpleType>           </xs:element>           <xs:element name="SKN">             <xs:simpleType>               <xs:restriction base="xs:string">                 <xs:pattern value="[0-9]{3}-[0-9]{3}"/>               </xs:restriction>             </xs:simpleType>           </xs:element>         </xs:sequence>       </xs:complexType>       <xs:complexType name="CustomerType">         <xs:sequence>           <xs:element name="title">             <xs:simpleType>               <xs:restriction base="xs:string">                 <xs:enumeration value="Mr"/>                 <xs:enumeration value="Mrs"/>                 <xs:enumeration value="Ms"/>                 <xs:enumeration value="Dr"/>                 <xs:enumeration value="Prof"/>               </xs:restriction>             </xs:simpleType>           </xs:element>           <xs:element name="firstname" type="xs:string"/>           <xs:element name="surname" type="xs:string"/>           <xs:element name="house" type="xs:string"             minOccurs="0"/>           <xs:element name="street" type="xs:string"/>           <xs:element name="city" type="xs:string"/>           <xs:element name="postcode" type="xs:string"/>         </xs:sequence>       </xs:complexType>       <xs:complexType name="CreditCardType">         <xs:sequence>           <xs:element name="card-type">             <xs:simpleType>               <xs:restriction base="xs:string">                 <xs:enumeration value="Visa"/>                 <xs:enumeration value="MasterCard"/>               </xs:restriction>             </xs:simpleType>           </xs:element>           <xs:element name="expiry">             <xs:simpleType>               <xs:restriction base="xs:string">                 <xs:pattern value="[0-9]{2}-[0-9]{2}"/>               </xs:restriction>             </xs:simpleType>           </xs:element>           <xs:element name="number">             <xs:simpleType name="CardNumberType">               <xs:restriction base="xs:string">                 <xs:pattern value="[0-9]{4} [0-9]{4} [0-9]{4} [0-9]{4}"/>               </xs:restriction>             </xs:simpleType>           </xs:element>           <xs:element name="holder" type="xs:string"/>         </xs:sequence>       </xs:complexType>     </xs:schema>   </wsdl:types> </wsdl:definitions> 

Having the schema, we can begin to construct the WSDL interface for our Web services, and begin to see just how we can turn this from a flow-diagram idea into an actual Web services reality.

Customer Web Service

In a typical interaction between a customer's system (which will be something like a shopping portal) and our online toaster shop's systems, we might expect a set of message exchanges looking something like that shown in Figure 6-59.

Figure 6-59. Buyer/seller message exchanges.

graphics/06fig20.jpg

The first thing to notice about the interaction shown in Figure 6-59 is that the messages are not synchronous, though there are obvious pairings. There is no notion of request-response in this interaction since the process started by the buyer sending a purchase message to the seller may take an arbitrarily long time to complete and, thus, a request-response style interaction is unjustifiable from both performance and reliability viewpoints. Instead, the seller receives the purchase message and at some point later, if all goes well, sends a message back to the buyer indicating that the purchase has been successful. If the customer's credit card cannot be authorized for the sale price, then the seller sends a cancellation message. If there are delays in the purchase, then the customer may send a cancellation message. If, however, the workflow isn't terminated by a customer cancellation message, the workflow terminates successfully by sending a dispatch note to the customer.

The customer's service has a WSDL interface designed to allow the toaster store to keep it abreast of the workflow as it progresses, as shown in Figure 6-60.

The WSDL in Figure 6-61 exposes three one-way operations that will be invoked by the toaster-vending service (and that are often referred to as callback operations) as the purchase workflow proceeds. Normally, the toaster-vending service will at some point send a DeliveryNotificationMessage message to invoke the notifyDelivery operation, but in the case where there has been a problem with the purchase, it may instead respond with an OrderCancelledMessage message. In the event of a delay in shipping the customer's purchase, the online store can call back the customer with a DelayNotificationMessage message to invoke the delayDelivery operation. This informs the customer of the delay and gives that customer a chance to cancel the order.

Figure 6-60. Customer service's WSDL interface.
 <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   targetNamespace="http://customer.example.org/wsdl"   xmlns:tns="http://customer.example.org/wsdl"   xmlns:df="http://toaster.example.org/definitions"   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <wsdl:import     namespace="http://toaster.example.org/definitions"     location="http://toaster.example.org/definitions.wsdl"/>   <wsdl:message name="DeliveryNotificationMessage">     <wsdl:part name="toaster" type="df:ToasterType"/>     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="dispatchDate" type="xs:dateTime"/>   </wsdl:message>   <wsdl:message name="DelayNotificationMessage">     <wsdl:part name="toaster" type="df:ToasterType"/>     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="orderDate" type="xs:dateTime"/>     <wsdl:part name="delayedBy" type="xs:positiveInteger"/>   </wsdl:message>   <wsdl:message name="OrderCancelledMessage">     <wsdl:part name="toaster" type="df:ToasterType"/>     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="reason" type="xs:string"/>   </wsdl:message>   <wsdl:portType name="CustomerPortType">     <wsdl:operation name="notifyDelivery">       <wsdl:input message="tns:DeliveryNotificationMessage"/>     </wsdl:operation>     <wsdl:operation name="delayDelivery">       <wsdl:input message="tns:DelayNotificationMessage"/>     </wsdl:operation>     <wsdl:operation name="cancelDelivery">       <wsdl:input message="tns:OrderCancelledMessage"/>     </wsdl:operation>   </wsdl:portType> </wsdl:definitions> 
Figure 6-61. Payment authorization message exchange pattern.

graphics/06fig21.jpg

Decoupling services by allowing them to communicate asynchronously is a sensible design decision in most enterprise-class Web services. If services communicate asynchronously, then they can fail (and recover) without compromising other services (clients of a service are not rudely interrupted by a connection failing, since there is no connection present most of the time). Then network resources are not wasted by keeping alive connections for large amounts of time.


The interaction between the online toaster shop and its partner bank is a little different from the interaction between a customer and the store in two main ways. First, the relationship between the toaster store and its bank is likely to be far more permanent than the transitory relationship struck up for a particular customer purchase. Second, since (at least within the scope of this workflow) the interaction with the partner is short-lived, we can afford to more closely couple the two systems. Practically, this means that we are able to use synchronous communications between the two services because we are satisfied of the high availability of the bank systems, the fact that its systems' interfaces are unlikely to change a great deal over time, and the fact that authorizing a credit card payment is a short-lived process. As such, the bank defines its WSDL to accommodate this style of interaction as shown in Figure 6-62, where the infrastructure to support the simple synchronous credit card authorization is exposed to a request-response operation called authorizeCreditCard, along with a similar synchronous refund operation called refundPayment.

Figure 6-62. Bank service WSDL.
 <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   targetNamespace="http://bank.example.org/wsdl"   xmlns:tns="http://bank.example.org/wsdl"   xmlns:df="http://toaster.example.org/definitions"   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <wsdl:import     namespace="http://toaster.example.org/definitions"     location="http://toaster.example.org/definitions.wsdl"/>   <wsdl:message name="CreditCardAuthorizationRequestMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>   </wsdl:message>   <wsdl:message name="CreditCardAuthorizedMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>   </wsdl:message>   <wsdl:message name="UnauthorizedPaymentMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>     <wsdl:part name="reason" type="xs:string"/>   </wsdl:message>   <wsdl:message name="RefundRequestMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>   </wsdl:message>   <wsdl:message name="PaymentRefundedMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>   </wsdl:message>   <wsdl:message name="RefundFailedMessage">     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="amount" type="xs:float"/>     <wsdl:part name="reason" type="xs:string"/>   </wsdl:message>   <wsdl:portType name="BankPortType">     <wsdl:operation name="authorizeCreditCard">       <wsdl:input         message="tns:CreditCardAuthorizationRequestMessage"/>       <wsdl:output         message="tns:CreditCardAuthorizedMessage"/>       <wsdl:fault name="unathorizedPaymentFault"         message="tns:UnauthorizedPaymentMessage"/>     </wsdl:operation>     <wsdl:operation name="refundPayment">       <wsdl:input message="tns:RefundRequestMessage"/>       <wsdl:output message="tns:PaymentRefundedMessage"/>       <wsdl:fault name="refundFault"         message="tns:RefundFailedMessage"/>     </wsdl:operation>   </wsdl:portType> </wsdl:definitions> 

The final piece of WSDL for our example is that of the OnlineToasters.com Web service, as shown in Figure 6-63.

Figure 6-63 contains a single portType declaration to allow customers to place orders with the OnlineToasters.com system. It also declares a number of message property elements and a number of bindings to a number of messages for each of those property elements through the propertyAlias declarations. As we shall see, these property declarations are used throughout the workflow to identify and correlate messages (bound to them through propertyAlias declarations) and workflow instances that logically belong to the same customer order using BPEL's correlation set features.

Figure 6-63. OnlineToasters.com WSDL interface.
 <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   targetNamespace="http://toaster.example.org/wsdl"   xmlns:tns="http://toaster.example.org/wsdl"   xmlns:df="http://toaster.example.org/definitions"   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"   xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/">   <wsdl:import     namespace="http://toaster.example.org/definitions"     location="http://toaster.example.org/definitions.wsdl"/>   <wsdl:message name="PurchaseMessage">     <wsdl:part name="toaster" type="df:ToasterType"/>     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="creditCard" type="df:CreditCardType"/>     <wsdl:part name="timestamp" type="xs:dateTime"/>   </wsdl:message>   <wsdl:message name="CancelMessage">     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="timestamp" type="xs:dateTime"/>   </wsdl:message>   <wsdl:message name="StockCheckRequestMessage">     <wsdl:part name="toaster" type="df:ToasterType"/>   </wsdl:message>   <wsdl:message name="StockCheckResponseMessage">     <wsdl:part name="daysToDelivery"       type="xs:postiveInteger"/>   </wsdl:message>   <wsdl:message name="CurrentTimeMessage">     <wsdl:part name="currentTime" type="xs:dateTime"/>   </wsdl:message>   <wsdl:message name="OrderPostedMessage">     <wsdl:part name="customer" type="df:CustomerType"/>     <wsdl:part name="timestamp" type="xs:dateTime"/>     <wsdl:part name="now" type="xs:dateTime"/>   </wsdl:message>   <wsdl:portType name="ToasterVendorPortType">     <wsdl:operation name="purchaseToaster">       <wsdl:input message="tns:PurchaseMessage"/>     </wsdl:operation>     <wsdl:operation name="cancelToaster">       <wsdl:input message="tns:CancelMessage"/>     </wsdl:operation>     <wsdl:operation name="stockCheck">       <wsdl:input message="tns:StockCheckRequestMessage"/>       <wsdl:output message="tns:StockCheckResponseMessage"/>     </wsdl:operation>     <wsdl:operation name="registerPostedOrder">       <wsdl:output message="tns:OrderPostedMessage"/>     </wsdl:operation>   </wsdl:portType>   <!-- BPEL message properties and propertyAliases -->   <bpws:property name="OrderCustomerProperty"     type="df:CustomerType"/>   <bpws:property name="OrderTimestampProperty"     type="xs:dateTime"/>   <bpws:propertyAlias     propertyName="tns:OrderCustomerProperty"     messageType="tns:PurchaseMessage" part="customer"/>   <bpws:propertyAlias     propertyName="tns:OrderCustomerProperty"     messageType="tns:CancelMessage" part="customer"/>   <bpws:propertyAlias     propertyName="tns:OrderCustomerProperty"     messageType="tns:OrderPostedMessage" part="customer"/>   <bpws:propertyAlias     propertyName="tns:OrderTimestampProperty"     messageType="tns:PurchaseMessage" part="timestamp"/>   <bpws:propertyAlias     propertyName="tns:OrderTimestampProperty"     messageType="tns:CancelMessage" part="timestamp"/>   <bpws:propertyAlias     propertyName="tns:OrderTimestampProperty"     messageType="tns:OrderPostedMessage" part="timestamp"/> </wsdl:definitions> 

More importantly, Figure 6-63 is also the starting point of the interface for our enterprise, and it is this interface that we will augment as we dig down further into BPEL. At this point OnlineToasters.com is (theoretically) up and running. All we need now is to work out how to integrate our bank and customers into our workflow. The first step in this process is to model our business processes in relation to those of our partners. In our example scenario, we capture the abstract roles of the enterprises with a number of serviceLinkType declarations as shown in Figure 6-64.

Each role in a BPEL service link is defined by a name for that role and a number of portType declarations that an entity playing that role has to support. For example, in Figure 6-65, any Web service playing the role of a customer must support the CustomerPortType portType.

Figure 6-64. Service link declarations.
 <wsdl:definitions   targetNamespace="http://toaster.example.org/slinks"   xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"   xmlns:slnk="http://schemas.xmlsoap.org/ws/2002/07/service-link/"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   xmlns:toast="http://toaster.example.org.wsdl"   xmlns:cust="http://customer.example.org/wsdl"   xmlns:bank="http://bank.example.org/wsdl">   <wsdl:import namespace="http://toaster.example.org/wsdl"     location="http://toaster.example.org/wsdl "/>   <import namespace="http://customer.example.org/wsdl"     location=" http://customer.example.org/wsdl "/>   <import namespace="http://bank.example.org/wsdl"     location=" http://bank.example.org/wsdl "/>   <slnk:serviceLinkType name="CallBackLink">     <slnk:role name="self">       <portType name="toast:ToasterVendorPortType"/>     </slnk:role>   </slnk:serviceLinkType>   <slnk:serviceLinkType name="CustomerVendorLink">     <slnk:role name="customer">       <portType name="cust:CustomerPortType"/>     </slnk:role>     <slnk:role name="vendor">       <portType name="toast:ToasterVendorPortType"/>     </slnk:role>   </slnk:serviceLinkType>   <slnk:serviceLinkType name="CardAuthorizerLink">     <slnk:role name="authorizer">       <portType name="bank:BankPortType"/>     </slnk:role>   </slnk:serviceLinkType>   <wsdl:service name="toasterServiceBusinessProcess"/> </wsdl:definitions> 

Usually each portType specified by a role originates in a different namespace, since partners are usually physically separate enterprises. However we can also define relationships where a service link is declared with portType declarations from the same namespace. In Figure 6-64, this is exemplified by the role self indicating that the implementing service must expose a ToasterVendorPortType portType which, as we saw in Figure 6-64, is within the namespace of the online toaster store's WSDL interface (and as we shall see in workflow process itself is used to allow our enterprise to invoke other services within its own enterprise boundary).

The final form of serviceLinkType is the one where one role is specified instead of the usual two. This indicates that a service is willing to bind to any other service without the requirement that the binding service has any particular interface characteristics. A good example of this in Figure 6-65 is the CardAuthorizerLink where the bank service is described as one that is quite unconcerned about who binds to it (at least as far as their interfaces are concerned). It is important to remember that unilateral role declarations are only useful in one-way or synchronous message exchanges since by definition an asynchronous bilateral exchange of messages requires a known portType (and address) to call back to.

At this point we have captured the relational aspects of the system. We know which enterprises are involved and we understand their interfaces, roles, and interrelations. The final stage in taking this forward to a workflow is to write the algorithm that performs the necessary computation and coordination between these parties to get a customer's order delivered. This workflow is shown split into logical units in Figure 6-65 through Figure 6-75.

Figure 6-65. Correlation set declaration.
 <bpws:process name="toasterPurchaseProcess"  targetNamespace="http://toaster.example.org/purchaseProcess"  suppressJoinFailure="yes"  xmlns:bpws="http://schemas.xmlsoap.org/ws/2002/07/business-process/"  xmlns:slinks="http://toaster.example.org/slinks"  xmlns:df="http://toaster.example.org/definitions"  xmlns:toast="http://toaster.example.org/wsdl"  xmlns:cust="http://customer.example.org/wsdl"  xmlns:bank="http://bank.example.org/wsdl"  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">  <bpws:correlations>    <!-- The customer details and timestamp are sufficient to         uniquely identify a purchase -->    <correlationSet name="OrderCorrelationSet"      properties="toast:OrderCustomerProperty,      toast:OrderTimestampProperty"/>  </bpws:correlations> 

Figure 6-65 shows the opening declarations of the workflow process where the various namespace affiliations of the elements in the process are introduced. More interestingly, it also introduces a correlation set which defines a unique key based on the CustomerOrderProperty and OrderTimestampProperty properties defined in Figure 6-63. When activated, this correlation set can be used to route messages to a particular process instance (or create a new process instance) based on the contents of the matching fields in those messages and the activity waiting for that message. In our workflow, the correlation set is initialized in Figure 6-71 by the receipt of a customer order, and is later used to correlate with a possible customer cancellation message for that order as in Figure 6-74.

Figure 6-66. Container declarations.
 <bpws:containers>     <!-- For receiving messages from customers -->     <bpws:container name="PurchaseOrders"       messageType="toast:PurchaseMessage"/>     <bpws:container name="Cancellations"       messageType="toast:CancelMessage"/>     <!-- For sending messages to customers -->     <bpws:container name="DispatchNotifications"       messageType="cust:DeliveryNotificationMessage"/>     <bpws:container name="DelayNotifications"       messageType="cust:DelayNotificationMessage"/>     <bpws:container name="CancellationNotifications"       messageType="cust:OrderCancelledMessage"/>     <!-- For authorizing credit card payment with          the bank -->     <bpws:container name="PaymentAuthorizationRequests" messageType="bank:CreditCardAuthorizationRequestMessage"/>     <bpws:container name="AuthorizedPayments"       messageType="bank:CreditCardAuthorizedMessage"/>     <bpws:container name="UnauthorizedPayments"       messageType="bank:UnauthorizedPaymentMessage"/>     <bpws:container name="RefundRequests"       messageType="bank:RefundRequestMessage"/>     <bpws:container name="RefundedPayments"       messageType="bank:PaymentRefundedMessage"/>     <bpws:container name="RefusedRefunds"       messageType="bank:RefundFailedMessage"/>     <!-- For performing stock checks -->     <bpws:container name="StockCheckItem"       messageType="toast:StockCheckRequestMessage"/>     <bpws:container name="ItemAvailability"       messageType="toast:StockCheckResponseMessage"/>     <!-- For creating dispatch notes -->     <bpws:container name="PostedOrders"       messageType="toast:OrderPostedMessage"/>   </bpws:containers> 

The container declarations for this process shown in Figure 6-66 are used to hold incoming messages while they are being processed, and as a placeholder for the construction of outgoing messages as their contents are being populated.

The partner elements shown in Figure 6-67 build on the previous serviceLinkType declarations, to create friendly-named entities with which our workflow algorithm engages in communication. Each partner declaration also specifies which role of the associated serviceLinkType will be played by our enterprise and which by a particular partner's.

Figure 6-67. Partner declarations.
 <!-- The entities involved in this workflow --> <bpws:partners>   <bpws:partner name="backEndService"     serviceLinkType="slinks:CallBackLink"     partnerRole="self"/>   <bpws:partner name="customer"     serviceLinkType="slinks:CustomerVendorLink"     myRole="vendor" partnerRole="customer"/>   <bpws:partner name="bank"     serviceLinkType="slinks:CardAuthorizerLink"     partnerRole="authorizer"/> </bpws:partners> 
Figure 6-68. Top-level fault handler declarations.
 <bpws:faultHandlers>   <bpws:catchAll>     <!-- If an unexpected fault happens at this level,          reverse the whole business transaction -->     <compensate/>   </bpws:catchAll> </bpws:faultHandlers> 

The fault handler shown in Figure 6-68 is designed to allow the workflow to shutdown gracefully in the event of an unexpected and otherwise unhandled fault propagating to the top level. In the event of any unhandled fault reaching this level, the catchAll activity is activated which forces the workflow to compensate, as per the compensation routine in Figure 6-69. This causes the compensation activity at this level to be executed, followed by recursive compensation on all subactivities.

The compensation activity shown in Figure 6-69 first defines that any faults arising during compensation are suppressed by having a catchAll fault handler contain an empty activity. In this case it is a safe thing to do because the only fault we expect is that the bank will fault if we try to refund a card from which it has not extracted a payment. However, in more industrial strength workflows, it is entirely plausible that compensation activities will contain their own sophisticated fault handling routines to ensure that compensation progresses even in the presence of faults.

In the unexceptional execution case, the compensation activity builds a refund request message by copying the details of the transaction from the PurchaseOrders container, which contains the details of the current order, into the RefundRequests container, which holds (at this point empty) messages suitable for sending to the bank to request a credit card refund. Once this message is constructed, it is sent to the bank through an invoke activity which specifies that the refundPayment operation is to be invoked on the bank partner using the message in the RefundRequests container as the input message to that operation and storing the results of that invocation in the RefundedPayments container. In the case where a fault is returned because the bank cannot refund the card (since there was no corresponding previous debit), the fault handler is invoked and the fault suppressed.

Figure 6-69. Top-level compensation handler for the workflow.
 <bpws:sequence>   <bpws:scope containerAccessSerializable="no">     <!-- The inverse of this activity is to refund the          customer's credit card and send a cancellation          message -->     <bpws:compensationHandler>       <!-- Fault handler to deal with transient network            faults when communicating with the bank -->       <bpws:faultHandlers>         <!-- Do nothing if the bank faults, because there's              no refund to process -->         <bpws:catch faultName="refundFault"           faultContainer="RefusedRefunds">           <bpws:empty/>           <!-- Surpress the fault -->         </bpws:catch>       </bpws:faultHandlers>       <!-- Build refund request message -->       <bpws:assign>         <bpws:copy>           <bpws:from container="PurchaseOrders"             part="creditCard"/>           <bpws:to container="RefundRequests"             part="creditCard"/>         </bpws:copy>       </bpws:assign>       <bpws:assign>         <bpws:copy>           <bpws:from container="PurchaseOrders"             part="toaster" query="/cost"/>           <bpws:to container="RefundRequests"             part="amount"/>         </bpws:copy>       </bpws:assign>       <!-- Instruct bank to process refund -->       <bpws:invoke partner="bank"         portType="bank:BankPortType"         operation="refundPayment"         inputContainer="RefundRequests"         outputContainer="RefundedPayments"/>     </bpws:compensationHandler> 

The net result of this handler being activated is that any order being processed is dropped, and any credit card payments that have been taken are cancelled.

Figure 6-70. Fault handlers for the credit card authorization subprocess.
 <!-- Handle possible faults (and possibly cancel      purchase)--> <bpws:faultHandlers>   <bpws:catch faultName="bank:unauthorizedPaymentFault"     faultContainer="UnauthorizedPayments">     <!-- Build the contents of the cancellation          notification message from the authorization          fault and customer's original purchase          order -->     <bpws:assign>       <bpws:copy>         <bpws:from container="UnauthorizedPayments"           part="reason"/>         <bpws:to container="CancellationNotifications"           part="reason"/>       </bpws:copy>     </bpws:assign>     <bpws:assign>       <bpws:copy>         <bpws:from container="PurchaseOrders"           part="customer"/>         <bpws:to container="CancellationNotifications"           part="customer"/>       </bpws:copy>     </bpws:assign>     <bpws:assign>       <bpws:copy>         <bpws:from container="PurchaseOrders"           part="toaster"/>         <bpws:to container="CancellationNotifications"           part="toaster"/>       </bpws:copy>     </bpws:assign>     <!-- Send the cancel message to the customer -->     <bpws:invoke partner="customer"       portType="cust:CustomerPortType"       operation="cancelDelivery"       inputContainer="Cancellations"/>     <!-- Terminate the workflow -->     <bpws:terminate/>   </bpws:catch> </bpws:faultHandlers> 

Figure 6-70 shows the fault handler for those situations where a customer order provides credit card details for which authorization and payment are subsequently denied by the bank, as signified by the return of an unauthorizedPaymentFault message in response to a authorizeCreditCard operation invocation on the bank Web service. This fault handler responds to that eventuality by building a CancellationNotificationMessage message, copying the reason element from the fault produced by the faulting invocation, and the customer and item details from the original purchase order into the CancellationNotifications container. This container is used as input for the one-way invoke activity on the customer's cancelDelivery operation to inform the customer that the order has been cancelled and why. The handler then terminates this workflow instance, which it is safe to do since at this point no billing has occurred nor have any items been shipped.

Figure 6-71. Receiving a customer order.
 <!-- Receive Customer Order and  Payment --> <bpws:receive partner="customer"   portType="toast:ToasterVendorPortType"   operation="purchaseToaster"   container="PurchaseOrders" createInstance="yes">   <bpws:correlations>     <bpws:correlation set="OrderCorrelationSet"       initiation="yes"/>   </bpws:correlations> </bpws:receive> 

The receive activity shown in Figure 6-71 is the entry point to this workflow. It accepts the invocation from a customer on our enterprise's purchaseToaster operation and stores the contents of the message in the PurchaseOrders container ready to be used in the workflow. The receive activity also initializes the OrderCorrelationSet correlation set with the customer and timestamp parts from the incoming PurchaseMessage message, forming a unique key that identifies this instance of the process from now onward. New messages arriving that contain these fields can now be automatically routed to the correct instance by the underlying BPEL engine.

The first thing that our workflow does when it receives a purchaseMessage message is to check whether the customer is good for payment! It does this by building a CreditCardAuthorizationRequestMessage message in the PaymentAuthorizationRequests container, which it populates with the credit card details and the cost of the order as shown in Figure 6-72. The credit card details are extracted directly from the purchaseMessage message through an assignment from the creditCard part of the purchaseMessage message to the creditCard part of the PaymentAuthorizationRequests container. The amount being charged is extracted from the toaster part of the purchaseMessage message, with the query string /cost being used to extract to particular piece of data from that message part. Once the message has been constructed, the bank is asked to take payment on the credit card via a synchronous invoke activity, which receives confirmation of the payment via a message delivered back to the AuthorizedPayments container. If the payment is not authorized, then the bank returns a fault which is handled by the fault handler shown in Figure 6-70 that terminates the purchase process.

Figure 6-72. Authorizing a credit card payment.
   <!-- Build the message to send to the credit card        authorization service -->   <bpws:assign>     <bpws:copy>       <bpws:from container="PurchaseOrders"         part="creditCard"/>       <bpws:to container="PaymentAuthorizationRequests"         part="creditCard"/>     </bpws:copy>   </bpws:assign>   <bpws:assign>     <bpws:copy>       <bpws:from container="PurchaseOrders"         part="toaster" query="/cost"/>       <bpws:to container="PaymentAuthorizationRequests"         part="amount"/>     </bpws:copy>   </bpws:assign>   <!-- Invoke the credit card authorization service -->   <bpws:invoke partner="bank"     portType="bank:BankPortType"     operation="authorizeCreditCard"     inputContainer="PaymentAuthorizationRequests"     outputContainer="AuthorizedPayments"/> </bpws:scope> <!-- End of authorization, from here on in we know      the customer has paid --> 

Once payment has been taken, the goods the customer has ordered are made ready for dispatch. The first step in this task is to check stock availability for the ordered item, which involves invoking a Web service within our own enterprise. This is shown in Figure 6-73 where a synchronous invoke activity is used to query our inventory for the customer's item. Figure 6-73 shows how the toaster part of the original PurchaseMessage message is used to build a StockCheckRequestMessage message in the StockCheckItem container, which is then used to invoke the stockCheck operation on the inventory Web service. The result of that invocation is placed in the ItemAvailability container, which is then subsequently used to determine how to respond to the customer as shown in Figure 6-74.

Figure 6-73. Obtaining stock from inventory or partner.
 <!-- Pick Stock --> <!-- Build stock check request message --> <bpws:assign>   <bpws:copy>     <bpws:from container="PurchaseOrders"       part="toaster"/>     <bpws:to container="StockCheckItem" part="toaster"/>   </bpws:copy> </bpws:assign> <!-- Call stock check and look at availability date --> <bpws:invoke partner="backEndService"   portType="toast:ToasterVendorPortType"   operation="stockCheck" inputContainer="StockCheckItem"   outputContainer="ItemAvailability"/> 
Figure 6-74. Dealing with possible stock delays.
 <bpws:switch>      <!-- If we can't get the toaster in 3 days or less,           then we have to inform the customer of the delay           and give them a chance to cancel-->      <bpws:case   condition="bpws:getContainerData(ItemAvailability, daysToDelivery) > 3">        <!-- Build the delay notification message -->        <bpws:assign>          <bpws:copy>            <bpws:from container="PurchaseOrders"              part="toaster"/>            <bpws:to container="DelayNotifications"              part="toaster"/>          </bpws:copy>        </bpws:assign>        <bpws:assign>          <bpws:copy>            <bpws:from container="PurchaseOrders"              part="customer"/>            <bpws:to container="DelayNotifications"              part="customer"/>          </bpws:copy>        </bpws:assign>        <bpws:assign>          <bpws:copy>            <bpws:from       expression="bpws:getContainerData (ItemAvailability, daysToDelivery)"/>            <bpws:to container="DelayNotifications"              part="delayedBy"/>          </bpws:copy>        </bpws:assign>        <bpws:assign>          <bpws:copy>            <bpws:from container="PurchaseOrders"              part="timestamp"/>            <bpws:to container="DelayNotifications"              part="orderDate"/>          </bpws:copy>        </bpws:assign>        <!-- Tell the customer about the delay -->        <bpws:invoke partner="customer"          portType="cust:CustomerPortType"          operation="delayNotification"          inputContainer="DelayNotifications"/>        <!-- And wait for them do cancel, if it is             their volition -->        <bpws:pick>          <bpws:onMessage partner="customer"            portType="toast:ToasterVendorPortType"            operation="cancelToaster"            container="Cancellations">            <!-- Correlate the received message with the                 current process -->            <bpws:correlations>              <bpws:correlation set="OrderCorrelationSet"                initiation="no"/>            </bpws:correlations>            <!-- If the customer cancels during the delay                 time, then compensate the whole workflow -->            <bpws:compensate/>          </bpws:onMessage>          <bpws:onAlarm           for="bpws:getContainerData (ItemAvailability, daysToDelivery)">            <!-- If the delay time has expired and the                 customer hasn't cancelled then continue with                 workflow (do nothing extra) -->            <bpws:empty/>          </bpws:onAlarm>        </bpws:pick>      </bpws:case>    </bpws:switch>    <!-- End of stock picking, we know now that we have the         item ready to ship --> 

Depending on the results of the stock check operation shown in Figure 6-73, the behavior of the section of the workflow shown in Figure 6-74 has two mutually exclusive paths that it can take. In the case where the stock is readily available (which our workflow defines as being ready to send in less than three days), then the activities shown in Figure 6-74 are effectively skipped and the workflow progresses to Figure 6-75 where the customer's items are shipped. If, however, the order will take three or more days to be ready to ship, then a more intricate protocol with the customer is initiated.

This is decided on in the case element of the switch activity in Figure 6-75, which uses a condition to determine whether the result of the stock check indicate the order will be ready within the internal three-day deadline. It checks the ItemAvailability container for the daysToDelivery part and compares that to the number 3 (remembering, of course, that we are dealing with XPath 1.0 where the only supported types are Boolean, string, and number). If the value in the daysToDelivery field is greater than 3, then the workflow builds a DelayNotificationMessage message in the DelayNotifications container and invokes the customer's delayNotification operation using this container as the input.

At this point the ball is in the customer's court. The customer may decide that the delay is simply unacceptable and will therefore cancel the order by invoking the cancelToaster operation on the online toaster shop, which activates the onMessage part of the pick activity. This onMessage is correlated with the OrderCorrelationSet correlation set so that only messages bound for an existing order will be routed to this activity, as is clear from its status as a follower marked by the initation="no" attribute. Finally, if the customer does decide to cancel and the onMessage part of the pick activity is activated, then the whole workflow instance is compensated, which means the customer's credit card will be refunded and no goods will be shipped.

Conversely, the customer may be willing to accept the delay advertised in the delayNotificationMessage message they received and after the delay has finished, the onAlarm part of the pick activity is activated. In this case executing the onAlarm activity causes an empty activity to be executed, before the workflow continues to the final order dispatch aspect shown in Figure 6-75.

The final part of our workflow simply informs the customer that the order has been dispatched and when it was sent. As the customer's order leaves the toaster vendor's premises, it is scanned by a barcode reader, which then invokes the registerPostedOrder operation on the toaster vendor's Web service. This operation is supported by a receive activity in the workflow script shown in Figure 6-75. That receive blocks until the arrival of an OrderPostedMessage message correlated to this process instance by its customer and timestamp properties as per the OrderCorrelationSet.

Once an appropriate message has been received, a DeliveryNotificationMessage message is created in the DispatchNotifications container through a number of assignments from the original PurchaseMessage and OrderPostedMessage messages. This container is then used as the input to an invoke activity that calls the customer's notifyDelivery operation, at which point the workflow instance ends.

Figure 6-75. Shipping the order and ending the workflow instance.
     <!-- Ship Order -->     <!-- Wait for barcode reader to scan outgoing box --> <bpws:receive partner="backEndService"     portType="toast:ToasterVendorPortType"     operation="registerPostedOrder"     container="PostedOrders" createInstance="no">     <bpws:correlations>       <bpws:correlation set="OrderCorrelationSet"         initiation="no">     </bpws:correlations>     <!-- Fill order details in dispatch note -->     <bpws:assign>       <bpws:copy>         <bpws:from container="PurchaseOrders"           part="toaster"/>         <bpws:to container="DispatchNotifications"           part="toaster"/>       </bpws:copy>     </bpws:assign>     <bpws:assign>       <bpws:copy>         <bpws:from container="PurchaseOrders"           part="customer"/>         <bpws:to container="DispatchNotifications"           part="customer"/>       </bpws:copy>     </bpws:assign>     <bpws:assign>       <bpws:copy>         <bpws:from container="PostedOrders"           part="now"/>         <bpws:to container="DispatchNotifications"           part="dispatchDate"/>       </bpws:copy>     </bpws:assign>     </bpws:receive>     <!-- Send dispatch note to customer -->     <bpws:invoke partner="customer"       portType="cust:CustomerPortType"       operation="notifyDelivery"       inputContainer="DispatchNotifications"/>     <!-- End of order shipping, the customer has          their toaster -->   </bpws:sequence> </bpws:process> 


Developing Enterprise Web Services. An Architect's Guide
Developing Enterprise Web Services: An Architects Guide: An Architects Guide
ISBN: 0131401602
EAN: 2147483647
Year: 2003
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net