Enterprise configuration management

Almost every application requires some form of configuration information. This information can be as simple as a database connection string or as complex as multipart and hierarchical user preference information. How and where to store an application’s configuration data are questions you often face as a developer.

Any large enterprise application has many moving blocks. They all need to be configured for a proper working of the application. As the application size increases or for scalability the same configuration has to be repeated in different applications. For most applications once the configuration has been changed the application needs to be restarted.

Sample Code (create a blank console project, add json.net nuget)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;

namespace CM
{
    // configuration management API, 
    //1. allow clients specify typesafe models for configuations
    //2. Store flat data on server (table) which is easy to edit
    //3. can be extended to have inheritance of values (overrides)
    //4. can be extended to lock / unlock certain property by admins etc.
    
    class Program
    {
        //Sample configuration model
        internal class SampleConfigModal
        {
            public SampleConfigModal()
            {
                Address = new Address();
            }
            public string Name { get; set; }
            public Address Address { get; set; }
            public int Age { get; set; }


        }
        public class Address
        {
            public string Street { get; set; }

        }

        // this is how client api will look like
        static void Main(string[] args)
        {
            // sample client code
            var data = new SampleConfigModal() { Name = "Rajnish", Age = 18, Address = new Address() { Street = "Oxley" } };

            // save Configuration
            SaveConfiguration("app", "section", data);

            //get Configuration
            var data2 = GetConfiguration("app", "section");
        }

        // Client side framework api -> call to rest end point
        private static T GetConfiguration(string appName, string SectionName) where T : new()
        {
            var defaultValue = new T();
            var samplePayload = JsonConvert.SerializeObject(defaultValue);
            var payload = GetConfiguration(appName, SectionName, samplePayload);
            return JsonConvert.DeserializeObject(payload);
        }

        // Client side framework api -> call to rest end point
        private static void SaveConfiguration(string appName, string SectionName, T data)
        {
            var payload = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            SaveConfigurationa(appName, SectionName, payload);
        }


        //---------------------------------------- Server Code -------------------------- 
        //-------------- server has no knowledge of configuration structure or model

        private static Dictionary<string, string> storage;

        private static void SaveConfigurationa(string appName, string SectionName, string payload, string enumForHierarchyLevel = null)
        {
            // transformer
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(payload);
            var keyValueData = Flatten(data, section);

            // check if user has permission for level overrides
            // store with proper overides

            // store the flat list in sql or data 
            //| KEY |           |Value|            |OverrideType| - default,sysadmin,appadmin,groups,user etc
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            storage = keyValueData;
        }

        private static string GetConfiguration(string appName, string SectionName, string samplePayload)
        {
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(samplePayload);
            var keyValueSample = Flatten(data, section);
            // update data from sql or data store
            // apply property override rules and get value from overrides if exists
            var keyValueData = keyValueSample.Select(x => new KeyValuePair<string, string>(x.Key, storage[x.Key]));

            //read these
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            UnFlatten(data, section, keyValueData);

            var formatedData = JsonConvert.SerializeObject(data);
            /*
             * {
                  "Name": "Rajnish",
                  "Address": {
                    "Street": "Oxley"
                  },
                  "Age": "18"
                }
             * */
            return formatedData;

        }
        
        // Server side json helper

        private static void UnFlatten(JObject jsonObject, string prefix, IEnumerable<KeyValuePair<string, string>> data)
        {
            foreach (var item in data)
            {
                var keyName = item.Key.Substring(prefix.Length + 1);
                var storageValue = item.Value;
                if (keyName.Contains("."))
                {
                    var keys = keyName.Split('.');
                    var jtoken = (JToken)jsonObject;
                    foreach (var k in keys)
                    {
                        jtoken = jtoken.SelectToken(k);
                    }
                    ((JValue)jtoken).Value = storageValue;
                }
                else
                {
                    jsonObject[keyName] = storageValue;
                }
            }
        }

        private static Dictionary<string, string> Flatten(JObject jsonObject, string prefix)
        {

            IEnumerable jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
            Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
            {
                properties.Add(string.Format("{0}.{1}", prefix, jToken.Path), jToken.ToString());
                return properties;
            });
            return results;
        }
    }
}

Silverlight WebSockets – Duplex Communication

A duplex communication system is a system composed of two connected parties or devices that can communicate with one another in both directions. Duplex communication is required when you need to send some data in the reverse direction i.e. from Server to client. For scenario purpose assume that something happens on server (some event is received from source) and message from server should be sent to connected clients. Off-course the solution should be highly scalable with low latency client and server.

Let’s talk about silverlight client in our scenario

 There could be couple of options with silverlight client demonstrated by Gill Cleeren In series of article “The duplex story: looking at duplex communication in Silverlight 4

