B2B Flow Language

(Version 3.0, Revision C)

 

Overview

The B2B flow language is a language for configuring the flow of data among services on the webMethods Business-to-Business Integration Server (B2B).  A service is an operation that takes a set of inputs and yields a set of outputs.  The flow language connects services together by identifying which outputs go to which inputs, manages the execution of services through conditional processing and error handling, and provides the infrastructure necessary for arbitrary data transformation.   An expression of the language defines a single service that integrates the behavior of other services; it is an integration language.  Just as the relatively simple logic of a printed circuit board integrates a collection of sophisticated chips, the flow language provides the simple logic necessary to integrate a collection of sophisticated business services.  The language is suitable for expression in a variety of representations, including graphical representations.   This specification defines the processing model for the flow language along with the simple XML representation that B2B uses for the language.

Table of Contents

1. Role of the Language

The flow language is a language for creating integration services on the webMethods Business-to-Business Integration Server (B2B).   Release 3.0 of B2B supports two kinds of services: flow services and Integration Module (IM) services.  Flow services integrate multiple services -- IM services and other flow services -- into a single service.  A flow service accomplishes this by defining how data flows among the services and by managing this flow of data.

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.

2. Terminology

This section defines many of the terms found in this specification.  The terms are defined in an order that allows definitions to build on previous definitions, so that a reader may gain a significant understanding of the flow language just by reading the definitions.  The section also defines some terms that appear elsewhere in webMethods literature.  These are the terms that take on a new or refined meaning as a consequence of introducing the flow language.
 
  
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.

3. Language Structure

The flow language is a language for building flow services.  Its fundamental unit is the flow operation.  A flow operation is an operation that acts on a pipeline, where a pipeline is a collection of name/value pairs known as fields.  The operation may change the pipeline or it may perform an action as a function of the names and values in the pipeline.  The operation may also invoke other flow operations, controlling the order of execution of the other operations or the conditions under which they execute.   The flow language provides only a few types of flow operations and relegates all other functionality to integration module (IM) services.  The few that it provides are those that are necessary for integrating services together.

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.
 


pipeline-architecture.gif (3694 bytes)

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.
 


containment.gif (7679 bytes)

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>
Element Attributes
attribute-name-1 Description of the attribute, including whether it is optional or mandatory on any given instance of the element.
...  
Valid Child Elements
element-type-name-1 Description of the element type, including the number of instances of the element that are allowed.
...  

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
<COMMENT>
Element Attributes
None
Valid Child Elements
None; a comment may only contain text.

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.

4. The Pipeline

A pipeline is an unordered set of name/value pairs.  Each name/value pair is called a field.  One familiar with developing B2B integration modules (IMs) will recognize that a pipeline is a Values object.  The name of a field is a character string.  A name should not contain the forward slash ('/'), since this character is used to delimit field names in the path expressions that map rules use.  The value of a field is any type of programming language object.  A value may even itself be a Values object.  A pipeline contains all the application-specific state of a flow service; it is the sole repository for information that the flow service must maintain to accomplish the service's unique function.  Consequently, the pipeline serves as both a scratchpad for the service and as the means by which parameters are passed to and received from other services that the flow service invokes.

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.

5. Error Handling

A flow service fails when a flow operation it contains fails.  A flow operation fails when the operation encounters an error condition.  For example, the INVOKE operation fails when the flow service or IM service it is calling fails.  A SEQUENCE operation fails if the last operation it attempts to execute fails.  A SEQUENCE operation with an EXIT-ON value of "Failure" will fail upon executing a child operation that fails, and a SEQUENCE operation with an EXIT-ON of "Success" will only fail if the last operation of the sequence fails.  A RETRY operation will fail if it exceeds the maximum number of retry attempts.  Other conditions exist as well that will cause a flow operation to fail.

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:

In particular, when the internal values of a pipeline field are changed, pipeline restoration will not undo the changes.

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.

6. The Flow Service

