Context
Context is the transaction's blackboard. It flows through every participant and carries everything they need to communicate — the incoming message, the response being built, database handles, audit data, and anything else a participant wants to share with participants that run later.
Structure
public class Context implements Externalizable, Loggeable, Cloneable, Pausable {
private transient Map<Object, Object> map; // volatile, not persisted
private Map<Object, Object> pmap; // persisted to persistent-space
}
Two maps:
| Map | Key | Persisted | Use |
|---|---|---|---|
map (transient) | Any object | No | In-flight data: ISOSource, open DB handles, computed values |
pmap (persistent) | Serializable | Yes (if persistent-space is configured) | Data that must survive a JVM crash: amounts, card data, timestamps |
Put data into the persistent map with:
ctx.put("AMOUNT", amount, true); // third arg = persist
Without the third arg (or false), data goes only to the transient map.
ContextConstants
ContextConstants is an enum of well-known keys that participants agree on:
public enum ContextConstants {
PROFILER, TIMESTAMP,
SOURCE, REQUEST, RESPONSE,
LOGEVT, DB, TX,
IRC, TXNNAME, RESULT,
MID, TID, PCODE, CARD,
AMOUNT, LOCAL_AMOUNT,
...
}
Each constant's toString() returns its name (e.g. SOURCE.toString() = "SOURCE"). Participants use the string form as the map key:
ISOMsg req = ctx.get(REQUEST.toString());
ISOSource src = ctx.get(SOURCE.toString());
ctx.put(RESPONSE.toString(), resp);
This is a convention, not enforcement — you can use any string or object as a key. Sticking to ContextConstants for the core fields makes participants interoperable.
What IncomingListener puts in Context
When IncomingListener creates a Context for an incoming message it populates three entries:
| Key | Type | Content |
|---|---|---|
TIMESTAMP | Date | Time the message was received |
SOURCE | ISOSource | The channel connection — used by SendResponse to send the reply |
REQUEST | ISOMsg | The incoming ISO message |
Participants read these and add their own:
// HandleNMM adds:
ctx.put(RESPONSE.toString(), resp); // the outbound reply
// A database participant might add:
ctx.put(DB.toString(), session); // Hibernate session, etc.
Logging
ctx.log(...) appends entries to the context's internal log event, which the TM prints atomically when the transaction completes:
ctx.log("approve mti=" + resp.getMTI() + " f70=" + f70);
ctx.log(someException);
This produces the <txn> log block you see in the Q2 log:
<txn id="1" name="" rc="PREPARED" elapsed="3ms">
<handle-nmm>approve mti=2810 f70=001</handle-nmm>
</txn>
Each <participant-realm> block contains messages logged by that participant. The entire block is written once when the transaction finishes — never interleaved with output from other concurrent transactions.
Result
ctx.getResult() returns a Result object used by participants to record structured outcomes (IRC codes, inhibit flags). For the tutorial it is not used directly, but SendResponse checks ctx.getResult().hasInhibit() — if any participant has set an inhibit flag, the response is suppressed even in the commit path.