Skip to main content

TransactionManager's new property

· 3 min read
Alejandro Revilla

TransactionManager is one of the most important features of jPOS. It is used to implement transaction flows using TransactionParticipants. Usually the participants are organized in groups and GroupSelector interface is used to implement decision points in the transaction flow. TransactionManager calls each participant in the order they appear in the deployment configuration xml file. When it encounters a participant that implements GroupSelector interface, its select() method is called which should return the names of the groups to be called next. TransactionManager then recursively calls participants in selected group(s). This calling of participants is done twice. First, the prepare() method is called during the PREPARE phase. If all the participants are PREPARED, the commit() method of the participants is called in the COMMIT phase. A participant can abstain from being called during COMMIT by returning PREPARED | NO_JOIN from its prepare() method. For GroupSelectors, the prepare() method is called first and then the select() method is called. If any of the participant returns ABORTED from its prepare() method, the transaction flow changes. From this point onwards, the prepareForAbort() method is called for those remaining participants that implement the AbortParticipant interface. Interestingly, if the participant that first aborts or any further participants happen to implement GroupSelector then their select() method is called based on the new **call-selector-on-abort** boolean property of the TransactionManager. Before this property was introduced, the TransactionManager would always call the select() method of such GroupSelectors. Therefore the default value of call-selector-for-abort property is true to ensure backward compatibility. Lets see an example. Suppose we have a TransactionManager configuration like below:

<txnmgr class="org.jpos.transaction.TransactionManager" logger="Q2">
<property name="queue" value="myTxnQueue" />
<property name="sessions" value="2" />
<property name="max-active-sessions" value="15" />
<property name="debug" value="true" />
....
<participant class="com.my.company.Participant1" logger="Q2" />
<participant class="com.my.company.Participant2" logger="Q2" />

<participant class="com.my.company.Selector1" logger="Q2">
<property name="12" value="group1 group2"/>
<property name="34" value="group3 group4"/>
</participant>

<group name="group1">
<participant class="com.my.company.Participant11" logger="Q2" />
<participant class="com.my.company.Participant12" logger="Q2" />
<participant class="com.my.company.Selector2" logger="Q2">
<property name="ab" value="groupA"/>
<property name="cd" value="groupC groupD"/>
</participant>
</group>

<group name="groupA">
<participant class="com.my.company.Participant11A" logger="Q2" />
<participant class="com.my.company.Participant11B" logger="Q2" />
</group>

<participant class="com.my.company.Participant3" logger="Q2" />
....
</txnmgr>

Let us assume that:

  • Selector1 and Selector2 implement GroupSelector interface
  • Selector1 will return "12"
  • Selector2 will return "ab"
  • Participant12, Participant11A, Participant3 implement AbortParticipant interface
  • Participant1's prepare method returns PREPARED | NO_JOIN
  • Participant2's prepare method returns PREPARED
  • Selector1's prepare() method returns ABORTED

Now, since the call-selector-on-abort parameter is not defined, it defaults to true and the transaction will be processed by the TransactionManager as below:

        prepare: com.my.company.Participant1 NO_JOIN
prepare: com.my.company.Participant2
prepare: com.my.company.Selector1 ABORTED
selector: com.my.company.Selector1 group1 group2
prepareForAbort: com.my.company.Participant12
selector: com.my.company.Selector2 groupA groupB
prepareForAbort: com.my.company.Participant11A
prepareForAbort: com.my.company.Participant3
abort: com.my.company.Participant2
abort: com.my.company.Selector1
abort: com.my.company.Participant12
abort: com.my.company.Participant11A
abort: com.my.company.Participant3
....

Now if we set the call-selector-on-abort property to false...

<txnmgr class="org.jpos.transaction.TransactionManager" logger="Q2">
<property name="queue" value="myTxnQueue" />
<property name="sessions" value="2" />
<property name="max-active-sessions" value="15" />
<property name="debug" value="true" />
**<property name="call-selector-on-abort" value="false" />**
....

With that the TransactionManager would behave something like this:

        prepare: com.my.company.Participant1 NO_JOIN
prepare: com.my.company.Participant2
prepare: com.my.company.Selector1 ABORTED
prepareForAbort: com.my.company.Participant3
abort: com.my.company.Participant2
abort: com.my.company.Selector1
abort: com.my.company.Participant3
....

As one can see, call-selector-for-abort property significantly affects the transaction flow when the transaction aborts. If no participant aborts, this property does not come into picture at all.