Web Services and Flows (WSFL), Page 3
You have previously seen (in the section Outsourcing Activity Implementations: Export) how you can outsource an activity's implementation to an external service provider by using the internal element to specify an activity's implementation. The use of the internal element is necessary when the connection to the external provider is kept private to the service, in the sense that it is not carried through a public WSDL interface; that is, it is not mediated by an operation in the service provider type that the flow declares.
Alternatively, an activity's implementation can be provided by exporting it. Exporting an activity makes it visible to the flow's clients in the form of an operation on the flow's service interface. In this case, an export element replaces the internal element used before, and a nested target element identifies the operation in the flow's own interface that is mapped to this activity.
Detailing the Reject activity to make use of export produces the following WSFL fragment:
<activity name="Reject"> <input name="orderData" message="tns:Order"/> <implement> <export> <target portType="cellPhoneOrderHandler" operation="reject"/> ... </export> </implement> ... </activity>
When an activity's implementation is exported, the flow engine executes the activity by either receiving an invocation from a client (if the operation to which the activity is exported is one-way or request-responsethat is, an inbound operation), or by invoking an operation on a partner (when the operation mapped to the activity is an outbound operationthat is, notification or solicit-response). For example, the reject operation in the preceding example is a notification operation on the phone ordering service interface, but corresponds to the invocation of a one-way operation in the customer interface (which is to say that the customer must be able to receive the rejection, and implies a peer-to-peer interaction model, see Global Models). Because of the uniform treatment that WSDL gives to inbound and outbound operations, WSFL deals with both cases in the same way.
In WSFL, a plug link can be provided inside the export element to indicate the operation in the target provider that is actually being invoked. You do not need to be concerned with this level of detail at this point. Plug links are introduced in the section Plug Links; additional details can be found in sections 4.5 and 4.6 of the WSFL specification (see the References section).
Note that in the example the message types of the activity and the exported operation don't match. In this case, an additional map element is required to perform the message transformation. The full specification of the export element from the preceding example would then be as follows:
<export> <target portType="cellPhoneOrderHandler" operation="reject"/> <map sourceMessage="tns:Order" targetMessage="tns:RejectionNotice" sourcePart="data" sourceField="customerInfo" targetPart="customerData"/> </export>
Life Cycle Operations, Flow Input, and Output
Many instances of a given flow model can execute at the same time on the associated run-time infrastructure. In WSFL, a new instance of a flow is created with either the call or spawn operation.
Call is defined as a WSDL request-response operation where the input message is the input of the flow, and the output message is the result from the flow. Spawn is defined as a WSDL one-way operation where the input message is the input of the flow. Both operations can be exported (that is, exposed to clients) with a different name and signature.
Thus, the more precise definition of the sample flow model doesn't represent the AcquireCellPhone operation as the flow's first activity, but as an exported spawn operation, creating the flow instance and passing input data. It also introduces a node representing said flow input data (a flow source), replacing the previously present ReceiveOrder activity:
<flowModel name="cellPhoneOrder"> serviceProviderType="cellPhoneOrderProvider"> <flowSource name="ReceiveOrder"> <output name="orderData" message="tns:Order"/> </flowSource> <export lifecycleAction="spawn"> <target portType="cellPhoneOrderHandler" operation="acquireCellPhone"/> </export> <activity name="CheckCreditHistory"> ... </activity> ... <controlLink source="ReceiveOrder" target="CheckCreditHistory" transitionCondition="customer/id=null" /> ...
Additional life cycle operations are available:
A flow can be suspended through the use of the suspend operation. When suspended, the flow's execution is discontinued, but the flow still exists in its current state.
A suspended flow can be resumed through the use of the resume operation. Execution of the flow continues from the state where it was suspended.
The current state of a running flow can be queried through the use of the inquire operation. This enables a business partner to find out how far a given business process's execution (seen via its public flow interface, as described in the Public and Private Flows section) has already progressed.
A running flow can be terminated. This aborts the flow's execution at its current state.
All the life cycle operations are offered as WSDL operations taking a flow instance ID as a parameter. The operations can be exported via an export element, as in the example in the Exported Activities section.
You have seen that a flow utilizes operations provided by several other services as the implementation of its activities. Another way to put this is to say that a flow defines a composition of services by specifying a certain usage pattern of its service providers' functionality. That is, the flow combines the operations made available by its providers to achieve a given compositional goal. You have also seen that a flow model itself can implement a certain service provider typethat is, it can become a service itself, offering a set of WSDL interfaces to possible clients. In particular, the flow could become part of yet another service composition itself. The result is a mechanism by which services can be recursively composed following a flow composition model.
In general, this form of composition assumes a peer-to-peer interaction model between services, in which the flow can invoke and be invoked by other services. However, the compositional model provided by flows is a centralized model, in which the composition is fully orchestrated from a single control point: the executing flow engine. Fully distributed compositions of services are introduced in the section Plug Links.
A special case of flow composition is based on hierarchical interactions between services, as opposed to the more flexible peer-to-peer model. In this case, an entire flow model can provide the implementation of a single activity of another flowthat is, it becomes a sub-flow of the invoking flow. You can achieve this kind of composition by using the flow model's Call operation to implement the other flow's activity. Input data from the activity is then passed to the flowSource of the sub-flow, and data from the sub-flow's flowSink is passed back to become the output of the activity.
Public and Private Flows
The introduction to this chapter mentioned the important fact that flow languages are used both to provide users of the service with a description of its behavior, and to actually implement the service. When used to describe service behavior, the information provided is strictly limited to what partners need to know to successfully interact with the service; implementations, on the other hand, must contain all the details that would enable a compatible interpreter to execute the service on behalf of requesters.
This section briefly reviews these two requirements and shows how a service's public and private behavior are related in a typical case.
Defining Service Behavior
The behavioral description of a service is the first and most important task of a flow language in the Web services space. It is intended for the service customer and provides the information that enables the customer to effectively use the service and understand how its requests will be processed. In particular, this includes:
The correct order in which the operations of the service need to be invoked.
The alternative paths that the execution of the service may follow based on the received information.
The set of steps that represent the possible states in the execution of the service, which the requester could possibly need to query in the course of a long-running interaction (as when using a lifecycle inquire operation; see the section Life Cycle Operations, Flow Input, and Output).
The data flow between steps, as needed to support the flow of control.
All this information can be easily encoded through the use of the flow model described in previous sections. A service description based on a flow model, on the other hand, does not imply anything about the actual implementation of the service. Implementation independence is a foremost concern in designing the Web services framework. Many services are likely to be implemented using technologies that don't rely on flow models, such as traditional object oriented languages. Regardless of the implementation technology, these services require a standards-based description of their behavior to facilitate their use and integration with other services.
Implementing a Service as a Flow: Private Versus Public Flows
On the other hand, the language necessary to provide a behavioral description is fundamentally enough to describe the full execution of that behavior on a compatible interpreter such as a traditional workflow engine. You just need to make sure that the language allows the specification of additional implementation details, such as what program or service to invoke at each activity in the flow and a set of endpoint and quality-of-service properties (such as security, transactionality, timing, and so on.) You have already seen that the WSFL model enables you to do this within the same language that you use for public flows.
There are two advantages to having a single language for both purposes: First, a common language enables developers to easily transform existing business processes (which are commonly encoded as flows) into actual Web services; second, a flow language capable of encoding actual implementations defines a platform-independent, standard, portable execution language for Web services. This implementation language relies on Web service standards (basically, WSDL and related specifications) to provide implementation details, and can be executed on any vendor's standardized flow engine.
A flow that provides the service implementation is usually not suitable as a public description of service behavior; on one hand, it may reveal information private to the service provider or even trade secrets, whereas on the other hand, the actual implementation flow is usually too complex and cluttered with details that have no relevance to customers. To highlight this difference, it's common to refer to the actual implementation flow as the private flow of the service, and to its simplified public description as the public flow.
Defining a Public Flow
The difference between a private flow (one with complete implementation information) and a simplified (or customer-facing) public flow is thus fundamentally one of authoring style. Consider now the cell phone ordering service that has served as an example.
The original process was depicted in Figure 18.5. A simplified flow is shown in Figure 18.6. Here, only the steps that are relevant to the interaction with the ordering customer are shown. Note, for instance, that the steps of checking credit history or creating a new account are not present in the simplified flow. The service user needs to know only that his request will be either rejected (resulting in a rejection message being sent back to him), or accepted (resulting in his credit card being charged and a bill sent). Likewise, if you proceed downstream through the flow, you will see that the activity AssignPhoneNumber is missing in the public flow, which is explained by the fact that this operation has internal relevance to the cell phone company but not to the customer.
The simplification of the flow is not limited to revealing fewer internal steps to the customer. First, observe the modification of the control flow, as the double control dependencies at SendRejection and ChargeCreditCard are removed. Note also that the relationship established with a shipper service in activity ShipPhone is not reflected in the flow of Figure 18.6, because this is a private detail that the service does not want to reveal to customers or competitors. In terms of the definition of the corresponding activities, you would have in this case an activity without an implementation block.
Drawing from this example, the typical relationship between the private and the public flow can be summarized as follows:
Public flows have only a subset of all the activities present in the private flow. The activities present in the public flow are those needed to support the proper flow of control and to provide users with information on the execution state.
Public flows have simpler flow of data and control, but provide full information about the sequencing of service operations.
The implementation of certain activities is not exposed in public flows.
A private flow and its corresponding public version both support the same customer-facing interfaces. From the customer perspective, both flows show the same service provider type.
Regardless of the differences between a service's private and public flows, both should match the observable behavior of the service: The same external interactions with the customer need to be present in both representations, and the sequencing and control dependencies between them must be equivalent, in the sense that the two descriptions should accept as valid the same sequences of message exchanged between a client and the service. Making sure that this type of equivalence holds can be hard to show in the case of complex flows. The important point to keep in mind, however, is that from a service user's perspective, both flows should behave in exactly the same way.