Notification API Overview
The Notification API allows your application to send notifications to users of a front-end trading application. With the API, your application can open a unique channel of communication for each user, send that user notifications with arbitrary fields and allow the user to respond with actions for each notification received. Examples of notifications you may want to send include system level notifications, such as a service going down or trade life-cycle events, such as order fills.
The StreamLink Alerts API provides a means of integrating with with the Notification API. It allows a user to subscribe to their NotifcationChannel, prompting a call into the INotificationApplicationListener with the Notification API's representation of that channel. The StreamLink Alerts API can be used to send the preconfigured action, 'DISMISS', or any other custom action. The 'DISMISS' action is the only action to have automatic behaviour associated with it. When a 'DISMISS' is sent by the StreamLink Alerts API, the notification in question will be removed from the cache, and that user will be unsubscribed from that Notification.
Under the hood, a NotificationChannel is a container on the subject /PRIVATE/NOTIFICATIONS/u/CONTAINER. The channel is populated with Notification records identified by sourceName and user provided unique id. The NotificationProvider allows multiple NotificationChannels, each associated with a user, to be serviced on a single namespace.
Key Concepts
- Source Name - identifies the NotificationProvider in logs and over jmx. Passed into constructor of NotificationConfiguration.
- NotificationChannel - corresponds to a set of notifications for a single user. Notifications are added to and removed from this.
- Notification - a notification with arbitrary fields sent to the user subscribed to the NotificationChannel
Getting Started
Instantiating a NotificationProvider
Here is how you would instantiate a NotificationProvider given a NotificationApplicationListener called NotificationGenerator:
using System;
using Caplin.DataSource;
using Caplin.Logging;
using Caplin.DataSource.Notification;
using System.Threading;
using System.Collections.Generic;
namespace Caplin.DataSource.Notification.Example
{
public class InstantiatingANotificationProvider
{
private ILogger logger;
public InstantiatingANotificationProvider(IDataSource dataSource, NotificationConfiguration configuration)
{
this.logger = dataSource.Logger;
new NotificationProvider(dataSource, configuration, new NotificationGenerator(logger));
dataSource.AddConnectionListener(new ConnectionListener(logger));
}
public static void Main(String[] args)
{
ConsoleLogger logger = new ConsoleLogger();
IDataSource dataSource = new DataSource("notificationExample.conf", logger);
NotificationConfiguration configuration = new NotificationConfiguration("NotificationProviderId");
new InstantiatingANotificationProvider(dataSource, configuration);
dataSource.Start();
while (true)
{
Thread.Sleep(1000);
}
}
}
public class ConnectionListener : IConnectionListener
{
private ILogger logger;
public ConnectionListener(ILogger logger)
{
this.logger = logger;
}
public void ServiceStatus(IServiceStatusEvent ev)
{
logger.Log(LogLevels.Info, "status", "ServiceStatus changed to " + ev.Status);
}
public void PeerStatus(IPeerStatusEvent ev)
{
logger.Log(LogLevels.Info, "status", "PeerStatus changed to " + ev.Status);
}
}
}
Sample ApplicationListener
Here is the example ApplicationListener, NotificationGenerator:
using System;
using System.Collections.Generic;
using System.Threading;
using Caplin.Logging;
using Caplin.DataSource.Notification;
namespace Caplin.DataSource.Notification.Example
{
public class NotificationGenerator : INotificationApplicationListener, INotificationChannelListener
{
private static string[] CURRENCY_PAIRS = new string[] { "EUR/USD", "USD/JPY", "GBP/USD", "USD/CHF", "EUR/GBP", "EUR/JPY", "EUR/CHF", "AUD/USD", "USD/CAD", "NZD/USD" };
private Logging.ILogger logger;
private Random random;
private int currentMessageIndex = 0;
public NotificationGenerator(Logging.ILogger logger)
{
this.logger = logger;
this.random = new Random();
}
public void NotificationChannelOpened(INotificationChannel channel)
{
logger.Log(LogLevels.Info, "channel", "NotificationChannelOpened: " + channel.Subject);
channel.SetNotificationChannelListener(this);
SendRandomNotificationOnChannel(channel);
}
private void SendRandomNotificationOnChannel(INotificationChannel channel)
{
Notification notification = CreateRandomNotification(channel.Username);
channel.SendNotification(notification);
}
public Notification CreateRandomNotification(String username)
{
Notification notification = new Notification(Convert.ToString(++currentMessageIndex));
notification.SetField("tradeDate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss z"));
notification.SetField("currencyPair", CURRENCY_PAIRS[random.Next(CURRENCY_PAIRS.Length)]);
notification.SetField("submittedBy", username);
return notification;
}
public void NotificationChannelClosed(INotificationChannel channel)
{
logger.Log(LogLevels.Info, "channel", "NotificationChannelClosed: " + channel.Subject);
}
public void OnNotificationAction(INotificationChannel channel, string notificationId, string action)
{
logger.Log(LogLevels.Info, "channel", "OnNotificationAction: " + channel.Subject, "NotificationID: " + notificationId, "Action: " + action);
}
public void OnNotificationRequest(INotificationChannel channel, string notificationId)
{
logger.Log(LogLevels.Info, "channel", "OnNotificationRequest: " + channel.Subject, "NotificationID: " + notificationId);
}
}
}