Overview
FIXEdge/J provides the ability to store information in the memory for purposes of message enrichment during the routing process. This functionality is based on a key-value storage.
While processing a message from one endpoint to another, FIXEdge/J may store any information in the storage and then retrieve it while routing in the opposite direction.
Advantages: storing data in-memory confers performance advantages.
Disadvantages: FIXEdge/J will lose all stored information on restart.
![](https://b2bits.atlassian.net/wiki/download/attachments/6076775/context_architecture.png?version=1&modificationDate=1611877711047&cacheVersion=1&api=v2)
Configuration
There are several methods in the Groovy rules that can be used for this functionality:
Method name | Methods signature | Description | Example |
---|
saveContext | Object saveContext(Object key, Object value) | Associates the specified value with the specified key in this storage. If the storage previously contained mapping for the key, the old value is replaced by the specified value.
Params: - key – the key with which the specified value is to be associated
- value – the value to be associated with the specified key
Returns: - the previous value associated with the key, or NULL if there was no mapping for the key.
| def senderCompID = msg.getTagValueAsString( 49 )
def clOrdId = msg.getTagValueAsString( 11 )
def uniqueId = generateUniqueId()
saveContext(uniqueId, [ 49 : senderCompID, 11 : clOrdId])
|
or def senderCompID = msg.getTagValueAsString( 49 )
def clOrdId = msg.getTagValueAsString( 11 )
def uniqueId = generateUniqueId()
saveContext(uniqueId + 49 , senderCompID)
saveContext(uniqueId + 11 , clOrdId)
|
|
removeContext | Object removeContext(Object key) | Removes the mapping for the key from the given storage if it is present. The storage will not contain mapping for the specified key once the call returns. Params: - key – the key whose mapping is to be removed from the storage
Returns: - the previous value associated with the key, or NULL if there was no mapping for the key.
| def uniqueId = msg.getTagValueAsString( 11 )
removeContext(uniqueId)
|
|
getContext | Object getContext(Object key) | Returns the value to which the specified key is mapped, or NULL if this map contains no mapping for the key. Params: - key – the key whose associated value is to be returned
Returns: | def uniqueId = msg.getTagValueAsString( 11 )
def savedContext = getContext(uniqueId)
msg.set( 11 , savedContext. get ( 11 ))
|
or
def uniqueId = msg.getTagValueAsString( 11 )
def savedId = getContext(uniqueId + 11 )
msg.set( 11 , savedId)
|
|
cleanContext | void cleanContext(Predicate<Map.Entry<Object, Object>> consumer) | Removes entries from context according to the provided predicate. | cleanContext({entry -> entry.getKey() == "orderId" })
|
|
Examples
ClOrdID replacement
Business case: broker should replace the identificators provided by the Clients to the IDs corresponding to Exchange's requirements and format.
![](https://b2bits.atlassian.net/wiki/download/attachments/6076775/plantuml1175204216966428310.png?version=1&modificationDate=1611877711016&cacheVersion=1&api=v2)
[
rule("Identificator_Order")
//Condition block
.condition({
ctx ->
def msg = ctx.getMessage()
//Apply rule for the messages with DeliverToCompID = Exchange
return msg.isTagExists(Header.DeliverToCompID) && msg.getTagValueAsString(Header.DeliverToCompID) == "Exchange"
})
//Action block
.action({
ctx ->
def msg = ctx.getMessage()
//Get values from the tags DeliverToCompID (128) and SenderCompID (49)
def destination = msg.getTagValueAsString(Header.DeliverToCompID)
def senderCompID = msg.getTagValueAsString(Header.SenderCompID)
//Get the full date from the tag TransactTime (60) for using in the message
def fullDate = msg.getTagValueAsString(60)
//Get the date only from the tag TransactTime (60) for storing in the context
def dateOnly = fullDate.substring(0, 8)
try {
//Replace OrigClOrdID (41) with the unique generated ID from the context
//The order and replace should have the same date (or date should be excluded from the key)
if (msg.isTagExists(41)) {
def origClOrdID = msg.getTagValueAsString(41)
def origId = senderCompID+origClOrdID+dateOnly
logger.info("I transformed ID, {}", origId)
def savedContext = getContext(origId)
msg.set(41, savedContext.get("generatedID"))
}
//Replace ClOrdID (11) with the unique generated ID and store it into the context
if (msg.isTagExists(11)) {
def uniqueId = generateUniqueId()
def clOrdId = msg.getTagValueAsString(11)
logger.info("I saved the following: '{}' by the following key: '{}'", [49: senderCompID, 11: clOrdId, 60: dateOnly], uniqueId)
saveContext(uniqueId, [49: senderCompID, 11: clOrdId, 60: dateOnly])
def id = senderCompID+clOrdId+dateOnly
saveContext(id, ["generatedID": uniqueId])
logger.info("I saved the following: '{}' by the following key: '{}'", ["generatedID": uniqueId], id)
msg.set(11, uniqueId)
}
//Use values from the tags DeliverToCompID (128) and SenderCompID (49) for routing
msg.set(Header.OnBehalfOfCompID, senderCompID)
send(routingContext, ctx, destination)
} catch (all) {
logger.error(all.getMessage(), all)
routingContext.sendReject(ctx.getSourceParams(), ctx.getMessageEvent(), "Cannot process the message")
}
ctx.exit()
})
.build(),
rule("Identificator_Report")
//Condition block
.condition({
ctx ->
def msg = ctx.getMessage()
//Apply rule for the messages with DeliverToCompID contains 'Client'
return msg.isTagExists(Header.DeliverToCompID) && msg.getTagValueAsString(Header.DeliverToCompID).matches("Client.*")
})
//Action block
.action({
ctx ->
def msg = ctx.getMessage()
//Get values from the tags DeliverToCompID (128) and SenderCompID (49)
def destination = msg.getTagValueAsString(Header.DeliverToCompID)
def senderCompID = msg.getTagValueAsString(Header.SenderCompID)
try {
//Replace OrigClOrdID (41) with the original identifier received from the Client
if (msg.isTagExists(41)) {
def uniqueOrigClOrdID = msg.getTagValueAsString(41)
def savedOrigContext = getContext(uniqueOrigClOrdID)
msg.set(41, savedOrigContext.get(11))
}
//Replace ClOrdID (11) with the original identifier received from the Client
if (msg.isTagExists(11)) {
def uniqueClOrdId = msg.getTagValueAsString(11)
def savedContext = getContext(uniqueClOrdId)
def clientID = savedContext.get(11)
msg.set(11, clientID)
if ((msg.getTagValueAsString(39) == "2")||(msg.getTagValueAsString(39)=="4")||(msg.getTagValueAsString(39)=="8")||(msg.getTagValueAsString(39)=="C")||(msg.getTagValueAsString(39)=="5")){
logger.info("I received ER filled, {}", uniqueClOrdId)
removeContext(uniqueClOrdId)
def removedContext = getContext(uniqueClOrdId)
def fullDate = msg.getTagValueAsString(60)
def dateOnly = fullDate.substring(0, 8)
def id = destination+clientID+dateOnly
removeContext(id)
msg.set(58, String.valueOf(removedContext))
}
}
//Use values from the tags DeliverToCompID (128) and SenderCompID (49) for routing
msg.set(Header.OnBehalfOfCompID, senderCompID)
send(routingContext, ctx, destination)
} catch (all) {
logger.error(all.getMessage(), all)
routingContext.sendReject(ctx.getSourceParams(), ctx.getMessageEvent(), "Something's not quite right")
}
ctx.exit()
})
.build(),
//Add other rules
getRejectionRule(routingContext)
]
Saving/updating data
...
def senderCompID = msg.getTagValueAsString(49)
def clOrdId = msg.getTagValueAsString(11)
def uniqueId = generateUniqueId()
saveContext(uniqueId, [49: senderCompID, 11: clOrdId])
msg.set(11, uniqueId)
//route message
...
Getting/removing data
...
def uniqueId = msg.getTagValueAsString(11)
def savedContext = getContext(uniqueId)
removeContext(uniqueId)
msg.set(11, savedContext.get(11))
msg.set(56, savedContext.get(49))
//route message
...
Saving repeating group
...
def map = [:]
def numberOfEntries = msg.getTagAsInt(453)
map["NoPartyIDs"] = numberOfEntries
for (int i = 1; i <= numberOfEntries; i++) {
map["PartyID" + i] = msg.getTagValueAsString(448, i)
map["PartyIDSource" + i] = msg.getTagValueAsString(447, i)
map["PartyRole" + i] = msg.getTagValueAsString(452, i)
}
numberOfEntries = msg.getTagAsInt(604)
map["NoLegSecurityAltID"] = numberOfEntries
for (int i = 1; i <= numberOfEntries; i++) {
map["LegSecurityAltID" + i] = msg.getTagValueAsString(605, i)
map["LegSecurityAltIDSource" + i] = msg.getTagValueAsString(606, i)
}
saveContext(uniqueId, map)
...
Getting repeating group
...
def savedMap = getContext(uniqueId)
def numberOfEntries = savedMap["NoPartyIDs"]
msg.set(453, numberOfEntries);
for (int i = 1; i <= numberOfEntries; i++) {
msg.addTag(448, savedMap["PartyID" + i])
msg.addTag(447, savedMap["PartyIDSource" + i])
msg.addTag(452, savedMap["PartyRole" + i])
}
numberOfEntries = savedMap["NoLegSecurityAltID"]
msg.set(604, numberOfEntries);
for (int i = 1; i <= numberOfEntries; i++) {
msg.addTag(605, savedMap["LegSecurityAltID" + i])
msg.addTag(606, savedMap["LegSecurityAltIDSource" + i])
}
...