Skip to main content

<sync>

Wait for a response from one or more asynchronous requests.

Syntax

<sequence>
   <call name="A" async="1" />
   <call name="B" async="1" />
   ...
   <sync calls="A,B" type="all" timeout="3600"/> 
</sequence>

Attributes and Elements

calls attribute

Required. A list of the names of one or more asynchronous <call> elements that <sync> will wait for. Specify a comma-separated list of <call> element names. This value can be provided as a literal string, or by using the ObjectScript @ indirection operator to refer to the value of a context variable. See details below.

allowresync attribute

Optional. If true, the <sync> element can “poll” repeatedly to detect completion of an asynchronous call. That is, you can <sync> repeatedly on the same call. This feature is useful when a call may take an indefinite time to complete. The default allowresync value is false. Specify 1 (true) or 0 (false).

timeout attribute

Optional. Specifies the time, in seconds, to wait for the responses, as an expression that evaluates to an XML xsd:dateTime value. For example: 2023:10:19T10:10.

type attribute

Optional. Specify either "all" (the default) or "any"

name, disabled, xpos, ypos, xend, yend attributes

Description

A typical business process makes one or more requests to external systems. These requests are usually made asynchronously to compensate for the fact that the external system may be slow to respond or occasionally unavailable. The <sync> element provides an easy way to wait for a response from one or more asynchronous calls. It is used in conjunction with the <call> element.

The behavior of the <sync> element is specified via the calls, timeout, and type attributes. The type attribute either has the value all, which specifies that the <sync> should wait for a response from all calls, or any, which specifies that it will only wait for the first response it receives (in this case, the remaining responses are discarded in the same manner as an expired timeout).

The following BPL fragment makes a two asynchronous requests, A and B, and then uses the <sync> element to wait for their responses (for up to one hour):

<sequence>
  <call name="A" async="1" />
  <call name="B" async="1" />
  <sync calls="A,B" type="all" timeout="3600" />
</sequence>

Any responses received after the timeout period are marked with a status of Discarded, and are not processed by the business process. If no value is provided for a timeout, the <sync> element continues to wait until all responses are received, regardless of the amount of time that passes. Meanwhile, however, the business process is saved to disk and the job in which it was running is freed up to host other business processes while the <sync> element is waiting.

The following sample BPL <process> issues two calls, then waits for 5 seconds.

<process request="Demo.Loan.Msg.Application">
  <context>
    <property name="BankName" type="%String"/>
    <property name="IsApproved" type="%Boolean"/>
    <property name="InterestRate" type="%Numeric"/>
    <property name="Results" type="Demo.Loan.Msg.Approval" collection="list"/>
    <property name="Iterator" type="%String"/>
    <property name="ThisResult" type="Demo.Loan.Msg.Approval"/>
  </context>
  <sequence>
    <trace value='"received application for "_request.Name'/>
    <call name="BankUS" target="Demo.Loan.BankUS" async="1">
    <annotation>
      <![CDATA[Send an asynchronous request to Bank US.]]>
    </annotation>
    <request type="Demo.Loan.Msg.Application">
      <assign property="callrequest" value="request"/>
    </request>
    <response type="Demo.Loan.Msg.Approval">
      <assign property="context.Results"
              value="callresponse"
              action="append"/>
    </response>
  </call>

  <call name="BankSoprano" target="Demo.Loan.BankSoprano" async="1">
    <annotation>
      <![CDATA[Send an asynchronous request to Bank Soprano.]]>
    </annotation>
    <request type="Demo.Loan.Msg.Application">
      <assign property="callrequest" value="request"/>
    </request>
    <response type="Demo.Loan.Msg.Approval">
      <assign property="context.Results"
              value="callresponse"
              action="append"/>
    </response>
  </call>

  <call name="BankManana" target="Demo.Loan.BankManana" async="1">
    <annotation>
      <![CDATA[Send an asynchronous request to Bank Manana.]]>
    </annotation>
    <request type="Demo.Loan.Msg.Application">
      <assign property="callrequest" value="request"/>
    </request>
    <response type="Demo.Loan.Msg.Approval">
      <assign property="context.Results"
              value="callresponse"
              action="append"/>
    </response>
  </call>

  <sync name='Wait for Banks'
        calls="BankUS,BankSoprano,BankManana"
        type="all"
        timeout="5">
    <annotation>
      <![CDATA[Wait for responses from the banks. Wait up to 5 seconds.]]>
    </annotation>
  </sync>
  <trace value='"sync complete"'/>
  </sequence>
