Check out MockMotor

Documentation

GenericParallel is a Proxy Service that takes a list of requests for one or more services, executes them in parallel with configurable concurrency and error handling strategy and gives you back the list of responses.

GenericParallel is a simpler and more functional wrapper over Split & Join facility.

Intro: Retrieving User Profiles in Parallel

Suppose we have a UserProfile service that takes the user id and returns the user profile, and its local entry proxy is located at Profiles/UserProfile

Example request:

<gpsa:GetUserProfileRequest xmlns:gpsa="http://userprofile">
  <gpsa:Data>12345678</gpsa:Data>
</gpsa:GetUserProfileRequest>

But we need to retrieve a few user profiles at once! Sequential calls will take too long, so let’s use GenericParallel to do the concurrent call for us:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header></soapenv:Header>
  <soapenv:Body>
    <typ:GPS xmlns:typ="http://genericparallel/types">
     <typ:Requests>

      <typ:Request GPSTarget="Profiles/UserProfile">

        <gpsa:GetUserProfileRequest xmlns:gpsa="http://userprofile">
          <gpsa:Data>12345678</gpsa:Data>
        </gpsa:GetUserProfileRequest>

      </typ:Request>

      <typ:Request GPSTarget="Profiles/UserProfile">

        <gpsa:GetUserProfileRequest xmlns:gpsa="http://userprofile">
          <gpsa:Data>90876543</gpsa:Data>
        </gpsa:GetUserProfileRequest>

      </typ:Request>

     </typ:Requests>
    </typ:GPS>
  </soapenv:Body>
</soapenv:Envelope>

Each GetUserProfile request is placed into its own GPS Request element.

The GPS Request is told by @GPSTarget attribute to direct the call to a (by default) proxy service located on path Profiles/UserProfile.

All other settings (max parallel calls, error handling mode etc) are by default for now.

Note that GenericParallel can call multiple distinct services within one invocation; all you need to do is to specify different @GPSTarget values.

GenericParallel will route the requests to the Profile/UserProfile proxy, collect the responses into a list and give it back to us:

    <types:GPSResponse xmlns:types="http://genericparallel/types">
     <types:Responses>

      <types:Response GPSIndex="1" gpsbatchindex="1">

         <soap-env:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
          <soap-env:Body>
           <gpsa:GetUserProfileResponse xmlns:gpsa="http://userprofile">
            <gpsa:UserProfile>...</gpsa:UserProfile>
           </gpsa:GetUserProfileResponse>
          </soap-env:Body>
         </soap-env:Envelope>

      </types:Response>

      <types:Response GPSIndex="2" GPSBatchIndex="1">

         <soap-env:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
          <soap-env:Body>
           <gpsa:GetUserProfileResponse xmlns:gpsa="http://userprofile">
            <gpsa:UserProfile>...</gpsa:UserProfile>
           </gpsa:GetUserProfileResponse>
          </soap-env:Body>
         </soap-env:Envelope>

      </types:Response>

    </types:Responses>
    </types:GPSResponse>

Done! That was easy, eh?! ;)

Note @GPSIndex attribute in GPS Response element. This is the position of the request in the original list, so the responses can be corresponded to the requests.

By default, the returned list is already sorted by GPSIndex, so the first response is for the first request, and so on. For performance reasons though this can be turned off with GPSDoNotSort attribute:

  <typ:GPS GPSDoNotSort="true">
    ...
    <typ:Requests>

Calling Business Services, Pipelines and Flows

GenericParallel is able to determine the type of the target resource at runtime. However, to save the time required for this probing, it is recommended to provide the GPSTargetType attribute explicitly.

Target Resource Type GPSTargetType value
Proxy Service ProxyService
Business Service BusinessService
Pipeline (OSB 12+) Pipeline
Split-Join AKA Flow (OSB 12+) FLOW

Example:

<typ:Request GPSTargetType="BusinessService" GPSTarget="Profiles/UserProfileBiz">
  ...

The 1.3 attribute used for the same purpose, GPSTargetIsProxy, is deprecated.

Cross-Project Calls to Pipelines and Flows