Silverlight 4.0 now supports 3 different types of duplex communications

  • HTTP Polling Duplex: Duplex requires that the server can itself initiate communication, which is not supported by HTTP, HTTP Polling duplex (HTTP long polling, or Comet-style) protocol allows us to do bi-directional communication over HTTP. What happens behind the scenes is the following:
    • The Silverlight client initiates the communication by sending an initial request to the service.
    • After the client is registered with the service, it continuously starts polling the service for updates.
    • Whenever the service has new messages to send to the client, they are queued up until a new poll arrives from the client.

The HTTP Polling Duplex is therefore not real duplexing: it creates an illusion of duplex communication by polling (at network layer) so frequently that it looks as if the messages are pushed from the service to the client. If you still want to use this method here is the link for you to tune performance of http polling duplex.

  • Sockets: Socket is implemented through the use of a TcpListener, Being pure TCP endpoints, sockets are fast and true duplex communicators. However sockets in Silverlight can only work over ports between 4502 and 4534 making it unsuitable for duplex communication over the internet. Secondly you need to build policy server to provide clientaccesspolicy.xml for sockets.Note that Silverlight 4 also allows that the policy calls are made over HTTP.
  • net.tcp binding: this entirely new binding, added in Silverlight 4, also supports duplex communication. net.tcp combines advantages of both polling duplex and sockets. Its programming model is, just like polling duplex, based on WCF. That means that we can take advantage of automatic proxy generation inside Visual Studio.
  • Poll based communication: Not scalable, out of scope.

So what’s next?

 HTML 5 – WebSockets

Web socket protocol or WebSockets are “TCP for the Web,” a next-generation bidirectional communication technology for web applications being standardized in part of Web Applications 1.0.

The Web Sockets API enables web applications to handle bidirectional communications with server-side process in a straightforward way. Once you get a Web Socket connection, you can send data from browser to server by calling a send() method, and receive data from server to browser by an onmessage event handler. The protocol is not raw TCP because it needs to provide the browser’s “same-origin” security model. It’s also not HTTP because web socket traffic differers from HTTP’s request-response model. you do need a new server implementation to communicate with web sockets .

web socket prototype is out now at http://html5labs.interoperabilitybridges.com/prototypes/available-for-download/websockets/html5protos_Download or websocket msi from here

 for more information see : http://tomasz.janczuk.org/2010/12/websockets-wcf-service-silverlight-and.html

Please also find the published websocket transport binding (Incomplete project)  here which i was trying to build using custom duplex WCF channel.

Custom Domain Service Factory

Parameterized constructors are not allowed in WCF RIA domain service, however your object model may requires to have constructor with arguments. For example EmployeeService (in this example) is WCF RIA Domain Service which requires Authentication and have constructor with authenticated User instance as parameter. The activate the service in such scenario you need to provide your own custom domain factory calls and plug the factory in Application_Start event of Global.aspx.

Sample Employee Service class

[EnableClientAccess()]
[RequiresAuthentication()]
public class EmployeeService : DomainService
{
        
    public EmployeeService(User user)
    {
            
    }
    //......
        
}

Custom Domain service factory

internal sealed class DomainServiceFactory : IDomainServiceFactory
{
    private IDomainServiceFactory _defaultFactory;


    public DomainServiceFactory(IDomainServiceFactory defaultFactory)
    {
        _defaultFactory = defaultFactory;
    }

    public static void Setup()
    {
        if (!(DomainService.Factory is DomainServiceFactory))
        {
            DomainService.Factory = new DomainServiceFactory(DomainService.Factory);
        }
    }

    public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context)
    {
        if (domainServiceType == typeof(EmployeeService))
        {
            DomainServiceContext authServiceContext =
                new DomainServiceContext(context, DomainOperationType.Query);
            AuthenticationService authService =
                (AuthenticationService)_defaultFactory.CreateDomainService(typeof(AuthenticationService), authServiceContext);
            User user = authService.GetUser();

            DomainService domainService = (DomainService)Activator.CreateInstance(domainServiceType, user);
            domainService.Initialize(context);
            return domainService;
        }
        else
        {
            return _defaultFactory.CreateDomainService(domainServiceType, context);
        }
    }

    public void ReleaseDomainService(DomainService domainService)
    {
        if (domainService is EmployeeService)
        {
            domainService.Dispose();
        }
        else
        {
            _defaultFactory.ReleaseDomainService(domainService);
        }
    }
}

Setup Domain service factory

protected void Application_Start(object sender, EventArgs e)
{
    DomainServiceFactory.Setup();

}