See: Description
Interface | Description |
---|---|
BlotterApplicationListener |
Passed into the constructor of
BlotterProvider , callbacks are made
when requests and discards for blotter channels are received:
BlotterApplicationListener.blotterChannelOpened(com.caplin.datasource.blotter.BlotterChannel) and
BlotterApplicationListener.blotterChannelClosed(com.caplin.datasource.blotter.BlotterChannel) . |
BlotterChannel |
BlotterChannel is the entry point for sending out
BlotterItem s. |
BlotterChannelListener |
Specifically for the case where there is more than one Integration Adapter providing for the same channel subject.
|
Class | Description |
---|---|
BlotterConfiguration |
The type Blotter configuration, which can be used to create a
BlotterProvider instance. |
BlotterItem |
BlotterItem is the data type representing a single item on the blotter.
|
BlotterProvider |
This is the entry point into the Blotter API.
|
This package contains the public elements of the BlotterAPI.
The BlotterProvider
is the main entry point of the API. You instantiate one providing an unstarted datasource, BlotterConfiguration
object and an implementation of the BlotterApplicationListener
interface. @see BlotterProvider
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. With a single tier blotter (no nesting), each channel is represented to the Liberator as a container and each constituent item a record.
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 its own uniqueId, a child of this BlotterItem will have the former BlotterItem's uniqueId followed by a semicolon followed by the latter BlotterItem's uniqueId. That is, with three BlotterItems with uniqueIds "parent", "child" and "grandchild", the tree paths will be: "parent", "parent:child" and "parent:child:grandchild" respectively. |
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 |
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. |
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
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.
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 = DataSourceFactory.createDataSource(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()); } }
Please send bug reports and comments to Caplin support