Many mobile applications include a remote data provider web service that provides users with updated information or allows users to communicate with each other. In some cases mobile applications 'pull', i.e. request and receive, information. Often, the data provider service 'pushes' updated information to mobile applications in the form of push notifications. With push notifications, the device does not have to wake up periodically to check whether new data is available, thereby conserving battery life. For more details on push notifications, look up chapter 8 in this series. Mobile application developers who target iPhone, Android and Windows Phone need a separate data provider for each type of device since they use different protocols for push notifications. Developers need to use the Apple Push Notification Service (APNS) for iOS devices, Cloud to Device Messaging Framework (C2DM) for Android devices and Microsoft Push Notification Service (MPNS) for Windows Phone Devices. Mobile application developers need to manage different types of devices and they need to interface with each notification service in order to push messages. View or download the Push Notification Sample / Source Code. The Microsoft Interoperability Strategy Group is a Microsoft team focused on interoperability. It has developed a Common Push Notification Service sample and published it as an open source project, that makes it easy to send push messages to iPhone, Android and Windows Phone devices using the appropriate protocol. The Common push notification service has the following design goals for developers The sample implementation uses Azure but the sample code can be modified to make it run on ASP.net. We have provided tips to do so below. Azure provides a compelling benefit that a short burst of notifications can be handled easily by dynamically provisioning additional resources. This paper is divided into three parts: A Common Push Notification Service sample has three primary entities, namely, subscriptions, devices and notifications. Common Push Notification Service sample is implemented as an Azure service. It consists of a web role that exposes REST end points for device, subscription and notification management and a worker role that sends push notifications. Common Push Notification Service has two end points: The engine manages the list of subscriptions and the devices that have signed up for the subscriptions. It keeps the device information that is needed to send notification messages via each proprietary notification service. It also manages messages to be sent to devices. Message pumps forward notification messages to iPhone, Android and Windows Phone devices via APNS, C2DM or MPNS respectively. The respective notification services forward the messages to the devices. This section guides you through the steps to run the sample service and explains the basics of the service. The sample includes three different solutions: Scenario: In this section, we will look at the steps necessary to use AzurePushMessages. We will send push notifications to MessageReceiver using PushMessageDataProvier. Steps needed to send notifications to iOS and Android devices are similar; you can review those using included Android and iPhone sample applications. Make sure you have installed Visual Studio 2010 or Visual Studio C# Express 2010, Visual Studio Web Developer Express 2010, and Windows Phone 7 tools for Visual Studio. We will use C# Express to run the data provider, Web Developer Express to run the Common Notification Service and Windows Phone 7 tools to run the sample WP7 application. This chapter does not cover Azure development or Windows Phone development. If you are not familiar with Windows Phone 7 programming, review the material at App Hub. To make yourself familiar with WP7 and Microsoft Push Notification Service, read this MSDN article. To learn about developing Azure applications, read Introduction to the Windows Azure Platform, Developing for Windows Azure andDeveloping and Deploying Windows Azure Apps in Visual Studio 2010. In this step, we will run CPNS locally. Before we deploy the service to Azure, we will deploy it on the local compute and storage Azure fabric. Once we have tested it locally, we will deploy it to Azure cloud. You will see IE start with http://127.0.0.1/ address with "HTTP error 403" error which is expected since the web service end points are not at this address. Normally, an Azure application starts locally at http://127.0.0.1:81 , but sometimes it may use a different port. If it uses a different port, you will need to modify the sources for both, the WP7 sample application and PushMessageDataProvider sample application, in order to use the new port. Look forhttp://127.0.0.1:81 in the other two projects and replace port number 81 with the new port number. In this step, we will use the sample data provider application to create subscriptions and send messages to devices. In this step, we will run the sample Windows Phone 7 application to receive notifications sent by PushMessageDataProvider. In this step, we will send notifications using PushMessageDataProvider. In a few seconds, you should receive a toast message on the Windows Phone 7 application as shown below: We can send a raw notification message using PushMessageProvider in the same manner which will show up on the sample Phone application similar to above toast. Let us look at how toast messages can be received even when the application is not running on the phone. Quit the MessageReceiver application running on Windows Phone 7 by clicking the back arrow button on the phone. Use the PushMessageProvider application to send the another toast message. In a few seconds, you should see the toast appear on the phone emulator. While both raw and toast messages can be received by the application while the application is running, a toast message is received by the device and shown as a toast even when the application is not running. After a few seconds, you should see the updated tile as shown below: You can use the Common Push Notification Service to send a push message to iPhone, Android, and Windows Phone 7. The following images show an example where a message sent to Football subscription is forwarded to iPhone, Android, and Windows Phone 7 which are signed up for the Football subscription. As you see below, the same message manifests in a different way on the two platforms. On iPhone, the common message is sent as a normal iPhone push notification whereas on Windows Phone 7 it is sent as a tile message. On Android, each application can choose how to handle the push notification. In order to send push messages to iPhone, the sender service must be registered with Apple Developer portal and use certificate authentication. You will need to provision to use APNS, download certificates, configure Azure or ASP.net service and deploy the certificates with your service. Read details about how to send Push Notifications using ASP.net, follow instructions listed here. 1. Add your apn_developer_identity.p12 file to your Azure Worker role project. In WorkerRole.cs file add the name of your certificate file and your cert password. For sending push notifications to Android devices, follow the instructions as Android Cloud to Device Messaging Framework and signup to use C2DM. 2. In addition, add your registered Google username and password at C2DMConnection.cs file in CPNS: Before you deploy the application to Windows Azure, you will need to create a Windows Live ID and sign up for Azure. You can start with a 30 day free trial to do so. For detailed instructions on how to deploy to Azure, review Windows Azure How-to Topics and follow the steps in "How to: Deploy a Windows Azure Application" or use the instructions in Deploying Applications to Windows Azure. Once you signed up for your Azure subscription and created Azure storage, you can deploy the service to Azure using Visual Studio. After deploying it successfully, note the URL of the application in the staging environment. You need to use your management certificate in Azure ServiceConfiguration.cscfg and ServiceDefinition.csdef file as shown below. For more information, read Visual Studio Azure documentation. 1. In Azure ServiceConfiguration.cscfg, modify certificates node 2. Similarly, in your ServiceDefinition.csdef file, update the certificates node Update the URLs in the MessageReceiver and PushMessageDataProvider projects with the staging URL using the instructions given above. Execute the same steps described above to send and receive push notifications. In this section, we will show you how to integrate with the Common Push Notification Service using a sample mobile application and its corresponding data provider web service. While your actual mobile application and the accompanying web service are likely to be very different and more complex, its interface with the Common Push Notification Service is likely to be very similar. The end point for managing subscriptions provides an API to add, delete or list subscriptions in the system. The REST APIs listed below are written using WCF service contract definition format. WebInvoke refers to a POST message whereas WebGet refers to a GET message. Let us look at how these APIs are used in our sample PushMessageDataProvider application shown above. The user enters the name and description of the subscription and clicks Add to create a new subscription. In response to Add, the subCreateBtn_Click event handler, listed below,is invoked. The event handler sets up a URL for the "create" REST call and calls the SendPostRequest method. In the SendPostRequest method, we encode the POST data, if any, and create an HttpWebRequest that adds an authentication header with username and password. The HTTP Post request is sent to the subscription REST end point. subCreateBtn_Click receives the server response synchronously and displays it to the user. In your data provider web service or application, you will need to use the above end point in a similar manner to create subscriptions. Common Push Notification Service includes three end points, one each for registering iOS, Android and Windows Phone devices respectively. In this design that uses separate endpoints for each device type, additional end points can be added without affecting existing code. The code fragment below shows how the sample Windows Phone 7 application uses the API to register itself. For Windows Phone, the register API is called after a unique device ID and a notification channel have been created by the Windows Phone application. You can see the details in the Windows Phone sample application. To know more about how to use Microsoft Push Notification Service, refer to P ush Notifications Overview for Windows Phone. The code snippet uses the deviceID and channel URI tuple to register with the Common Push Notification Service. Common Push Notification Service stores the channel URI indexed by the device ID and later uses the URI to forward a notification to MPNS. The APNS use the unique device ID for sending notifications. Consequently, the iOS device registration API only requires a device ID. Similarly, Google C2DM uses device registration ID for sending notifications and the API uses the unique registration ID as a parameter in the register API. While we have not shown the Objective-C code to register the iOS device, or the Java code for Android, the code that makes an HTTP POST request is similar in both cases. The end point used by devices to sign up for notification subscriptions consists of methods to sign up, list or delete subscriptions. In PushMessageReceiver, our sample Windows Phone 7 application, the user needs to explicitly sign up for notification subscriptions. However, you may choose to signup the user for a subscription implicitly in your application. For example, based on the user's location, you may choose to enlist the user in a specific traffic subscription. As an application developer, you can decide the most appropriate model for notification subscriptions. In our sample application the device subscriptions are shown by retrieving the subscription information from the Common Notification Service using the "/subs/{deviceID}" API.. In our WP7 sample application, we have created a helper class called RESTConnection in order to send GET or POST requests. SendGetRequest is a simple method which makes an HTTP GET request. Once the asynchronous GET request is completed, RestConnection class invokes the DownloadHandler event handler which in turn invokes pushRestConn_DownloadHandler. The response, serialized using XML, is parsed and queried using LINQ. We retrieve all the SubscriptionInfo objects that include name, description and the signup status of the device. We bind the list of subscriptions with the subscriptions listbox. In the above UI, the user opts in or out of a particular subscription using checkboxes on the screen. In response to the user action, we add or remove the device from that notification subscription using /sub/add/{subID}/{deviceID} and /sub/delete/{subID}/{deviceID} APIs resp. An application may also choose to add or remove the subscription implicitly by using these APIs. The SubscribeToNotification and UnSubscribeToNotification methods use SendPostRequest method shown above. The PushMessageDataProvider application panel, shown below, is used to send a notification message to specific subscriptions. The mobile application application manager selects the type of message to send, types the message text and other parameters and clicks Send. When the application manager clicksSend, the data provider application collects the message text, the subscription name, and create a REST message for sending a notification. The REST APIs for sending for all types of messages are invoked in the same way. The above method uses the SendPostRequest method shown above. As described above, the service REST end points are implemented by the Azure web role, whereas the message pump is implemented by the Azure worker role. Our service sample uses Azure table storage to store the notification subscriptions, devices IDsn and device sign up data. Azure web role uses LINQ to store data received via REST API in Azure tables. On the other hand, notification messages received by the Azure web role are added to an Azure Queue. The worker role fetches each message from the notification queue, processes the message, and forwards it. The worker role looks up the list of devices for each subscription and sends the message to each device via APNS, C2DM or MPNS, depending upon the type of the device. The AzurePushMessages solution has three different projects: The AzurePushMessages solution also includes a fourth project of the same name which contains Azure roles. The web role implements four different end points, an iOS registration endpoint (IIOSService.cs), an Android registration endpoint (IAndroidService.cs), a WP7 registration endpoint (IWP7Service.cs) and the subscription and notification endpoint (IPushService.cs). These interfaces are implemented in iOSDevice.svc.cs, AndroidDevice.svc.cs, WP7Device.svc.cs and Push.svc.cs files respectively. The RegisterDevice method in WP7Device.svc.cs file registers WP7 devices. This method first checks the validity of the WP7 device ID and the URI and then saves or updates the device details to the Azure table. It may be necessary to update the device details since Windows Phone 7, device notification URI may change over time. We have implemented a helper class called DeviceManager that encapsulates all Azure tables operations making it easy to manage devices. . The device data is encapsulated using DeviceDataModel class and persisted in table. Now that we have seen how devices are registered, let us see what it takes to add a new device type. To add a new device type, here are the steps : Let us look at CreateSubscription method which may be invoked only by authorized users. The method authenticates the user using username/passwordvia AuthManager class. Since we cannot configure IIS for basic authentication on Azure, we have implemented own authentication manager. The method uses the SubscriptionInfoManager , a class that encapsulates subscriptions, to save the subscriptions. The code for this class to save the subscription information in Azure tables is similar to the code that is used for saving the device information. Notification messages may be sent only by authorized users. After authenticating users SendToastNotification method checks the validity of the subscription. It then creates a new notification message, such as a toast shown below, using message text received in the REST call. This message is then enqued in the Azure queue. Let us see how ToastMessage class is implemented. ToastMessage is subclass of PushMessage which is uses a dictionary of key-value pairs in addition to message type and the subscription. All other message types, namely, raw message, tile message, iOS notification message as well as common message which can be sent to iPhone, Android, and Windows Phone, are defined in a similar manner. For all message types, the message specific data is stored in the dictionary. This makes it extensible for other message types. To add a new message type We use the Azure worker role a message pump to forward notifications In the Azure worker role run method, we instantiate the connection classes, namely, APNSConnection, C2DMConnection, and MPNSConection which handle the MPNS, C2DM or to APNS respectively. The worker role polls the message queue and waits for a notification message and forwards it using the appropriate connection class. It checks each connection class to see if it supports the message type and forwards it to each device that has signed for the subscription of that message. The MPNSConnection class implements the EnqueueMessage wherein it extracts the data specific to the message type and sends the message to Microsoft Push Notification Service. The code used to send the toast notification is based on this sample: How to: Send a Push Notification for Windows Phone . If you want to extend the Common Push Notification Service to send notifications to other notification services, you will need to make the following changes: Common Push Notification Service can be implemented using ASP.net by modifying this sample. The overall structure of the service can remain the same using the same three main components. The key changes needed are to The CPNS REST endpoints for device registration, subscription management and notifications will remain unchanged. However, instead of implementing the in an Azure web role, use ASP.net REST web service. Instead of Azure diagnostics traces, use ASP.net trace logs. Various classes used to encapsulate device information, subscription data and notification messages, can remain unchanged. However, the device manager, device subscription manager and subscription information manager will need to be changed to persist the information using database. Functionality provided by Azure tables will have to be implemented using SQL tables. The notification message queue will also have to be implemented using SQL tables. The functionality provided by the Azure worker role will need to be implemented using Windows service. Windows service will need to periodically query the SQL table for new and unprocessed messages and forward the message just like the Azure worker role does. The actual processing of messages will remain the same. Instead of using Azure diagnostics, the code will need to use Windows eventing to log errors and trace data. The Common Push Notification Service can scale to handle large number of notification messages using Azure . Mobile application data provider service needs to send only one message for the specific subscription to the Azure notification service. The Common Push Notification Service, in turn, sends notification messages for each device that is signed up for the subscription. The notifications to a large number of devices can be handled by the CPNS, freeing the application data provider from sending messages to each device. Another advantage is that the burst processing and network traffic can be handled by the Common Push Notification Service by deploying additional worker roles, which can work in parallel to handle the extra workload. The Common Push Notification Service provides an easy way to manage push notifications to iPhone, Android and Windows Phone 7 devices It frees up mobile application developers from the need to manage devices or to implement separate data provide services for each type of devices. Using the Common Push Notification Service, they can send notifications to subscribing devices. It is easy to integrate Common Push Notification Service with iPhone, Android and Windows Phone 7 applications. The REST APIs used by the Common Push Notification Service are very simple and can be integrated with a few lines of code. Common Push Notification service provides a modular architecture making it easy to add support for additional device types, message types, and push notification protocols. CPNS sample can be modified to implement it using ASP.net.Table of Contents
The Common Push Notification Service (CPNS)
Design of a Sample Common Push Notification Service
Architecture of a Common Push Notification Service
Service End Points
Common Notification Service engine.
Message Pump
Using the Common Push Notification Sample Service
Before you Begin
Deploying the Sample Service Locally
Using Data Provider Application
Windows Phone 7 Sample Application
Sending a Push Notifications
Sending a Tile Notification
Sending Push Notifications to iPhone, Android and Windows Phone 7
Configuration Changes To Support iPhone and Android
private
const
string
ApnsP12File =
"apn_developer_identity.p12"
;
// This is the password that you protected your p12File
// If you did not use a password, set it as null or an empty string
private
const
string
ApnsP12FilePassword =
"YourAPNCertPwd"
;
private
const
string
Username =
"YourGoogleEmail@gmail.com"
;
private
const
string
Password =
"YourGooglePassword"
;
Deploying CPNS to Azure
Configuration change
<
Certificates
>
<
Certificate
name
=
"YourCert"
thumbprint
=
"CertThumbPrint"
thumbprintAlgorithm
=
"sha1"
/>
</
Certificates
>
<
Certificates
>
<
Certificate
name
=
"YourCert"
storeLocation
=
"LocalMachine"
storeName
=
"My"
/>
</
Certificates
>
Integrating with the Common Push Notification Service
Subscription Management End Point
//list all subscriptions in the system. User may pick from these if needed
[WebGet(UriTemplate =
"/subs"
)]
//list all subscriptions in the system. User may pick from these if needed
[WebInvoke(UriTemplate =
"/sub/create/{subID}?desc={description}"
)]
//list all subscriptions that the device has signed up for
[WebGet(UriTemplate =
"/subs/{deviceID}"
)]
//sign up a device for a particular subscription
[WebInvoke(UriTemplate =
"/sub/add/{subID}/{deviceID}"
)]
// remove a device subscription
[WebInvoke(UriTemplate =
"/sub/delete/{subID}/{deviceID}"
)]
private
void
subCreateBtn_Click(
object
sender, RoutedEventArgs e)
{
statusTextBlock.Text =
""
;
string
subNameStr = subNameTextBox.Text;
string
descStr = descTextBox.Text;
string
status = SendPostRequest(BaseAddress +
"/sub/create/"
+subNameStr +
"?desc="
+descStr,
""
);
statusTextBlock.Text = status;
}
private
static
string
SendPostRequest(
string
url,
string
postData)
{
ASCIIEncoding encoding =
new
ASCIIEncoding();
byte
[] data = encoding.GetBytes(postData);
// Prepare web request...
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create(url);
request.Credentials =
new
NetworkCredential(username, password);
request.PreAuthenticate =
true
;
request.Method =
"POST"
;
request.ContentType =
"application/x-www-form-urlencoded"
;
request.ContentLength = data.Length;
Stream newStream = request.GetRequestStream();
// Send the data.
newStream.Write(data, 0, data.Length);
try
{
using
(HttpWebResponse response = request.GetResponse()
as
HttpWebResponse)
{
// Get the response stream
StreamReader reader =
new
StreamReader(response.GetResponseStream());
return
reader.ReadToEnd();
}
}
catch
(WebException e)
{
return
(
"Exception : "
+ e.Message);
}
}
Device Management End Points
API for Registering Windows Phone 7 devices
//Registers a device in the notification service
[WebInvoke(UriTemplate =
"/register/{deviceID}?uri={uri}"
)]
//Delete a device from the notification service for which it is regisered. Removes all subscriptions associated with it
[WebInvoke(UriTemplate =
"/unregister/{deviceID}"
)]
APIs for Registering iOS and Android devices
//Registers a device in the notification service
[WebInvoke(UriTemplate =
"/register/{deviceID}"
)]
//Delete a device from the notification service for which it is regisered. Removes all subscriptions associated with it
[WebInvoke(UriTemplate =
"/unregister/{deviceID}"
)]
private
void
RegisterForNotifications()
{
string
uriStr = _channel.ChannelUri.ToString();
encodedUriStr = EncodeTo64(uriStr);
string
deviceAddUrl =
"/register/"
+ deviceIDStr +
"?type=WP7&uri="
+ encodedUriStr;
deviceRestConn.SendPostRequest(deviceAddUrl,
""
);
…
}
Signing up for Notification Subscription
//list all subscriptions for which the device has signed up
[WebGet(UriTemplate =
"/subs/{deviceID}"
)]
//sign up a device for a particular subscription
[WebInvoke(UriTemplate =
"/sub/add/{subID}/{deviceID}"
)]
// remove a device from a subscription
[WebInvoke(UriTemplate =
"/sub/delete/{subID}/{deviceID}"
)]
Displaying Available Device Subscriptions
public
class
RESTConnection
{
…
public
void
SendGetRequest(
string
url,
string
token)
{
_url = url;
WebClient pushMsgService =
new
WebClient();
pushMsgService.Credentials =
new
NetworkCredential(_username, _password);
pushMsgService.DownloadStringCompleted +=
new
System.Net.DownloadStringCompletedEventHandler(pushMsgService_DownloadStringCompleted);
pushMsgService.DownloadStringAsync(
new
Uri(_baseUrl + url), token);
}
private
void
pushMsgService_DownloadStringCompleted(
object
sender, System.Net.DownloadStringCompletedEventArgs e)
{
if
(DownloadHandler !=
null
)
DownloadHandler(
this
, e);
}
public
void
SendPostRequest(
string
url,
string
postData,
string
token)
{
_url = url;
WebClient pushMsgService =
new
WebClient();
pushMsgService.Credentials =
new
NetworkCredential(_username, _password);
pushMsgService.UploadStringCompleted +=
new
System.Net.UploadStringCompletedEventHandler(pushMsgService_UploadStringCompleted);
pushMsgService.UploadStringAsync(
new
Uri(_baseUrl + url),
"POST"
,
""
, token);
}
private
void
pushMsgService_UploadStringCompleted(
object
sender, System.Net.UploadStringCompletedEventArgs e)
{
if
(UploadHandler !=
null
)
UploadHandler(
this
, e);
}
…
}
private
void
SetupSubscriptionOptions()
{
// set up notification channel and subscribe to notifications
pushRestConn.SendGetRequest(
"/subs/"
+ deviceIDStr,
"subs"
);
pushRestConn.DownloadHandler +=
new
DownloadEventHandler(pushRestConn_DownloadHandler);
}
void
pushRestConn_DownloadHandler(
object
sender, DownloadStringCompletedEventArgs e)
{
pushRestConn.DownloadHandler -=
new
DownloadEventHandler(pushRestConn_DownloadHandler);
string
userToken = (e
as
DownloadStringCompletedEventArgs).UserState
as
string
;
if
(userToken ==
"subs"
) {
XDocument xdoc = XDocument.Parse(e.Result);
IEnumerable<XElement> xelements = xdoc.Descendants();
var subscriptions = from element
in
xdoc.Descendants(
"{http://schemas.datacontract.org/2004/07/MsgHelperLib.Common}DeviceSubscriptionInfo"
)
select
new
SubscriptionInfo
{
Description = element.Element(
"{http://schemas.datacontract.org/2004/07/MsgHelperLib.Common}Description"
).Value,
IsSubscribed = element.Element(
"{http://schemas.datacontract.org/2004/07/MsgHelperLib.Common}IsSubscribed"
).Value ==
"true"
?
true
:
false
};
subscriptionsList.ItemsSource = subscriptions;
}
}
Add/Remove Subscriptions
void
cb_Click(
object
sender, RoutedEventArgs e)
{
CheckBox cb = sender
as
CheckBox;
if
((
bool
) cb.IsChecked)
{
SubscribeToNotification((
string
)cb.Tag);
}
else
{
UnSubscribeToNotification((
string
)cb.Tag);
}
}
private
void
SubscribeToNotification(
string
p)
{
string
deviceSubUrl =
"/sub/add/"
+ p +
"/"
+ deviceIDStr;
pushRestConn.SendPostRequest(deviceSubUrl,
""
,
"subscribe"
);
…
}
private
void
UnSubscribeToNotification(
string
p)
{
string
deviceSubUrl =
"/sub/delete/"
+ p +
"/"
+ deviceIDStr;
pushRestConn.SendPostRequest(deviceSubUrl,
""
,
"unsubscribe"
);
…
}
Sending Push Notification Messages
private
void
SendToastMessage()
{
msgStatusLbl.Content =
""
;
try
{
string
msg = msgTxtBox.Text;
string
sub = GetSelectedSubscription();
if
(!
string
.IsNullOrEmpty(msg))
{
SendPostRequest(BaseAddress +
"/message/toast/"
+ sub +
"?mesg="
+ msg,
""
);
msgStatusLbl.Content =
"Message sent successfully"
;
}
else
msgStatusLbl.Content =
"Message should not be blank"
;
}
catch
(Exception e)
{
msgStatusLbl.Content =
"Exception: "
+ e.Message;
}
}
Design of a Common Push Notification Service
The Web Role
Device Registration
public
string
RegisterDevice(
string
deviceID,
string
uri)
{
if
(IsDeviceIDValid(deviceID) && IsUriValid(
ref
uri,
true
))
return
push.RegisterDevice(deviceID, DEVICE_TYPE_NAME, uri);
else
throw
new
WebFaultException<
string
>(Util.GetExceptionString(PushMessageErrors.Err_IllLegalDeviceID), HttpStatusCode.BadRequest);
}
public
string
RegisterDevice(
string
deviceID,
string
type,
string
uri)
{
PushMessageErrors err;
// the device endpoints have made sure that device IDs are valid. This method is not visible on the outside.
try
{
err = devMgr.AddOrUpdateDevice(type, deviceID, uri);
}
catch
(Exception e)
{
Trace.TraceError(
string
.Format(
"Internal Error: RegisterDevice device: {0}, type:{1}, uri:{2}, Error: {3}"
, deviceID, type, uri, e.Message));
throw
new
WebFaultException<
string
>(e.Message, System.Net.HttpStatusCode.InternalServerError);
}
//if there was error in adding the device, return error.
if
(err != PushMessageErrors.Success)
throw
new
WebFaultException<
string
>(Util.GetExceptionString(err), HttpStatusCode.BadRequest);
else
return
"Success"
;
}
public
PushMessageErrors AddOrUpdateDevice(
string
devTypeName,
string
deviceID,
string
uri)
{
DeviceDataModel ddm = dds.SelectByDeviceIDAndType(deviceID, devTypeName);
try
{
if
(ddm ==
null
)
{
//no such device, create it
ddm =
new
DeviceDataModel(devTypeName, deviceID);
ddm.Address = uri;
dds.Insert(ddm);
return
PushMessageErrors.Success;
}
else
{
//for WP7, we will update the URI with the new value
ddm.Address = uri;
dds.Update(ddm);
return
PushMessageErrors.Success;
}
}
catch
(Exception e)
{
throw
(
new
ApplicationException(e.Message));
}
}
Adding a new Device Type
Notification Subscriptions
public
string
CreateSubscription(
string
subName,
string
description)
{
//user must be authenticated
if
(!AuthManager.AuthenticateUser())
{
//if not, return 401
AuthManager.ConstructAuthResponse();
return
null
;
}
PushMessageErrors err;
try
{
//ask the subscription manager to add one more subscription name
err = subscriptionInfoMgr.AddSubscriptionInfo(subName, description);
}
catch
(Exception e)
{
Trace.TraceError(
string
.Format(
"Internal Error: CreateSubscription subName: {0}, Error: {1}"
, subName, e.Message));
throw
new
WebFaultException<
string
>(e.Message, System.Net.HttpStatusCode.InternalServerError);
}
if
(err != PushMessageErrors.Success)
throw
new
WebFaultException<
string
>(Util.GetExceptionString(err), System.Net.HttpStatusCode.BadRequest);
else
return
"success"
;
}
Notification Messages
[OperationContract]
[WebInvoke(UriTemplate =
"/message/toast/{subID}?mesg={message}"
)]
string
SendToastNotification(
string
subID,
string
message);
…
public
string
SendToastNotification(
string
subscriptionName,
string
toast)
{
if
(!AuthManager.AuthenticateUser())
{
AuthManager.ConstructAuthResponse();
return
null
;
}
//make sure subscription name is created
bool
subExists = subscriptionInfoMgr.IsSubscriptionRegistered(subscriptionName);
if
(!subExists)
{
throw
new
WebFaultException<
string
>(Util.GetExceptionString(PushMessageErrors.Err_SubscriptionNameNotFound), System.Net.HttpStatusCode.BadRequest);
}
try
{
ToastMessage toastMsg =
new
ToastMessage(subscriptionName, toast);
msgQueue.Enque(toastMsg);
return
"success"
;
}
catch
(Exception e)
{
Trace.TraceError(
string
.Format(
"Internal Error: SendToast subscription: {0} toast: {1}, Error: {2}"
, subscriptionName, toast, e.Message));
throw
new
WebFaultException<
string
>(e.Message, System.Net.HttpStatusCode.InternalServerError);
}
}
public
class
PushMessage
{
public
short
Type {
get
;
set
; }
public
string
SubscriptionName {
get
;
set
; }
public
Dictionary<
string
,
string
> message {
get
;
set
;}
public
PushMessage(
string
subscriptionName,
short
type)
{
SubscriptionName = subscriptionName;
Type = type;
message =
new
Dictionary<
string
,
string
>();
}
public
PushMessage()
{
}
}
public
class
ToastMessage: PushMessage
{
public
ToastMessage(
string
subscriptionName,
string
toast)
:
base
(subscriptionName, (
short
) PushMessageType.Toast)
{
message.Add(
"toast"
, toast);
}
public
String Toast {
get
{
return
message[
"toast"
]; } }
public
ToastMessage()
:
base
()
{
}
}
Adding a new Message Type
Message Pump
mpnsConnection =
new
MPNSConnection(WP7BatchingPolicy.Immediately, 3, MPNSCertificate);
//get the devicetype MPNS supports - only WP7
mpnsDevType = mpnsConnection.SupportedDeviceType;
//Get the type of messages it handles, toast, raw, tile and common
mpnsSupportedMessages = mpnsConnection.HandlesMessageTypes(Enum.GetValues(enumType));
//create APNSConnection for Apple Push notification. Use Sandbox using certs and use 3 retries before lo
apnsConnection =
new
APNSConnection(
true
, APNSp12File, APNSp12FilePassword, 3);
//get the type of device it support
apnsDevType = apnsConnection.SupportedDeviceType;
//find the types of messages it supports. iPhone and Common types
apnsSupportedMessages = apnsConnection.HandlesMessageTypes(Enum.GetValues(enumType));
…
while
(
true
)
{
//sleep for the leftover time and dequeue a message and then process it. We sleep for at most "WorkerRoleLoopTime" in every cycle
currentTime = DateTime.Now;
int
millSecForNextCheck = WorkerRoleLoopTime - currentTime.Subtract(lastMsgCheckTime).Milliseconds;
Thread.Sleep(millSecForNextCheck > 0 ? millSecForNextCheck : 0);
lastMsgCheckTime = DateTime.Now;
if
((pushMsg = msgQueue.Deque()) !=
null
)
{
//process will look at the message type and send it to appropriate connection
processMessage(pushMsg);
Trace.WriteLine(
"APMWorkerRole Received message"
,
"Information"
);
}
}
//based on the type of message, it is sent to appropriate connection
private
void
processMessage(PushMessage pushMsg)
{
//check if APNS and MPNS support a given type
bool
ifAPNSSupportsType = apnsSupportedMessages[(PushMessageType)pushMsg.Type];
bool
ifMPNSSupportsType = mpnsSupportedMessages[(PushMessageType)pushMsg.Type];
if
(ifAPNSSupportsType)
{
//if APNS supports it, get the list of devices of the type APNS supports.
IEnumerable<DeviceDataModel> ddmList = sds.SelectByDeviceTypeandSubscription(pushMsg.SubscriptionName, apnsDevType);
apnsConnection.EnqueueMessage(ddmList, pushMsg);
}
if
(ifMPNSSupportsType)
{
//if MPNS supports the message type, get the list of devices of MPNS type and subscribed to a given subscription
IEnumerable<DeviceDataModel> ddmList = sds.SelectByDeviceTypeandSubscription(pushMsg.SubscriptionName, mpnsDevType);
mpnsConnection.EnqueueMessage(ddmList, pushMsg);
}
}
/* MPNSConnection Class */
public
override
void
EnqueueMessage(IEnumerable<DeviceDataModel> devices, PushMessage pm)
{
try
{
if
(pm.Type == (
short
)PushMessageType.Toast)
EnqueWP7ToastNotification(devices, pm.message[
"toast"
]);
else
if
(pm.Type == (
short
)PushMessageType.Raw)
EnqueWP7RawNotification(devices, pm.message[
"raw"
]);
else
if
(pm.Type == (
short
)PushMessageType.Tile || pm.Type == (
short
)PushMessageType.common)
EnqueWP7TileNotification(devices, pm.message[
"title"
],
int
.Parse(pm.message[
"count"
]), pm.message[
"url"
]);
else
return
;
}
catch
(Exception e)
{
if
(
this
.NotificationError !=
null
)
this
.NotificationError(
this
,
new
NotificationFormatException(pm));
}
}
Adding Support for Other Notification Services
Implementing CPNS using ASP.net
Scalability of the Service
Conclusions
Resources
1
2
3
4
5
1
2
3
1
2
3
1
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
출처 : http://windowsphone.interoperabilitybridges.com/articles/common-push-notification-service-sample