Groups and GroupSelector
A single flat participant chain works for simple servers. Production systems handle many message types — authorisations, reversals, network management, balance inquiries — each with a different processing pipeline. Groups and GroupSelector provide the mechanism for runtime routing between pipelines.
Participant groups
In the TM descriptor, <participant> elements outside any <group> belong to the default group. Every transaction runs the default group first. Named groups contain additional participants and are only added to the chain when selected at runtime.
<txnmgr class="org.jpos.transaction.TransactionManager" logger="Q2" name="txnmgr">
<property name="queue" value="TXN" />
<property name="sessions" value="4" />
<!-- default group: runs for every transaction -->
<participant class="com.example.DecodeRequest" realm="decode" />
<participant class="com.example.SelectGroup" realm="select" /> <!-- GroupSelector -->
<participant class="org.jpos.transaction.participant.SendResponse"
realm="send-response" />
<!-- named groups: run only when selected -->
<group name="nmm">
<participant class="org.jpos.tutorial.HandleNMM" realm="handle-nmm" />
</group>
<group name="authorization">
<participant class="com.example.ValidateCard" realm="validate" />
<participant class="com.example.AuthorizeOnline" realm="authorize" />
<participant class="com.example.RecordTransaction" realm="record" />
</group>
<group name="reversal">
<participant class="com.example.FindOriginal" realm="find-orig" />
<participant class="com.example.ReverseOnline" realm="reverse" />
<participant class="com.example.RecordReversal" realm="record-rev" />
</group>
</txnmgr>
GroupSelector
Any participant can implement GroupSelector to inject additional groups into the chain at runtime:
public interface GroupSelector extends TransactionParticipant {
String select(long id, Serializable context);
}
select() returns a space- or comma-separated list of group names. The TM splices those groups into the participant iterator immediately after the selector and resumes the prepare chain. All remaining participants in the default group (like SendResponse) remain at the end.
public class SelectGroup implements GroupSelector {
@Override
public int prepare(long id, Serializable context) {
return PREPARED | READONLY;
}
@Override
public String select(long id, Serializable context) {
Context ctx = (Context) context;
ISOMsg req = ctx.get(REQUEST.toString());
try {
String mti = req.getMTI();
if (mti.endsWith("00")) return "nmm";
if (mti.equals("0200") || mti.equals("0201")) return "authorization";
if (mti.equals("0420") || mti.equals("0421")) return "reversal";
} catch (ISOException e) {
ctx.log(e);
}
return null; // null → no additional group, continues with default chain
}
}
Execution order
For a 0200 financial request:
default group:
1. DecodeRequest.prepare() → PREPARED
2. SelectGroup.prepare() → PREPARED
SelectGroup.select() → "authorization"
[authorization group inserted here]
3. ValidateCard.prepare() → PREPARED
4. AuthorizeOnline.prepare() → PREPARED
5. RecordTransaction.prepare() → PREPARED
6. SendResponse.prepare() → PREPARED
commit (reverse):
6. SendResponse.commit() → sends response
5. RecordTransaction.commit() → finalise ledger entry
4. AuthorizeOnline.commit() → confirm online auth
3. ValidateCard.commit() → (no-op)
2. SelectGroup.commit() → (no-op)
1. DecodeRequest.commit() → (no-op)
Selector on abort
By default, GroupSelector.select() is called regardless of whether the transaction is aborting. This means the selector can influence the abort path — for example, routing a failed transaction to a different clean-up group. This behaviour is controlled by:
<property name="call-selector-on-abort" value="true" /> <!-- default -->
Set to false to skip select() when already aborting. Usually you want it on so that SendResponse (declared in the default group after the selector) still gets added to the abort chain.
Multiple groups from one selector
select() can return multiple group names, space- or comma-separated:
// Route to validation + authorization + recording
return "validate authorization record";
Groups are spliced in the order listed. This lets you compose pipelines from reusable building blocks.
abortOnMisconfiguredGroups
<property name="abort-on-misconfigured-groups" value="true" />
If the selector returns a group name that is not defined in the TM descriptor, the TM logs a warning by default and continues. With abort-on-misconfigured-groups=true, it aborts the transaction instead. Useful in strict environments to catch misconfiguration early.