Sequence Diagrams

  1. Process instance creation
  2. ProcessContext Initialisation
  3. Dispatching incoming messages
  4. An Example

ProcessInstance creation

A BPEL process can be created when a particular receive activity of a process is called. It must be an activity within the process which is not preceded by an other activity and which is marked with a "createInstance" property set to "true". Not in all cases calling such a receive activity will result in the creation of a process. Processes with multiple start activities might be already created and have their instance creating receive activities called. This will be considered as a separate case. It is further necessary that a process is known to the BPEL engine in order to be started.

Glossary: In the following descriptions the BPEL processes will be called processes for short. The information related to a process instance, will be encapsulated within a process context. In the concrete implementation, for the sake of simplicity, the process and the process context will be encapsulated in a object called process instance.

As to explain how bexee creates a new process we will consider the following case:

A receive activity is called, the correspondent process has been deployed and the process has not been created by another receive activity.

The bexee engine receives a call for a receive activity; the call is represented by a message. A factory for process instances finds the correspondent process and tries to find a process context. As the process instance is not yet created, the process context can't be found. Nevertheless the factory returns a process instance with a process and a null process context. The process instance object will then be passed together with the message representing the receive activity call to a process controller. In bexee the logic for process execution and handling will be concentrated within a process controller. It therefore will be up to the controller to see that the process context is null. A null process context is an indication to the process controller that it's necessary to instantiate the process. The process controller will create a new process context, and initiate it before passing the message to the correspondent receive activity. Process instance initiation. This is represented in the following sequence diagram.

Sequence Diagram

Process context creation and initiation will be described in the following section.

ProcessContext Initialisation

When an incoming Message for a Receive Activity arrives, the engine has to decide, whether a new instance of a process has to be created or the message has to be passed to an existing process instance. This decision is taken based on the process specific correlation data. In case it is not possible to correlate the arriving message to an existing process instance, a new process instance has to be created. The process instance factory will create a new process instance, find the right BPEL process and associate a null process context with it. Such a process instance will be passed together with the message to the process controller. As we intend to concentrate process handling logic within the process controller, it's the process controller that hat to handle the situation where the process context is null. Receiving a process instance with a null process context, the controller will create a new context and initiate it instead of passing the message to the correspondent receive activity.



After the creation of a new process context, the message representing a receive call is not processed with the correspondent receive activity. The process instance must be initiated first, i.e. the partner links, partners, variables, correlation sets, fault handlers, compensation handlers and the event handlers have to be initiated first before the message with the correspondent activity will be processed. Internally the process is represented as a tree and the process instance specific data is encapsulated within the process context. Initiating the process is done recursively until the correspondent first receive activity is processed. The activity then consumes its call represented as a message. After processing the receive activity, the following activities are processed until a next receive activity is processed. The processing doesn't continue there because the receive call message has been consumed by the last receive. The processing returns to the process controller and waits then for a next receive call.

initiateProcess.gif

Dispatching incoming messages

This section describes in further detail how exactly an incoming message is dispatched to the ProcessController.

Asynchronous request/response

We will first consider the easier case, where a message arrives at the engine for processing but the client doesn't need the answer right away. Actually it is not the client that makes this decision, but this is specified in the deployed BPEL process but this shall not be of further importance for what we will try to explain.

  1. The bexee main controller creates a new DispatcherThread.
  2. The bexee main controller starts the thread and immediately returns (maybe by issuing a response back to the client that the process has been started).
  3. At the same time the new thread will trigger the ProcessController to process the message.
  4. Once the ProcessController finishes processing the message, it will either send the result to a call back interface, if one was provided or, if not, just terminate.

Synchronous request/response

Things get a bit more tricky when a client starts a synchronous BPEL process. This means that the client needs to wait until the processing of the entire BPEL process is done in order to get the response.

As we saw in the preceding section the processing of an incoming message will always occur inside a new thread. The reason of doing this is explained in Processing asynchronous <invoke>s.

For the main controller, this means that it has to wait until the entire BPEL process has been processed.

  1. The bexee main controller creates a new DispatcherThread.
  2. The bexee main controller starts the thread and acquires a lock on the ProcessContext object. Hence the main thread that was started by the client is now waiting.
  3. In the meantime the newly created thread will trigger the ProcessController to process the message.
  4. Once the ProcessController finishes processing the message, it will store the result in the ProcessContext and notify all locking threads.
  5. The bexee main controller will therefore resume its execution, fetch the result out of the ProcessContext and send a response back to the client.
Sequence Diagram
Bexee main controller
// locate ProcessContext ctx

Thread dispatcher = new DispatcherThread(ctx);
dispatcher.start();

synchronized (ctx)
{
    try
    {
        ctx.wait();
    }
    catch (InterruptedException e)
    {
        // exception handling
    }
}

return ctx.getResult();
DispatcherThread
public void run()
{
    ProcessController.getinstance().dispatchMessage(instance, message);
}
ProcessController
ctx.setResult(...);