A flow service is a service that is implemented in the flow language.  It is the container for all flow operations of the service.  Every flow service has a name that other software entities use to identify the service within the B2B namespace.  A flow service may also have a service signature that explicitly defines the inputs and outputs of the service.  When a flow service has a signature, it is the B2B server that associates the service with its signature.

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.
 


flow-service.gif (6880 bytes)

Figure 3.  The Flow Service

 
Element Type Name
<FLOW>
Element Attributes
NAME Unqualified name of the flow service.  Mandatory.  This is the name that the service will assume within the B2B namespace.
VERSION Flow language version.  Mandatory.  Version of the flow language to which the service conforms.  Must be "3.0".
CLEAN-UP Whether to clean-up the pipeline before exiting.  Optional.   Valid values are "True" and "False".  If the value is "True" and the flow service has a service signature, then upon completing the execution of the flow service the service removes all fields from the pipeline except for the fields that the service signature indicates are outputs of the service.  Otherwise, the service returns all of the fields found in its pipeline upon completing execution.  When the attribute is not present, the value defaults to "True".
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the operation to terminate before attempting to abort the operation.  The operation will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
Any flow element other than FLOW Child operation.  A flow service must contain one or more flow elements.  The flow service performs its child operations as if they were contained in the default sequence operation.

7. Flow Operations

A flow operation is an operation that the flow language defines.  Every flow operation operates on the pipeline of its containing flow service.  The operation may modify the pipeline and it may perform an action based on the values in the pipeline. This section documents the behavior and the XML syntax of the different flow operations.

7.1. Sequence Operation

A sequence operation sequentially executes its child operations.  The sequence is the fundamental operation of the flow language since many of the other operations behave as if their child operations were contained in a sequence.  The sequence operation successively applies each of its child operations to the flow's pipeline.  A sequence may be configured to terminate when a child operation fails, when a child operation succeeds, or when all child operations have been executed, regardless of whether any succeeded or failed.   The default behavior is to exit prematurely when a child operation fails, and such a sequence is known as the "default sequence operation."

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.
 


sequence-operation.gif (6762 bytes)

Figure 4.  The Sequence Operation

 
Element Type Name
<SEQUENCE>
Element Attributes
NAME Instance name.  Optional.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
EXIT-ON Condition under which to exit prematurely.  Optional.  Valid values are "Failure", "Success", and "Done".  A value of "Failure" indicates that child operations are to be executed sequentially until either one fails or all are executed.  This is the most common use of SEQUENCE and is the default behavior when the EXIT-ON attribute is absent.   "Success" indicates that child operations are to be executed sequentially for as long as they fail.  The first successfully executing child operation successfully concludes the sequence.  "Done" indicates that all child operations are to be executed, regardless of whether they succeed.
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the operation to terminate before attempting to abort the operation.  The operation will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
Any flow element other than FLOW Child operation.  A sequence must contain one or more flow elements.  A sequence sequentially executes each of its child operations, executing them according to their order of occurrence in the XML document.

7.2. Map Operation

The map operation defines a transformation on a pipeline.  A map allows one to copy fields, move/rename fields, delete fields, and set field values.  It also allows one to merge multiple array fields into a single array or split an array field into multiple arrays.  Maps are generally used to prepare the fields of a pipeline so that they contain the required inputs for a subsequent flow operation.  Each map consists of a set of map rules, where each map rule describes a particular transformation to apply to the pipeline.  If a map rule specifies a transformation that cannot be applied to the pipeline, the map rule is ignored and no error condition is generated; if such a map rule must be executed to yield proper flow behavior, it is assumed that a subsequent flow operation will detect the invalid pipeline and signal an error.  Map rules are therefore only applied when they pertain to a given pipeline.
 
  
Element Type Name
<MAP>
Element Attributes
NAME Instance name.  Optional.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
ADORNS Adornment disposition.  Optional.  Maps may be designed so that they are tightly coupled with a preceding or following flow operation.  A map might be designed specifically to provide inputs for the following operation, or it might be designed specifically to clean up after a preceding operation.  This attribute allows a map to suggest to flow builder tools that the map should be displayed as adorning a neighboring flow operation.  The valid values of this attribute are "Next", "Previous", and "Standalone".  When the attribute is not present, a value of "Standalone" is assumed.  The value of this attribute has no affect on the run-time behavior of a flow service.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
COPY Copy a field from one location to another.  Any number of these elements may appear.
MOVE Move a field from one location to another.  Also used to rename a field.  Any number of these elements may appear.
DELETE Delete a field.  Any number of these elements may appear.
SET Set a field to the provided value.  May be used to either override a value already in the pipeline or to provide a default value for a field not yet having a value.  Any number of these elements may appear.