OSB 12 is not permitting dynamic calls to pipelines and flows located in another project. GenericParallel contains a workaround for this limitation.

To perform a cross-project call to a pipeline or a flow, copy GenericParallelThunk proxy and pipeline into the target project. GenericParallel, if this proxy is found, will use it as an project entry point for all calls to pipelines and flows.

Specifying the Operation

Most services are able to deduct the operation name by the SOAP body’s first child element.

However, when you have to specify the operation, provide it in the GPSOperation attribute:

<typ:Request GPSOperation="GetUserProfile" GPSTarget="Profiles/UserProfile">
  ...

Note that GPSOperation is not the same as SOAPAction HTTP header. GPSOperation should contain the operation name as defined in WSDL and as seen in OSB console.

Limiting the Concurrency

When a service cannot handle too many parallel requests, you may limit how many requests GenericParallel makes at the same time.

This limit is specified in GPSMaxParallel attribute of the GPS element.

The complete list of requests passed to GenericParallel will be split into batches no more than GPSMaxParallel in size.

<soapenv:Body>
  <typ:GPS GPSMaxParallel="2">
    ...
    <typ:Requests>

The default value of GPSMaxParallel is 5.

GPSMaxParallel must be a positive integer, if specified.

The maximum concurrency is global for all requests within the current call. It is currently not possible to set a separate concurrency for different target services within the same call.

Concurrency Debug Attributes

For testing purposes, GenericParallel also reports which batch a request was sent in.

For instance, if GPSMaxParallel is set to 2, requests 1 and 2 (GPSIndex = 1 or 2) will be sent in batch 1 (GPSBatchIndex=1), while requests 3 and 4 (GPSIndex = 3 or 4) will be sent in batch 2 (GPSBatchIndex=2), and so on.

The 3rd response then will have these debug attributes:

<types:Response GPSIndex="3" GPSBatchIndex="2">
...

Skipping Some Requests

Sometimes you need to suppress programmatically some of the parallel calls (for example, when the account doesn’t have a cable TV, no need to call for TV account details). If you retrieve the responses by their relative number in the batch, skipping calls creates some troubles because the positions of the responses change.

To skip a call but still keep its slot, GPS supports a dummy payload . If it is provided instead of the request payload, GPS does not perform a call for this request, but simply returns an empty response.

