Wednesday, 26 July 2017

PCS and Correlations: the next big thing cavemen already used...

You can use BPM, BPEL or Workflow to orchestrate or direct regular processes to get a job done from the beginning through a certain flow with a few decision-points, alternate and parallel flows to the end. A common use that is fine and usefull for most projects. And it can be seen as the driver for software companies to develop process/workflow engines.

However, there are cases that one process spawns off multiple other process instances that are some how related to one particular business case, involved person, or a uniquely distinguishable entity. And often those processes have to interact, with each other. For instance, this year in Split I came up with an idea for a role playing advanture game using chatbots and PCS. Each player would initiate a PCS instance, that when interacting with each other can detect if players meet each other at a certain location.

Correlation Sets are key here...

Right after the acquisition of Collaxa in 2004, in my Oracle Consulting era, I got the chance to be a BPEL trainer for Oracle University, doing several trainings for a few bigger consulting companies in the Netherlands. One of the subjects was about Asynchronous Processes and how the Process Manager used WS-Addressing to correlate the response to the running instance that sent out the request. But together with that Correlation Sets were mentioned. And I did not understand it: why would you need Correlation Sets when the Process Manager handles it all transparently using WS-Addressing? Otherwise put: it was time for me to do some projects.

PCS, BPM Suite and SOA Suite share the same process engine that originated from the early BPEL Process Engine. And as you can detect from my anecdote: Correlation Sets are key in this story. And this functionality is around from the medieval ages. In fact, recently they discovered char-coal drawings in a cave in France, indicating that people form pre-historic times already used Correlation Sets in their BPEL's...

Prototype this...

Let's say you have a customer that is a large company with several responsible participants that are allowed to sign the contract. Some of them are full-authorised signers, while others only have partial authorisation. So either one of the fully authorised participants signs, which would complete the contract, or some of the partial signers have signed that pass the contract over the signing threshold.

For this case we keep it simple: either one of the full authorised participant should have signed or all of the partial signers should have signed. But we'll handle this in a (set of) business rule(s), so it can be made more complex to resemble real-world cases.

The singing process spawns off single signing processes for each participant asynchronously:
This is a quite simple looping, as explained in my previous PCS-Article. This can be made simpler using a multi-instance embedded sub-process:

However, for us, this was a later addition.

This single sign process results in a task for the particular signer. When signed it will respond back to the main signing process, that receives the responses of the actual signers:
For every respondent it checks if it results in the contract being fully signed. If it isn't it will wait for a subsequent sign-response. For the technical interested: since this Receive is coupled to the end activity of the signle-sign process:
it is using the earlier mentioned WS-Addressing to correlate each responding asynchronous sub-process to this particular instance. When one or more signers respond just at the moment the process is busy with determining if it has to wait for another receive, these responses are kept by the process manager and delivered at the moment the process instance activates the receive activity again.

Now the thing is, when the contract is fully signed, there still can be several single sign process active. For each of those a task resides in the task-list of the particular participant, but that does not add to the signing-validity of the contract anymore. Those tasks need to be withdrawn:
In this loop, all the signers are checked and for those that did not sign yet, a Withdraw Signing Event is thrown. Correlated to the single signing instance will cause that process to end, removing the particular task from the participants list.

Let's get into the details...

The list of signers is defined as:



We need to know the signers for a contract.  For each Signer we have some attributes. Two toggles are important here:
  • signed: did the signer sign the contract?
  • fullSignAuthority: is the signer fully authorised to sign the contract? 
I also declared a separate object to contain a list of signers. I don't like list based attributes among single attributes. I want to make it explicit that an attribute contains a list of elements.

The main sign process is initiated with a BusinessCustomerSignEvent:
It contains a reference to the Chamber of Commerce number and the account, opportunity and quote id's in SalesCloud or Configure Price Quote Cloud Services. But also the list of Signers component.

When initiating the Single Sign Process, the BusinessCustomerSingleSignEvent is used:
As shown, its the same as the BusinessCustomerSignEvent exept for the signer: it's a single element here. But to be able to identify the particular signer, we introduced a signerIndex.

Then, lastly, there is the WithdrawSigner event:
Here the signer is not particularly important. The correlating attributes are:
  • accountPartyId
  • opportunityId
  • signerIndex
These correlating attributes are defined by the accountPartyId identifying the customer. For that customer, multiple opportunities can arrise. So actually the contract relates to a particular opportunity. And each signer is identified by a sequence/index.

Implementing the details...

First the Single Sign Process is modelled like:


Referring to the Single Sign Process, defining the correlation starts with defining the correlation key:

This pane is called using the 'shuffle'-icon in the button bar, next to the play button.

The Correlation Key is used to identify a Correlation Set, and can consist of one or more properties. Those properties are then mapped to the correlating activities. You define a key using the plus icon. Then you define one or more properties. Then you assign those to the key, using the shuttle icons in the middle.

The  process starts with the Single Sign event, with the interface defined based on the BusinessCustomerSignEvent :

 But the interesting part are the correlations:

In the properties of the Start activity, click on the Correlations tab.
Since it it's a Start event, the only possible choice we have is to initialize a correlation set.
You need to add a correlation key, the one you defined before.