7.2.1. Map Rules

A map rule is a simple operation on a field of the pipeline.  The field may occur in any Values object found within the pipeline, including the pipeline itself.  Every map rule uses a path expression to name this field.  Path expressions are similar to file system paths, where fields containing Values objects are analogous to directories (or folders) and where all other fields are analogous to files.  A path expression naming a given field is a string consisting of the concatenation of the names of all fields that contain the given field, ending with the name of the field itself.  Moreover, a forward slash ('/') delimits every two names of the expression.  A slash may precede the entire expression without changing the meaning of the expression, but the expression "/", consisting of only the forward slash, is not a valid expression; the pipeline itself is not addressable.  Path expressions cannot address fields whose names contain a forward slash.

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
<COPY>
Element Attributes
FROM Field to copy from.  Mandatory.  This is a path expression that identifies a field of the pipeline that is to be copied. If the named field is not present in the pipeline, the map rule is ignored.  This field is copied to the field named by the TO attribute.
TO Field to copy to (assigned field).  Mandatory.  This is a path expression that identifies the field to which is copied the field named by the FROM attribute.  If the field does not exist, it is created.  If the field already exists, its value is overwritten with the value given by this map rule.
Valid Child Elements
None
 
Element Type Name
<MOVE>
Element Attributes
FROM Field to move.  Mandatory.  This is a path expression that identifies a field of the pipeline that is to be moved. If the named field is not present in the pipeline, the map rule is ignored.  This field is be copied to the field named by the TO attribute in the manner of the COPY rule and is deleted afterwards.  A field is renamed by moving it to a new field within the same Values object.
TO Field to move to (assigned field).  Mandatory.  This is a path expression that identifies the field to which is copied the field named by the FROM attribute.  If the field does not exist, it is created.
Valid Child Elements
None
 
Element Type Name
<DELETE>
Element Attributes
FIELD Field to delete.  Mandatory.  This is a path expression that identifies a field of the pipeline that is to be deleted. If the named field is not present in the pipeline, the map rule is ignored.
Valid Child Elements
None
 
Element Type Name
<SET>
Element Attributes
FIELD Field to set (assigned field).  Mandatory.  This is a path expression that identifies a field of the pipeline whose value is to be set. If the named field is not already present in the pipeline, it will be created.  The value assigned to this field is given by the content of the SET element.
ENCODING Content encoding.  Mandatory.  This is the encoding in which the content of the SET element is expressed.  Valid encodings are "Base64" and "80/20".
OVERWRITE Overwrite permission.  Optional.  Valid values are "True" and "False".  A value of "True" indicates that it is acceptable for the map rule to set the value of a field that already has a value.   The value given by the SET rule will overwrite the value already in the pipeline.   This is equivalent to hard-coding the field to the given value.  A value of "False" indicates that the field is only to be set if it does not already have a value.  Hence, "False" indicates that the value of the SET rule is a default that is to take effect only when no value is already provided.  When the attribute is absent, it defaults to a value of "True".
VARIABLES Variable presence flag.  Optional.  Valid values are "True" and "False".  A value of "True" indicates that all string variables occurring in the assigned value are to be substituted with the strings that they represent.  A value of "False" indicates that string variables are not to be substituted.  The "False" value is useful when a value may contain a character sequence that might be incorrectly interpreted as a string variable.  When the attribute is absent, it defaults to a value of "True".   String variables are described below.
Valid Child Elements
Varies with encoding Value to which to set the field.  The content of the SET element contains the value to assign to the field, but it is expressed in the encoding given by the ENCODING attribute.

