다음을 통해 공유


Owin SignalR WebApi Self Hosted

Table of contents
Introduction
Source Code
Project Outline
Setting up the project
OwinSignalR.Data
OwinSignalR.Pulse
OwinSignalR.Client
OwinSignalR.Notificator

Introduction

What is the Problem? – You are responsible for multiple applications that need to show real time notifications, show real time graphs that update as the data becomes available, update dashboards as live events happen via the backend processes. You also need to keep track of all the connected clients (users) and authenticate the connections.

The solution you came up with (if you are already using SignalR)? – Implement SignalR in all of your applications (Hubs that your JavaScript client connects to and waits for a callback/notification). You have multiple code bases that implement the same functionality; that is notifying connected clients (users) that a specific event took place, and passing on the data for that event.

The solution that I propose? – Implement one standalone/isolated SignalR project/solution that manages all your clients (users) connections, knows who is connected at all time, receives notifications from your backend process and forwards on the event(s) to the intended connected client(s) (user(s)). This standalone/isolated project/solution will also authenticate all your connected clients via unique API tokens and authorized referral URLS (allowed domains).

Here is a graph of how the Website/Web Application connects and receives data.

Source Code

You can download the code here

Project Outline

The solution consists of the following projects:

  1. OwinSignalR.Data
  2. OwinSignalR.Pulse
  3. OwinSignalR.Common
  4. OwinSignalR.PulseHost
  5. OwinSignalR.Notificator
  6. OwinSignalR.Client
  1. OwinSignalR.Data

    This project contains the data layer and database access code

  2. OwinSignalR.Pulse

    This project receives the SignalR connections, and also receives the data that is sent from any backend process or backend service.

  3. OwinSignalR.Common

    This project contains common code used by the other projects. It also contains the code used to connect to the OwinSignalR.Pulse project from any backend process or backend service.

  4. OwinSignalR.PulseHost

    This is a console application that hosts the OwinSignalR.Pulse project.

  5. OwinSignalR.Notificator

    This project acts as a backend service/process that is consintly sending data to the Website/WebApplication

  6. OwinSignalR.Client

    This is a demo Website/Webapplication that connects to OwinSignalR.Pulse and receives constant data updates from OwinSignalR.Notificator.

Setting up the project:

  1. Step1: Download and build the application to resolve the packages
  2. Step2: Run the CreateDatabase.sql script found in the root on your SQL server instance. This will create the database structure and will also insert a test application with the relevant information
  3. Step3: Right click on the solution and select “Multiple startup projects”, it should look similar to this:
  4. Step4: Expand the OwinSignalR.Client project and set Demo1.html as the startup page
  5. Step5: Run the solution

2 console applications should start up and 1 website. After the projects start you should see the following (the line below “OwinSignalR…” is a unique id for the SignalR connection. Yours will probably be different)

If you don’t see a connection Id in the OwinSignalR.PulseHost console window, please refresh the demo.html page (f5 or ctr+f5)

When you see the unique Id in the first console window the demo.html page connected successfully with the OwinSignalR.PulseHost. You can now hit the enter key in the OwinSignalR.Notificator console window to start sending the notifications to the demo.html page. The gauges should start moving and the”Activity feed” should start receiving notifications of events as they happen.

Let’s discuss the projects:

