Search This Blog

Sunday, October 23, 2011

Silverlight: Notification Messages from Desktop Application

Summary: A simple example showing how a Silverlight application can subscribe for notification messages from a desktop application.


Introduction
The example bellow shows a scenario where the Silverlight application needs to observe desired events from a desktop application.
(E.g. some data got updated and your Silverlight client needs to react accordingly.)

The example consists of the Silverlight application and the standalone desktop application.
The desktop application calculates given numbers and notifies the result via the broker.
The Silverlight client subscribes in the broker to be notified whenever a calculation is performed.
Silverlight and desktop communicates via TCP.

The example can be downloaded from here.

The example is based on the Eneter Messaging Framework that provides components for various communication scenarios.
(The framework is free for non-commercial use (License) and can be downloaded from http://www.eneter.net.
The online help for developers can be found at http://www.eneter.net/OnlineHelp/EneterMessagingFramework/Index.html.)



Desktop Application
The desktop application is responsible for counting two numbers and notifying the result to subscribed clients.
As you can see on the picture, the desktop application contains three communication components:
  • TCP Policy Server
  • Broker
  • Duplex Dispatcher.

TCP Policy Server
The TCP communication between Silverlight and a desktop application requires the policy server. The policy server is the special service listening to the port 943. When a Silverlight application tries to open the TCP connection the Silverlight framework internally sends the request to the port 943 and waits for the response. The policy server receives the request and responses with the policy file.
If the policy file allows the communication, Silverlight opens the connection and the communication can start.

Broker
The Broker is the component providing the publish-subscribe communication. In our case, the Silverlight client uses BrokerClient to subscribe for desired notification messages.
The desktop application uses BrokerClient to send notification messages to the broker.
Then when Broker receives the notification message, it forwards it to all subscribed clients.

Duplex Dispatcher
The Duplex Dispatcher allows Broker communicating via TCP and the local Synchronous messaging at the same time.
The reason is, the Silverlight client communicates via TCP, but the internal BrokerClient communicates via Synchronous messaging (because it would not be effective to communicate inside one process via TCP).
Therefore, the Dispatcher component is used. The Dispatcher component receives messages on attached input ports and forwards them to all attached output ports.
In our case the Dispatcher receives messages via TCP and Synchronous Messaging and forwards them to the Broker.

The implementation is here:

using System;
using System.Windows.Forms;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.SynchronousMessagingSystem;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.Messaging.Nodes.Broker;
using Eneter.Messaging.Nodes.Dispatcher;

namespace DesktopCalculator
{
    public partial class Form1 : Form
    {
        private TcpPolicyServer myPolicyServer;
        private IDuplexBrokerClient myBrokerClient;
        private IDuplexDispatcher myDispatcher;

        public Form1()
        {
            InitializeComponent();

            // Start the policy server.
            // Note: Silverlight requires the policy server to start the communication.
            //       The policy server tells the silverlight if the communication is permitted.
            myPolicyServer = new TcpPolicyServer();
            myPolicyServer.StartPolicyServer();


            // Local messaging connecting the Dispatcher and the Broker.
            IMessagingSystemFactory aLocalMessaging = new SynchronousMessagingSystemFactory();

            // Create the broker forwarding notifications to subscribed clients.
            IDuplexBrokerFactory aBrokerFactory = new DuplexBrokerFactory();
            IDuplexBroker aBroker = aBrokerFactory.CreateBroker();

            // Attach the input channel to the broker.
            // Note: The broker will receive via this channel messages from the Dispatcher.
            IDuplexInputChannel aBrokerInputChannel =
                aLocalMessaging.CreateDuplexInputChannel("MyBrokerChannelId");
            aBroker.AttachDuplexInputChannel(aBrokerInputChannel);

            // Create the dispatcher.
            // The dispatcher will receive TCP messages and also local messages and forward them
            // to the broker.
            // (I.e. broker can get messages from local messaging and from TCP at the same time.)
            IDuplexDispatcherFactory aDispatcherFactory = new DuplexDispatcherFactory(aLocalMessaging);
            myDispatcher = aDispatcherFactory.CreateDuplexDispatcher();

            // Add the broker channel to the dispatcher.
            myDispatcher.AddDuplexOutputChannel("MyBrokerChannelId");

            // Attach the input channel receiving messages from the broker client.
            // Note: The channel id is same as the broker client has.
            IDuplexInputChannel aDispatcherLocalInputChannel =
                aLocalMessaging.CreateDuplexInputChannel("MyLocalBrokerClientId");
            myDispatcher.AttachDuplexInputChannel(aDispatcherLocalInputChannel);


            // Create the broker client notifying calculated results to the broker.
            myBrokerClient = aBrokerFactory.CreateBrokerClient();

            // Attach the output channel to the broker.
            // Note: The broker client will use this channel to send messages to the dispatcher.
            IDuplexOutputChannel aBrokerClientOutputChannel =
                aLocalMessaging.CreateDuplexOutputChannel("MyLocalBrokerClientId");
            myBrokerClient.AttachDuplexOutputChannel(aBrokerClientOutputChannel);


            // TCP messaging for the communication with the Silverlight client.
            IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();

            // Note: Silverlight can communicate only on ports: 4502-4532
            IDuplexInputChannel aTcpInputChannel =
                aTcpMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:4510");

            // Finaly attach the TCP input channel to the dispatcher and start listening
            // to Silverlight clients.
            myDispatcher.AttachDuplexInputChannel(aTcpInputChannel);
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Detach input channels from the dispatcher and stop the listening.
            myDispatcher.DetachDuplexInputChannel();

            // Stop the policy server.
            myPolicyServer.StopPolicyServer();
        }

        private void CalculateBtn_Click(object sender, EventArgs e)
        {
            // Calculate numbers.
            int aResult = int.Parse(Number1TextBox.Text) + int.Parse(Number2TextBox.Text);

            // Show the result in the text box.
            ResultTextBox.Text = aResult.ToString();

            // Notify the result to the broker.
            myBrokerClient.SendMessage("MyResultMessageId", ResultTextBox.Text);
        }
    }
}

Silverlight Client Application
The Silverlight client is responsible for subscribing to receive notifications from the desktop application - whenewer two numbers are calculated.
To subscribe for messages, the Silverlight application uses BrokerClient communicating via the TCP channel with the desktop application.

The implementation of the client is here:

using System.Windows;
using System.Windows.Controls;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.Messaging.Nodes.Broker;

namespace SilverlightObserver
{
    public partial class MainPage : UserControl
    {
        private IDuplexBrokerClient myBrokerClient;
        private bool myIsSubscribedFlag;

        public MainPage()
        {
            InitializeComponent();

            // Create the BrokerClient to subscribe and receive notifications.
            IDuplexBrokerFactory aBrokerFactory = new DuplexBrokerFactory();
            myBrokerClient = aBrokerFactory.CreateBrokerClient();

            // Handle received notifications.
            myBrokerClient.BrokerMessageReceived += OnBrokerMessageReceived;


            // TCP messaging for the communication with the desktop application.
            IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();

            // Create the TCP output channel and attach it to the BrokerClient.
            IDuplexOutputChannel aTcpOutputChannel =
                aTcpMessaging.CreateDuplexOutputChannel("tcp://127.0.0.1:4510");

            myBrokerClient.AttachDuplexOutputChannel(aTcpOutputChannel);
        }


        private void LayoutRoot_Unloaded(object sender, RoutedEventArgs e)
        {
            // Detach the TCP output channel and stop the communication.
            myBrokerClient.DetachDuplexOutputChannel();
        }

        private void SubscribeBtn_Click(object sender, RoutedEventArgs e)
        {
            // If not subscribed, then subscribe for the same type
            // as is notified from the desktop.
            if (!myIsSubscribedFlag)
            {
                myBrokerClient.Subscribe("MyResultMessageId");
                myIsSubscribedFlag = true;
            }
        }

        private void UnsubscribeBtn_Click(object sender, RoutedEventArgs e)
        {
            // If subscribed, then unsubscribe.
            if (myIsSubscribedFlag)
            {
                myBrokerClient.Unsubscribe();
                myIsSubscribedFlag = false;
            }
        }

        private void OnBrokerMessageReceived(object sender, BrokerMessageReceivedEventArgs e)
        {
            // Display the notified message.
            ResultTextBox.Text = e.Message as string;
        }
    }
}

Communicating Applications



Have a nice day :-)

