A typical use of the flow language is to create a service chain. In a service chain, the flow service invokes an initial service with the inputs that were provided to the service. The outputs of this initial service are then passed as inputs to another service. This cascade of passing outputs to inputs proceeds until the last service in the chain has been executed. The outputs of this last service become the outputs of the flow service itself. One might use a service chain to drive a purchase. The first service might login to a site and establish an account ID. The next service might ask the site to return the product ID for a product having a particular name. The final service might order a product of the found ID and return an order number.
Another use of the flow language is to retry services that fail. One might create a flow service that attempts to execute another service. If the service fails with an error, the flow service might wait a few seconds and then try the service again. The flow service can specify the number of attempts it should make before giving up and failing itself. The retry service is especially useful with services that access web sites that may get too busy to handle a request.
Still another use of the flow language is to define a set of alternative services such that if any one service fails, another should be attempted. The flow service specifies the order in which to attempt the services. The flow service succeeds if any one of the service it contains succeeds, and it fails if all of the contained services fail. This kind of flow service is useful when each of the services is keyed to a specific condition, so that only the service that is appropriate for the condition executes. For example, services that bind an XML or an HTML document are keyed to a particular type of document and will fail to bind to a document of the wrong type. The flow language allows a service to attempt multiple bindings.
The flow language has many other uses too. One may use it to place timeouts on the durations of operations, to select a service to execute based on data values, and to perform a sequence of operations once for each value in a set of values. Of particular significance is the use of the flow language to transform data from one representation into another. Bridging between the data representations required by different applications is one of the more challenging hurdles of application-to-application integration. The flow language significantly eases this challenge by supporting name transformations, structural transformations, and value transformations.
Name transformations are the simplest type of transformations. Services may name the data values that transfer between them, and they may choose different names for the same data values. For example, one service might use the term "PurchaseDate" while another uses the term "DatePurchased". Name transformations rename data values as they pass between services so that the data values have the appropriate names when they arrive at a target service.
Services may also represent the same data values in different data structures. For example, one service might list associated data values (such as name, date, and address) in three separate unstructured arrays while another service represents the same information in a single array of structures. In the case of the array of structures, each structure would contain one tuple of the associated values (name, date, and address). A structural transformation could be applied to this example to translate the three arrays into the single array of structures or to translate the single array of structures into the three arrays. Structural transformations convert data between different but equivalent data structures.
Value transformations are the most open-ended kind of transformations. They are needed to convert between the different lexical formats in which services represent data. For example, one service may use the value "1" to represent the month of January while another uses the value "JAN" and still another uses the value "January". A flow service accomplishes value transformations by delegating responsibility to perform the transformation to integration module (IM) services. An IM service may in turn contain program code that performs the transformation or it may leverage from existing transformation services by itself delegating to a third party resource, such as an Internet web site. In this manner a flow service may use IM services to provide any kind of value transformation. Value transformations are useful for converting service input and output data into a common format so that multiple services or Internet sites may communicate.
Previous versions of B2B provided some of the flow language functionality through WIDL mappings. However, WIDL mapping services only made this functionality available to other WIDL mapping services. WIDL mappings were also unable to perform data transformations. The flow language replaces WIDL mappings. All of the functionality that WIDL mapping services were able to provide other WIDL mapping services are now available to all services, including IM services. Previous functionality is made available either through flow language facilities, such as the facility for chaining services, or through IM services that the B2B provides for flow services to call, such as an IM services that binds HTML or XML documents.
Term | Definition |
Flow Engine | The flow engine is the software component that is responsible for executing flow language expressions. |
Service | A service is an operation that resides in the B2B Integration Server namespace. Every service has a name, a set of input parameters, and a set of output parameters. A service may simply yield output parameters that are a function of its input parameters, or it may apply the input parameters to perform an action that affects the state of a system, or it may do both. There are only two kinds of services: IM services and flow services. |
Flow Service | A flow service is a service that is implemented in the flow language. The flow language is a language for implementing flow services, so every complete expression of the language is a flow service. In the XML representation, the FLOW element is the unit that implements a flow service. |
Flow | 'Flow' is a shorter and more hip name for flow service. |
Integration Module (IM) Service | An Integration Module (IM) service is a service that a B2B IM implements. An IM is a software module that implements a set of services in any of a variety of programming languages. |
Service Signature | A service signature is an abstract definition of the input and output parameters of a service. Service signatures each have a name that uniquely identifies the service within the B2B namespace. Each also lists the names and types of each input and output parameter. For any service, it is possible to define a service signature that describes the service, in which case the service is said to 'have' that service signature. |
Field | A field is a name/value pair; it is a name that has been paired with a value. The name is a string of characters, and the value is any kind of programming language object. Every field also has an associated type that indicates what kinds of values are valid for the field. |
Pipeline | A pipeline is a set of fields. The fields are unordered within the set. The name of a field uniquely identifies the field within the set -- no two fields will ever have the same name. A flow service receives its inputs via a pipeline, stores transient state information in the pipeline while executing the service, and returns its outputs via the same pipeline. |
Flow Operation | A flow operation is an operation that the flow language defines. Every flow operation operates on a pipeline. The operation may modify the pipeline and it may perform an action based on the values in the pipeline. |
Container Operation | A container operation is a flow operation that contains other flow operations. These other operations are known as the child operations of the container. The conditions under which the container executes its child operations vary with the type of container. |
Flow Element | A flow element is an XML element type that represents a particular type of flow operation. |
Sequence | A sequence is a type of container operation. The child operations of a sequence are ordered. When a sequence is invoked, the sequence sequentially executes its children against the pipeline. Sequence operations may be configured to terminate prematurely when a child operation fails or when a child operation succeeds, and it may be configured to attempt all operations regardless of failures. |
Map | A map is a flow operation that applies simple transformations to the fields of a pipeline. A map may rename fields, move fields, remove fields, add fields, or assign the values of fields that are already present. Maps are typically used to prepare the pipeline for use by a subsequent operation, such as to rename fields to names required by the operation, or to clean up the pipeline after a preceding operation, such as to remove fields that the operation added but which are not needed. |
The flow language typically groups flow operations into sequences. A
sequence is a set of flow operations that sequentially operate on a
pipeline. Each operation may modify the pipeline or perform an action
based on the fields in the pipeline. Together the operations of the
sequence accomplish a task. For example, the first operation might load an
XML document into the pipeline. The second operation might extract data
from the document and add the data to the pipeline. The last operation
might transform the data into the required format. Figure 1 depicts the
basic architecture of a sequence.
Figure 1. Architecture of a Sequence
Every flow service is a sequence of flow operations. A flow operation
may itself be a sequence, and hence may contain other flow operations. Not
all operations that contain other operations are sequences. For example,
the branch operation is not a sequence. The class of flow operations
that may contain other flow operations are known as container operations.
Since flow operations may contain other flow operations, one may represent a
flow service as a tree of flow operations. This architecture allows the
flow designer to put any flow operation under the influence of any other flow
operation, thus maximizing the diversity of solutions available. Figure 2
illustrates this compositional property of flow operations.
Figure 2. Composition of Flow Operations
The flow service is the most granular structure that the flow language defines. A flow service is accessible via the B2B namespace. Except as constrained by access controls, one may invoke a flow service by issuing an HTTP request to B2B, by invoking the service from within an IM service, or by invoking the service from within another flow service. When a flow service invokes another flow service, the calling service hands its pipeline to the other service and trusts the other service not to overwrite any intermediate values that the caller has in the pipeline. To prevent such conflicts, each flow service creates its own pipeline from the the input pipeline by copying the input pipeline.
A flow service is expressed in XML as a single XML document. The document uses the hierarchical relationships available to XML elements to model the hierarchy of flow operations. The different flow operations are each assigned an element type name, and the properties of a flow operation are specified via attributes place on its representing XML element. Elements that represent flow operations are known as flow elements. Additional element types may be defined to provide the details of a flow operation.
This specification uses a common tabular format to define each of the flow
language element types. Each table takes the following form:
Element Type Name |
| ||||
Element Attributes |
| ||||
Valid Child Elements |
|
In this table, the title "<ELEMENT>" is a placeholder for the element type name of the element that the table defines. An element may have zero or more attributes defined for it, and each will appear in the the "Element Attributes" row. The "Valid Child Elements" row lists the different types of elements that may occur in the content of the element, if any are allowed. Most flow elements may contain other flow elements, and most of these specify that the child elements are implicitly contained within an operation referred to as the "default sequence operation." The behavior of such an element is equivalent to the behavior that results from further nesting all of the children in a SEQUENCE element that uses only its default attribute values.
Any flow element may contain a COMMENT element, but it may not contain more
than one COMMENT element. One may use the content of this element to
annotate a flow service. The annotation is intended to describe the
flow element to which it belongs. In this manner, every flow element
of a flow service may have an attendant annotation. The COMMENT element is
defined simply as described in the following table:
Element Type Name |
| |
Element Attributes |
| |
Valid Child Elements |
|
The following table provides a brief description of all of the different
element types that the flow language defines:
FLOW | Element that represents the entire flow service. Root element of the entire XML document. |
SEQUENCE | Flow operation that sequentially executes a set of child flow operations. |
MAP | Flow operation containing map rules that perform name and structure transformations on the pipeline. |
BRANCH | Flow operation that selects a child flow operation to perform based on a value in the pipeline. |
RETRY | Flow operation that retries a sequence of child operations until a retry condition is met. |
LOOP | Flow operation that iterates over a set of values and that may optionally produce another set of values. |
INVOKE | Flow operation that invokes an IM service or another flow service. |
COPY | Map rule that copies a pipeline field to another field. |
MOVE | Map rule that moves a pipeline field from one place to another. |
DELETE | Map rule that deletes a pipeline field. |
SET | Map rule that sets the value of a pipeline field. |
COMMENT | Element that annotates its containing element. |
See section 8. Example Flow Service for a flow service that exemplifies many of the above elements.
To execute a flow service the flow engine hands the service a pipeline that contains all of the service's input parameters. This pipeline is known as the input pipeline. If the flow service is being called by another flow service, the input pipeline will be the pipeline of the other service. The pipeline will contain all of the state information of the other service. For example, the input pipeline might contain output values from several of the caller's preceding flow operations. The caller trusts that the service it is calling will not modify this state information.
A flow service helps guarantee the integrity of the input pipeline by creating a shallow copy of the input pipeline and using the copy as the service's pipeline. The term "shallow copy" indicates that the values within the copied pipeline are not themselves copies; instead values are shared between the two pipelines. For example, if the original pipeline contains a field named "doc" whose value is a particular instance of a document, then the copied pipeline will contain a field named "doc" whose value is the same document instance. Modifying the document instance from one pipeline results in modifications to the document instance available through the other pipeline. However, the pipelines themselves remain different instances, so fields may be added to or removed from one pipeline without affecting the other pipeline. Likewise, the value of a field in one pipeline may be replaced with a different value without affecting the value of the namesake field in the other pipeline. The shallow copy therefore scopes the pipeline to a particular service or operation.
After creating a copy of the input pipeline, the flow service is prepared to execute operations against the new pipeline. All of the flow operations within a flow service share this one pipeline. Many flow operations, including calls to other services, require input parameters. Map operations are able to add fields to the pipeline and to set the values of fields that are already in the pipeline. Data may also be put in the pipeline by invoking other services. After invoking a service, the flow adds the outputs of the service to the pipeline.
When a flow service invokes another service it passes its pipeline to the
other service. Let's refer to this pipeline as the caller's
pipeline. The called service may return an output pipeline when it
completes, though it may also return nothing. The following table
describes how the caller updates its pipeline as a function of what the invoked
service returns:
What the Invoked Service Returns | How the Caller Updates Its Pipeline |
Caller's Pipeline | The caller does nothing. If the invoked service provides outputs, it did so by placing them in the caller's pipeline. |
New Pipeline | For each field in the output pipeline, the caller adds the field to the caller's pipeline. Whenever the output pipeline and the caller's pipeline have a commonly named field, the field in the output pipeline replaces the field in the caller's pipeline. |
Nothing or Null | The caller does nothing. Technically the caller should have no outputs, but it may have put outputs in the caller's pipeline. |
A flow operation that invokes another service trusts the other service to assign only fields that are officially outputs of the service. If the service has a service signature, the service signature defines the official outputs. Otherwise the official outputs are defined in whatever public documentation is available for the service. This approach maximizes the flexibility and the efficiency of flow services. Flow services are only capable of returning new pipelines, but IM services are capable of returning any of the above. Hence, it is possible to write IM services that introspect the state of a flow service, and it is possible to write IM services that are very fast because they do not copy the input pipeline. IM services in general do not need to copy the input pipeline, since they can maintain internal state by other means.
Consider what happens when one flow service adds a field to the pipeline and then calls another flow service. Suppose the first service needs the value of this field sometime after calling the second service. If the second service overwrites this value and returns the same field, then according to the above rules the value is put in the first flow service's pipeline, thus overwriting the first service's value for this field. When the first service proceeds and uses the field's value, the service uses the wrong value: it uses the value supplied by the second flow service rather than the value originally assigned by the first flow service. Were the second service to return only fields that are publicly documented outputs of the service, the designer of the first service could take precautions to ensure that name collisions do not arise, perhaps by using map operations.
The easiest way to ensure that a flow service preserves the integrity of the input pipeline is to assign a service signature to the service and to set the CLEAN-UP attribute of the service to "True" value. Services are created this way by default from within the Developer. This configures the service to automatically remove all fields from the pipeline that are not explicitly identified as output parameters in the service signature. Otherwise, one may design a flow service that preserves pipeline integrity naturally or one may add a map operation to the end of the flow service to remove unwanted output parameters.
Normally, when a child flow operation fails, the flow operation that contains the child also fails. In this way, an error may bubble up the hierarchy of flow operations and cause the entire flow service to fail. As the error bubbles up, the pipeline remains in the state it had at the time the error occurred. Because complex flow operations may be built from simpler flow operations, when an error occurs in an operation, there may be little certainty about the contents of the pipeline. Hence, unless the service is simple and carefully designed, no statement can be made about outputs of a failed flow service.
However, certain flow operations may catch an error reported by a child flow operation. The operation may simply ignore the error or it may perform some action in response to the error. For example, when the child of a RETRY operation fails, the RETRY operation may ignore the error and then attempt to perform all of its child operations again. A SEQUENCE operation with an EXIT-ON value of "Success" ignores failed child operations before returning upon successfully executing a child operation. When such operations proceed despite a failure, they first restore the pipeline to a known state. The RETRY operation restores the pipeline to the state it had when the RETRY operation was initially executed. In the case of a SEQUENCE operation having an EXIT-ON attribute value of "Done", failed child operations are ignored, and after each failed operation the pipeline is restored to the state it had prior to executing the most recently failed child operation.
The restoration process guarantees that the Values object representing the pipeline has exactly the same fields it had in the prior state, and it guarantees that these fields contain the same value instances. However, it does not restore the state of the value instances themselves. For example, suppose a pipeline originally contains a field 'X' that holds a Values object, and suppose a field 'Y' in the 'X' Values object is subsequently given a new value. When the pipeline is restored to its original state, the field 'X' is guaranteed to be there and is guaranteed to contain the same Values object, but the Values object will still reflect all subsequent changes, and 'Y' will still have the new value rather than its original value.
The process of restoring a pipeline to a previous state may be described as follows:
On some occasions a flow operation that fails may want to communicate information to a containing flow operation without having pipeline restoration remove the information. For example, an operation may attempt to access a web page and find that the web page actually received is not the desired web page. However, the actual web page received contains a message that the nested operation would like to preserve. The nested operation may accomplish this task by storing the message internally within the value of a pipeline field that pipeline restoration will not delete or replace. Since pipeline restoration does not restore field internals to their previous state, the message remains available after restoration occurs. The nested operation might store the message away, if it is present, prior to executing the operation that may fail.
When a flow service is invoked it is handed a pipeline that contains the
service's input parameters. This pipeline is known as the input
pipeline. The flow service creates its own local pipeline by creating a
shallow copy of the input pipeline. The operations contained in the flow
service then execute against this pipeline. When the service finishes
executing, and the service is configured to perform a clean-up of the output
parameters, the service removes unwanted output parameters from the
pipeline. The clean-up removes all parameters from the pipeline that
are not declared in the service signature. Finally, the service completes,
returning its output parameters in the pipeline. If an immediate
child operation of the flow service fails, the service itself fails. See
the sections 3.
Language Structure and 4.
The Pipeline for more information.
Figure 3. The Flow Service
Element Type Name |
| ||||||||
Element Attributes |
| ||||||||
Valid Child Elements |
|
If any child operation fails, the sequence restores the pipeline to the set
of fields it had prior to executing the operation, so that only the successful
operations produce lasting changes on the pipeline. However, there is one
exception to this rule. The pipeline is not restored if the failure
of a child operation causes the entire sequence to fail. A sequence that
exists on either the success or failure of a child operation succeeds if the
last child operation it executes succeeds, and the sequence fails
otherwise. A sequence that ignores all child successes and failures always
itself succeeds.
Figure 4. The Sequence Operation
Element Type Name |
| ||||||
Element Attributes |
| ||||||
Valid Child Elements |
|
Element Type Name |
| ||||||||||
Element Attributes |
| ||||||||||
Valid Child Elements |
|
For example, if a pipeline contained a Values object in a field named 'X', and if this Values object contained yet another Values object in a field named 'Y', and if the Values object in 'Y' contained a field named 'Z', then the path expression identifying the value of field X would be "X" and the path expression identifying the value of field Z would be "X/Y/Z".
The following tables define the different map rules:
Element Type Name |
| ||||
Element Attributes |
| ||||
Valid Child Elements |
|
Element Type Name |
| ||||
Element Attributes |
| ||||
Valid Child Elements |
|
Element Type Name |
| ||
Element Attributes |
| ||
Valid Child Elements |
|
Element Type Name |
| ||||||||
Element Attributes |
| ||||||||
Valid Child Elements |
|
The constraints are expressed in terms of the field to which a map rule assigns a value. The field is known as the assigned field. The COPY, MOVE, and SET rules have assigned fields, but the DELETE rule does not. In the path expression that identifies the assigned field, the assigned field is the field whose name occurs last in the expression. The constraints on map rules follow:
The path dimension of a field is expressed in terms of the field's primitive type. The path dimension is the number of arrays to which the primitive type belongs in the path through the pipeline to the field. A field's value has the same path dimension as the field itself. A value does not initially belong to a field if it is provided via the SET rule. In this case, the path dimension of the value is the same as the number of dimensions that the value has. The path dimension of a single string is 0, that of a string array is 1, and that of a string table is 2. The following examples help to further clarify path dimensions:
When the path dimensions of the value and the assigned field disagree, the
behavior that occurs depends on which path dimension is higher. The
different scenarios and their behavior are described as follows:
Scenario | Behavior |
The assigned field has the same path dimension as the value being assigned. | The members of the value being assigned are copied into the assigned field so that the array indexes that identify the member remain unchanged for corresponding dimensions. For example, if each string in a string array is copied into a field in an array of Values objects, the index of the string in the array is the index of the Values object whose field contains the same string. Indexes will correspond for higher dimensions as well. |
The assigned field has a higher path dimension than the value being assigned, and the assigned field does not already have a value. | The assigned field is created so that the appropriate dimension contains exactly one instance of the value. For example, if a string is assigned to a field of type string array, the value of the field becomes a string array whose only member is the provided string. If the assigned field already has some of the outer dimensions created even though the assigned field itself does not exist, the outer dimensions must still agree in size with the outer dimensions of the value, but the map rule will create only the inner dimensions. |
The assigned field has the higher path dimension than the value being assigned, and the assigned field already has a value. | The entire assigned field is overwritten with multiple copies of the provided value. For example, if a string is assigned to a field that already contains a string array, each member of the string array is overwritten with the provided string array, so that the string array ends up containing only copies of the provided string. |
The assigned field has a lower path dimension than the value being assigned. | The value may be thought of as a collection of structures whose dimensions each equal the path dimension of the assigned field. The assigned field is assigned from the first member of this collection. For example, if a string array is assigned to a string field, the field is set to the value of the first string in the string array. |
The behaviors described in this table are well-defined provided that when members of a single- or multi-dimensional value are copied into a field that already contains a value, the size of any dimension in the copied value must have the same size as the corresponding dimension in the field's existing value. For example, if one has an array of 30 members and the other has an array of 29 members, the 30th member of the first array cannot be copied, so the operation will fail. Likewise, if the copy occurs in the reverse direction, the 30th member cannot be overwritten, so the value will be left in a partially defined state. The copy in the reverse direction will therefore also fail. The last of the above constraints enforces this rule.
<MAP> <DELETE FIELD="Price"/> <COPY FROM="Price" TO="Amount"/> </MAP> |
<MAP> <DELETE FIELD="Price"/> <MOVE FROM="Price" TO="Amount"/> </MAP> |
<MAP> <SET FIELD="Product" ENCODING="80/20"> <RECORD> <FIELD NAME="Model">321-AB</FIELD> <FIELD NAME="Price">123.45</FIELD> </RECORD> </SET> <COPY FROM="Product/Price" TO="Amount"/> </MAP> |
<MAP> <COPY FROM="Amount" TO="Price"/> <COPY FROM="Price" TO="Amount"/> </MAP> |
<MAP> <DELETE FIELD="Amount"/> <MOVE FROM="Amount" TO="Price"/> <MOVE FROM="Amount" TO="Total"/> </MAP> |
For example, suppose a pipeline contains exactly one field, a field 'X' whose value is a Values object, and suppose this Values object contains some set of fields, none of which is named 'Y'. The path expression "Z" names no existing field, so were a COPY, a MOVE, or a SET rule to assign a value to "Z", the 'Z' field would be created in the pipeline and assigned the given value. Likewise, the path expression "X/Z" names no existing field, so assigning a value to "X/Z" results in a field named 'Z' being created in the existing 'X' Values object and being assigned the value given by the map rule. Also, the path expression "Q/R" names no existing field, so a Values object named 'Q' would be created in the pipeline, and within it a field named 'R' would be assigned the value that the map rule specifies. The path expression "X/Q/R" would have a similar effect, since a Values object named 'Q' would be created in the existing 'X' field. Finally, consider the expressions "Q/R/S/T" and "X/Q/R/S/T". Both of these expressions result in the creation of multiple nested Values object fields.
The creation process is intelligent about when to create arrays. The necessary intelligence derives from the service signatures describing the services that the flow invokes. If a single string is assigned to a string array, the map creates an array of strings that contains only the single string. Suppose the pipeline contains only one field, where the field has name 'S' and the field contains a string array. If a map rule copies 'S' to the non-existent field given by the path expression "X/Y", the map uses information derived from the service signatures to determine which of 'X' and 'Y' is the array. If 'X' is the array, the map creates an array of Values objects in 'X', where each Values object contains a string field named 'Y'. If 'Y' is the array, the map creates a single Values object in 'X' and copies 'S' to 'Y'.
Each child of branch has an associated instance name. The branch operation executes the child operation whose instance name is the desired instance name. If no child has the name but the branch contains an unnamed child operation, then the branch executes the unnamed child operation. If no child has the name and the branch does not contain an unnamed child, the branch operation fails with no change to the pipeline.
If any child operation fails, the pipeline is left in its partially completed
state and the branch operation itself fails. The branch operation also
fails if the pipeline does not contain the switch field.
Figure 5. The Branch Operation
Element Type Name |
| ||||||
Element Attributes |
| ||||||
Valid Child Elements |
|
The desired outcome of the sequence is known as the retry condition. The retry condition is either success or failure. If any operation in the sequence fails, the pipeline is left in its partially completed condition and the sequence fails. The sequence succeeds only when all of the children of the sequence succeed. The retry operation will repeatedly execute the sequence for as long as the sequence satisfies the retry condition, pausing a given number of milliseconds between attempts. Prior to attempting each execution of the sequence, the operation restores the pipeline to the state it had before the first attempt.
The retry operation also has an associated count that signifies the maximum
number of attempts the operation should perform. If the sequence satisfies
the retry condition a number of times equal to this count, the retry operation
itself fails, leaving the pipeline in its partially completed state.
Figure 6. The Retry Operation
Element Type Name |
| ||||||||||
Element Attributes |
| ||||||||||
Valid Child Elements |
|
Attributes of the loop operation name the input array and the output array. The output array attribute is optional and may be excluded to have the operation loop over the input array without aggregating results into an output array. The operation collapses one dimension of the input array so that for each execution of the loop sequence, the value of the field that once contained the input array actually contains one member of the input array. This member is the member associated with the particular iteration of the loop. The sequence operates on this field. If the loop is to collect values into an output array, each iteration of the sequence concludes by putting a member of the output array in the field named by the output array attribute. Not every iteration need generate an output value. To indicate that an iteration has no value for the output array, the iteration simply does not assign a value to the output array field.
Upon completing all iterations of the loop, the pipeline contains the following fields:
Consider the example LOOP operation shown in section 8. Example Flow Service. The loop inputs an array of part descriptions named "PartList" and outputs an array of confirmations named "Confirms". The part descriptions are represented as an array of Values objects, where each Values object contains a "ProductID" and a "Quantity" field. The confirmations are represented as an array of Values objects, where each Values object contains a "Vendor", an "OrderNumber" and a "ShipDate" field. On a given iteration of the loop sequence, the value of the "PartList" field actually assumes the value of one of the members of the part descriptions array. Rather than being an array of Values objects, as it is outside the loop, inside the loop "PartList" is a single Values object. This object contains the "ProductID" and the "Quantity" field of one member of the original array. The sequence creates a Values object and stores it in the "Confirms" field, setting its "Vendor", "OrderNumber", and "ShipDate" fields as appropriate for the given part. Each sequence produces a different "Confirms" Values object. As the loop executes, it collects all these output Values objects. When the loop completes, it sets the "Confirms" field to an array containing all of the Values objects produced during the loop and restores "PartList" to its original array value.
The loop operation will fail if the pipeline does not contain the input array
at the time the loop begins execution. The loop operation also fails when
a child operation of the loop fails during any iteration. Regardless of
whether the loop operation succeeds or fails, the loop restores the
dimensionality of the input array before terminating. If it was generating
an output array, prior to terminating it also expands the dimensionality of the
output array and includes in the output array all output values produced by
successful iterations of the loop.
Figure 7. The Loop Operation
Element Type Name |
| ||||||||
Element Attributes |
| ||||||||
Valid Child Elements |
|
When the called service completes it returns an output pipeline.
If this output pipeline is identical to the flow service's pipeline, the flow
service has nothing to do. However, if the output pipeline is not the flow
service's pipeline, the invoke operation copies the fields from the output
pipeline to the service's pipeline, overwriting any fields that are identically
named. Section 4.
The Pipeline provides more information about pipeline management during
service invocation.
Figure 8. The Invoke Operation
Element Type Name |
| ||||||
Element Attributes |
| ||||||
Valid Child Elements |
|
Service |
| ||||||||||||||||
Description | The PurchasePCParts service allows a client to purchase multiple computer parts from multiple suppliers. The service buys each part from a supplier and prioritizes the suppliers. If a supplier does not have a particular part, the service attempts to get the part from the next supplier in priority order. In this way, the service orders each part from whichever vendor has the part. The service returns confirmation information for each part ordered. | ||||||||||||||||
Inputs |
| ||||||||||||||||
Outputs |
|
<FLOW NAME="PurchasePCParts"> <COMMENT>Example assumes suppliers all use same PIDs. First we'll get a list of all the PIDs from some available catalog. Then we'll add the PIDs to the input PartList. Last, we iterate over the parts in PartList to order each part and keep a record of the order confirmations.</COMMENT> <MAP> <COMMENT>We'll be calling the service GetPIDsFromModels, which requires the name of the catalog for which the model names are valid along with an array of model names to look up. Here we extract the model names from the PartList array and create a new string array ModelList to hold them.</COMMENT> <SET FIELD="CatalogName" ENCODING="80/20"> <STRING>Computer Hardware</STRING> </SET> <MOVE FROM="PartList/Model" TO="ModelList"/> </MAP> <INVOKE SERVICE="Catalog:GetPIDsFromModels"/> <MAP> <COMMENT>GetPIDsFromModels returned a string array PIDList of product IDs, one ID per model number. Here we pull the PIDs out of the string array and put them in the PartList array to complete our product descriptions. Also, assume we have to rename Purchaser to the name Customer that the supplier services required.</COMMENT> <MOVE FROM="PIDList" TO="PartList/ProductID"/> <MOVE FROM="Purchaser" TO="Customer"/> </MAP> <LOOP IN-ARRAY="PartList" OUT-ARRAY="Confirms"> <COMMENT>Loop over the parts in PartList. For each iteration of the loop, the PartList field is converted to a member of the original PartList. A given iteration therefore operates on only one member of the list. At the end of an iteration, the Confirms field has a member to add to the result list. When the loop exists, it will put an array called Confirms in the pipeline to contain all the individual Confirms. </COMMENT> <MAP> <COMMENT>The OrderPart service takes a PID and a Qty field, so we need to get them from the current part.</COMMENT> <COPY FROM="PartList/ProductID" TO="PID"/> <COPY FROM="PartList/Quantity" TO="Qty"/> </MAP> <SEQUENCE EXIT-ON="SUCCESS"> <COMMENT>This operation attempts to execute each child operation in turn. When one succeeds, the operation terminates successfully. We're trying each supplier in turn until we find one that has the part. If none has the part, a MAP sets VendorName to indicate this. Since this MAP will always succeed when executed, the sequence will never fail.</COMMENT> <INVOKE SERVICE="SupplierA:OrderPart"/> <INVOKE SERVICE="SupplierB:OrderPart"/> <INVOKE SERVICE="SupplierC:OrderPart"/> <INVOKE SERVICE="SupplierD:OrderPart"/> <MAP> <SET FIELD="VendorName" ENCODING="80/20"> <STRING>*NONE*</STRING> </SET> </MAP> </SEQUENCE> <MAP> <COMMENT>We now have confirmation information for the current part. Store it away in the field that the LOOP operation will collect into the output array.</COMMENT> <MOVE FROM="VendorName" TO="Confirms/Vendor"/> <MOVE FROM="OrderNumber" TO="Confirms/OrderNumber"/> <MOVE FROM="ShipmentDate" TO="Confirms/ShipDate"/> </MAP> </LOOP> </FLOW> |