Blotter API Overview

The Blotter API provides convenient abstractions for maintaining a blotter in real-time.

A blotter is a table of generally static data augmented over time. The rows correspond to BlotterItems and the table to a BlotterChannel. The BlotterProvider allows multiple BlotterChannels, each associated with a user, to be serviced in a single namespace. Each channel is represented to the Liberator as a container and each constituent item a record.


Key Concepts

Blotter API
BlotterChannel Corresponds to a single published blotter. Blotter Items are added to this.
BlotterItem Corresponds to a single row in the blotter

Nested Blotter Concepts
Nested Blotter A Blotter that has more than one tier of data associated with a user. To use nesting, you must set the SubcontainerNamespace. With nesting, any BlotterItems may have associated child BlotterItems.
BlotterItem parent Mechanism provided for adding BlotterItem children to a parent BlotterItem. BlotterItem.setParent(com.caplin.datasource.blotter.BlotterItem)
Subcontainer Representation The default representation of the tree. Every BlotterItem with children will be have a field 'SubcontainerSubject'. The subcontainer contains all of the children of that BlotterItem. If those children have children themselves, the former will have their own 'SubcontainerSubject' field. In the case that BlotterConfiguration#setSubcontainerMaxDepth(int) is set, the every tier from the top of the tree down to the 'subcontainerMaxDepth'th tier will use the Subcontainer Representation and all tiers lower than that will use the Materialised Path Representation.
SubcontainerSubject field BlotterItem field with subject of subcontainer in the Subcontainer Representation portion of tree.
Materialised Path Representation An optional representation of a Nested Blotter. The children and children's children, and so on, of a subcontainer at a depth of 'subcontainerMaxDepth' will all be returned upon requesting this 'SubcontainerSubject'. No BlotterItem in the Materialised Path Representation portion of the tree (from the 'subcontainerMaxDepth'th tier and down) will have a 'SubcontainerSubject' field. Instead its location in the tree can be determined from its 'Address' field. See Subcontainer Representation for details of when this applies.
Address field Set on all BlotterItems throughout a Nested Blotter, the 'Address' field contains a representation of this BlotterItem's location in the tree. The representation is as follows, a BlotterItem with no children will simply have it's parent redords subject followed by a colon and it's own subject, a child of this BlotterItem will have the former BlotterItem's record subject followed by a colon followed by the latter BlotterItem's subject. That is, with three BlotterItems with uniqueIds "parent", "child" and "grandchild", the Address will be: "/CHANNEL/ITEM/parent", "/CHANNEL/ITEM/parent:/CHANNEL/ITEM/child" and "/CHANNEL/ITEM/parent:/CHANNEL/ITEM/child:/CHANNEL/ITEM/grandchild" respectively. The subjects will be generated according to the namespaces configured in the BlotterConfiguration.

Configuration
Blotter Identifier Identifies the blotter in logs and over jmx
Channel Namespace Specifies where to extract the username from an incoming blotter channel subscription.
Item Namespace Specifies where to extract the username and item identifier from an incoming blotter item subscription.
Delta Updates Setting this option to true tells the Blotter API to only send out the data that has changed between a BlotterItem being sent and re-sent into the BlotterChannel.
Subcontainer Namespace (nested blotter only) Specifies where to extract the username and item identifier from an incoming blotter subcontainer subscription.
SubcontainerMaxDepth (nested blotter only) How many layers of subcontainers to use before using a materialised path representation of nested BlotterItems

More on Namespaces

Each Namespace should match an object-map in rttpd.conf

Namespace type Namespace string rttpd.conf object-map Mechanism
ChannelNamespace "/BLOTTER/%u/CHANNEL" object-map /BLOTTER/CHANNEL /BLOTTER/%u/CHANNEL Takes subscriptions on /BLOTTER/CHANNEL and substitutes the %u with the username of that user session.
ItemNamespace "/BLOTTER/%u/ITEM/%i" object-map /BLOTTER/ITEM/%2 /BLOTTER/%u/ITEM/%2 Takes subscriptions on /BLOTTER/ITEM/uniqueId and substitutes the %u with the username of that user session and leaves the item identifier unchanged.
SubcontainerNamespace (nested blotter only) "/BLOTTER/%u/SUBCONTAINER/%i" no object mapping necessary The subject contained within the 'SubcontainerSubject' field will contain the fully mapped subject.

More on Representations

The tree in this example would be constructed as follows:

import java.util.ArrayList;
import java.util.List;

import com.caplin.datasource.blotter.BlotterChannel;
import com.caplin.datasource.blotter.BlotterItem;

public class CreatingATree
{
        