3 comments:

  1. Hi, thanks very much for such a useful library. It has helped us a lot. However, we encountered a small issue when running the code you have presented in this tutorial. It happens when
    myDispatcher.DetachDuplexInputChannel(); and myPolicyServer.StopPolicyServer(); methods are called. We encountered the following exception 'System.ObjectDisposedException' when calling the above lines of code. This exception then results in us not being able to execute another library(WiiMote lib) that we are using. Any help would be appreciated !

    ReplyDelete
  2. Hi CR CODERZ, I am really glad the library helped you.
    I have tried to investigate the issue you mentioned but I could not reproduce it.
    Here is the link where you can download the whole example from the article. Please try, if you can reproduce the problem there.
    http://eneter.net/Downloads/Examples/NotificationsFromDesktop.zip

    Also, you can enable debug traces of the eneter framework for more detailed information: (you can uncomment the following lines in the example)

    // Enabling detailed debug tracing to the file.
    EneterTrace.DetailLevel = EneterTrace.EDetailLevel.Debug;
    EneterTrace.TraceLog = new StreamWriter("d:/tracefile.txt");


    Please investigate the problem with enabled debug tracing and feel free to send me the trace file.

    ReplyDelete
  3. Thanks Ondrej we'll try it out and get back to you.

    ReplyDelete