synchronized (ctx)
{
    ctx.notify();
}

Warning Multithreading issues with Mozilla Browsers

Note that there seems to be a serious restriction when using a Mozilla browser: If a web resource is requested that takes a long time to send a response (e.g. a synchronous request that started a long-running BPEL process) it is NOT possible to send a request to that same resource again. It is of course still possible to send a request to other resources on the same server and Tomcat will use a new thread to process the request as expected. But it looks like Mozilla does not send a request to exactly the same resource until a response for the first request has been received.

The came across this issue by using either Mozilla/5.0 rv: 1.7.3 or Mozilla/5.0 rv: 1.6 (Firefox).

Further investigations were not undertaken due to time constraints and the thought that calling a web service from within a web browser is rather unusual. But nevertheless this is a serious limitations and should definitely be taken seriously if the project will be continued seriously.

Processing asynchronous <invoke>s

It is possible that a BPEL process doesn't terminate after the first message it received, because it might have to wait for more messages from other sources. This could be the case if we execute an asynchronous <invokes> activity inside a BPEL process. The process will continue it's execution until it reaches a point where an input response is needed, e.g. a <receive> element.

<!--  initiate the remote process -->
<invoke name="invokeAsyncService" partnerLink="AsyncBPELService"
    portType="services:AsyncBPELService"
    operation="initiate" inputVariable="request"/>

<!--  receive the result of the remote process -->
<receive name="receive_invokeAsyncService" partnerLink="AsyncBPELService"
    portType="services:AsyncBPELServiceCallback"
    operation="onResult" variable="response"/>

Message-driven process execution

Because the arriving new message will again trigger process execution (in exactly the same way a new message does as described above). Hence we are confronted with a message-driven model and it is thus not necessary for the ProcessController to wait until a new message arrives. Quite the contrary, the thread that triggered the ProcessController will terminate and if a new message arrives again later, the ProcessController will be triggered again by a new thread to process the message and so on.

This leads to the big advantage that the ProcessController doesn't have to wait at a given activity or remember where it had to wait.

All the ProcessController does when it arrives at a <receive> element is informing the ProcessContext about the element. The ProcessContext maintains a list of Receive activities. This list will be used by the ProcessController to see whether the process instance in a state where it can accept a given incoming message.

An Example

The following example tries to explain how an incoming request is processed by the ProcessController.

Note that this example doesn't make much sense and is incomplete as parts of the BPEL document have been deleted for the sake of the simplicity of this illustration.

Below you'll find an excerpt of a BPEL document and the corresponding sequence diagram. Note that the processing of the <partnerLinks> elements is omitted in the sequence diagram and the processing of the <variables> elements is only shown once in the diagram.

BPEL excerpt
<process name="bexeeExample">

  <partnerLinks>
    <partnerLink name="client" />
    <partnerLink name="service" />
  </partnerLinks>

  <variables>
    <variable name="input" messageType="tns:ReceiveRequestMessage"/>
    <variable name="response" messageType="tns:ReceiveResultMessage"/>
  </variables>

  <sequence>

    <!-- receive input from requestor -->
    <receive name="receiveInput" partnerLink="client"
             operation="initiate" variable="input"
             createInstance="yes"/>

    <!--  initiate the remote process -->
    <invoke name="asyncInvoke" partnerLink="service"
        portType="services:AsyncBPELService"
        operation="initiate" inputVariable="input"/>

    <!--  receive the result from the invoked service -->
    <receive name="receiveResult" partnerLink="service"
        portType="services:AsyncBPELServiceCallback"
        operation="onResult" variable="response"/>

    <!-- continue processing and invoke client callback -->

  </sequence>

</process>
Sequence diagram
Sequence Diagram
  1. The new incoming message is dispatched to the ProcessController.
  2. The ProcessController checks if the ProcessContext was used before.
  3. Because the incoming message will start a new process the above method will return false and the ProcessController initialized the ProcessContext by calling its init() method.
  4. The ProcessController gets a list of <Receive> objects from the ProcessContext. This list represents the ports on the process that are ready to receive an incoming message.
  5. The ProcessController checks if the incoming message can be matched onto one of the retrieved <Receive> objects. Because the process state changes over time this list also does and it is thus important for the ProcessController to check if the process instance is ready to accept incoming message.
  6. After a match has been found the ProcessController removes the object from the ProcessContext.
  7. Yet the process hasn't been created, as the incoming message was the first one to process for this particular process instance. The ProcessController thus needs to start at the very top of the process. It therefore calls the accept() on the top object, which is the <Process> object.
  8. All the <Process> object does within it's accept() is starting a call back to the ProcessController in order to get processed (this is called double-dispatching).
  9. Inside the process(Process, ProcessInstance) method the ProcessController expands the Process objects children and starts recursively calling accept() on all of them.
  10. This will continue until the second Receive object is encountered. The processing stops here and returns because the process can only continue after another incoming message has been received and matched. Once that happens (not shown in the diagram) the procedure begins again as described, but this time on the second Receive object.