OwinSignalR.Data

  • OwinSignalR.Data /Configuration/AutomapperConfiguration.cs

    This file contains the mapping code to map EntityFrOwinSignalR.Pulseamework Models to DTO’s

    public static  class AutomapperConfiguration
    {
      public static  void Configure() 
      { 
        Mapper.CreateMap<Application, ApplicationDto>()
            .ForMember(dest => dest.ApplicationId          , opt => opt.MapFrom(src => src.ApplicationId))
            .ForMember(dest => dest.ApplicationName        , opt => opt.MapFrom(src => src.ApplicationName))
            .ForMember(dest => dest.ApiToken               , opt => opt.MapFrom(src => src.ApiToken))
            .ForMember(dest => dest.ApplicationSecret      , opt => opt.MapFrom(src => src.ApplicationSecret))
            .ForMember(dest => dest.ApplicationReferralUrls, opt => opt.MapFrom(src => src.ApplicationReferralUrls));
     
        Mapper.CreateMap<ApplicationReferralUrl, ApplicationReferralUrlDto>()
            .ForMember(dest => dest.ApplicationReferralUrlId, opt => opt.MapFrom(src => src.ApplicationReferralUrlId))
            .ForMember(dest => dest.ApplicationId           , opt => opt.MapFrom(src => src.ApplicationId))
            .ForMember(dest => dest.Url                     , opt => opt.MapFrom(src => src.Url));
      }
    }
    

     

  • OwinSignalR.Data/DataAccessors/ApplicationDataAccessor.cs

    This file contains the data access code that fetches the data from the database.

    public interface  IApplicationDataAccessor 
    {
      Application FetchApplication(string apiToken);
    }
     
    public class  ApplicationDataAccessor
        : DataAccessorBase, IApplicationDataAccessor
    {
      public Application FetchApplication(
        string apiToken)
      {
        var query = (from a in  OwinSignalrDbContext.Applications
                        where a.ApiToken == apiToken
                        select a).Include("ApplicationReferralUrls");
     
        return query.FirstOrDefault();
      }
    }
    

     

  • OwinSignalR.Data/DataAccessors/DataAccessorBase.cs

    This is the base class from where all DataAccessors enherit from to access the DbContext

    public class  DataAccessorBase
    {
      protected IDataContext DataContext 
      {
        get
        {
          return ObjectFactory.GetInstance<IDataContext>();
        }
      }
      protected IOwinSignalrDbContext OwinSignalrDbContext 
      {
        get
        {
          return DataContext.OwinSignalrDbContext;
        }
      }
    }
    

     

  • OwinSignalR.Data/Models/IDataContext.cs

    This file contains the IDataContext interface and the implementation for it. This interface is used when we need to access the IOwinSignalrDbContext interface.

    public interface  IDataContext
    {
      IOwinSignalrDbContext OwinSignalrDbContext { get; }
      void Initialize(IOwinSignalrDbContext owinSignalrDbContext);
    }
     
    public class  DataContext 
      : IDataContext
    {
      #region Private Members
      private IOwinSignalrDbContext _owinSignalrDbContext;
      #endregion
     
      public IOwinSignalrDbContext OwinSignalrDbContext
      {
        get
        {
            return _owinSignalrDbContext;
        }            
      }
     
      public void  Initialize(
        IOwinSignalrDbContext owinSignalrDbContext)
      {
        _owinSignalrDbContext = owinSignalrDbContext;
      }
    }
    

     

  • OwinSignalR.Data/Models/IOwinSignalrDbContext.cs

    This file contains the IOwinSignalrDbContext interface and the implementation for it. This interface is used when we need to access the EntityFramework DbContext

    public interface  IOwinSignalrDbContext 
    {
      DbSet<Application>            Applications            { get; set; }
      DbSet<ApplicationReferralUrl> ApplicationReferralUrls { get; set; }
    }
     
    public partial  class OwinSignalrDbContext
        : IOwinSignalrDbContext
    {
     
    }
    

     

  • OwinSignalR.Data/Models/OwinSignalrDbContext.edmx

    This file is the EntityFramework edmx that is generated from the database. You might have noticed that the OwinSignalrDbContext is marked as partial, the EntityFramework edmx contains the other partial class that actually implements the rest of IOwinSignalrDbContext

  • OwinSignalR.Data/Services/ApplicationService.cs

    This service is used by all referencing projects to fetch Application related information.

    public interface  IApplicationService 
    {
      ApplicationDto FetchApplication(string apiToken);
    }
     
    public class  ApplicationService
      : IApplicationService
    {
     public IApplicationDataAccessor ApplicationDataAccessor {  get; set; }        
     
     public ApplicationDto FetchApplication(
        string apiToken)
     {
       return Mapper.Map<ApplicationDto>(ApplicationDataAccessor.FetchApplication(apiToken));
     }
    }
    

