MOBILE

Windows Phone 7 Development : Push Notifications - Implementing Cloud Service to Track Push Notifications

5/24/2011 3:57:00 PM
So far in the preceding walkthroughs, you have used a somewhat unrealistic approach to communicating push notification URLs from the Windows Phone 7 client application to the push notification server. You copied that URL from the Debug window of the client application and pasted it into the server application, where it was used to send tiles, toasts, and raw notifications to the Windows Phone 7 applications. To make the stock alerts application a bit more real-world, however, you must automate the URL communication piece. In this section, you will learn how to do that using a cloud service built with the Microsoft Windows Communication Foundation (WCF) stack of technologies.

1. Creating a WCF Service to Track Notification Recipients

In this section, we will show you how to enhance the PNServer application built previously by adding a WCF service to it. WCF is a very powerful technology with an array of configuration options for creating and hosting cloud services. You will be building what is known as a self-hosted service, which means that it will be hosted within the Windows Forms application and you will write code to initialize and start that service. Another important point about this service is that it will be a RESTful service, which, for our purposes right now, means that you can access operations of the service over the properly formatted URLs, as you will see shortly.

Before you create a RESTful WCF service, however, you may need to make a small change in the Visual Studio environment to reference assemblies you need to create that service. The reason for this is that, by default, Visual Studio creates a lightweight profile for client applications, such as Windows Forms or Windows Presentation Foundation (WPF) applications. This lightweight profile omits many web-related assemblies by default because the chances of a true client application needing them are slim.

The setting that controls which assemblies are included or left out is the Target Framework setting, and it is located on your project's Properties page. You need to change this setting from ".Net Framework 4 Client Profile" to ".Net Framework 4." To accomplish that, open the PNServer project if it's not already open, right-click the project name, and then select Properties. Locate the Target Framework setting and set it to ".Net Framework 4," as illustrated in Figure 17-9.

Now follow these steps to complete creation of the WCF service. First, before creating the service, you need to include the System.ServiceModel.Web assembly to the PNServer project.

  1. Right-click the project name and select Add Reference. Locate the System.ServiceModel.Web assembly in the list, highlight it, and click OK.

Now, you will add WCF service files to the project. Adding the WCF service files will consist of two parts: creating what is known as a Service Contract, which will appear in the form of an Interface file, and defining a class that will physically implement the methods defined within the Service Contract.

  1. To create the Service Contract, right-click the project name, choose Add=>New Item, and then scroll almost all the way to the bottom and pick WCF Service. Name the service "Registration Service," and then click OK.

  2. Add the following statement to the top of the IRegistrationService.cs file created:

    using System.ServiceModel.Web;

  3. Add the following code to the IRegistrationService.cs file:

    [ServiceContract]
    public interface IRegistrationService
    {
    [OperationContract, WebGet]
    void Register(string uri);

    [OperationContract, WebGet]
    void Unregister(string uri);
    }

Note how you defined two operations for the service to perform: Register new Windows Phone 7 clients for push notifications and Unregister them.

Now it's time to add the implementation of the Register and Unregister methods.

  1. Double-click the RegistrationService.cs file that Visual Studio added to your project. Make the RegistrationService.cs file look like the code here.

    public class RegistrationService : IRegistrationService
    {
    private static List<Uri> subscribers = new List<Uri>();
    private static object obj = new object();

    public void Register(string uri)
    {
    Uri channelUri = new Uri(uri, UriKind.Absolute);
    Subscribe(channelUri);
    }

    public void Unregister(string uri)
    {
    Uri channelUri = new Uri(uri, UriKind.Absolute);
    Unsubscribe(channelUri);
    }

    private void Subscribe(Uri channelUri)
    {
    lock (obj)
    {
    if (!subscribers.Exists((u) => u == channelUri))
    {
    subscribers.Add(channelUri);
    }
    }
    }

    public static void Unsubscribe(Uri channelUri)
    {
    lock (obj)
    {
    subscribers.Remove(channelUri);
    }
    }

    public static List<Uri> GetSubscribers()
    {
    return subscribers;
    }
    }


Take a look closer look at the code that you just added to the RegistrationService.cs file. Notice that the RegistrationService class implements the IRegistrationService interface on the very first line—this is important! Aside from that, the code is pretty straightforward: a collection of push notification URIs is maintained in the static subscribers variable, and every client that calls the Register method of the service gets added to that list of subscribers. The lock function is used to prevent multiple clients changing the same data at the same exact moment in time, possibly resulting in incomplete and unpredictable data.

In the beginning of this section, we said that a WCF service hosted by a Windows Forms application needs initialization code to start up. One of the places this initialization code can go is in the load event of Form1.

  1. Here's the code you need to start up the service. Copy it to the load event of Form1 here:

    ServiceHost host;
    host = new ServiceHost(typeof(RegistrationService));
    host.Open();