E.g.:

      <typ:GPS>
         <typ:Requests>
            <typ:Request GPSTarget="Accounts/TV">
                {
                   if( $req/*:tvAccount ) then 

                   else
                     <typ:Skip/>
                }
            </typ:Request>

Note that the Skip element must be in the GenericParallel own namespace, “http://genericparallel/types".

If the request is skipped, the response will be empty and is also marked with GPSSkip attribute:

   <types:Response GPSIndex="1" GPSBatchindex="1" GPSSkip="true">
     <soap-env:Envelope>
       <soap-env:Body></soap-env:Body>
     </soap-env:Envelope>
   </types:Response>

Handling Faults

GenericParallel simplifies the error handling activities to the choice of one of the three error handling strategies.

The error-handling behaviour is controlled by GPSFailMode attribute.

Fail If Any Request Fails (Default)

By default, GenericParallel assumes that all responses are equally important and that getting a fault in any of them means the whole parallel call has to fail too.

This failure handling mode is called ANY_REQUEST_FAILS and is on by default, though it can be specified explicitly:

  <typ:GPS GPSFailMode="ANY_REQUEST_FAILS">
    <typ:Requests>
      ...

In ANY_REQUEST_FAILS mode, when a request fails, GenericParallel stops execution immediately and returns a soapenv:Fault:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
   <soap-env:Body>
    <soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
     <faultcode>soapenv:Server</faultcode>
     <faultstring>GPSFailMode is ANY_REQUEST_FAILS and one request has failed</faultstring>
     <detail>

       <typ:GPSResponse xmlns:typ="http://genericparallel/types">
         <typ:Responses>
          <typ:Response GPSIndex="2" GPSBatchIndex="1">
           <soapenv:Envelope>
             <soapenv:Body>
               <soapenv:Fault>
                <faultcode>soapenv:Server</faultcode>
                <faultstring>USR-0001: Not Found</faultstring>
                <detail>
                ...

The detail element of the fault contains the responses accumulated so far, including the failed one, so you can troubleshoot the parallel call as easy as you do a sequential one. You can also cache the successful responses and retry only the failed ones.

Please note that the list of responses under Fault/detail may not be complete. Remember that GenericParallel had to cancel the operation, and some of the requests may not be completed or has not even started yet.

Do Not Fail, Report All Failures

  <typ:GPS GPSFailMode="NEVER">
    <typ:Requests>
    ...    

In this second most useful mode, GenericParallel executes all requests, even if some of them fail, and return failures as soapenv:Fault in the GPS Response elements.

    <types:GPSResponse xmlns:types="http://genericparallel/types">
      <types:Responses>

        <types:Response GPSIndex="1" GPSBatchIndex="1">


          <soap-env:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
          <soap-env:Body>
            <gpsa:GetUserProfileResponse xmlns:gpsa="http://userprofile">
              <gpsa:UserProfile>...</gpsa:UserProfile>
            </gpsa:GetUserProfileResponse>
          </soap-env:Body>
          </soap-env:Envelope>

        </types:Response>

        <types:Response GPSIndex="2" GPSBatchIndex="1">


          <soapenv:Envelope>
          <soapenv:Body>
            <soapenv:Fault>
              <faultcode>soapenv:Server</faultcode>
              <faultstring>USR-0001: Not Found</faultstring>
              <detail></detail>
            </soapenv:Fault>
          </soap-env:Body>
          </soap-env:Envelope>

        </types:Response>

      </types:Responses>
    </types:GPSResponse>

Fail If All The Requests Failed

<soapenv:Body>
  <typ:GPS GPSFailMode="ALL_REQUESTS_FAIL">
    <typ:Requests>
    ...

Sometimes it is enough to get one response to continue, event if the rest has failed.

GenericParallel supports this mode with GPSFailMode=“ALL_REQUESTS_FAIL”, meaning GenericParallel will respond with Fault if and only if all the requests failed.

Just like with ANY_REQUEST_FAILS, the Fault will contain the list of responses (faults in this case) so the debug is easy:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
   <soap-env:Body>
    <soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
     <faultcode>soapenv:Server</faultcode>
     <faultstring>GPSFailMode is ALL_REQUESTS_FAIL and all requests have failed</faultstring>
     <detail>
      <typ:GPSResponse xmlns:typ="http://genericparallel/types">
         <typ:Responses>
          <typ:Response GPSIndex="1" GPSBatchIndex="1">
          ...

Passing User Headers

User headers is a useful way of passing service meta-information at execution time. GenericParallel supports user headers too.

Passing User Headers to the Service

In GenericParallel user headers are passed as attributes of Request element.

    <typ:GPS xmlns:typ="http://genericparallel/types">
     <typ:Requests>

      <typ:Request DebugFile="12345678.out" GPSTarget="Profiles/UserProfile" DebugLevel="5">
        <gpsa:GetUserProfileRequest xmlns:gpsa="http://userprofile">
        <gpsa:Data>12345678</gpsa:Data>
        </gpsa:GetUserProfileRequest>
      </typ:request>

      <typ:Request GPSTarget="Profiles/UserProfile" DebugLevel="0">
        <gpsa:GetUserProfileRequest xmlns:gpsa="http://userprofile">
        <gpsa:Data>90876543</gpsa:Data>
        </gpsa:GetUserProfileRequest>
      </typ:Request>

     </typ:Requests>
    </typ:GPS>

Here an user header DebugInfo with value 5 is passed with the first request, and the same header with value 0 is passed along with second request.

In addition, an user Header DebugFile with value 12345678.out is passed along with the first request.

Passing User Headers from the Service

GenericParallel also passes all response user headers back to caller, placing them as Response attributes, as with DebugFileCreated user header below:

  <types:GPSResponse xmlns:types="http://genericparallel/types">
   <types:Responses>

    <types:Response GSPIndex="1" GPSBatchIndex="1" DebugFileCreated="12345678.out">
      ...