</process>

Whenever a <sync> element is executed, the BPL engine inserts the name of the <sync> element into the message header so that it is visible in later Message Browser and Visual Trace displays.

Unique Names for <call> Elements

If you attempt to define a <call> element with the same name as another <call> element, the BPL editor displays an error message and requires that you provide a unique name.

Indirection in the calls Attribute

The value of the calls attribute is a string. This string must provide a comma-separated list of <call> element names. The string can be a literal value:

calls="BankUS,BankSoprano,BankManana" 

Or the @ indirection operator can be used to access the value of a context variable that contains the appropriate string:

calls="@context.myListOfCalls" 

syncresponses

There is an additional mechanism for dealing with responses received as a result of the <call> element.

Whenever the <sync> element is used, it fills a collection with the various responses it receives. This collection is a variable in the business process execution context called syncresponses. The execution context also provides a integer variable called synctimedout. The two variables synctimedout and syncresponses work together as follows:

Object Description
syncresponses syncresponses is a collection of response objects, keyed by the names of the <call> activities being synchronized. Only completed calls are represented. You can retrieve a response from syncresponses only after a <sync> and before the end of the current <sequence>. Do so using the syntax syncresponses.GetAt("MyName") where the relevant call was defined as <call name="MyName">
synctimedout

The synctimedout value is an integer. synctimedout indicates the outcome of a <sync> activity after several calls. You can test the value of synctimedout after the <sync> and before the end of the <sequence> that contains the calls and <sync>. synctimedout has one of three values:

  • If 0, no call timed out. All the calls had time to complete. This is also the value if the <sync> activity had no timeout set.

  • If 1, at least one call timed out. This means not all <call> activities completed before the timeout.

  • If 2, at least one call was interrupted before it could complete.

Generally you will test synctimedout for status and then retrieve the responses from completed calls out of the syncresponses collection.

As soon as a <sync> activity executes, the syncresponses collection is cleared in preparation for new responses. As the calls return, their responses go into the syncresponses collection. When the <sync> activity completes, syncresponses may contain some or all of the responses that you were waiting for.

For example, suppose you <sync> on Call1 and Call2 with this syntax:

<sync type="all" timeout="60">

Suppose that Call1 returns within 60 seconds, but Call2 does not. At this point, syncresponses contains the response to Call1, but not Call2. You can test the value of synctimedout to determine whether or not to expect the appropriate values to be present in syncresponses.

Following a <sync> activity, you can access whatever responses have returned by using the name of the <call> activity as a key. For a call defined as:

<call name="nameOfCall">

You would access the response using this syntax:

syncresponses.GetAt("nameOfCall")

Suppose the following sequence executes:

<sequence>
  <call name="A" async="1" />
  <call name="B" async="1" />
  <call name="C" async="1" />
  <sync calls="A,B,C" type="all" />
</sequence>

After the <sync> element completes, the syncresponses collection will contain references to three response objects, as follows:

  • syncresponses.GetAt("A") = Response from A (if any)

  • syncresponses.GetAt("B") = Response from B (if any)

  • syncresponses.GetAt("C") = Response from C (if any)

If no responses were received, the syncresponses collection will be empty.

Note:

For more information about the business process execution context, see <assign>, and see Developing BPL Processes.

syncresponses in Multiple Threads

When you use the <sync> element in conjunction with <flow>, be aware that there is a separate syncresponses collection for each thread, including the primary thread in which the <process> itself executes. Therefore, in the course of a business process there may be different syncresponses collections that go in and out of scope; each has relevance only in its immediate <sequence> and not in any other.