Strings of the value that are reachable through Values objects may contain string variables.  A string is reachable if it is possible to name it with a path expression.  Also, if the value is itself a string, it may contain string variables.  A string variable is a substring of the form "%<substitute>%", where <substitute> is a path expression that names a field of the pipeline.  A string may contain this substring nestled among other characters, but it is valid for a string to be identical to a string variable.  A string may also contain multiple string variables, each conforming to this substring.   The two-character sequence "%%" represents a single '%' character that does not signal the start of a string variable.

When the SET rule is performed, each string variable is examined for possible substitution from the pipeline.  If <substitute> names a pipeline field whose value is a string, the substring is replaced with the value of this field.  Otherwise the substring is not replaced and is interpreted as a sequence of characters rather than as a string variable.

String variable substitution occurs even when the string is contained within an array, such as when the string is a member of a string array or is contained within an array of Values objects.

7.2.2. Constraints

The map rules that are valid together in a map are subject to constraints.  The constraints prevent maps from defining contradictory or ambiguous behavior that might otherwise arise given the order-independent nature of map rules within a map.  The constraints are intended to define a space of map rules having intuitive behavior so that one may only write map rules having well-defined meanings.  For the most part, a flow designer should not have to worry about constraints when creating map operations.   Most violations of these constraints can be detected at flow design time, so the webMethods Developer client will prevent the designer from creating invalid map rules.

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:

  1. No two map rules may have the same assigned field.
  2. No assigned field may contain another assigned field.
  3. No deleted or moved field may contain an assigned field.
  4. The value and the assigned field must have the same primitive type, unless the assigned field is of type Object.
  5. Path dimensions common to the value and the assigned field must have the same sizes.
The terms "primitive type" and "path dimension" require definition.   The primitive type of a value is the value's data type when it is stripped of all dimensionality.  For example, the primitive type of a string array or string table is a string.  The primitive type of a field is the primitive type of the value that may be assigned to the field, according to either this specification or the service signature that defines the field.  Hence, assuming that all other constraints are met, one may assign an array of strings to a field taking a string and one may assign a string to a field taking an array of strings.  One may also assign a string to a special primitive type known as "Object".  Any primitive type may be assigned to the Object type.

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:

Given that only primitive types need match to perform an assignment, one may copy or move each string in a string array to a field in an array of Values objects.  That is, several flat arrays of values may be merged together into a single array of Values objects, where each field in a Values object derives from a different array.  Likewise, one may split an array of Values objects into a set of arrays, one corresponding to each field of the Values object.  In these cases the path dimensions of the value to be assigned and the assigned field are the same, but the path dimensions need not be the same.

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.

7.2.3. Order Independence

All map rules in a given map are executed against the original pipeline, so the order in which the map rules appear within the element is insignificant.  For example, in the following map the "Price" field is copied to the "Amount" field even though the delete rule occurs before the copy rule: 
<MAP>
  <DELETE FIELD="Price"/>
  <COPY FROM="Price" TO="Amount"/>
</MAP>
Likewise, in the following map the DELETE rule is redundant with the MOVE rule, since the MOVE rule already changes the pipeline so that the field "Price" is not present: 
<MAP>
  <DELETE FIELD="Price"/>
  <MOVE FROM="Price" TO="Amount"/>
</MAP>
Also, the COPY rule in the following map will have no effect if the field Product/Price is not already in the pipeline before the COPY, since no map rule of a given map may act on the results of another map rule found in the same 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>
The order-independence property of map rules allows one to exchange the values of two fields using a simple map such as the following: 
<MAP>
  <COPY FROM="Amount" TO="Price"/>
  <COPY FROM="Price" TO="Amount"/>