OwinSignalR.Pulse

  • OwinSignalR.Pulse/Attributes/ApiAuthorizeAttribute.cs

    This file contains code to check if the API call contains a valid API Token, Application secret or if it is coming from one of the predefined URL's

    public class  ApiAuthorizeAttribute
      : AuthorizeAttribute
    {
      public IApplicationService ApplicationService { get; set; }
     
      public ApiAuthorizeAttribute() 
      {
        StructureMap.ObjectFactory.BuildUp(this);
      }
     
      protected override  bool IsAuthorized(
        HttpActionContext actionContext)
      {
        var queryString = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
     
        string token;
        string applicationSecret;
     
        if (!queryString.TryGetValue(Constants.TOKEN_QUERYSTRING, out  token))
        {
            return false;
        }
     
        if (!queryString.TryGetValue(Constants.APPLICATION_SECRET, out  applicationSecret))
        {
            return false;
        }
     
        if (String.IsNullOrEmpty(applicationSecret))
        {
            if (!IsValidReferer(token, actionContext))
            {
                return false;
            }
        }
        else if  (!IsApplicationSecretValidForToken(token, applicationSecret))
        {
            return false;
        }
     
        return true;
      }
     
      private bool  IsApplicationSecretValidForToken(
            string token
        , string  applicationSecret)
      {
        var application = ApplicationService.FetchApplication(token);
     
        if (application == null)
        {
            return false;
        }
     
        return application.ApplicationSecret == applicationSecret;
      }
     
      private bool  IsValidReferer(
        string token
        , HttpActionContext actionContext)
      {
        var application = ApplicationService.FetchApplication(token);
     
        if (application == null)
        {
            return false;
        }
     
        IEnumerable<string> headerValue;
     
        if (!actionContext.Request.Headers.TryGetValues("Referer", out  headerValue))
        {
            return false;
        }
        else
        {
            var urlReferral = Helpers.HttpHelper.GetUrlReferer(headerValue.ElementAt(0));
            return application.ApplicationReferralUrls.Any(x => x.Url == urlReferral);
        }
      }
    }
    

     

  • OwinSignalR.Pulse/Attributes/HubAuthorizeAttribute.cs

    This file contains code to check if the Hub conenction contains a valid API Token and if it is coming from one of the predefined URL's

    public class  HubAuthorizeAttribute
      : AuthorizeAttribute
    {
      public IApplicationService ApplicationService { get; set; }
     
      public HubAuthorizeAttribute() 
      {
        StructureMap.ObjectFactory.BuildUp(this);
      }
     
      public override  bool AuthorizeHubConnection(
            Microsoft.AspNet.SignalR.Hubs.HubDescriptor hubDescriptor
        , IRequest request)
      {
        var authorizeHubConnection = base.AuthorizeHubConnection(hubDescriptor, request);
     
        var tokenValues    = request.QueryString.GetValues(Constants.TOKEN_QUERYSTRING);
        var clientIdValues = request.QueryString.GetValues(Constants.UNIQUE_CLIENT_ID);
     
        if ((tokenValues.Count() == 0 || tokenValues.Count() > 1) || (clientIdValues.Count() == 0 || clientIdValues.Count() > 1))
        {
            return false;
        }
     
        var application = ApplicationService.FetchApplication(tokenValues.ElementAt(0));
     
        if (application == null)
        {
            return false;
        }
     
        var urlReferer = GetUrlReferer(request);
     
        return application.ApplicationReferralUrls.Any(x => x.Url == urlReferer);
      }
     
      protected override  bool UserAuthorized(
        System.Security.Principal.IPrincipal user)
      {
        return true;
      }
     
      private string  GetUrlReferer(
        IRequest request)
      {
        return Helpers.HttpHelper.GetUrlReferer(request.Headers["Referer"]);
      }
    }
    

     

  • OwinSignalR.Pulse/Attributes/DependencyInjectionConfiguration.cs

    This file contains the setup code for the Dependency Injection used throughout the application

    public static  class DependencyInjectionConfiguration
    {
      public static  void Configure()
      {
        ObjectFactory.Initialize(x =>
        {
            x.For<Data.Models.IDataContext>().HybridHttpOrThreadLocalScoped().Use<Data.Models.DataContext>();
                     
            x.For<Data.Services.IApplicationService>().Use<Data.Services.ApplicationService>();
            x.For<Data.DataAccessors.IApplicationDataAccessor>().Use<Data.DataAccessors.ApplicationDataAccessor>();
     
            x.SetAllProperties(y =>
            {   
                y.OfType<Data.Services.IApplicationService>();
                y.OfType<Data.DataAccessors.IApplicationDataAccessor>();                                        
            });
        });
      }
    }
    

     

  • OwinSignalR.Pulse/Helpers/HttpHelper.cs

    This file contains code to easily extract the URL referrer (domain from where the request is coming from)

    public static  class HttpHelper
    {
      public static  string GetUrlReferer(
        string referer)
      {
        if (string.IsNullOrEmpty(referer))
        {
            return string.Empty;
        }
     
        referer = referer.IndexOf("://") > -1 ? referer.Substring(referer.IndexOf("://") + 3) : referer;
        referer = referer.IndexOf("/") > -1 ? referer.Substring(0, referer.IndexOf("/") - 1) : referer;
        referer = referer.IndexOf(":") > -1 ? referer.Split(':')[0] : referer;
     
        return referer;
      }
    }
    

     

  • OwinSignalR.Pulse/ConnectionManager.cs

    This file contains code to keep track of all the connections made via SignalR

    public class  ConnectionManager
    {
      #region Private Members
      private static  ConcurrentDictionary<Tuple<string, string>, ClientConnection> _clients;
      #endregion
     
      #region Public Properties
      public static  ClientConnection ActiveConnections(
            string token
        , string  clientId)
      {
        if (_clients == null)
        {
            return new  ClientConnection();
        }
        else
        {
            ClientConnection value;
     
            if (_clients.TryGetValue(new Tuple<string, string>(token, clientId), out  value))
            {
                return value;
            }
            else
            {
                return new  ClientConnection();
            }
        }
      }
      #endregion
     
      #region Static Methods
      public static  void Add(
            string token
        , string  clientId
        , string  connectionId)
      {
        if (_clients == null)
        {
            _clients = new  ConcurrentDictionary<Tuple<string, string>, ClientConnection>();
        }
     
        var user = _clients.GetOrAdd(new Tuple<string, string>(token, clientId), new  ClientConnection
        {
            Connections = new  List<ClientConnectionDetials>()
        });
     
        lock (user.Connections)
        {
            user.Connections.Add(new ClientConnectionDetials { ConnectionId = connectionId });
        }
      }
     
      public static  void Remove(
            string token
        , string  clientId
        , string  connectionId)
      {
        ClientConnection clientConnection;
        _clients.TryGetValue(new Tuple<string, string>(token, clientId), out  clientConnection);
     
        if (clientConnection != null)
        {
            lock (clientConnection.Connections)
            {
                clientConnection.Connections.RemoveAll(x => x.ConnectionId == connectionId);
     
                if (!clientConnection.Connections.Any())
                {   
                    ClientConnection removedClient;
                    _clients.TryRemove(new Tuple<string, string>(token, clientId), out  removedClient);
                }
            }
        }
      }
     
      public static  void ClearConnections()
      {
        lock (_clients)
        {
            _clients = new  ConcurrentDictionary<Tuple<string, string>, ClientConnection>();
        }
      }
      #endregion
     
      public class  ClientConnection
      {
        public List<ClientConnectionDetials> Connections { get; set; }
     
        public ClientConnection()
        {
            Connections = new  List<ClientConnectionDetials>();
        }
      }
     
      public class  ClientConnectionDetials
      {
        public string  ConnectionId { get; set; }
      }
    }
    

     

  • OwinSignalR.Pulse/Host.cs

    This file contains the code that starts up the WebApp

    public class  Host
    {
      private static  IDisposable _webServer;
     
      public static  void Start()
      {
        var url = System.Configuration.ConfigurationManager.AppSettings["PulseUrl"];
        _webServer = WebApp.Start(url);
        Console.WriteLine(String.Format("OwinSignalR notifications now running on {0}", url));
      }
     
      public static  void Stop()
      {
        if (_webServer != null)
        {
            _webServer.Dispose();
        }
      }
    }
    

     

  • OwinSignalR.Pulse/PulseController.cs

    Handles all the Api requests sent to the OwinSignalR.Pulse project

      [ApiAuthorize]
      public class  PulseController
        : ApiController
      {
      public IApplicationService ApplicationService { get; set; }
     
      public PulseController() 
      {
        StructureMap.ObjectFactory.BuildUp(this);
      }         
             
      [HttpPost]
      public IHttpActionResult Connect(
       string token
        , string  clientId
        , string  method            
        , [System.Web.Http.FromBody]object value
        , string  applicationSecret = "")
      {
        try
        {   
            InvokeClientHubMethod(token, clientId, method, value);
            return Ok();
        }
        catch (Exception excp)
        {
            Startup.Logger.Error("Error on PulseController.Connect", excp);
            return InternalServerError(excp);
        }            
      }
     
      #region Private Methods
      private void  InvokeClientHubMethod(
       string token
        , string  clientId
        , string  method
        , params  object[] args)
      {
        var context = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<Pulse.PulseHub>();
        IClientProxy clientProxy;
     
        if (String.IsNullOrEmpty(clientId))
        {
            clientProxy = context.Clients.All;
        }
        else
        {
            clientProxy = context.Clients.Clients(ConnectionManager.ActiveConnections(token, clientId).Connections.Select(x => x.ConnectionId).ToList());
        }
     
        clientProxy.Invoke(method, args);
      }         
      #endregion
    }
    

     

  • OwinSignalR.Pulse/PulseHub.cs

    Handles all the SignalR request/connections

      [HubAuthorize]
      public class  PulseHub
        :  Hub
      {
         #regionPrivate Properties
        private string  Token
        {
        get
        {
            var token = Context.QueryString[Constants.TOKEN_QUERYSTRING];
     
            if (String.IsNullOrEmpty(token))
            {
                throw new  UnauthorizedAccessException("Token not specified");
            }
     
            return token;
        }
        }
     
        private string  ClientId
        {
        get
        {
            var clientId = Context.QueryString[Common.Constants.UNIQUE_CLIENT_ID];
     
            if (String.IsNullOrEmpty(clientId))
            {
                throw new  UnauthorizedAccessException("ClientId not specified");
            }
     
            return clientId;
        }
        }
        #endregion
     
        #region IHub Interface
        public override  System.Threading.Tasks.Task OnConnected()
        {
        try
        {
            Console.WriteLine(Context.ConnectionId);
     
        #if DEBUG
            Startup.Logger.Info(String.Format("OnConnected: Token {0} ClientId {1}", Token, ClientId));
        #endif
     
            ConnectionManager.Add(Token, ClientId, Context.ConnectionId);
        }
        catch (Exception excp)
        {
            Startup.Logger.Error(excp.Message);
        }
     
        return base.OnConnected();
       }
     
      public override  System.Threading.Tasks.Task OnDisconnected(
        bool stopCalled)
      {
        try
        {
      #if DEBUG
            Startup.Logger.Info(String.Format("SignalR OnDisconnected: Token {0} ClientId {1}", Token, ClientId));
      #endif
            ConnectionManager.Remove(Token, ClientId, Context.ConnectionId);
        }
        catch (Exception excp)
        {
            Startup.Logger.Error(excp.Message);
        }
     
        return base.OnDisconnected(stopCalled);
      }
      #endregion    
    }
    

     

  • OwinSignalR.Pulse/Startup.cs

    This file contains all the startup code required for the OwinSignalR.Pulse project. It registers the WebApi Controllers and the SignalR Hubs.

    public class  Startup
    {
      #region Private Members
      private static  readonly log4net.ILog _log =   log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
      #endregion
     
      #region Public Properties
      public static  log4net.ILog Logger
      {
        get
        {
            return _log;
        }
      }
      #endregion
     
      public void  Configuration(
        IAppBuilder app)
      {
        Pulse.Configuration.DependencyInjectionConfiguration.Configure();
        OwinSignalR.Data.Configuration.AutomapperConfiguration.Configure();
     
        log4net.Config.XmlConfigurator.Configure();
     
        RegsiterHubs();
     
        app.UseCors(CorsOptions.AllowAll);
     
        app.Use<StructureMapOWINMiddleware>();
     
        app.MapSignalR();
     
        HttpConfiguration config = new  HttpConfiguration();
     
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new  { id = RouteParameter.Optional }
        );
                 
        app.UseWebApi(config);
      }
     
      #region Private Methods
      private void  RegsiterHubs()
      {
        GlobalHost.DependencyResolver.Register(typeof(PulseHub), () => new  PulseHub());
      }
     
      private void  SetJsonFormatting(
        HttpConfiguration config)
      {
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new  CamelCasePropertyNamesContractResolver();
        config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
      }
      #endregion
    }
    

     

  • OwinSignalR.Pulse/Structuremap.Owin.cs

    OWIN middleware that sets up the EntityFramework DbContext per request.

    public class  StructureMapOWINMiddleware
        :   OwinMiddleware
      {
        public StructureMapOWINMiddleware(
        OwinMiddleware next)
        : base(next)
      {
     
      }
     
      public async override Task Invoke(IOwinContext context)
      {
        StructureMap.ObjectFactory.GetInstance<IDataContext>().Initialize(new Data.Models.OwinSignalrDbContext());
        await Next.Invoke(context);
      }
    }
    

