Solace Systems – Managing Subscriptions on Behalf of Clients
Like most messaging products, once you start distributing data, you need to lock down who can access the data. In the case of Solace Systems this can be nicely handled by the “Managing of Subscriptions on Behalf of Other Clients” functionality (see Solace Systems Feature Guide). What follows are some notes and code snippets that provide one view on how to leverage subscriptions.
Before starting out your Solace Systems appliance needs to have a least one user that has the “Subscription Manager” privilege. This user will essentially facilitate the managing of client subscriptions, and hence should be thought of as an admin user for all intents and purposes. All “client” users should be setup with an ACL profile that prevents the addition of subscriptions – effectively and on-boarding process.
With the above in place, we can now effectively begin to explore managed subscriptions. One important point before proceeding, a client must be connected to the appliance before subscriptions can be managed on their behalf. So essentially the following would be one possible work flow to subscription management:
- Business server application (assume running a OS service) is connected to Solace System appliance with a username that has “Subscription Management” privilege.
- “Client” start desktop application, logs onto Solace System appliance, and makes a remote call to a server to logon to business application
- Business server on receipt client “logon” request, adds appropriate subscriptions to client based on entitlements
- “client” desktop application begins to see appropriate message flow
In addition what is nice is that the Solace System API offers the ability for subscriptions to be added/removed on demand based on user actions by the client application sending a request to the Subscription Manager who dynamically adds/removes a messaging system subscription(s) matching the content being requested.
Given the above workflow, point 1 would do something similar to:
using (ISession session = ConnectToAppliance(managerUser))
{
Debug.Assert(session.Connect() == ReturnCode.SOLCLIENT_OK, "Should have logged in to Solace Systems Appliance");
Debug.Assert(session.IsCapable(CapabilityType.SUBSCRIPTION_MANAGER), string.Format("{0} Not Supported. Exiting", CapabilityType.SUBSCRIPTION_MANAGER));
}
private ISession ConnectToAppliance(string clientName)
{
var contextProps = new ContextProperties();
var sessionProps = new SessionProperties
{
VPNName = "???",
Host = "???",
UserName = "???",
ClientName = ???,
Password = string.Empty,
ReconnectRetries = 3,
ReapplySubscriptions = true
};
_context = null;
try
{
Debug.WriteLine("Creating the context ...");
_context = ContextFactory.Instance.CreateContext(contextProps, null);
Debug.WriteLine(string.Format("Creating the {0} session ...", clientName));
return _context.CreateSession(sessionProps, HandleMessageEvent, HandleSessionEvent);
}
catch (Exception ex)
{
var sb = new StringBuilder();
sb.Append(string.Format("Encountered an exception: {0}n", ex.Message));
sb.Append(string.Format("tType = {0}n", ex.GetType().ToString()));
sb.Append(string.Format("tStack = {0}n", ex.StackTrace));
Debug.WriteLine(sb.ToString());
}
return null;
}
Point 2 would leverage something similar to the following code:
using (ISession session = ConnectToAppliance(clientUser))
{
if (session.Connect() == ReturnCode.SOLCLIENT_OK)
{
}
}
Point 3 would subscribe on behalf of the client using the following code.
Server steps are
- Login to solace router
- Receive client login request
- Add subscriptions on behalf of client in #2
// Following code runs on the server - effectively a Subscription Manager process
// Login as manager, add topic for a user, and check user can now login
using (ISession session = ConnectToAppliance(managerUser))
{
session.Connect();
if (session.IsCapable(CapabilityType.SUBSCRIPTION_MANAGER))
{
ContextFactory cf = ContextFactory.Instance;
// Following code is really client code in a client process
// Client (in another process) connects to Solace System Appliance
using (ISession userSession = ConnectToAppliance(clientUser))
{
userSession.Connect();
// Request comes to business server, passing clientUser as a parameter
// server process execute following code
using (IClientName clientName = cf.CreateClientName(clientUser))
{
using (ITopic serviceTopic = ContextFactory.Instance.CreateTopic(Topic))
{
// Grants subscription on behalf of client. Assume here that serviceTopic is say a topic providing currency pair prices
session.Subscribe(clientName, serviceTopic,
SubscribeFlag.RequestConfirm | SubscribeFlag.WaitForConfirm, null);
// Send message from server via serviceTopic. Client should now receive this message, as subscription has been granted
using (var message = ContextFactory.Instance.CreateMessage())
{
message.Destination = serviceTopic;
message.DeliveryMode = MessageDeliveryMode.Direct;
message.BinaryAttachment = Encoding.ASCII.GetBytes("Hello client");
session.Send(message);
}
Thread.Sleep(2000);
}
}
}
}
}

Great post. You could further expand point 2 to use the Solace API’s request/response mechanism to send the logon request to the business server. The request is sent to the well known/configured topic of the business server. The implication of this is that the architecture can have many business servers listening to a client logon topic for these logon events and each can manage the entitlements of their own topic spaces. When one request is sent to the topic all servers receive and each can choose to respond or ignore.
Using request/response in this way helps keep the clients and business servers decoupled allowing for better modularity and scaling.
Cheers, Greg.