        void sendTree(BlotterChannel blotterChannel){
                
                BlotterItem trade1 = new BlotterItem("Trade 1");
                BlotterItem itemA = new BlotterItem("Item A");
                BlotterItem itemB = new BlotterItem("Item B");
                
                itemA.setParent(trade1);
                itemB.setParent(trade1);
                
                BlotterItem trade2 = new BlotterItem("Trade 2");
                BlotterItem itemC = new BlotterItem("Item C");
                BlotterItem itemD = new BlotterItem("Item D");
                
                itemC.setParent(trade2);
                itemD.setParent(trade2);
                
                List blotterItemsToSend = new ArrayList();
                blotterItemsToSend.add(trade1);
                blotterItemsToSend.add(trade2);
                blotterItemsToSend.add(itemA);
                blotterItemsToSend.add(itemB);
                blotterItemsToSend.add(itemC);
                blotterItemsToSend.add(itemD);
                
                blotterChannel.sendBlotterItems(blotterItemsToSend);
        }
        
}

On the left is the Subcontainer Representation. This is the representation that will be used by default. In this case, subscribing to the 'ChannelSubejct' will return two BlotterItems, 'Trade 1' and 'Trade 2'. Each of these BlotterItems is a parent, so they will both have the field 'SubcontainerSubject'. Subscribing to 'Trade 1's 'SubcontainerSubject' will return the children of 'Trade 1', 'Trade 1:Item A' and 'Trade 1:Item B' and likewise for 'Trade 2's 'SubcontainerSubject'.

On the right is the Materialised Path Representation. The Blotter API is configured to use this representation from the top of the tree by setting BlotterConfiguration.setSubcontainerMaxDepth(int) to nought. In this case, subscribing to the 'ChannelSubject' would return a flat representation of the tree, all the BlotterItems associated with this channel. The position of the BlotterItems in the tree is represented only by the 'Address' field which will contain the uniqueIds of every parent of this BlotterItem in order and this BlotterItem's uniqueId.

Getting Started

Instantiating a BlotterProvider

Here is how you would instantiate a BlotterProvider given a BlotterApplicationListener called BlotterUpdateGenerator:

import com.caplin.datasource.*;
import com.caplin.datasource.blotter.*;

import java.util.logging.Logger;

public class Main implements ConnectionListener
{
        
        public static final String CHANNEL_NAMESPACE = "/BLOTTER/%u/CHANNEL";
        public static final String ITEM_NAMESPACE = "/BLOTTER/%u/ITEM/%i";
        
        private final Logger logger;
        
        public Main(DataSource dataSource)
        {
                this.logger = dataSource.getLogger();
                dataSource.addConnectionListener(this);
                
                BlotterConfiguration configuration = new BlotterConfiguration("BlotterJavaExample",
                CHANNEL_NAMESPACE, ITEM_NAMESPACE);
                BlotterApplicationListener blotterUpdateGenerator = new BlotterUpdateGenerator(logger);
                new BlotterProvider(dataSource, configuration, blotterUpdateGenerator);
        }
        
        @Override
        public void onPeerStatus(PeerStatusEvent peerStatusEvent)
        {
                logger.info("onPeerStatus: " + peerStatusEvent);
        }
        
        public static void main(String[] args) throws Exception
        {
                //arguments could be -f ./Blade/DataSource/etc/datasource.conf
                //	--config-base-location=global_config
                DataSource dataSource = DataSource.fromArgs(args);
                
                //the DataSource should not be started before creating a BlotterProvider
                //to ensure all blotter requests are received
                new Main(dataSource);
                dataSource.start();
        }
}

Sample ApplicationListener Here is the example ApplicationListener, BlotterUpdateGenerator:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Logger;

import com.caplin.datasource.blotter.*;

public class BlotterUpdateGenerator implements BlotterApplicationListener
{
        
        private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        private final Logger logger;
        
        private int currentMessageIndex = 0;
        
        public BlotterUpdateGenerator(Logger logger)
        {
                this.logger = logger;
        }
        
        @Override
        public void blotterChannelOpened(BlotterChannel channel)
        {
                // References to channels are obtained here. A reference
                // would usually be kept so that blotter items can be added and
                // removed with sendBlotterItem(s) and removeBlotterItem(s)
                // as and when.
                
                logger.info("blotterChannelOpened: " + channel.getSubject());
                channel.sendBlotterItem(blotterItemFor(channel.getUsername()));
        }
        
        private BlotterItem blotterItemFor(String username)
        {
                //fields used here must be in your fields.conf
                
                BlotterItem item = new BlotterItem(Integer.toString(++currentMessageIndex));
                item.setField("tradeDate", DATE_FORMAT.format(new Date()));
                item.setField("currencyPair", "EUR/USD");
                item.setField("submittedBy", username);
                return item;
        }
        
        @Override
        public void blotterChannelClosed(BlotterChannel channel)
        {
                //any channel cleanup should go here
                
                logger.info("blotterChannelClosed: " + channel.getSubject());
        }
}

Packages 
Package Description
com.caplin.datasource.blotter
This package contains the public elements of the BlotterAPI.