OwinSignalR.Client

  • OwinSignalR.Client/Content/bootstrap-theme.min.cs

    This is the bootstrap theme css file

  • OwinSignalR.Client/Content/bootstrap.min.cs

    This is the bootstrap library css file

  • OwinSignalR.Client/Scripts/bootstrap.min.js

    This is the bootstrap library javascript file

  • OwinSignalR.Client/Scripts/jquery-2.2.0.min.js

    This is the jquery library javascript file

  • OwinSignalR.Client/Scripts/jquery.signalR-2.2.0.min.js

    This is the SignalR library javascript file

  • OwinSignalR.Client/Scripts/OwinSignalR.js

    This is the main javascript file to connect to the PulseHub

    function connect(token, clientId, callbacks) {
      $.connection.hub.stop();
      $.connection.hub.qs = { 'Token': token, 'ClientId': clientId };
      $.connection.hub.url = signalrRoot + '/signalr';    
     
      var pulseHub = $.connection.pulseHub;
     
      if (pulseHub !== null && pulseHub !== undefined) {
        for (var i = 0; i < callbacks.length; i++) {
          for (var method in callbacks[i]) {
            pulseHub.client[method] = callbacks[i][method];                
          }            
        }        
      }
     
      $.connection.hub.start({ transport: 'longPolling'  });
    }
    

     

  • OwinSignalR.Client/Demo.html

    This is the demo html page

    <head>
        <title></title>
        <meta charset="utf-8" />
     
        <link href="Content/bootstrap.min.css" rel="stylesheet" />
        <link href="Content/bootstrap-theme.min.css" rel="stylesheet" />
     
        <script src="Scripts/jquery-2.2.0.min.js"></script>
        <script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
     
        <script src="http://localhost:8014/signalr/hubs"></script>
     
        <script>
            var signalrRoot = 'http://localhost:8014';
        </script>
     
        <script src="Scripts/OwinSignalR.js"></script>
     
        <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
     
        <script type="text/javascript">
            google.charts.load('current', { 'packages': ['gauge'] });
            google.charts.setOnLoadCallback(drawChart);
            var chart;
            var data;
            var options;
     
            function drawChart() {
     
                data = google.visualization.arrayToDataTable([
                     ['Label', 'Value'],
                     ['Memory', 80],
                     ['CPU', 55],
                     ['Network', 68]
                ]);
     
                options = {
                    width: 200, height: 700,
                    redFrom: 90, redTo: 100,
                    yellowFrom: 75, yellowTo: 90,
                    minorTicks: 5
                };
     
                chart = new google.visualization.Gauge(document.getElementById('chart_div'));
     
                chart.draw(data, options);
            }
        </script>
     
        <script type="text/javascript">
            connect('48a2000467394b008938cc77b4529e3a', 'testUser', [{
                testCallBack: function (args) {
                    $('#activityFeedMessage').hide();
                    $('#activityFeed').prepend('<li class="list-group-item"><label class="label label-success">' + args.createDate + '</label><br />' + args.message + '</li>');
                },
                serverLoadNetwork: function (args) {
                    data.setValue(2, 1, args);
                    chart.draw(data, options);
                },
                serverLoadCPU: function (args) {
                    data.setValue(1, 1, args);
                    chart.draw(data, options);
                },
                serverLoadMemory: function (args) {
                    data.setValue(0, 1, args);
                    chart.draw(data, options);
                }
            }]);
        </script>
     
        <style type="text/css">
            .list-group {
                max-height: 500px;
                overflow-y: scroll;
            }
        </style>
    </head>
    <body>
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="#">Demo</a></li>                    
                    </ul>
                </div>
            </div>
        </nav>
        <div class="container">
            <div class="row">
                <div class="col-sm-offset-3 col-sm-6">
                    <div id="chart_div"></div>                               
                </div>
                <div class="col-sm-3">
                    <div class="panel panel-primary">
                        <div class="panel-heading">Activity feed</div>
                        <div class="panel-body" id="activityFeedMessage">
                            <span class="label label-warning">no activity to display</span>
                        </div>
                        <ul class="list-group" id="activityFeed"></ul>
                    </div>
                </div>
            </div>
        </div>
    </body>
    

     

