In my previous post I wrote about how we (the ROBIN dev-team) decided to use WebSync for our real-time solution. This time around I want to share how we implemented WebSync in ROBIN.

Architecture
WebSync Server supports multiple application architectures.

Integrated application architecture:

 Img 01: Integrated Application Architecture WebSync
Img 01: Integrated Application Architecture WebSync

The integrated architecture means that the webapplication and WebSync Server run in the same IIS pool. In Azure this means you can spin up a new instance when the running instance(s) run out of resources.

Isolated Application Architecture:

002_websync_isolated_archImg 02: Isolated Application Architecture WebSync

The isolated architecture means that the webapplication and WebSync Server run in separate IIS application pools.
At this moment in time we implemented the integrated architecture, but we are debating if we need to go for the isolated architecture, we think it is more robust and gives us the opportunity to decouple the real-time, or notification logic even more.

ROBIN Deployment Architecture
We have the following deployment architecture:

Img 03: ROBIN Deployment ArchitectureImg 03: ROBIN Deployment Architecture

As image 03 shows we use the integrated architecture, realtime is part of our Process Services layer. In our Frontends, API’s and Background Worker layers we leverage Websync to do the real-time handling for us.

Storage Provider
WebSync needs storage, and supports the following providers out-of-the-box:

  • Sticky Providers
    • In Memory Provider (Default)
    • Sticky Sql Provider
  • Stateless Providers
    • Stateless Sql Provider
    • Azure Table Provider

We use the Stateless Sql Provider, because it is the fastest provider WebSync supports that works on Azure. We already had an on premise working version that used the Sticky SQL Provider, switching seemed the easiest. We think switching to the Azure Table Provider will be easy and we will do this if we need it for scalability reasons, we would like to avoid this because we want the best performance possible.

Implementation details
To use WebSync we had to implement a few parts:

  • Database deployment script (we do not want WebSync to generate the tables it needs in our production environment)
  • Configuration (web.config for the most part, we really want to leverage Azure’s ServiceConfiguration for this, but webSync does not support it at this moment)
  • Server-side publishing
  • Client real-time handling

Database deployment script and configuration
The database deployment was easy, because WebSync can generate the tables for you (default). The only thing we had an issue with, is that if you do not want WebSync generating the tables, but create them yourself, you have to add that in config. you have to use the manageSchema atrribute:

<websync>
  <server providertype="FM.WebSync.Server.Providers.Stateless.SqlProvider">
    <providersettings>
      <add name="connectionStringName" value="ConnectionstringToUse" />
      <add name="manageSchema" value="false" />
    </providersettings>
  </server>
</websync>

Listing 01: manageSchema = false

Server-side publishing
On the server-side we lean heavily on Inversion of Control or Dependency Injection. We find it is real helpfull to compose our objects as we see fit, and it also helps tremendously in our TDD workflow. Because we need to support al kind of clients (webapplications, iPhone, other mobile devices) we evolved to the following (simplified) implementation:

IRequestHandlerWrapper

namespace Robin.FrontEnds.Realtime.Interfaces
{
    public interface IRequestHandlerWrapper
    {
        void Publish(long userPersonId, string data, string channelBase);
    }
}

Listing 02: IRequestHandlerWrapper

The IRequestHandlerWrapper is the interface we use to abstract the Publish method that WebSync uses away.

RequestHandlerWrapper

public class RequestHandlerWrapper : IRequestHandlerWrapper
    {
        private const string DashboardChannelFormat = "/{0}/{1}";
        
        public void Publish(long userPersonId, string data, string channelBase)
        {
            var channel = CreateDashboardChannel(channelBase, userPersonId);
            var publication = new Publication
            {
                Channel = channel,
                DataJson = data
            };

            RequestHandler.Publish(publication);
        }

        private static string CreateDashboardChannel(string channelBase, long userPersonId)
        {
            return string.Format(CultureInfo.InvariantCulture, DashboardChannelFormat, channelBase, userPersonId);
        }
    }

Listing 03: RequestHandlerWrapper

RequestHandlerWrapper implements IRequestHandlerWrapper. Line 14 uses FM.WebSync.Server.RequestHandler and executes the Publish().

Ofcourse, our implementation consists of more, but this, in short, is how we wrap WebSync. We got a RealtimeService (which implements IRealtimeService)and have the notion of 'Publisher', for every frontend (webapplication, device) we have a Publisher implementation, we inject these using Dependency injection. It makes us really flexible. For every new Publisher type we only have to create a new Publisher implementation, or leverage an existing one.

Client real-time handling
Our main webapplication has a ‘one page architecture’, we have a single page and use Javascript to show different parts of the application on that page. We use module and jquery widget factory  patterns. We are looking into backbone, ember and knockout. We have the notion of a ‘Communicator’ in our clientside architecture. In the Communicator we handle the subscriptions and all incoming real-time (push) messages.

Robin.communicator

robin.communicator = (function ()
{
    var self = this;

    fm.websync.client.init({
        requestUrl: robin.ui.constants.WebSyncRequestUrl
    });

    fm.websync.client.connect({
        onSuccess: function (args)
        {
            self.connected = true;
        }
    });

    addDashboardSubscription = function (agentId)
    {
        fm.websync.client.subscribe
        ({
            channel: '/desktop/' + agentId,
            onReceive: function (args)
            {
                var data;
                var notification = args.data;
                if (notification.NotificationType === robin.ui.constants.ConversationAdded)
                {
                    data = {
                        InboxItems: [notification.InboxItem]
                    };

                    $.publish(robin.ui.buildDashboardTopic(robin.ui.constants.NewInboxItemsTopic), [data]);
                }
                else if (notification.NotificationType === robin.ui.constants.ConversationRead)
                {
                    data = {
                        'ConversationId': notification.ConversationId
                    };
                    $.publish(robin.ui.buildConversationTopic(notification.ConversationId, robin.ui.constants.ReadTopic), [data]);
                }
            }
        });
    };

    removeDashboardSubscription = function (agentId)
    {
        fm.websync.client.unsubscribe
        ({
            channel: '/desktop/' + agentId,
            onSuccess: function (args)
            {
            }
        });
    };

    return {
        addDashboardSubscription: addDashboardSubscription,
        removeDashboardSubscription: removeDashboardSubscription,
        destroy: destroy
    };

})();