</MAP>
It is also possible that two or more map rules each result in the removal of the same field.  These map rules are not contradictory, as the map operation will execute each map rule prior to removing the field.  Hence, the following map is valid: 
<MAP>
  <DELETE FIELD="Amount"/>
  <MOVE FROM="Amount" TO="Price"/>
  <MOVE FROM="Amount" TO="Total"/>
</MAP>
All three map rules in this map result in the removal of the "Amount" field.   A MOVE rule is equivalent to a COPY and a DELETE, and no field is deleted more than once, so the above map results in the "Amount" field being copied to both the "Price" and the "Total" field before being deleted.  The DELETE rule is redundant in the above map.

7.2.4. Field Creation

The COPY, MOVE, and SET map rules are all capable of assigning a value to a field, and in the process of assigning the value the map rule may create one or more new fields.  The field whose value is to be assigned is given by a path expression.  As previously described, a path expression consists of a list of field names that are delimited by slashes.  To locate the field given by this path expression, a map walks down the pipeline accessing each field named in the path expression.  If at any time a field named in the expression is not found in the pipeline, the field is created and added to the pipeline.  If the new field is not the last field in the expression, a new Values object is created and assigned to be the value of the field.  If the new field is the last field in the expression (the assigned field), then the value given by the map rule is assigned to the field.

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'.

7.3. Branch Operation

The branch operation selects one of its child operations to execute and executes it.   It is not a sequence.  It is effectively an operation that branches to one of its child operations.  Each branch operation has a value known as the switch.  The switch determines how the child operation is selected. The switch is the name of a field in the pipeline, and the value of this field is a name known as the desired instance name.

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.
 


branch-operation.gif (13905 bytes)

Figure 5.  The Branch Operation

 
Element Type Name
<BRANCH>
Element Attributes
NAME Instance name.  Optional.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
SWITCH Path expression of the field that selects the child operation to execute.   Mandatory.  The value of this attribute is a path expression that identifies a field found in the pipeline.  The value of this pipeline field contains the desired instance name.   The branch operation executes the child whose instance name is identical to the desired instance name.
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the operation to terminate before attempting to abort the operation.  The operation will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
Any flow element other than FLOW Child operation.  A branch must contain one or more child elements.  No two elements may have the same NAME.   An element that has no NAME or that has a NAME whose value is "*" is called the default element.  A branch may not contain more than one default element, but it is valid for a branch to contain no default element.  SWITCH identifies a pipeline field.  The operation identifies the child element whose NAME has a value equal to the value of this field and then executes the operation associated with this element.  If no element has a NAME with this value and there is no default element, then no action is taken and no error occurs.  If no element has a NAME with this value and there is a default element, then the branch operation executes the operation associated with the default element.

7.4. Retry Operation

The retry operation is a means for repeatedly performing a sequence of operations until the sequence succeeds.  One may also use the retry operation to repeatedly perform the sequence until it fails.  The children of the retry operation comprise the sequence, and with each iteration the operation executes the sequence of children as if they belonged to the default sequence operation.

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.
 


retry-operation.gif (11562 bytes)

Figure 6.  The Retry Operation

 
Element Type Name
<RETRY>
Element Attributes
NAME Instance name.  Optional.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
COUNT Maximum number of times to execute the sequence.  Mandatory.  This value must be greater than or equal to one.  If the value is one only one attempt will be made to execute the sequence.  Hence, the retry operation is most useful with COUNT values greater than or equal to 2.
BACK-OFF Number of milliseconds to wait between attempts to execute the sequence.   Optional.  A value of zero indicates that the sequence is to be retried repeatedly without delay.  Defaults to zero.
LOOP-ON Retry condition.  Optional.  This is the condition under which to retry executing the sequence.  The valid values are "Success" and "Failure".  A sequence that succeeds satisfies the "Success" condition, and a sequence that fails satisfies the "Failure" condition.   Defaults to "Failure".
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the operation to terminate before attempting to abort the operation.  The operation will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
Any flow element other than FLOW Child operation.  A retry operation must contain one or more flow elements.  The retry operation performs its child operations as if they were contained in the default sequence operation.  However, if the sequence satisfies the retry condition (as defined for the LOOP-ON attribute), the retry operation pauses for a number of milliseconds equal to the value of the BACK-OFF attribute and then attempts to execute the entire sequence again.  The operation attempts to execute the sequence at most a number of times equal to the value of the COUNT attribute.