OwinSignalR.Notificator

  • NotificationHelper.cs

    This file contains the code that updates the "Activity Feed"

    public static  class NotificationHelper
    {
      private static  Timer _notificationTimer;
     
      private static  List<string> _possibleMessages = new  List<string> { { "Batch process complete"  }, { "SQL Indexes rebuild complete"  }, { "Integrity check complete"  }, { "Temp files deleted"  }, { "File ready for processing"  }, { "Server now available"  }, { "Inactive accounts deactivated"  }, { "Welcome emails job complete"  }, { "Annual renew program emails sent"  } };
      private static  Random       _randomMessage    = new Random();
     
      public static  void Start() 
      {
        _notificationTimer = new  Timer(OnNotificationTimerCallBack, null, 1000, 2500);
      }
     
      private static  void OnNotificationTimerCallBack(
        object state)
      {
        var service           = new  NotificationService();
        var token             = System.Configuration.ConfigurationManager.AppSettings["Token"];
        var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"];
     
        service.Send(token, applicationSecret, "testUser",  "testCallBack",  new  TestClass
        {
            CreateDate  = DateTime.Now.ToString("yyyy/MM/dd HH:ss"),
            Message     = _possibleMessages.ElementAt(_randomMessage.Next(_possibleMessages.Count - 1))
        });
     
        Console.WriteLine("Sent {0}", DateTime.Now);
      }
     
      public class  TestClass 
      {
        public string  CreateDate { get; set; }
        public string  Message    { get; set; }
      }
    }
    
  • Program.cs

  • ServerLoadHelper.cs

    This file contains the code that updated the Graphs/Gauges with the fake server load data.

    public class  ServerLoadHelper
    {
      private static  Timer _serverLoadMemoryTimer; 
      private static  Timer _serverLoadCPUTimer;
      private static  Timer _serverLoadNetworkTimer;
     
      private static  Random _serverLoad = new Random();            
     
      public static  void Start() 
      { 
        _serverLoadMemoryTimer  = new  Timer(OnServerLoadMemoryTimer , null, 1000, 1000);
        _serverLoadCPUTimer     = new  Timer(OnServerLoadCPUTimer    , null, 1000, 1700);
        _serverLoadNetworkTimer = new  Timer(OnServerLoadNetworkTimer, null, 1000, 2300);
      }
     
      private static  void OnServerLoadNetworkTimer(
        object state)
      {
        var service           = new  NotificationService();
        var token             = System.Configuration.ConfigurationManager.AppSettings["Token"];
        var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"];
     
        service.Send(token, applicationSecret, "testUser",  "serverLoadNetwork", _serverLoad.Next(100));
      }
     
      private static  void OnServerLoadCPUTimer(
        object state)
      {
        var service           = new  NotificationService();
        var token             = System.Configuration.ConfigurationManager.AppSettings["Token"];
        var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"];
     
        service.Send(token, applicationSecret, "testUser",  "serverLoadCPU", _serverLoad.Next(100));
      }
     
      private static  void OnServerLoadMemoryTimer(
        object state)
      {
        var service           = new  NotificationService();
        var token             = System.Configuration.ConfigurationManager.AppSettings["Token"];
        var applicationSecret = System.Configuration.ConfigurationManager.AppSettings["ApplicationSecret"];
     
        service.Send(token, applicationSecret, "testUser",  "serverLoadMemory", _serverLoad.Next(100));
      }
    }