Then map the properties of the key to attributes of the events arguments.

When the Single Sign proces is initiated, the flow splits in a parallel flow. Both conditional paths are based on the condition that the signed attribute is false. If the signed attribute is true, however unlikely, the process is ended.

As can be seen the flow separates into branches leading to a Sign Contract activity, and a Catch  Withdraw Signer Event  activity. So, both are waiting. The Catch event is based on the WithdrawSigner event and is also based on the correlation key:

Here we have the option to Initialize a correlation key. But, in this case this is not quite sensible. We do want to correlate to the earlier initialized Correlation Set. And here we map the properties to the attributes of the WithdrawSigner event activity argument.

Sharp eyes would notice that the Sign Contract Activity has a boundary event on it:
It also catches a WithdrawSigner event, with a comparable correlation set. Actually, this boundary event could have sufficed for our case. But then the process would be boringly simple... And I wanted to show also a construct that allows for intra-process interactions. Here events are thrown and catched between the branches within an instance.

Flowing through the Single Sign process


So, what happens is that either a signer signs the contract, or the main process throws a WithdrawSigner event.

If the Signer signs, the WithdrawSigner Catch event needs to be released. So, in the main flow after the Sign Contract activity, a throw WithdrawSigner event is done:
 It targets the Withdraw Signer event, from the same process. The flow will look like:


This releases the Catch event in the lower flow-branch.
When this signer was a full signer, the main process flows on to do a withdraw of the not signed signer-tasks:
It would be nice to drill down into the decision functions. They're partly used as Script Tasks. And partly to determine the looping. The Check Signing Status checks if one of the fully authorised signers signed, or if there aren't any partial signers that haven't signed yet.

Maybe the most interesting one is the Determine Signers to Withdraw Tasks for:

If the signer has signed, then the signer is removed from the list. This adds to the list functions mentioned in my previous article. The resulting list is traversed to throw a withdraw event for each of the tasks.

If the main process throws a Withdraw Signing Event for an instance, and the signer has not signed then (which is probably the case), it should throw an event to the cancel the Sign Contract boundary event:
What results in a flow like:


And this will remove the tasks from the lists of all the participants.

Conclusion

This was one of the nicest constructs I made in PCS so far. In the "BPEL pre-history" I found that the Correlation Set concept was a very differentiating feature in the Process Engine market. I could not solve some of the more complex problems in the past without Correlation Sets. Maybe this experience made me very fond of this feature. And it's nice to be able to demonstrate it in PCS.

As said, the Boundary event on the Sign Contract activity would already suffice. It turned out I did not need the complex parallel flow with the extra branch to wait for the Withdraw Signer event. But wouldn't you agree this process looks more interesting? It at least also demonstrates how to release blocking paralallel branches within a process.

Friday, 21 July 2017

Process Cloud Service and how to loop and select elements from a list

For more than half a year I've been 'dying' to write posts about some of the constructs I've developed in my last Process Cloud Service project. At last I have the opportunity. And I hope I'll be able to write some more. But for starters, one of the problems I encountered is that I needed to process a list of something(s) and select elements from it. Or even better, how to build up a new list based on an input-list and some rules. Oh and do looping, or actually determine how to finish a loop based on a list of elements. Without a count() function in the PCS Expressions...

Questions

If you have met these kinds of problems and the tool at hand is PCS, then you probably ran in (some of) the following questions:
  • Why don't we try to solve this in SOA CS or possibly ICS?
  • Where are the script tasks we have in BPM Suite? (Sorry, this is an obvious one, but still)
  • How to count the elements of a list? Or, where are the functions in PCS?
  • How to add elements to a list?
  • Etc.
To address a few of those...