7.5. Loop Operation

The loop operation contains a set of child operations that it executes as if they were contained in the default sequence operation.   The loop operation executes its sequence of children once for each value that occurs in a particular field of the pipeline, and it is capable of aggregating the separate results of each invocation into a single output array.  The value of the field over which the loop iterates is known as the input array, and the value into which the loop collects results is known as the output array.

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:

In particular, note that the value of the input array field in the last iteration is replaced with the original input array.  Note also that the value of the output array field in the last iteration is replaced with the expanded array of all output values.

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.
 


loop-operation.gif (15178 bytes)

Figure 7.  The Loop Operation

 
Element Type Name
<LOOP>
Element Attributes
NAME Instance name.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
IN-ARRAY Input array over which to iterate.  Mandatory.  This is the name of a field in the pipeline whose value is either an array.  The loop operation executes its contents once for each member value in the array. Within any iteration of the loop sequence, the member value is available through the field named by this attribute; the LOOP operation collapses one dimension from the field on entry into the loop and restores the dimensions when the operation completes.
OUT-ARRAY Output array into which to place output array values.  Optional.  When the loop terminates, the field having this name will contain all of the output values that the loop generated.  An iteration of the loop generates a value that is to be collected into this array by assigning the value to the field having the name given by OUT-ARRAY.  Upon exiting the operation, the loop expands the dimensionality of this field so that it becomes an array of all values ever created by the loop iterations.  If the attribute is not present, the loop will not collect output values into an output array.
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the operation to terminate before attempting to abort the operation.  The operation will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.
Any flow element other than FLOW Child operation.  A loop must contain one or more flow elements.  The loop performs its child operations as if they were contained in a default sequence operation.  The sequence is executed once for each value in the input array.

7.6. Invoke Operation

The invoke operation invokes another service.  Let's refer to this other service as the called service.  The operation optionally times the duration of the invocation.  To provide the called service with input parameters the flow service passes in its entire pipeline and trusts the called service to maintain the integrity of the pipeline.

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.
 


invoke-operation.gif (6814 bytes)

Figure 8.  The Invoke Operation

 
Element Type Name
<INVOKE>
Element Attributes
NAME Instance name.  Optional.  Any flow element may have a name attribute that uniquely identifies the element within the container operation to which it belongs.  The branch operation uses this attribute, as may user interface software.
SERVICE Fully-qualified name of the service to invoke.  Mandatory.  This may be any service in the B2B namespace.
TIMEOUT Timeout period.  Optional.  This is the number of milliseconds to wait for the invoked service to terminate before attempting to abort the service.  The service will only abort on timeout if it heeds Java thread interruptions.  A timeout period of zero disables the timeout mechanism.  If the attribute is absent, the timeout period defaults to zero.
Valid Child Elements
COMMENT Comment.  At most one comment may appear.

8. Example Flow Service

This section defines a service named PurchasePCParts and follows the definition with an implementation of the service expressed in the flow language.  The service is a simplified example of how one may apply the flow language.
 
  
Service
PurchasePCParts
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  
Purchaser String Req'd Name of the organization that is doing the purchasing.  This example assumes that all suppliers key off this name to identify the purchaser's account.
PartList Values[] Req'd List of all the parts that the purchaser wishes to purchase.  Each part description is defined as follows: 
Model String Req'd Model number of the part.
Quantity String Req'd Quantity to purchase.
Outputs  
Confirms Values[] List of confirmations for each part in PartList.  Each confirmation is defined as follows: 
Vendor String Name of the vendor from which the part was purchased.  If no vendor carries this part, the value of this field is "*NONE*".
OrderNumber String Order number that the vendor assigned for the order.
ShipDate String Date on which the vendor will ship the part.
 
<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>