Exceptions from invoked applications or services can be propagated to synchronous callers of Stardust services by using a global option. You can set this option in the server side carnot.properties file as described in the documentation:
Carnot.Engine.ErrorHandling.ApplicationExceptionPropagation = [never | onRollback | always]
As per the documentation, the behavior is defined as follows:
never: The engine ignores the status of the current transaction and tries to interrupt the current activity thread. For scenario b) this will eventually be undone on transaction rollback.
onRollback: The engine checks the status of the current transaction and interrupts the activity thread if and only if the transaction is not already marked for forced rollback.
always: The engine marks the current transaction for forced rollback if this is not already the case and continues as with onRollback.
The default value of this option is “never”.
Two scenarios need to be distinguished before deciding on the appropriate transaction behavior for the application:
Scenario A: An exception is raised, but the transaction is not yet forced to rollback
The engine has a choice to either interrupt the current activity thread, or to undo the current activity thread by forcing the current transaction to rollback.
Scenario B: An exception is raised, and the current transaction is already forced to rollback
The engine has no choice, any attempt to interrupt the current activity thread will be undone when the transaction eventually rolls back.
Illustration (scenario a)
We can simulate the scenario by raising an application exception in our workflow (i.e. the transaction manager has not raised an exception forcing a rollback). Then we study the behavior of the engine using two different options for the property “Carnot.Engine.ErrorHandling.ApplicationExceptionPropagation” viz. “never” and “always”.
Figure 1: Sample workflow for exception propagation discussion
Consider the workflow highlighted in Figure 1. Let us initially set Carnot.Engine.ErrorHandling.ApplicationExceptionPropagation = never (default value) in the server side carnot.properties file.
The “Generate Exception” activity is linked to a POJO that actually throws an exception as seen in Figures 2 and 3 below.
Executing the model causes an exception to be thrown as expected and the server side trace shown in Figure 4.
Viewing the process details in the Business Control Center reveals that the process has been interrupted at the Generate Exception activity (Figure 5).
Figure 5: Process details as seen in the Business Control Center
We are thus able to validate that setting the ApplicationExceptionPropagation value to “never “ has resulted in the process being interrupted. We now change the value of the above setting to “always” and run the example again.
On running the workflow with the new setting, we observe the server side exception shown in Figure 6 when the Generate Exception activity is executed. The log clearly illustrates that a rollback has occurred.
A client side ServiceException as shown in Figure 7 is also displayed.
The worklist now displays the “Pause” activity again (Figure 8) (refer to Figure 1 for an outline of the workflow) since this is the last manual activity (and therefore committed) that was executed before the rollback.
The Business Control Center view (Figure 9 below) shows that the process is still active in spite of the exception since the transaction was rolled back successfully.
This concludes our discussion of Scenario a.
Illustration (scenario b)
To illustrate scenario b we will be required to simulate a scenario where an exception has been raised and a transaction has already been rolled back. We can then test the behavior of the system under these circumstances. We choose to reproduce the above scenario by producing a database exception on insertion of a new record in the database. We modify our mapping file (Figure 10) to accept a custom id generator that returns a value that already exists in the database (PK constraint violation).
As expected, execution of the workflow throws an exception as shown in Figure 12 below.
We note that the exception is thrown at the ChangeAccountDetails activity since this is the first manual activity within the process and therefore the first point within the workflow where the engine attempts to commit the transaction. The transaction however is already rolled back by the XA manager owing to the SQL ConstraintViolationException. Furthermore, there is no entry for the Process OID associated with the above execution since the process has been completely rolled back as there was no persisted state associated with the process instance. This behavior is seen irrespective of the ApplicationExceptionPropagation setting defined in the carnot.properties file since the transaction is already marked for rollback. This concludes our discussion of scenario b.