As we added new/updated components I figured it'd be wise to post a little bit about transaction semantics in 11g - and how they can be used to leverage the underlying JTA infrastructure that is backing the execution of components.
a) Mediator:
On the inbound side, when mediator is invoked via binding.ws it will create an manage a new transaction, otherwise (e.g. if invoked through an adapter it will inherit the existing transaction)
On the inbound side, when mediator is invoked via binding.ws it will create an manage a new transaction, otherwise (e.g. if invoked through an adapter it will inherit the existing transaction)
In more generic terms:
If a transaction is present, Mediator participates in that existing transaction. If transaction is not present, Mediatorstarts the transaction
If a transaction is present, Mediator participates in that existing transaction. If transaction is not present, Mediatorstarts the transaction
In case of sync (aka sequential) routing rules, they will be all executed in the same transaction, and if an error occurs, a rollback will be issued.
Note: in this case even a fault policy that is configured against a mediator fault will NOT trigger.
Note: in this case even a fault policy that is configured against a mediator fault will NOT trigger.
In case of async routing rules, each rule will be executed in a new transaction, and if errors occur, a rollback will happen, and a fault policy can be configured to react to it.
b) BPEL:
By default (as in previous releases) BPEL will create a new transaction on request basis - that is if one already exists, it will be suspended, and a new one created. Upon completion of the child, the master one resumes.
However, if the request is async (that is one-way) we either inherit the transaction for the insert into the dehydration store (that is dlv_message in this case) or if one exists enlist transparently into it. So we guarantee you zero message loss. Either the invocation message is in the dehydration store, to be processed, or otherwise the consumer is notified via a fault.
By default (as in previous releases) BPEL will create a new transaction on request basis - that is if one already exists, it will be suspended, and a new one created. Upon completion of the child, the master one resumes.
However, if the request is async (that is one-way) we either inherit the transaction for the insert into the dehydration store (that is dlv_message in this case) or if one exists enlist transparently into it. So we guarantee you zero message loss. Either the invocation message is in the dehydration store, to be processed, or otherwise the consumer is notified via a fault.
In 10.1.3 we had a couple of flags that one could set on the consuming process (that is the partnerlink) as well as on the providing one, to chain an execution into a single global transaction. Namely, on the consumer side, one had to set transaction=participate on the partnerlink binding in bpel.xml, and on the providing sidetransaction=participate in the <configurations> section of bpel.xml.
In 11g BPEL - we adopted a more j(2)ee alike concept - and one only needs to set the (new) transaction flag on the calee bpel component. This can be done in the composite editor's source by adding bpel.config.transaction into a bpel component - as shown below
In 11g BPEL - we adopted a more j(2)ee alike concept - and one only needs to set the (new) transaction flag on the calee bpel component. This can be done in the composite editor's source by adding bpel.config.transaction into a bpel component - as shown below
<component name="InternalWarehouseService"> <implementation.bpel src="InternalWarehouseService.bpel"/> <property name="bpel.config.transaction" many="false" type="xs:string">required | requiresNew</property> </component>
The two possible values are required, which makes BPEL inherit the tx that is already there, or if not creating a new one (required), or suspending one if exists, and ALWAYS create a new one (requiresNew).
What are the implications of setting the above (the default is requiresNew)?
Case one - BPELCaller calls BPELCallee (the latter has requiresNew set)
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCallee transaction is saved, BPELCaller get's the fault and can catch it
b) BPELCallee throws (that is uses <throw>) a fault, that is NOT handled: BPELCallee tx get's rolled back, BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCallee tx get's rolled back, and BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCallee tx get's rolled back, BPELCaller get's a remote fault
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCallee transaction is saved, BPELCaller get's the fault and can catch it
b) BPELCallee throws (that is uses <throw>) a fault, that is NOT handled: BPELCallee tx get's rolled back, BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCallee tx get's rolled back, and BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCallee tx get's rolled back, BPELCaller get's a remote fault
Case two - BPELCaller calls BPELCallee (the latter has required set)
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCaller get's the fault and can catch it, BPELCaller owns the transaction - hence if he catches it - tx is committed, if BPELCaller does not handle it - global rollback.
b) BPELCallee throws (that is uses <throw>) a fault BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCaller tx get's rolled back, no way to catch it.
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCaller get's the fault and can catch it, BPELCaller owns the transaction - hence if he catches it - tx is committed, if BPELCaller does not handle it - global rollback.
b) BPELCallee throws (that is uses <throw>) a fault BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCaller tx get's rolled back, no way to catch it.
So with the above in place - one thing that I usually have people try when they are in my 11g courses is to create two processes (BPELMaster and BPELChild), both sync, each using the same db adapter reference that inserts the same record (and hence causes a PK violation). (And of course has set xADatasourceName)
So what happens in the default (that is without bpel.config.transaction property set). Once the fault occurs, and is not handled - the BPELChild will rollback. If the BPELMaster has a catch block his transaction will commit, so you end up with the record from the Master in the db.
If you don't catch the fault in the master as well - you get a second rollback - two different transactions though :-)
The second exercise is to set the bpel.config.transaction to required, and run the testcase again. If no fault handlers are in place - the whole tx will rollback, based on the BPELMaster's unhandled fault.
Now add a fault handler in the BPELMaster to catch the Fault from the BPELClient - and throw up a rollback fault. Globally the tx will rollback as well.
With this in place - you can really control transaction boundaries and model end2end transactional flows (if your source / targets are transactional as well of course)