The following example illustrates the use of synctimedout and syncresponses in three threads, the primary business process thread and two additional threads created by a <flow>:

XData BPL
{
<process>
  <context>
    <property name="ResultsFromNorth" type="%String"/>
    <property name="ResultsFromSouth" type="%String"/>
    <property name="ResultsFromEast" type="%String"/>
    <property name="ResultsFromWest" type="%String"/>
  </context>
  <sequence>
  // In this context, syncresponses refers to the primary process thread
    <flow>
    // This flow runs two sequences (two threads) in parallel

      <sequence name="thread1">
      // In this context, syncresponses refers to results in thread1
        <call name="A" />
        <call name="B"  />
        <sync calls="A,B" type="all" timeout="10" />
        // Did the synchronization time out before it finished?
        <if condition='synctimedout="1"'>
          <true>
            <trace value='"thread1 timeout: Call A or B did not return."' />
          </true>
          // If not, then the calls came back, so assign the results.
          <false>
            <assign property="context.ResultsFromEast"
                    value='syncresponses.GetAt("A")'
                    action="append"/>
            <assign property="context.ResultsFromWest"
                    value='syncresponses.GetAt("B")'
                    action="append"/>
          </false>
        </if>
        </sequence>

        <sequence name="thread2">
        // In this context, syncresponses refers to results in thread2
          <call name="C" />
          <call name="A" />
          <sync calls="C,A" type="all"/>
          // Assign the results
          <assign property="context.ResultsFromNorth"
                  value='syncresponses.GetAt("C")'
                  action="append"/>
          <assign property="context.ResultsFromSouth"
                  value='syncresponses.GetAt("A")'
                  action="append"/>
        </sequence>
      </flow>

    // In this context, syncresponses refers to the primary process thread
    <call name="E" />
  </sequence>
</process>
}

The <if> activity in this example has a condition that tests synctimedout against the integer value 1. synctimedout can have the value 0, 1, or 2 as described in the documentation for <call>. If the two values are equal, this <if> condition receives the integer value 1 and statements inside the <true> element are executed. Otherwise, statements inside the <false> element are executed.

allowresync

The BPL business process can make the <call> and then <sync> on this call multiple times, with or without a timeout. The <sync> allowresync attribute controls this behavior. If you set allowresync to 1 (true) this enables a subsequent <sync> on the same <call>. You can do this repeatedly until the call completes. A value of 0 (false) for <sync> allowresync disallows a subsequent <sync> on the same call. The default allowresync value is 0.

Suppose you have an asynchronous <call> A, a long-running activity whose response can be indefinitely delayed. Suppose you <sync> on A with a timeout of 5. This <sync> returns immediately if A is complete, or returns in 5 seconds if A is not complete but the timeout expires. Now, suppose you know that A can take an indefinite amount of time, but generally returns without problems. That is, suppose A usually completes within 5 seconds, but sometimes takes over an hour, and that the delay is acceptable when it occurs. In this case, you will want to check A frequently for completion, in case it does complete in the usual time, but also allow subsequent <sync> activities on the same <call>, in case it takes longer to complete.

The following would be typical usage:

<sequence>
  <call name="A" async="1" />
  <sync call="A" timeout="5" allowresync="1" />
  <while condition='synctimedout=1'>
    <alert value="Waiting for call A to complete."/>
    <sync call="A" timeout="5" allowresync="1" />
  </while>
</sequence>

If a timeout is not specified in the <sync>, then it is important to check the synctimedout variable before each <sync>. Otherwise the <sync> could be waiting for a call that has already completed.

Consecutive <sync> Timeout

Suppose you have multiple consecutive <sync> elements that refer to the same <call> element, and each <sync> has a timeout value. Once the first <sync> has been satisfied, either because the <call> has returned or because the <sync> timeout value has expired, the second <sync> element does not wait but instead completes immediately.

<sequence>
  <call name="A" async="1" />
  <sync name="Sync1" calls="A" type="all" timeout="60" />
  <sync name="Sync2" calls="A" type="all" timeout="300" />
</sequence>
FeedbackOpens in a new tab