[Editor's Note: This article is a programming example that illustrates the features of the Web Services Flow Language. It assumes that you already know the basic concepts behind workflow and WSFL. If you need a refresher, please see the Resources section for a selection of overview articles on WSFL.]
In Part 1, I outlined the various activity services that make up our portfolio management service between our fictional financial broker Strong.com, the New York Stock Exchange, and StockQuote.com's quoting services. I also introduced the flow model for our programming example. In this second part, I will explain what the complete picture of the process looks like including the flow and global models.
Control links and order of execution
When I last stopped in Part 1, I had shown defined the service provider type which is being specified in the flow model. Remember, the service provider type represents a set of port types which are implemented by service providers which implement our service provider type.
I have also identified the activities which are implemented by the flow, as well as the service providers which host those Web services which will ultimately implement the activities. The activities have been exported as WSDL operations, which must be implemented by the flow. So, I now have a Web service with three operations that are actually implemented by three separate Web services.
Now the flow model specifies the order of execution for the activities
as well as the data exchanges between activities. The controlLink
element represents an edge on the directed graph I presented in Part 1. The usage if controlLink
is as shown below:
<controlLink source="retrieveStockQuote" target="creditCheck"/> |
This controlLink indicates that the creditCheck activity
is activated after the retrieveStockQuote activity completes
successfully. Since there are no controlLink elements which specify the
retrieveStockQuote activity as a target, this activity is considered a
start activity (all start activities are scheduled for execution
when the flow is instantiated).
Control links are fairly flexible. A fork operation indicates
two parallel paths of execution. For example, let's say that after
activity 'A' completes, I would like to perform activity 'R'
simultaneously with activity 'W.' Let's assume that before I can perform
activity 'K,' both activities 'R' and 'W' must complete. I could model
this interaction with the following control links:
<controlLink source="A" target="R"/> <controlLink source="A" target="W"/> <controlLink source="R" target="K"/> <controlLink source="W" target="K"/> |
I define a fork after activity 'A' completes and a join operation before the 'K' activity is activated.
Control links may also specify transition conditions (Boolean expressions which are evaluated when an activity completes successfully). If the transition condition succeeds, the target activity is invoked. If the transition condition fails, the target activity is not invoked and parallel flows are evaluated to determine if the entire flow should be terminated.
The following controlLink indicates that after the
creditCheck activity completes, the executeTrade activity
should be invoked if the OutCheckCreditResponse messages 'response'
part is true. The transitionCondition defines
an XPath Boolean expression:
<controlLink source="creditCheck" target="executeTrade"
transitionCondition="OutCheckCreditResponse/response"/>
|
Join activities that have multiple incoming control links may
define a join condition that synchronizes the transition conditions. The
join activity below indicates that the transition conditions for both
activities 'R' and 'W' must evaluate to true before activity 'K' may be
invoked:
<activity name="K">
<joinCondition="R and W" when="deferred"/>
</activity>
|
The when attribute of the joinCondition element indicates
when the Boolean expression is to be evaluated. The value of the
when attribute is either deferred or immediate.
Deferred evaluations wait until all transition conditions have been
evaluated, while immediate evaluations process the transition
conditions as they are evaluated. For example, if the 'R' activity
terminated prior to the 'W' activity and the transition condition failed,
then the join condition would wait until activity 'W' terminated and its
transition condition was evaluated before evaluating to false.
Control links define the execution sequence for activities, but they do not provide instruction on how data is exchanged between the activities. Data links define message exchanges between activities. The decoupling of control and data links is useful because data is not necessarily transmitted when an activity yields execution to another activity.
The dataLink element defines a message exchange between a source activity and a target activity. The dataLink above indicates that a message was passed from the retrieveStockQuote activity to the creditCheck activity. While data links are decoupled from control links, a data link cannot be defined from a source activity to a target activity unless one or more control links exist from the source activity to the target.
<dataLink name="dataLink2" source="retrieveStockQuote" target="creditCheck">
<map sourceMessage="OutRetrieveStockQuoteResponse"
targetMessage="InCheckCreditRequest"
sourcePart="meth1_outType" targetPart="stockPrice"/>
</dataLink>
|
The map element is used to map the output message
from the source activity to the input message of the target.
<map sourceMessage="OutRetrieveStockQuoteResponse" targetMessage="InCheckCreditRequest" sourcePart="meth1_outType" targetPart="stockPrice"/> |
The map element above indicates that the meth1_outType
part of the OutRetrieveStockQuoteResponse message is
assigned to the stockPrice part of the InCheckCreditRequest message.
OutRetrieveStockQuoteResponse is the output message
generated by the retrieveStockQuote activity, while InCheckCreditRequest
is the input message processed by the creditCheck activity.
So it is clear how messages are exchanged between activities. But where
do the start activities receive data? For that matter, the
InCheckCreditRequest message needs more information than the stock price:
it also needs the client identifier so that I can access the client's account. I also need the number
of shares the client wishes to purchase in order to calculate the transaction costs.
To better understand the overall process, I present a conceptual representation of the flow model in Figure 1.
Figure 1: The flow model for our example

The flow model defines a Web service which takes input and generates
output -- so I need a means of accepting input and producing output. The
flowSource and flowSink are special activities supported by
WSFL specifically for this purpose. I assumed that when the flow is
instantiated, an InPurchaseStockRequest message is provided by the
flowSource activity. Additionally, an
OutPurchaseStockResponse message is exchanged with the
flowSink activity prior to the flow terminating:
<message name="InPurchaseStockRequest">
<part name="stockSymbol" type="xsd:string"/>
<part name="numOfShares" type="xsd:int"/>
<part name="clientID" type="xsd:long"/>
</message>
<message name="OutPurchaseStockRequest">
<part name="sucessfulTrade" type="xsd:boolean"/>
</message>
|
The dataLink0 data link indicates that a message is exchanged
between the flowSource activity and the retrieveStockQuote
activity.
<dataLink name="dataLink0" source="flowSource" target="retrieveStockQuote">
<map sourceMessage="InPurchaseStockRequest"
targetMessage="InRetrieveStockQuoteRequest"
sourcePart="stockSymbol" targetPart="meth1_inType1"/>
</dataLink>
|
The stockSymbol part of the InPurchaseStockRequest
message is assigned to the meth1_inType1 part of the
InRetrieveStockQuoteRequest message. The
InPurchaseStockRequest message provides data input for the
creditCheck activity via the flowSource activity as well. The
clientID and the numOfShares message parts are mapped to the
corresponding parts of the InCheckCreditRequest. Thus, once the
flow is initialized the client identifier and the number of shares is
inserted into a InCheckCreditRequest message. After the
retrieveStockQuote activity completes, the stock price is known and
added to the InVerfifyCreditRequest message.
<dataLink name="dataLink1" source="flowSource" target="creditCheck">
<map sourceMessage="InPurchaseStockRequest"
targetMessage="InCheckCreditRequest" sourcePart="clientID"
targetPart="clientID"/>
<map sourceMessage="InPurchaseStockRequest"
targetMessage="InCheckCreditRequest" sourcePart="numOfShares"
targetPart="quantity"/>
</dataLink>
|
The creditCheck activity generates an OutCheckCreditResponse message
via the verifyCredit operation. The result message part is a
Boolean value indicating whether the trade may be executed. The transition
condition defined by the control link from the creditCheck activity to the
executeTrade activity is evaluated.
<controlLink source="creditCheck" target="executeTrade"
transitionCondition="OutCheckCreditResponse/response"/>
|
The executeTrade activity accepts an InBidRequest message as
input. The stockSymbol and numOfShares parts of the
InPurchaseStockRequest message populate the InBidRequest
message.
<dataLink name="dataLink3" source="flowSource" target="executeTrade">
<map sourceMessage="InPurchaseStockRequest" targetMessage="InBidRequest"
sourcePart="stockSymbol" targetPart="symbol"/>
<map sourceMessage="InPurchaseStockRequest" targetMessage="InBidRequest"
sourcePart="numOfShares" targetPart="shareCount"/>
<dataLink>
|
The executeTrade activity generates an OutBidResponse message which
indicates whether the trade was executed successfully.
<dataLink name="dataLink4" source="executeTrade" target="flowSink">
<map sourceMessage="OutBidResponse"
targetMessage="OutPurchaseStockResponse" sourcePart="result"
targetPart="sucessfulTrade"/>
</dataLink>
|
The result message part of the OutBidResponse is passed
to the flowSink activity as the output of the flow.
Our WSFL flow model defines a service provider type, or public interface for a Web service which is assembled from other Web services. Beyond the exported retrieveStockQuote, verifyCredit, and bid operations, the StrongPuchaseFlow service provider type supports lifecycle operations. All WSFL Web services must support the following lifecycle operations:
- spawn
Creates an instance of the flow model and returns an instance id. - call
Creates an instance of the flow model and returns after the flow has completed. The call operation returns the result of the flow model instance. - suspend
Suspends an instance of the flow model given an instance id. - resume
Resumes a suspended flow model instance given an instance id. - enquire
Queries the status of a flow model instance given an instance id. The result of the enquire operation is the status of the flow. - terminate
Terminates a flow model instance given an instance id.
A WSFL global model binds Web service operations to operations defined by the aggregate service including exported and lifecycle operations. The global model for our example, shown in Listing 1, is conceptually represented in Figure 2.
Figure 2: The global model for our example

Listing 1: The global model
<globalModel name="StrongStockPurchaseProcess"
serviceProviderType="StockPurchaseProcess">
<serviceProvider name="NYSE_VirtualTradingFloor"
type="VirtualTradingFloor"/>
<serviceProvider name="StockQuoteRealTimeStockQuoteProvider"
type="RealTimeStockQuoteProvider"/>
<serviceProvider name="StrongAccountManager"
type="AccountManager"/>
<serviceProvider name="StrongPortfolioManager"
type="StockPurchaseFlow">
<export>
<source portType="StockPortfolioTrackerLifeCyclePT"
operation="spawn"/>
<target portType="StockPortfolioTrackerPT"
operation="purchaseStock"/>
</export>
</serviceProvider>
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT"
operation="retrieveStockQuote"/>
<target serviceProvider="StockQuoteRealTimeStockQuoteProvider"
portType="StockQuoteServiceImpl_Service"
operation="getStockPrice"/>
</plugLink>
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT" operation="verifyCredit"/>
<target serviceProvider="StrongAccountManager"
portType="AccountManagementPT" operation="checkCredit"/>
</plugLink>
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT" operation="bid"/>
<source serviceProvider="NYSE_VirtualTradingFloor"
portType="MarketExchangePT" operation="purchaseStockBlock"/>
</plugLink>
</globalModel>
|
The global model is named StrongStockPurchaseProcess and defines a new service provider type (i.e. StockPurchaseProcess).
<globalModel name="StrongStockPurchaseProcess"
serviceProviderType="StockPurchaseProcess">
...
<globalModel>
|
The global model then declares the service providers which will be referenced within the document:
<serviceProvider name="NYSE_VirtualTradingFloor"
type="VirtualTradingFloor"/>
<serviceProvider name="StockQuoteRealTimeStockQuoteProvider"
type="RealTimeStockQuoteProvider"/>
<serviceProvider name="StrongAccountManager" type="AccountManager"/>
|
The NYSE_VirtualTradingFloor,
StockQuoteRealTimeStockQuoteProvider and
StrongAccountManager service providers are defined just as they
were in the flow model. These service providers host the Web services
which implement the underlying operations that I started with in the
beginning of the chapter. The StrongPortfolioManager service
provider is new and implements the StockPurchaseFlow which was
defined by our flow model:
<serviceProvider name="StrongPortfolioManager" type="StockPurchaseFlow">
<export>
<source portType="StockPortfolioTrackerLifeCyclePT" operation="spawn"/>
<target portType="StockPortfolioTrackerPT" operation="purchaseStock"/>
</export>
</serviceProvider>
|
Notice that a locator is not defined for the
StrongPortfolioManager service provider. Flow developers may define
locators separately and implement the service provider type. The
StrongPortfolioManager service provider indicates that the
spawn operation is called implicitly when the purchaseStock
operation is invoked. Thus by calling the purchaseStock operation, the
flow model is instantiated.
The plugLink element binds an operation from a service
provider's public interface to an operation implemented by a service
provider:
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT" operation="retrieveStockQuote"/>
<target serviceProvider="StockQuoteRealTimeStockQuoteProvider"
portType="StockQuoteServiceImpl_Service" operation="getStockPrice"/>
</plugLink>
|
The plugLink indicates that the StrongPortfolioManager
service provider's retrieveStockQuote operation is bound to the
StockQuoteRealTimeStockQuoteProvider service provider's getStockPrice
operation. This binds our flow model's public interface to the
getStockPrice operation from our stock quote Web service.
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT" operation="verifyCredit"/>
<target serviceProvider="StrongAccountManager"
portType="AccountManagementPT"
operation="checkCredit"/>
</plugLink>
|
The verifyCredit operation from the
StockPortfolioTrackerPT port type from the flow model is bound to
the checkCredit operation of the Web service hosted by the
StrongAccountManager service provider. Finally, the bid
operation is bound to the purchaseStockBlock operation implemented
by the service with the WSDL interface definition located at http://nyse.com/vtf.wsdl.
<plugLink>
<source serviceProvider="StrongPortfolioManager"
portType="StockPortfolioTrackerPT" operation="bid"/>
<source serviceProvider="NYSE_VirtualTradingFloor"
portType="MarketExchangePT" operation="purchaseStockBlock"/>
</plugLink>
|
There is now enough information to finalize the workflow of our new Web service. You simply need to create a WSDL document that supports the relevant portTypes, operations and messages (see Listing 2).
Listing 2: Our finalized service in WSDL
<message name="InBidRequest">
<part name="symbol" type="xsd:string"/>
<part name="shareCount" type="xsd:int"/>
</message>
<message name="OutBidResponse">
<part name="sucessfulTrade" type="xsd:boolean"/>
</message>
<message name="InPurchaseStockRequest">
<part name="clientID" type="xsd:long"/>
<part name="numOfShares" type="xsd:int"/>
<part name="stockSymbol" type="xsd:string"/>
</message>
<message name="OutPurchaseStockRequest">
<partname="result" type="xsd:string"/>
</message>
<message name="InCheckCreditRequest">
<part name="clientID" type="xsd:long"/>
<part name="quantity" type="xsd:int"/>
<part name="stockPrice" type="xsd:float"/>
</message>
<message name="OutCheckCreditResponse">
<part name="response" type="xsd:boolean"/>
</message>
<message name="InRetrieveStockQuoteRequest">
<part name="meth1_inType1" type="xsd:string"/>
</message>
<message name="OutRetrieveStockQuoteResponse">
<part name="meth1_outType" type="xsd:float">
</message>
<portType name="StockPortfolioTrackerPT">
<operation name="bid">
<input message="InBidRequest"/>
<output message="OutBidResponse"/>
</operation>
<operation name="verifyCredit">
<input message="InCheckCreditRequest"/>
<output message="OutCheckCreditResponse"/>
</operation>
<operation name="retrieveStockQuote">
<input message="InRetrieveStockQuote"/>
<output message="OutRetrieveStockQuote"/>
</operation>
<operation name="purchaseStock">
<input message="InPurchaseStockRequest"/>
<output message="OutPurchaseStockResponse"/>
</operation>
</portType>
|
The WSDL fragment above describes the public interface of our aggregate Web service. Since my composition is a Web service itself, I could actually leverage WSFL to recursively define yet another aggregate Web service which integrates our service with other Web services. I have shown that with just a few grammar rules, WSFL is a flexible XML language for defining Web services which are composed of other Web services.
- "Programming Web Services with Java by Ajamu Wesley (Manning, 2002) ISBN 1930110421.
- XML Cover Pages on the Web Services Flow Language (WSFL).
- ebPML on WSFL: http://www.ebpml.org/wsfl.htm.
- Going With the Flow, Intelligent Enterprise
Magazine feature article about WSFL, September 2001.
- Web Services Overview (in PDF).
- The WSFL specification (in PDF).
- IBM alphaWorks Web Service Process Management Toolkit.
- OASIS Web Service Component Model (WSCM).
Ajamu Wesley is a Senior Architect within IBM's Emerging Internet Technology division. Ajamu develops technical strategy, and architecture while providing technical consulting services promoting the adoption of leading edge technologies. His current role is technical lead for Federated Identity. Ajamu has generated several external technical publications with his latest project being the authorship of "Programming WebServices", which is an instructive book for programmers focusing on WebServices and related technologies. He can be reached at awesley@us.ibm.com.




