Azure WNS to Win8 - Push Notifications for Metro Apps
Posted
by JoshReuben
on Geeks with Blogs
See other posts from Geeks with Blogs
or by JoshReuben
Published on Tue, 03 Jul 2012 17:09:26 GMT
Indexed on
2012/07/03
21:16 UTC
Read the original article
Hit count: 614
Background
The Windows Azure Toolkit for Windows 8 allows you to build a Windows Azure Cloud Service that can send Push Notifications to registered Metro apps via Windows Notification Service (WNS).
Some configuration is required - you need to:
- Register the Metro app for Windows Live Application Management
- Provide Package SID & Client Secret to WNS
- Modify the Azure Cloud App cscfg file and the Metro app package.appxmanifest file to contain matching Metro package name, SID and client secret.
The Mechanism: These notifications take the form of XAML Tile, Toast, Raw or Badge UI notifications. The core engine is provided via the WNS nuget recipe, which exposes an API for constructing payloads and posting notifications to WNS. An application receives push notifications by requesting a notification channel from WNS, which returns a channel URI that the application then registers with a cloud service. In the cloud service, A WnsAccessTokenProvider authenticates with WNS by providing its credentials, the package SID and secret key, and receives in return an access token that the provider caches and can reuse for multiple notification requests. The cloud service constructs a notification request by filling out a template class that contains the information that will be sent with the notification, including text and image references. Using the channel URI of a registered client, the cloud service can then send a notification whenever it has an update for the user.
The package contains the NotificationSendUtils class for submitting notifications.
The Windows Azure Toolkit for Windows 8 (WAT) provides the PNWorker sample pair of solutions - The Azure server side contains a WebRole & a WorkerRole. The WebRole allows submission of new push notifications into an Azure Queue which the WorkerRole extracts and processes.
Further background resources:
- http://watwindows8.codeplex.com/ - Windows Azure Toolkit for Windows 8
- http://watwindows8.codeplex.com/wikipage?title=Push%20Notification%20Worker%20Sample - WAT WNS sample setup
- http://watwindows8.codeplex.com/wikipage?title=Using%20the%20Windows%208%20Cloud%20Application%20Services%20Application – using Windows 8 with Cloud Application Services
A bit of Configuration
Register the Metro apps for Windows Live Application Management
From the current app manifest of your metro app Publish tab, copy the Package Display Name and the Publisher
From: https://manage.dev.live.com/Build/
- Package name: <-- we need to change this
- Client secret: keep this
- Package Security Identifier (SID): keep this
Verify the app here: https://manage.dev.live.com/Applications/Index - so this step is done
"If you wish to send push notifications in your application, provide your Package Security Identifier (SID) and client secret to WNS."
Provide Package SID & Client Secret to WNS
- http://msdn.microsoft.com/en-us/library/windows/apps/hh465407.aspx - How to authenticate with WNS
- https://appdev.microsoft.com/StorePortals/en-us/Account/Signup/PurchaseSubscription - register app with dashboard - need registration code or register a new account & pay $170 shekels
- http://msdn.microsoft.com/en-us/library/windows/apps/hh868184.aspx - Registering for a Windows Store developer account
- http://msdn.microsoft.com/en-us/library/windows/apps/hh868187.aspx - Picking a Microsoft account for the Windows Store
The WNS Nuget Recipe
The WNS Recipe is a nuget package that provides an API for authenticating against WNS, constructing payloads and posting notifications to WNS. After installing this package, a WnsRecipe assembly is added to project references.
To send notifications using WNS, first register the application at the Windows Push Notifications & Live Connect portal to obtain Package Security Identifier (SID) and a secret key that your cloud service uses to authenticate with WNS.
An application receives push notifications by requesting a notification channel from WNS, which returns a channel URI that the application then registers with a cloud service.
In the cloud service, the WnsAccessTokenProvider authenticates with WNS by providing its credentials, the package SID and secret key, and receives in return an access token that the provider caches and can reuse for multiple notification requests.
The cloud service constructs a notification request by filling out a template class that contains the information that will be sent with the notification, including text and image references.Using the channel URI of a registered client, the cloud service can then send a notification whenever it has an update for the user.
var provider = new WnsAccessTokenProvider(clientId, clientSecret);
var notification = new ToastNotification(provider) {
ToastType = ToastType.ToastText02,
Text = new List<string> { "blah"} };
notification.Send(channelUri);
the WNS Recipe is instrumented to write trace information via a trace listener – configuratively or programmatically from Application_Start():
WnsDiagnostics.Enable();
WnsDiagnostics.TraceSource.Listeners.Add(new DiagnosticMonitorTraceListener());
WnsDiagnostics.TraceSource.Switch.Level = SourceLevels.Verbose;
The WAT PNWorker Sample
The Azure server side contains a WebRole & a WorkerRole. The WebRole allows submission of new push notifications into an Azure Queue which the WorkerRole extracts and processes.
Overview of Push Notification Worker Sample
The toolkit includes a sample application based on the same solution structure as the one created by theWindows 8 Cloud Application Services project template. The sample demonstrates how to off-load the job of sending Windows Push Notifications using a Windows Azure worker role. You can find the source code in theSamples\PNWorker folder. This folder contains a full version of the sample application showing how to use Windows Push Notifications using ASP.NET Membership as the authentication mechanism.
The sample contains two different solution files:
- WATWindows.Azure.sln: This solution must be opened with Visual Studio 2010 and contains the projects related to the Windows Azure web and worker roles.
- WATWindows.Client.sln: This solution must be opened with Visual Studio 11 and contains the Windows Metro style application project.
Only Visual Studio 2010 supports Windows Azure cloud projects so you currently need to use this edition to launch the server application. This will change in a future release of the Windows Azure tools when support for Visual Studio 11 is enabled.
Important: Setting up the PNWorker Sample
Before running the PNWorker sample, you need to register the application and configure it:
1. Register the app: To register your application, go to the Windows Live Application Management site for Metro style apps at https://manage.dev.live.com/build and sign in with your Windows Live ID. In the Windows Push Notifications & Live Connect page, enter the following information.
Package Display Name | PNWorker.Sample |
Publisher | CN=127.0.0.1, O=TESTING ONLY, OU=Windows Azure DevFabric |
3. Once you register the application, make a note of the values shown in the portal for Client Secret,Package Name and Package SID.
4. Configure the app - double-click the SetupSample.cmd file located inside the Samples\PNWorker folder to launch a tool that will guide you through the process of configuring the sample. setup runs a PowerShell script that requires running with administration privileges to allow the scripts to execute in your machine. When prompted, enter the Client Secret, Package Name, and Package Security Identifier you obtained previously and wait until the tool finishes configuring your sample.
Running the PNWorker Sample
To run this sample, you must run both the client and the server application projects.
1. Open Visual Studio 2010 as an administrator. Open the WATWindows.Azure.sln solution. Set the start-up project of the solution as the cloud project. Run the app in the dev fabric to test.
2. Open Visual Studio 11 and open the WATWindows.Client.sln solution. Run the Metro client application. In the client application, click Reopen channel and send to server. à the application opens the channel and registers it with the cloud application, & the Output area shows the channel URI.
3. Refresh the WebRole's Push Notifications page to see the UI list the newly registered client.
4. Send notifications to the client application by clicking the Send Notification button.
Setup
3 command files + 1 powershell script:
- SetupSample.cmd –>
- SetupWPNS.vbs –>
- SetupWPNS.cmd –>
- SetupWPNS.UpdateWPNSCredentialsInServiceConfiguration.ps1
appears to set
- PackageName – from manifest
- Client Id package security id (SID) – from registration
- Client Secret – from registration
The following configs are modified:
- WATWindows\ServiceConfiguration.Cloud.cscfg
- WATWindows\ServiceConfiguration.Local.cscfg
- WATWindows.Client\package.appxmanifest
WatWindows.Notifications
A class library – it references the following WNS DLL:
C:\WorkDev\CountdownValue\AzureToolkits\WATWindows8\Samples\PNWorker\packages\WnsRecipe.0.0.3.0\lib\net40\WnsRecipe.dll
NotificationJobRequest
A DataContract for triggering notifications:
using System.Runtime.Serialization;
using Microsoft.Windows.Samples.Notifications;
[DataContract]
[KnownType(typeof(WnsAccessTokenProvider))]
public class NotificationJobRequest
{
[DataMember]
public bool ProcessAsync { get; set; }
[DataMember]
public string Payload { get; set; }
[DataMember]
public string ChannelUrl { get; set; }
[DataMember]
public NotificationType NotificationType { get; set; }
[DataMember]
public IAccessTokenProvider AccessTokenProvider { get; set; }
[DataMember]
public NotificationSendOptions NotificationSendOptions{ get; set; }
}
Investigated these types:
- WnsAccessTokenProvider – a DataContract that contains the client Id and client secret
- NotificationType – an enum that can be: Tile, Toast, badge, Raw
- IAccessTokenProvider – get or reset the access token
- NotificationSendOptions – SecondsTTL, NotificationPriority (enum), isCache, isRequestForStatus, Tag
There is also a NotificationJobSerializer class which basically wraps a DataContractSerializer serialization / deserialization of NotificationJobRequest
The WNSNotificationJobProcessor class
This class wraps the NotificationSendUtils API – it periodically extracts any NotificationJobRequest objects from a CloudQueue and submits them to WNS.
The ProcessJobMessageRequest method – this is the punchline: it will deserialize a CloudQueueMessage into a NotificationJobRequest & send pass its contents to NotificationUtils to SendAsynchronously / SendSynchronously, (and then dequeue the message).
public override void ProcessJobMessageRequest(CloudQueueMessage notificationJobMessageRequest)
{
Trace.WriteLine("Processing a new Notification Job Request", "Information");
NotificationJobRequest pushNotificationJob =
NotificationJobSerializer.Deserialize(notificationJobMessageRequest.AsString);
if (pushNotificationJob != null)
{
if (pushNotificationJob.ProcessAsync)
{
Trace.WriteLine("Sending the notification asynchronously", "Information");
NotificationSendUtils.SendAsynchronously(
new Uri(pushNotificationJob.ChannelUrl),
pushNotificationJob.AccessTokenProvider,
pushNotificationJob.Payload,
result => this.ProcessSendResult(pushNotificationJob, result),
result => this.ProcessSendResultError(pushNotificationJob, result),
pushNotificationJob.NotificationType,
pushNotificationJob.NotificationSendOptions);
}
else
{
Trace.WriteLine("Sending the notification synchronously", "Information");
NotificationSendResult result = NotificationSendUtils.Send(
new Uri(pushNotificationJob.ChannelUrl),
pushNotificationJob.AccessTokenProvider,
pushNotificationJob.Payload,
pushNotificationJob.NotificationType,
pushNotificationJob.NotificationSendOptions);
this.ProcessSendResult(pushNotificationJob, result);
}
}
else
{
Trace.WriteLine("Could not deserialize the notification job", "Error");
}
this.queue.DeleteMessage(notificationJobMessageRequest);
}
Investigation of NotificationSendUtils class - This is the engine – it exposes Send and a SendAsyncronously overloads that take the following params from the NotificationJobRequest:
- Channel Uri
- AccessTokenProvider
- Payload
- NotificationType
- NotificationSendOptions
WebRole
WebRole is a large MVC project – it references WatWindows.Notifications as well as the following WNS DLL: \AzureToolkits\WATWindows8\Samples\PNWorker\packages\WnsRecipe.0.0.3.0\lib\net40\NotificationsExtensions.dll
Controllers\PushNotificationController.cs
Notification related namespaces:
using Notifications;
using NotificationsExtensions;
using NotificationsExtensions.BadgeContent;
using NotificationsExtensions.RawContent;
using NotificationsExtensions.TileContent;
using NotificationsExtensions.ToastContent;
using Windows.Samples.Notifications;
TokenProvider – initialized from the Azure RoleEnvironment:
IAccessTokenProvider tokenProvider = new WnsAccessTokenProvider(
RoleEnvironment.GetConfigurationSettingValue("WNSPackageSID"),
RoleEnvironment.GetConfigurationSettingValue("WNSClientSecret"));
SendNotification method – calls QueuePushMessage method to create and serialize a NotificationJobRequest and enqueue it in a CloudQueue
[HttpPost]
public ActionResult SendNotification(
[ModelBinder(typeof(NotificationTemplateModelBinder))] INotificationContent notification,
string channelUrl,
NotificationPriority priority = NotificationPriority.Normal)
{
var payload = notification.GetContent();
var options = new NotificationSendOptions()
{
Priority = priority
};
var notificationType =
notification is IBadgeNotificationContent ? NotificationType.Badge :
notification is IRawNotificationContent ? NotificationType.Raw :
notification is ITileNotificationContent ? NotificationType.Tile :
NotificationType.Toast;
this.QueuePushMessage(payload, channelUrl, notificationType, options);
object response = new
{
Status = "Queued for delivery to WNS"
};
return this.Json(response);
}
GetSendTemplate method:
Create the cshtml partial rendering based on the notification type
[HttpPost]
public ActionResult GetSendTemplate(NotificationTemplateViewModel templateOptions)
{
PartialViewResult result = null;
switch (templateOptions.NotificationType)
{
case "Badge":
templateOptions.BadgeGlyphValueContent = Enum.GetNames(typeof(
GlyphValue));
ViewBag.ViewData = templateOptions;
result = PartialView("_" + templateOptions.NotificationTemplateType);
break;
case "Raw":
ViewBag.ViewData = templateOptions;
result = PartialView("_Raw");
break;
case "Toast":
templateOptions.TileImages = this.blobClient.GetAllBlobsInContainer(ConfigReader.GetConfigValue("TileImagesContainer")).OrderBy(i => i.FileName).ToList();
templateOptions.ToastAudioContent = Enum.GetNames(typeof(
ToastAudioContent));
templateOptions.Priorities = Enum.GetNames(typeof(
NotificationPriority));
ViewBag.ViewData = templateOptions;
result = PartialView("_" + templateOptions.NotificationTemplateType);
break;
case "Tile":
templateOptions.TileImages = this.blobClient.GetAllBlobsInContainer(ConfigReader.GetConfigValue("TileImagesContainer")).OrderBy(i => i.FileName).ToList();
ViewBag.ViewData = templateOptions;
result = PartialView("_" + templateOptions.NotificationTemplateType);
break;
}
return result;
}
Investigated these types:
- ToastAudioContent – an enum of different Win8 sound effects for toast notifications
- GlyphValue – an enum of different Win8 icons for badge notifications
·
Infrastructure\NotificationTemplateModelBinder.cs
WNS Namespace references
using NotificationsExtensions.BadgeContent;
using NotificationsExtensions.RawContent;
using NotificationsExtensions.TileContent;
using NotificationsExtensions.ToastContent;
Various NotificationFactory derived types can server as bindable models in MVC for creating INotificationContent types. Default values are also set for IWideTileNotificationContent & IToastNotificationContent.
Type factoryType = null;
switch (notificationType)
{
case "Badge":
factoryType = typeof(BadgeContentFactory);
break;
case "Tile":
factoryType = typeof(TileContentFactory);
break;
case "Toast":
factoryType = typeof(ToastContentFactory);
break;
case "Raw":
factoryType = typeof(RawContentFactory);
break;
}
Investigated these types:
- BadgeContentFactory – CreateBadgeGlyph, CreateBadgeNumeric (???)
- TileContentFactory – many notification content creation methods , apparently one for every tile layout type
- ToastContentFactory – many notification content creation methods , apparently one for every toast layout type
- RawContentFactory – passing strings
WorkerRole
WNS Namespace references
using Notifications;
using Notifications.WNS;
using Windows.Samples.Notifications;
OnStart() Method – on Worker Role startup, initialize the NotificationJobSerializer, the CloudQueue, and the WNSNotificationJobProcessor
_notificationJobSerializer = new NotificationJobSerializer();
_cloudQueueClient = this.account.CreateCloudQueueClient();
_pushNotificationRequestsQueue = _cloudQueueClient.GetQueueReference(ConfigReader.GetConfigValue("RequestQueueName"));
_processor = new WNSNotificationJobProcessor(_notificationJobSerializer, _pushNotificationRequestsQueue);
Run() Method – poll the Azure Queue for NotificationJobRequest messages & process them:
while (true)
{
Trace.WriteLine("Checking for Messages", "Information");
try
{
Parallel.ForEach(
this.pushNotificationRequestsQueue.GetMessages(this.batchSize),
this.processor.ProcessJobMessageRequest);
}
catch (Exception e)
{
Trace.WriteLine(e.ToString(), "Error");
}
Trace.WriteLine(string.Format("Sleeping for {0} seconds", this.pollIntervalMiliseconds / 1000));
Thread.Sleep(this.pollIntervalMiliseconds);
}
How I learned to appreciate Win8
There is really only one application architecture for Windows 8 apps: Metro client side and Azure backend – and that is a good thing. With WNS, tier integration is so automated that you don’t even have to leverage a HTTP push API such as SignalR. This is a pretty powerful development paradigm, and has changed the way I look at Windows 8 for RAD business apps. When I originally looked at Win8 and the WinRT API, my first opinion on Win8 dev was as follows –
- GOOD:WinRT, WRL, C++/CX, WinJS, XAML (& ease of Direct3D integration);
- BAD: low projected market penetration,.NET lobotomized (Only 8% of .NET 4.5 classes can be used in Win8 non-desktop apps - http://bit.ly/HRuJr7);
- UGLY:Metro pascal tiles!
Perhaps my 80s teenage years gave me a punk reactionary sense of revulsion towards the Partridge Family 70s style that Metro UX seems to have appropriated:
On second thought though, it simplifies UI dev to a single paradigm (although UX guys will need to change career) – you will not find an easier app dev environment.
Speculation: If LightSwitch is going to support HTML5 client app generation, then its a safe guess to say that vnext will support Win8 Metro XAML - a much easier port from Silverlight XAML. Given the VS2012 LightSwitch integration as a thumbs up from the powers that be at MS, and given that Win8 C#/XAML Metro apps tend towards a streamlined 'golden straight-jacket' cookie cutter app dev style with an Azure back-end supporting Win8 push notifications... --> its easy to extrapolate than LightSwitch vnext could well be the Win8 Metro XAML to Azure RAD tool of choice! The hook is already there - :) Why else have the space next to the HTML Client box?
This high level of application development abstraction will facilitate rapid app cookie-cutter architecture-infrastructure frameworks for wrapping any app. This will allow me to avoid too much XAML code-monkeying around & focus on my area of interest: Technical Computing.
© Geeks with Blogs or respective owner