Tuesday, August 3, 2010

Event-state modeling in Cordys

 

Case

Recently we have been working on a solution that can be considered as a message broker / hub:

It’s functionality is mainly to receive, validate and route messages between parties in a specific business segment over different channels.

 

All messages have a specific type, syntax and semantics. Messages may be interrelated; e.g. one message may be a response to another message. A business wide protocol-standard describes the complete interrelation between message types, and specifies the possible sequence of messages in a flow: A so called "conversation". In a conversation any message of a specific type can be only followed by a message of one or more other specific types.
In case we want to monitor conversations we need to model the messaging protocol. This protocol can be considered as an event-state diagram: The last received (and expected!) message in the message sequence reflects the state, and the reception of a new message reflects an event.
 
Multiple concurrent conversations may be active at a time. This requires a correlation id to link the reception of a new message (see  the previous blog “Events in Cordys”) to an active conversation (or to start a new one, if there is no flow active for that particular correlation id).

 

Implementation

What does it take to implement such an event-state diagram in Cordys BOP-4?

 

The core of the solution consists of three processes, a decision table and a database-table (with access web services):
  • Monitor-process: A process that describes the state model
  • HandleMessage-process: A process that is started on new receives new messages and that triggers events.
  • WaitForEvent-process: A process that actually waits for an event to be triggered by HandleMessage, where the message is passed
  • StateTransitions-decision table: A decision table containing all valid state transions that can be triggered by a message. 
  • InstanceCorrelation- database table: A database table for storage of the instance id and correlation id

 

Monitor-process

Monitor is the main process. It describes the overal state model. At runtimes each instance reflects a conversation and it effectively keeps the current state of the “conversation”. A Correlation Id is required to correlate the messages to this conversation. The Monitor process simply consists of a number of states that are interrelated: From a particular state an occuring event may cause a transition to one of one or more possible new states. A state is in fact nothing more than a sub-process call to WaitForEvent. WaitForEvent waits for an event and after a correct (expected) message is received it returns the resulting (new) state to the monitoring process. Only in case a state may have more than 1 possible resulting states the monitoring process should use a decision to effectuate the correct transition. All other logic is handles in the WaitForEvent (by invoking the StateTransitions-decision table)
 
Because the Monitor-process is started with the first message of a new conversation it has an input message, having the same format as the messages received by WaitForEvent.
 
     Example of a monitor process

 

WaitForEvent-process

Monitor specifies two main parameters to the WaitForEvent-sub process: The current state of Monitor and the Correlation Id that belongs to the conversation.

The first step in the WaitForEvent is to store its Instance Id in a “InstanceCorrelation” table, together with the Correlation Id before invoking the “Receive Message” step. (See also my previous blog “Events in Cordys”).

Once an event has been triggeredby the HandleMessage-process the WaitForEvent event checks whether it is an event that it expected, depending on the current state. The logic for this check is implemented in the decision table "StateTransitions", that I will explain below.

 

If a message is received that is not an expected successor of the current state, an exception occurs that ought to be handled. (In our case we simply log an “Unexpected Message” error). After this the process waits for a next event to be triggered.

If the message that is received is expected however, the decision table returns the “new state”, which is returned to the monitoring process. Before returning to the monitor however first the InstanceCorrelation table is cleaned up by removing the record that was inserted at the beginning of the process.

 
    "WaitForEvent"-process  
 

HandleMessage-process

HandleMessage is triggered at reception of a new message. It first uses the correlation id in the message to look up a corresponding Instance Id of a waiting process in the InstanceCorrelation table. If no such record is found, we use the decision table to check if the event is an start-event for a new conversation. If so, a new process instance of Monitor is started -via the invokation of a generated Web Service on Monitor-  and passing the message.

In case the Instance Id is found, a notification to WaitForEvent is sent using ExecuteProcess, passing Instance Id and the message (as explained in my previous blog “Events in Cordys”).
 
    "HandleMessage"-process
 
StateTransitions-decision table
The StateTransitions decision table contains all valid "current"state - message combinations, and the resulting new state. (Similar logic may also be implemented e.g. in a matrix, implemented in a database table, see also my blog "logic in Cordys").
 
Note that actually all logic on state transitions is effectively implemented in this decision table, and not in the Monitor process: The Monitor process is only intended to keep the current state of a conversation: Not to execute any transition logic, apart from navigating to the correct new state based on the out-come of the StateTransitions decision table!
 
    "StateTransitions"-decission table
 
Later I will zoom in on the reporting over the monitoring process.
 
 

Regards,

 
Harald van der Weel,

Monday, August 2, 2010

Events in Cordys

  

In certain occassions BPM processes need to able to process events.

 

Consider the example of a sales process (e.g. CloseDeal) that sends a quote to a customer and waits for the response.

Handling client responses may be the task of a separate process (e.g. ReceiveQuotationResponse) which listens to a specific channel (e.g. SMTP) for customer messages. Once a response (e.g. a reply on a quote) is received it simply should be passed on to the waiting process (CloseDeal), to allow it to process the response and continue.

 

The good news is that BPMN provides an event mechanism via  “Send Message” and “Receive Message”  steps. Though implementation of a case similar to the one above in Cordys BOP-4 (CU9) is a bit more complicated than one would expect. The issue is that Cordys supports only the Send Message step for child-processes to trigger an event in a parent process.

In our case this is not sufficient, as ReceiveQuotationResponse is not an child but an independent process.

 

Fortunately Cordys offers a work-around:

It allows to start new and to continue existing (waiting) process instances by using an “ExecuteProcess” webservice.

 

How does it work?

The process to be notified of the event (CloseDeal in our example case) should include a “Receive Message” step at the position where the notification event is expectewd (e.g. just after sending the quotation).

In the properties of the “Receive Message” step the input message must be set. This is the (Process Specific defined) message that is expected with the notification.  

 
The notifying process (ReceiveQuotationResponse ) now may invoke the ExecuteProcess web service (found in BPM runtime references “Method Set Process Execution”) to send a notification to the waiting process.

 

This invocation has 3 mandatory input parameters to be set:

  • The field “type” must contain (the literal) “instance”
  • The field “message” must contain a correct (and complete) message instance of the type that is expected by the waiting process (and hence is having the same definition as the one in the “Receive Message).
  • The field “receiver” should contain the correct instance id of the waiting process. The instance id can be obtained from the instance properties (instance:identifier) of the waiting process.
 
Passing the instance Id
The question is: How can the notifying process (ReceiveQuotationResponse) obtain the instance id of the process to be notified?

 

The solution I developed requires a correlation id. The correlation id is an identifying field that is available in both processes and equal in instances of of these processes that are related in this context. In case of the sales process the Quotation Id may be used for instance.

The waiting process stores the instance id together with the correlation id in a database table. The notifying process may use the correlation id (e.g. received in the customer message) to retrieve the instance id, and use pass this with the ExecuteProcess invocation.

 

In concrete the solution requires:

-          A database table, consisting of (at least) two fields: CorrelationId (primary key) and InstanceId (and an access web services)

-          Two steps (at least) in de the process that should receive the notification:

o   The step is to store the instance id with in de database together with the correlation id

o   The second is a “Receive Message” step

-          Two steps (at least) in the notifying process

o   The first to read the instance id based on the correlation id

o   The second to invoke the ExecuteProcess

 
Note that the approach may require additional processing like cleaning up the database table and error checking.
 
A future enhancement in Cordys to the Send en Receive message steps that allows to specify a correlation id would be a great improvement.