Listing 04: robin.communicator

As you can see in listing 04 the communicator also uses a jQuery plugin for pub/sub. This plugin makes it possible to use the publish-subscribe pattern throughout our modules and widgets. This plugin uses topics to differentiate between publications. Whenever a real-time message is pushed from the server, a $.publish(…)event for a particular topic is fired and all subscribers to this topic will recieve the message.

I made the listings as small as possible, to show the intend. The WebSync client api is clean and self explaining. You have to init, than connect and subscribe.
We have lots of advantages from using WebSync, the administration of what clients are connected to which subscriptions is abstracted away for the most part. The way WebSync handles all browsers is a big benefit also, we implemented WebSockets ourselves and had to do all that work manually untill now.

Henry Cordes
My thoughts exactly…


At Trinicom, the place I work, we are creating a new customer experience product. We lean heavily on good engineering practices and use Scrum as our development process.
For the application we defined cornerstones, or fundamentals: usability being one of them.
Because our application needs to be zero footprint web based, we use JavaScript a lot, mostly JQuery actually. We structure our JavaScript code using module and jquery widget patterns. We are thinking about implementing an mvc framework for Javascript, or use something like knockout.js, because we depend on real-time data.
Our application needs real-time info and because we develop for the future we are leveraging Websockets to make the real-time updates a reality.
As I mentioned we lean on good engineering practices, TDD is important for our C# code, so because we develop more and more JavaScript code, we are using TDD for our client-side code also.

QUnit
We use QUnit for our JavaScript unit tests. We use Continuous Integration and run our unit tests at every check-in, on TFS 2010. We wanted that for our JavaScript unit tests also.
So we started searching for information, there was very little. It seems that most ASP.NET devs do not integrate JavaScript unit tests into their builds, or do not unit test their JavaScript at all.

Web Client Developer Guidance
The Web Client Developer Guidance from Patterns & Practices on Codeplex provides some information on how to integrate QUnit unit tests into a TFS 2008 build. The problem was, that the guidance is incomplete and unclear. The P&P group created the QUnitExtensions.js that provides the familiar Asserts C# developers know and love, isTrue, isFalse, areEqual, IsNull, isNotNull, isUndefined, isNullOrUndefined, isNotNullNorUndefined.
The guidance exist of a ASP.NET MVC  app that takes JSON and serializes that to some xml format. The xml format is than persisted on disk. The JSON is provided by the QUnit testrunner that is extended and uses the url to a Controller of ASP.NET MVC app that takes JSON. The extended testrunner serializes the testresults into a hidden formfield and submits the contents of the field to the MVC app’s controller.

The format that was persisted to disk is unfamiliar and does not seem to map to any TFS format. The guidance says all you have to do is write the xml file to disk and tell the buildserver where it is by using an msbuild script and the result is written to the buildlog etc. That did not do the trick for us.

Workflow Activity
We created a Workflow activity that we use in our build workflow.

[RequiredArgument]
public InArgument ResultFile { get; set; }

[RequiredArgument]
public InArgument BuildDetail { get; set; }

protected override void Execute(CodeActivityContext context)
{
	IBuildDetail buildDetail = context.GetValue(this.BuildDetail);
	string file = context.GetValue(this.ResultFile);

	TestRun testRun = DeserializeTestRun(file);
	string outcome = GetOutcome(testRun.TestsFailed);

	IActivityTracking currentTracking = context.GetExtension().GetActivityTracking(context);
	IBuildInformationNode childNode = currentTracking.Node.Children.CreateNode();
	childNode.Type = currentTracking.Node.Type;

	IBuildStep  buildStep = childNode.Children.AddBuildStep("JS Unittest Build Step", string.Format("The Javascript Unittests for build: {0} have run", testRun.Name));
	if(testRun.TestsFailed > 0)
	{
	   IBuildError buildError = childNode.Children.AddBuildError(string.Format("The Javascript UnittestRun {0} ", outcome), DateTime.Now);
		buildError.Save();
	}

	WriteNewBuildInformationNode(currentTracking, string.Format("The Javascript UnittestRun for build: {0} {1}",  testRun.Name,outcome));
	WriteNewBuildInformationNode(currentTracking, string.Format("{0} test(s) run", testRun.TestsTotal));
	WriteNewBuildInformationNode(currentTracking, string.Format("{0} test(s) succesfull", testRun.TestsPassed));
	WriteNewBuildInformationNode(currentTracking, string.Format("{0} test(s) failed", testRun.TestsFailed));
	WriteTestCaseInformation(testRun.TestCases, currentTracking, buildDetail);

	SetBuildStatus(testRun.TestsFailed > 0, buildStep, buildDetail);

	if (testRun.TestsFailed == 0)
	{
		WriteToBuildSummary(buildDetail, string.Format("The Javascript UnittestRun for build: {0} {1}", testRun.Name, outcome));
	}

	buildStep.FinishTime = DateTime.Now;
	buildStep.Save();
	buildDetail.Information.Save();
}	

Listing 1

The activity takes a file and deserializes the xml into objects, it than uses the object graph to report into the buildlog if the unit test where succesfull, or not.

TestRun Domain objects