Many of these things we ran into are actually orchestration issues. And as with the all-time discussions on when to use SOA and when BPM, we advise doing complex service orchestration  (with message processing) in SOA CS, or if possible in ICS. But when we started with this project, the tools we were given were PCS and ICS. And where ICS lacked the more advanced logic processing in the orchestration integrations, at the time (it's improved over time). And sometimes it really is fun to try to accomplish things that were mentioned not being possible. Go where no man has gone before, that sort of stuff.

Script tasks? I guess PCS Product Management gets tired of answering this question. But the real thing is: we do need to do determinations based on the outcome of services. But also doing logic before doing an activity. In the input data association of an activity, you can only assign into the input-arguments of the activity. You can't update process variables. You can do that in the output data-association. But not all activities have a output-data-associations. And there are cases where you don't have an applicable previous activity. For instance in loops.

The case

Let's get into a case. Let's say you have a list of product-subscriptions that your customer has acquired from you. And for some reason some of those products need a review. In our case the customer faces a takeover, and is part of a hierarchy. So you want to fetch all the applicable products in the company hierarchy and add all of those in a list of Review Product Tasks.

The list of products is acquired by calling an ICS service that fetches a list of products from SalesCloudService and calls Oracle Policy Automation to filter the applicable products from the list. That's the list we need to add to the list that should contain the Review Product Tasks for products acquired from all the companies in the hierarchy. The result list looks like:
The cocNumber is the Chamber of Commerce number of the company. For the rest we keep things simple.

The part of interest of the process is:
In the basis, its not that hard. Except for:
  • I don't have an other activity with an output data-association in the loop to do calculations to base my test upon.
  • Again, we don't have script tasks with functions. So I miss-use a decision function as a script task. Which does the job quite well by the way.
  • Oh, and the nice thing of the Business Rules Engine (the same as used in SOASuite and BPM Suite) is that it does have functions. But we'll get into the magic of that in a minute.
The first activity is the service call to the ICS service.  In the output we copy the response message to a DO and it initializes the productIdx to 1. It's a nice example of the fact that we can do (simple) calculations and Data Object manipulations in the output data-association. Those aren't confined to the arguments of the activity.
Within the response message we have a list of products, but of course, those are in the structure of the service definition. We need to copy the elements over to the input of the decision function.

The input of the Add Review Product Task decision function looks like:

The output is:

So in goes the list of products and the product to add. The Decision Functions results into an updated list.
But, how to do that?
First, let's look in to the input data association:

The first rule is to copy the reviewProductTasksDO (the list we're building) to the corresponding input argument. Then the particular product is created by entering the different elements. As said, some where in the structure of the response message of the ICS service, we have a list of products. Using the productIdx variable we select one if the products and that indexed product is copied element by element.

Now, the rule is:

The first 2 conditions are actually declarations of identifiers we refer to in the rest of the rule: reviewProductTask, being the single object as input  and reviewProductTaskList containing the list of reviewProductTasks. Then we only want to add the reviewProductTask if it does not exist, to prevent the rule getting into a infinite loop. Since reviewProductTaskList is in fact an object that has an attribute being a list of reviewProductTask elements, that list contains a set of functions. One of those is the contains() function:
reviewProductTaskList.reviewProductTask.contains(reviewProductTask)

The goal of this function is quite obvious, it returns true() or false(). If the contains() function returns true, then a a call action is performed. And there the magic happens:
reviewProductTaskList.reviewProductTask.append

The reviewProductTaskList.reviewProductTask list also has an append() function. The input of that is the reviewProductTask object variable, the same that went into contains() as a parameter. The real nice thing? This also works in BPM Suite and SOA Suite! Remember? The Business Rules Engine in those products is the exact same thing! To me this was a bit of a revelation. I wasn't aware of the list functions in BRE.

So, now the rule is fired and the list is extended. We need to assign the result to the particular DataObject. So the output data association is:
Quite obvious, again, but the output argument, being the reviewProductTasksOut object, needs to be assigned to the reviewProductTasksDO Data Object. But also the productIdx needs to be increased.

The Decision Function activity loops back to the Exclusive Gateway. How to determine if we're done? Remember we don't have a count() function in the expression builder? The trick is done in the No-transition:





The condition is:
getCriticalProductsResponseMessage4TakeOverCpyDO.data.products.product[productIdx] == null 
When the productIdx is increased and it gets a value greater then the number of product elements in the list, then indexing that (non-existent) product will lead to null. And that can be tested.

And again, it's too obvious, and yet it took me a while to get to this.

The check if we need to get another company in the hierarchy is comparable, only it is indicated by the ICS service. If it is the top-level company, then it has no reference to a parent:
getCriticalProductsResponseMessage4TakeOverCpyDO.data.parentAccountPartyId != null

Conclusion

It might seem so obvious when you read this. And I suppose it is. But, some of those constructs and tests/conditions are not so apparent from the UI. So I hope I have been able to get some of you at the right track.

In one of the follow-up blog-posts I'd like to address Correlations.

Tuesday, 4 July 2017

Weblogic log level mapping

For quite some time now I wondered about the differences in log-levels. For instance, if you configure the log levels in the classes in SB12c or SOA Suite,  You see levels as INCIDENT , ERROR, and TRACE, even with several sub leverls (1, 16, 32). But on the Server Log configuration in Weblogic, you see levels a s ERROR, NOTICE and DEBUG, or TRACE.  And then in Java, JDK logging we even have other log levels.

Today I experienced the urge to look it up and fout this nice document. It describes the configuration of logging in Fusion Middleware. It contains this nice table:


ODL
WebLogic Server
Java
OFF OFF 2147483647 - OFF
INCIDENT_ERROR:1 (EMERGENCY) 1100
INCIDENT_ERROR:4 EMERGENCY 1090
INCIDENT_ERROR:14 ALERT 1060
INCIDENT_ERROR:24 CRITICAL 1030
ERROR:1 (ERROR) 1000 - SEVERE
ERROR:7 ERROR 980
WARNING:1 WARNING 900 - WARNING
WARNING:7 NOTICE 880
NOTIFICATION:1 INFO 800 – INFO
NOTIFICATION:16 (DEBUG) 700 - CONFIG
TRACE:1 (DEBUG) 500 – FINE
TRACE:1 DEBUG 495
TRACE:16 (TRACE) 400 - FINER
TRACE:32 (TRACE) 300 - FINEST
TRACE:32 TRACE 295

This helps quite neatly. Especially, because to have ODL logging get through to the Server log-files, you need to set the appropriate level on the Weblogic server.