You are almost done—now you need only to provide some configuration parameters for the WCF service to run.

  1. Open the app.config file and add the following configuration parameters to the <system.ServiceModel> element (you should already have configuration settings defined within <system.ServiceModel>, but now you need to make sure those settings match precisely what is pasted here):

    <system.serviceModel>
    <behaviors>
    <endpointBehaviors>
    <behavior name="EndpointPNServerServiceBehavior">
    <webHttp />
    </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
    <behavior name="">
    <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
    </serviceBehaviors>
    </behaviors>
    <services>
    <service name="PNServer.RegistrationService">
    <endpoint address="http://localhost/RegistrationService"
    behaviorConfiguration="EndpointPNServerServiceBehavior"
    binding="webHttpBinding"
    contract="WP7_Push_Notifications.IRegistrationService">
    </endpoint>
    </service>
    </services>
    </system.serviceModel>


In a nutshell, with these settings you have configured your service to listen at the following address: http://localhost/RegistrationService. You have also specified that the requests to this service will be coming over the http protocol.

Finally, you will modify the main application form (Form1) and add a Broadcast button that will send a push notification to all subscribed clients. Once clicked, the button click handler will get a list of all clients subscribed and send each one of them a push notification (toast notification in the following code). Here's how to do that.

  1. Open Form1.cs in Design view and add a button to that form underneath the "Send Notification" button.

    1. Change the button's text to "Broadcast," as shown in Figure 2.

    2. Change the button's name to "btnBroadcast," double-click it, and make sure that the button's Click event contains the following code:

      private void btnBroadcast_Click(object sender, EventArgs e)
      {
      if (txtTitle.Text == string.Empty || txtText.Text == string.Empty)
      {
      MessageBox.Show("Please enter text and title to send");
      return;
      }

      List<Uri> allSubscribersUri = RegistrationService.GetSubscribers();

      foreach (Uri subscriberUri in allSubscribersUri)
      {
      sendPushNotificationToClient(subscriberUri.ToString());
      }
      }


    Figure 1. To add RESTful WCF service to the Windows Forms application, set the application's target framework to .NET Framework 4.
  2. Add the following code to the sendPushNotificationToClient function:

    private void sendPushNotificationToClient(string url)
    {
    HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(url);

    sendNotificationRequest.Method = "POST";
    sendNotificationRequest.Headers = new WebHeaderCollection();
    sendNotificationRequest.ContentType = "text/xml";

    sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
    sendNotificationRequest.Headers.Add("X-NotificationClass", "2");

    string str = string.Format(TilePushXML, txtTitle.Text, txtText.Text);
    byte[] strBytes = new UTF8Encoding().GetBytes(str);
    sendNotificationRequest.ContentLength = strBytes.Length;
    using (Stream requestStream = sendNotificationRequest.GetRequestStream())
    {
    requestStream.Write(strBytes, 0, strBytes.Length);
    }

    try
    {
    HttpWebResponse response =
    (HttpWebResponse)sendNotificationRequest.GetResponse();
    string notificationStatus = response.Headers["X-NotificationStatus"];
    string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
    lblStatus.Text = "Status: " + notificationStatus + " : " +
    deviceConnectionStatus;
    }
    catch (Exception ex)
    {
    //handle 404 (URI not found) and other exceptions that may occur
    lblStatus.Text = "Failed to connect, exception detail: " + ex.Message;
    }


Note that the TilePushXML variable has been previously defined when we talked about Tile Notifications—specifically, in the Creating an Application to Send Notifications section. With the WCF service tracking subscribed clients and sending push notifications complete, it is now time to enhance the client application to call the web service with its push notification URL.

Figure 2. Main application form with Broadcast button

2. Modifying the Client to Call the WCF Service

The Windows Phone 7 Push Notification client application needs to be modified to call the newly implemented web service with the push notification URL. Previously, we briefly mentioned that the convenience of creating a RESTful WCF service lies in the fact that the operations of that web service can be accessed as URLs. For instance, the URL http://localhost/RegistrationService/Register?uri={0}accesses the Register function of the web service created in the previous section; the uri parameter is supplied on the QueryString. With that in mind, you can go ahead and complete the Windows Phone 7 Push Notification client implementation by creating the functions that will register/unregister a Windows Phone 7 client with the server:

  1. Launch Visual Studio 2010 Express for Windows Phone and open the PNClient project.

  2. Locate the ChannelUri property getter and setter and change them to the following (notice the use of two new functions, RegisterUriWithServer and UnregisterUriFromServer).

    public Uri ChannelUri
    {
    get { return channelUri; }
    set
    {
    //unregister the old URI from the server
    if (channelUri!=null)
    UnregisterUriFromServer(channelUri);

    //register the new URI with the server
    RegisterUriWithServer(value);

    channelUri = value;
    OnChannelUriChanged(value);
    }
    }

  3. Now add the following two functions to invoke the WCF service that you have created (note that when it comes time to release your service to production, you will be most likely deploying this service somewhere in the cloud).

    private void RegisterUriWithServer(Uri newChannelUri)
    {
    //Hardcode for solution - need to be updated in case the REST WCF service addresschange
    string baseUri = "http://localhost/RegistrationService/Register?uri={0}";
    string theUri = String.Format(baseUri, newChannelUri.ToString());
    WebClient client = new WebClient();
    client.DownloadStringCompleted += (s, e) =>
    {
    if (e.Error == null)
    Dispatcher.BeginInvoke(() => {
    txtURI.Text = "changing uri to " + newChannelUri.ToString();
    });
    else
    Dispatcher.BeginInvoke(() =>
    {
    txtURI.Text = "registration failed " + e.Error.Message;
    });
    };
    client.DownloadStringAsync(new Uri(theUri));

    }

    private void UnregisterUriFromServer(Uri oldChannelUri)
    {
    //Hardcode for solution - need to be updated in case the REST WCF service address change
    string baseUri = "http://localhost/RegistrationService/Unregister?uri={0}";
    string theUri = String.Format(baseUri, oldChannelUri.ToString());
    WebClient client = new WebClient();
    client.DownloadStringCompleted += (s, e) =>
    {
    if (e.Error == null)
    Dispatcher.BeginInvoke(() =>
    {
    txtURI.Text = "unregistered uri " + oldChannelUri.ToString();
    });
    else
    Dispatcher.BeginInvoke(() =>
    {
    txtURI.Text = "registration delete failed " + e.Error.Message;
    });
    };
    client.DownloadStringAsync(new Uri(theUri));
    }


In the preceding code, notice that the URL of the cloud is hard-coded—this URL must match the URL you have specified in the configuration file (app.config) for the WCF service. Notice also how the event handlers (client.DownloadStringCompleted) are wired up—those event handlers provide the status updates on whether the registration/unregistration succeeded or failed.

At this point, you have completed writing both the server and the client piece for automated push notification. It is now time to verify that the server is able to keep track and notify its clients appropriately, without the need to manually copy and paste the push notification URL.

3. Verifying Automated Push Notification Subscriber Tracking

To test automated push notification tracking, the very first thing you have to do is make sure that the WCF service starts up appropriately and that it is able to process requests coming in. Here's how:

  1. WCF Services are designed with security in mind and there are numerous security configuration options for those services. To bypass security configuration options so that you can test the service you built, you will need to run the WCF Service project as Administrator. The quickest way to accomplish that would be to exit Visual Studio, then right-click on the shortcut to Visual Studio and choose "Run as Administrator" option. Once Visual Studio comes up, open the PNServer solution. You are now set to run PNServer as Administrator.

  2. To verify that the WCF service is indeed ready to accept client connections, set a breakpoint at the first line of the Register function of the RegistrationService class, and then press F5 to start the PNServer application.

  3. If the application is running and the Windows form shown in Figure 2 is displayed, then fire up Internet Explorer (or any other browser) and go to the following URL:

    http://localhost/RegistrationService/Register?uri=http://www.microsoft.com

If the breakpoint gets hit after you access this URL, this means that the service is running and it is ready for clients to connect.

If the breakpoint does not get hit and you see a message that the page cannot be displayed, verify that the content in the <system.ServiceModel> section of yourapp.config file in the PNServer project matches the content of that file described in the section on creating a WCF service. Most likely, some sort of configuration issue is preventing you from properly launching the service.

Once you've confirmed that the service is running, you can observe the automated push notification subscriber tracking in action by following these steps:

  1. Launch PNClient application and click the Create Channel button. If you still have the breakpoint set in the Register function of the WCF service, that breakpoint should be hit.

  2. To be able to see toast notifications on the phone, you need to pin the application icon to the Start screen. To accomplish that, click the phone's Windows button, and then click the arrow (=>) to open the Windows Phone 7 Options screen, as previously shown in this Figure. Click and hold the left mouse button (also referred to as a "long click") to bring up the pop-up menu shown in this Figure, and then click the Pin to Start option.

  3. With the application icon pinned onto the Start screen, you are ready to receive notifications on the phone. In the PNServer application window, enter the title and the text of the notification message to send and press the Broadcast button. A second or two later, you should see the push notification coming through to the phone.

With clients and cloud service dynamically exchanging push notification URLs and clients accepting push notifications, this is a good point to conclude push notifications walkthroughs. The next sections will give you a perspective on using push notifications in the real world and summarize what you have learned in this article. The solution that you have built in this article provides the "full lifecycle" implementation of Push Notifications; however, it has a set of limitations that should be considered before deploying it to production. Windows Phone 7 client applications that go down do not unregister themselves from the server; therefore, the server will try to send notifications to non-existent channels. The server lacks persistency—all of the connected client addresses are kept in-memory, which means that they all will be lost should the service be shut down accidentally or on purpose. Finally, there's no centralized scheduling or event-based mechanism for distributing notifications: you have to push the button on the Windows Forms application to distribute the notifications. In the real world, the notifications will most likely be distributed in response to some external events (such as Microsoft stock rising rapidly), and the service has to be smart about handling those.

Other  
  •  Windows Phone 7 Development : Push Notifications - Implementing Raw Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Tile Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Toast Notifications
  •  iPhone Application Development : Creating a Navigation-Based Application
  •  Windows Phone 7 Development : Push Notifications - Introducing the Push Notifications Architecture
  •  Windows Phone 7 Development : Push Notifications - Understanding Push Notifications
  •  Windows Phone 7 Development : Handling Multiple Concurrent Requests with Rx.NET
  •  WAP and Mobile HTML Security : Application Attacks on Mobile HTML Sites
  •  WAP and Mobile HTML Security : Authentication on WAP/Mobile HTML Sites & Encryption
  •  iPhone Application Development : Displaying and Navigating Data Using Table Views - Building a Simple Table View Application
  •  iPhone Application Development : Understanding Table Views and Navigation Controllers
  •  Windows Phone 7 Development : Revising WeatherRx to Manage Slow Data Connections
  •  Windows Phone 7 Development : Handling Data Connection Issues with Rx.NET
  •  Windows Phone 7 Development : Handling Errors in Rx.NET
  •  Windows Phone 7 Development : Using Rx.NET with Web Services to Asynchronously Retrieve Weather Data
  •  Windows Phone 7 Development : Media - Adding Sounds to an Application
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 4) - Implementing the Summary View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 3) - Implementing the Volume View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 2) - Implementing the Area View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 1)
  •  
    Top 10
    Windows 7 : Using Advanced Security Options (part 1) - Configuring the Action Center & Performing a Manual Scan
    IIS 7.0 : Securing Communications with Secure Socket Layer (SSL)
    Parallel Programming with Microsoft .Net : Pipelines - Variations
    SQL Server 2008 : Retrieving and Transforming XML Data
    Windows Phone 7 Advanced Programming Model : Advanced Data Binding (part 4) - Data Bind to Anything
    SharePoint 2010 : Securing SharePoint’s SQL Server Installation
    Sharepoint 2007: Create a New Document
    The ASP.NET AJAX Infrastructure
    Adobe InDesign CS5 : Importing Graphic Objects (part 1) - Understanding Adobe Bridge
    Android Security : Creating New Manifest Permissions
    Most View
    Updates & Drivers (April – 2012)
    Exchange Server 2010 : SIP Protocol
    iPhone Application Development : Displaying and Navigating Data Using Table Views - Building a Simple Table View Application
    Programming the Mobile Web : Widgets and Offline Webapps - Platforms (part 4) - Windows Mobile & BlackBerry
    Installing Exchange Server 2010 : Post-setup configuration (part 1)
    Working with Device in Vista
    iPhone 3D Programming : Textures and Image Capture - Generating and Transforming OpenGL Textures with Quartz
    Exchange Server 2010 : Developments in High Availability (part 3) : Backup and restore
    Safeguarding Confidential Data in SharePoint 2010 : Outlining Database Mirroring Requirements
    Optimizing for Vertical Search : Optimizing for Image Search (part 2) - Optimizing Through Flickr and Other Image Sharing Sites
    SQL Server 2008 : Transact-SQL Programming - The OUTPUT Clause
    Leveraging and Optimizing Search in SharePoint 2010 : Customizing the Search User Interface
    Windows Server 2003 : Creating and Managing User Objects
    Legal Trouble with Social Networks (Part 3) - User tracking on Facebook & Small businesses are vulnerable
    D-Link DIR-645 - Whole-Home Router
    Windows 7 : General Maintenance Tools (part 3) - Checking Your Disks for Errors & Optimizing Disk Performance
    The SQL Programming Language : Complex Queries and Join Queries (part 1)
    Designing a Windows Server 2008 R2 Active Directory : Understanding the Single Domain Model
    How to set up your own virtual private network (Part 1)
    Aerocool Strike-X GT Black Edition