First of all it is necessary that the invoked process has been deployed on bexee in order to be started. 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 preceeded by any 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 or processes that need more than one user interaction to complete might have already been created. This will be considered as a separate case.
Glossary: In the following sections 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 implementationthe process its context will be encapsulated in a container object called process instance.
As to explain how bexee creates new processes we will consider the following case:
A process has been deployed and the process has not yet been created by another receive activity. Then a receive activity is called.
The bexee engine receives a call for a receive activity, which is
represented as a message. A bexee provider (aka. Axis pivot handler or
provider) creates a new dispatcher with the given message. The
dispatcher finds the process and a process instance given the the
passed-in message. It also let creates a new process controller, which
will be responsible for executing the process. After the creation of the
dispatcher, the provider calls the dispatcher's dispatch()
method. As a result of this method, the dispatcher dispatches the
message to the process controller in a new thread. Depending whether the
process is synchronous or asynchronous, the dispatcher will either wait
for the process controller to terminate and return a result or return
directly back to the client that sent the message. Either way the it
will cause the process controller to execute the process. The controller
will then traverse the activities of the process and execute them. This
procedure is represented in the following sequence
diagram:
Process context creation and initiation will be described in the following section.
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 whether the message has to be passed to an existing process instance. This decision is made 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 dispatcher will create a new process instance, find the right BPEL process and associate a new empty process context with it. Such a process instance will be passed to the process controller together with the message. After the creation of a new process context, the ProcessController will initiate the process instance, i.e. the partner links, partners, variables, correlation sets, fault handlers, compensation handlers and the event handlers. After the initiation, the ProcessController passes the received BexeeMessage throught the process util it reaches the correspondent first receive activity . The activity then consumes the 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.
This section describes in further detail how exactly an incoming
message is dispatched to the ProcessController
.
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 now.
The system creates a new Dispatcher
and dispatches
the inbound message to it.
The Dispatcher
starts the system starts the
ProcessController
in a new thread.
The Dispatcher
either immediately returns control
back to the system, possibly by returning an appropriate return
message.
At the same time the ProcessController
will start
to process the arrived message.
Once the ProcessController
finishes processing the
message, it will either send the result to a call back interface
provided by the client or just terminate.
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, an incoming message will always be processed in a new thread. The reason of doing this is explained in Processing asynchronous <invoke>s, further down in this section.
For the Dispatcher
, this means, that it has to wait
until the entire BPEL process has been processed and thus
terminated.
The system creates a new Dispatcher
and dispatches
the inbound message to it.
The Dispatcher
starts the system starts the
ProcessController
in a new thread.
Right after kicking off the ProcessController
the
Dispatcher
acquires a lock on the
ProcessContext
object. Hence the main thread that started
the Dispatcher
will be put to sleep.
In the meantime the the ProcessController
will
start to process the arrived message.
Once the ProcessController
finishes processing all
the activities in the process, it will store the result of the process
execution in the ProcessContext
and notify the locking
thread.
It is important to note that there might be further
input from the user or other partner services is needed, to fully
terminate a given process and that the Dispatcher
will
not return a result until every activity has been processed and the
process terminates.
The Dispatcher
will then resume its execution,
fetch the result out of the ProcessContext
and give it
back to the system.
synchronized (ctx) { try { // wait until the process has finished and get the result ctx.wait(); result = ctx.getResult(); } catch (InterruptedException e) { throw new DispatcherException("Dispatcher interrupted", e); } } public void run() { controller.processMessage(instance, message); }
public synchronized void setResult(Object result) { this.result = result; this.notify(); }
Note that there seems to be a serious restriction when using Mozilla browsers: 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.
We 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 consideration that calling a web service from within a web browser is rather unusual. But nevertheless this is a serious limitations and should definitely be inspected carefully if the project will be continued seriously.
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 an asynchronous <invokes> activity inside a BPEL process is executed. The process will continue it's execution until it reaches a point where further 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"/>
Because the arriving new message will again trigger process
execution (in exactly the same way a new message does) 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 stopped the last time.
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 such as <partnerLinks>
or
<variables>
are not shown for the sake of the
simplicity of this illustration.
Below you'll find an excerpt of a BPEL document and the corresponding sequence diagram.
<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 --> <reply name="receiveResult" partnerLink="client" operation="initiate" variable="response"/> </sequence> </process>