In-memory data context

Overview

FIXEdge/J provides an 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: in-memory data storing confers performance advantages.

Disadvantages: FIXEdge/J will lose all stored information on restart. 

Configuration

There are several methods in the Groovy rules that can be used for this functionality:

Method nameMethods signatureDescriptionExample
saveContextObject saveContext(Object key, Object value)

Associates the specified value with the specified key in this storage. If the storage previously contained a mapping for the key, the former 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)

removeContextObject removeContext(Object key)

Removes the mapping for the key from the given storage if it is present.

The storage will not contain a mapping for the specified key once the call returns.

Params:

  • key – the key which 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)

getContextObject 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 with which the associated value is to be returned

Returns:

  • the value to which the specified key is mapped, or NULL if this map contains no mapping for the key

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)

cleanContextvoid cleanContext(Predicate<Map.Entry<Object, Object>> consumer)Removes entries from the 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.


rules.groovy
[
        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

rules.groovy
...
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

rules.groovy
...
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

rules.groovy
...
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

rules.groovy
...
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])
}
...