This is in continuation to an earlier post SOA Transaction Boundaries – Part 1 , regarding SOA Transaction Boundaries.
Let us continue with adding some more functionality to our base process. Base process has following activities
- Consume message from JMS queue using XA
- BPEL component inserts the data into “sourcetbl” table using XA datasource.
- BPEL component has oneWayDelivery set to “sync” and “transaction” set to required.
- No catch all branch
- Throws rollback at the end of bpel flow.
Base process runs everything in a single transaction and the rollback fault rolls back data inserted into table and puts message back in JMS queue.
Let us a consider a hypothetical requirement.
- After picking up message from JMS queue, BPEL should insert into “sourcetbl” and “targettbl”.
- Failure during insertion into tables, should rollback message in JMS queue
- Record gets inserted into both table or none of the tables. In other words, if insert to “targettbl” fails, “sourcetbl” insert should rollback.
- If successfully inserted into source and target table, update source table flag with “Y”. if this fails, JMS message ALONE should rollback. Records inserted into source/target table should not rollback.
- Refer the image below for the required transaction boundaries.
- Invoke1 and Invoke 2 should be part of separate transaction. They should rollback /commit together.
- If Invoke3 fails, it shouldn’t affect invoke1/2. It should rollback only Txn 1.
Base process Modification
- Add another DB adapter to insert into second table. Use XA datasource.
- Add one more DB adapter to update sourcetable flag with Y. Use XA datasource.
- Add invoke activities after the first invoke in the bpel flow. Ensure the variables are initialized in existing or new assign activity.
- Remove “Throw” activity.
- Add a catchAll branch. Throw “Remote Fault”.
- If binding fault is thrown from DbAdapter and a rollback happens, subsequent redelivery of message is rejected by JMS Adapter with error “Resource adapter invoked onReject() without an associated exception.”
- Refer this Resource Adapter invoked onReject Error post.
Do we have the required transaction boundaries with above modifications? Let us find out.
Test Scenario 1
Let us deploy and run the process. The process would complete successfully. It would pick message from JMS; insert record into source table; insert record into target table; update source table flag to ‘Y’.
Test Scenario 2
Let us now try to introduce error while inserting into “targettbl”. Provide invalid JNDI for this.
We can see there are no records in Source/Target table. JMS Message is also rolled back. Modify the process to have valid JNDI so that update goes through fine during next test case.
Test Scenario 3
Now, Let us try to introduce error while updating “Flag” to Y in source table. Modify the process, to have invalid JNDI for third Invoke and deploy the process.
Put a message with a new seq number. Seq number 1 is already in DB. Try with 2.
We can see from audit trail that source & target table insert is completed. However, record with seq number 2 doesn’t exist in those table. Because of error in third invoke , everything has rolled back. Our hypothetical requirement is
If successfully inserted into source and target table, update source table flag with "Y". if this fails, JMS message ALONE should rollback. Records inserted into source/target table should not rollback.
However, in our case, source and target table have also rolled back. The reason is required transaction boundary is not achieved in the bpel structure we have.
We want Invoke1 and Invoke2 to be in a separate transaction. How do we do that?
Defining Transaction Boundary
Unfortunately there is no direct mechanism at Partner Link level to say these partner links should be in different transaction. If there is only one resource, we can use non-xa, which will not participate in global transaction. However, we have two resources.
As transaction related properties are available only for BPEL component and not for adapters, we need to embed these adapter calls in a bpel component.
Let us modify our bpel process to look like below image.
Add a new BPEL Component to composite.xml. Let us specify “oneWayDeliveryPolicy” as “sync” and “Transaction” as “required”.
Modify composite.xml to look like below image.
Call second BPEL component from first. Remove reference of Source/target table inserts from first BPEL component.
Add Invoke activities for source/target table inserts.
Testing Modified Process
Verify all JNDIs are correct and deploy the process. Add a message in queue with seq 2. Process should go through fine without any error. Table will contain two records. We will repeat the same test scenarios as before.
Test Scenario 1
Provide Invalid JNDI for “targettbl” insert. Deploy and run the process. Specify seq number as 3.
We would see everything rolled back as required. No records in tables. Message put back in JMS queue.
Put back valid JNDI in “targettbl” insert.
Test Scenario 2
Provide invalid JNDI for updating flag to “Y”. Deploy and run the process. Specify seq number as 3.
The result is same as above. Everything rolled back. Message put back in queue. Even though we have embeded in another bpel component everything was executed in single transaction. It is because of the setting we had put initially.
oneWayDeliveryPolicy – sync. This setting makes first BPEL and Second BPEL to execute in same thread.
transaction – required. This setting makes the second BPEL to join first BPEL’s transaction instead of creating a new transaction. So, instead of creating two transactions, everything was done in single transaction as earlier.
Test Scenario 3
To make second BPEL component to create new transaction, modify “transaction” property to “requiresNew”. Deploy and run the process. Specify seq number as 4.
This time, the transaction boundaries would have been created the way we wanted it and our hypothetical requirement is satisfied. Records will be committed in Source and Target table. However, update would have failed and it will roll back only the JMS message.
When working with Adapters, SOA doesn’t provide interface to specify transaction parameters at adapter level. We need to embed those into BPEL components and specify transaction parameters for BPEL component. By specifying values for oneWayDeliveryPolicy and transaction property we can adjust transaction boundaries and achieve desired results.
In the next post, we will see how throwing and handling faults affects transactions.