Sdílet prostřednictvím


Using Streaming Notifications with Exchange 2010

By About the Author

Summary: Streaming notifications, which combine the functionalities of push and pull notifications, are new in Microsoft Exchange Server 2010 Service Pack 1 (SP1). This article explains how streaming notifications work and when to use them.

Applies to: Microsoft Exchange Server 2010 Service Pack 1 (SP1)

Last modified: August 16, 2011

Applies to: Exchange Server 2010 | Exchange Server 2010 Service Pack 1 (SP1) | Office 2010

In this article
Introduction
Push and Pull Notifications
A New Star Is Born
Where Is My Watermark?
Other Things to Consider
Sample Application
About the Author

Introduction

You can use the Microsoft Exchange Web Services (EWS) Managed API to work with events in EWS. Events enable applications to monitor mailbox and public folders for changes. For example, a Customer Relationship Management (CRM) application might monitor several mailboxes for new email messages and replicate the contents of those messages to a database. Microsoft Exchange Server 2007 includes two types of notification mechanisms: push notifications and pull notifications. Each of these mechanisms has both advantages and limitations. Microsoft Exchange Server 2010 Service Pack 1 (SP1) introduces streaming notifications, which combine the power of push and pull notifications.

Push and Pull Notifications

Pull notifications are easy to use; an application just calls the SubscribeToPullNotifications method on a configured ExchangeService class instance. The EWS Managed API then returns a PullNotification class instance that you can use to poll for pending notifications on the Exchange server. This class has a GetEvents method that you can use to poll the server for changes. The disadvantage of pull notifications is that applications cannot determine in advance whether there are pending notifications on the server, and therefore they have to call this method periodically. Additionally, the minimum time resolution (the time between the occurrence of the event on the server and when the notification reaches the client) is tightly coupled to the poll frequency of the client. The only way to get faster notifications is to configure more frequent polling. While this might be adequate for small-scale use, it certainly puts pressure on the network and the Exchange server if many clients monitor mailboxes with a small poll interval.

In Exchange 2007, the alternative is to use push notifications. With push notifications, instead of polling the Exchange server for changes on a subscription, you set up a Web service and tell the Exchange server to call this Web service whenever a notification is available. The advantages of this approach are obvious. Because Exchange sends notifications in near real time, polling is not required. Therefore, the span of time between an event and the notification of a client is drastically reduced compared to that for pull notifications. The same is true for the traffic and the number of calls to EWS. Unfortunately, these advantages come with a cost. First, you have to set up the Web service and implement the required service contract. Second, this solution does not always work. Because the Exchange server calls back to the Web service, the service must be reachable over an HTTP or HTTPS connection (port 80 or 443 respectively). While this approach is okay for tightly coupled environments such as corporate networks, it fails with hosted solutions. Administrators are reluctant to open their firewalls for the required connections — and with good reason.

To sum up both the advantages and disadvantages of the two types of notifications, pull notifications are easy to use and they work without the need for additional infratstructure, but they lack a near-real-time notification of the client and do not scale well. Push notifications, on the other hand, offer near-real-time notification of clients, but they are hard to set up and do not work in all environments.

A New Star Is Born

Enter streaming notifications, introduced in Microsoft Exchange Server 2010 Service Pack 1 (SP1). Streaming notifications are similar to ActiveSync push notifications on mobile devices. The client opens a connection to the Exchange server with a timeout that is between 1 and 30 minutes. The Exchange server then uses this connection to push notifications over the wire. This effectively combines the advantages of both pull and push subscriptions, without any of their drawbacks. Only a few lines of code are required; no infrastructure support, such as client-side Web services, is needed for streaming notifications. They work in nearly every scenario, and they provide near-real-time notifications.

The EWS Managed API 1.1 includes support for streaming notifications. You can download the EWS Managed API from the Microsoft Download Center.

The streaming notification infrastructure on the client consists of two parts: the StreamingSubscriptionConnection class and the StreamingSubscription class. One StreamingSubscriptionConnection instance can handle multiple StreamingSubscription instances, even if they monitor different mailboxes.

Creating a streaming notification subscription involves two steps. First, you have to create a new streaming subscription by using the SubscribeToStreamingNotification method of the ExchangeService class. Then you need to pass the ID of the folder or folders that you want to monitor, as well as the types of events that you are interested in, as shown in the following example.

StreamingSubscription streamingsubscription = service.SubscribeToStreamingNotifications(new FolderId[] {WellKnownFolderName.Inbox}, EventType.NewMail);

The following table lists the possible values for the eventTypes parameter.

Parameter value

Description

NewMail

Notify the client if a new email has arrived in the mailbox.

Deleted

Notify the client if an item or folder has been deleted.

Modified

Notify the client if an item or folder has been modified.

Moved

Notify the client if an item or folder has been moved to a different location.

Copied

Notify the client if an item or folder has been copied to a different location.

Created

Notify the client if a new item or folder has been created.

FreeBusyChanged

Notify the client if the free/busy schedule has changed.

Note

The eventTypes parameter expects an array of EventType values, so you can monitor a folder for multiple types of notifications.

Now that you have a working subscription, you can use the StreamingSubscriptionConnection class to get the changes from the Exchange server to your application, as shown in the following example.

var connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(subscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnSubscriptionError += OnSubscriptionError;
connection.OnDisconnect += OnDisconnect;

The first line creates a new StreamingSubscriptionConnection instance and binds it to the existing ExchangeService class instance with a time-out of 30 minutes. After all the events have been wired up with last three lines in the example above, the connection needs to be established with a call to the Open method, as shown in the following example.

connection.Open();

The StreamingSubscriptionConnection instance now monitors for notifications from the server and raises the appropriate events.

The OnNotificationEvent event is raised for each notification received from the server. Because this example requests only notifications about new email messages, the event handler for this event is quite basic, as shown in the following example.

private static void OnNotificationEvent(object sender, NotificationEventArgs args){
   foreach (var notification in args.Events){
      if (notification.EventType != EventType.NewMail) continue;
      Console.WriteLine("A new mail has been created");
      var itemEvent = (ItemEvent) notification;
      Console.WriteLine("The item ID of the new mail is {0}", itemEvent.ItemId.UniqueId);
   }
}

The handler iterates through all events sent by the Exchange server and filters out NewMail events. If such an event is found, the unique ID of the new email is printed to the console. In a real-world application, you can use the unique ID with the Item.Bind method to bind directly to the item, and you can process it according to the business rules of your client application.

After the time-out of the connection has elapsed, the OnDisconnect event is raised by the StreamingSubscriptionConnection instance. If your application still needs to monitor the subscription, you can just call the Open method of the StreamingSubscriptionConnection instance again to reconnect to the Exchange server. This behavior is expected and is not an error condition.

If an error occurs during the communication with the Exchange server, the OnSubscriptionErorr event is raised. You can examine the error that occurred by accessing the Exception property of the event argument parameter of the handler. In the current version of the EWS Managed API, the Exception property always contains an instance of the ServiceResponseException class. Note that you can get much more information about the error that occurred if you cast the exception appropriately.

Note

Although the current version of the EWS Managed API only puts a ServiceResponseException instance in the Exception property, you should use a safe-cast (using the as operator in C# or the TryCast operator in Visual Basic) to cast the exception to a ServiceResponseException instance. Future versions of the EWS Managed API might put different types of exceptions in this property.

When you are finished monitoring the subscription, you should do the following:

  • Unsubscribe from the subscription by calling the StreamingSubscription.Unsubscribe method.

  • Close the connection by calling the StreamingSubscriptionConnection.Close method.

Where Is My Watermark?

If you have worked with push or pull subscriptions, you are probably accustomed to watermarks. With such subscriptions, each notification sends a unique watermark that you can use later to restart the subscription at that point in time. Microsoft Exchange then replays all notifications that have occurred since the watermark was first issued.

Streaming notifications do not work this way. Instead, you can use a combination of notifications and the ExchangeService.SyncFolderItems method. The SyncFolderItems method gives you a snapshot of the changes that occurred in a particular folder. This method also returns a small cookie that contains information about the synchronization state. You can pass this synchronization state to subsequent calls of the SyncFolderItems method to get all changes that have occurred since that synchronization cookie was first issued. You should persist this synchronization state somewhere (either on disk or in a database) so that the application does not need to process all of the items in the folder again.

Combining streaming notifications with the SyncFolderItems method entails performing the following actions:

  • After your application starts, use the SyncFolderItems method to process all unprocessed items in the folder.

  • Create a streaming notification and connection as specified earlier in this article.

  • Use the SyncFolderItems method again to process all events that occurred since the last synchronization and before the subscription was activated.

  • On each OnNotification event, use the GetItem operation to get additional properties of the specified items.

  • Call the SyncFolderItems method periodically to update the local synchronization cookie.

This approach may seem counterintuitive; the program would be much simpler if the OnNotification event handler would just call the SyncFolderItems operation, thereby centralizing the synchronization routine. However, you can use the SyncFolderItems operation in either of two ways that have very different performance outcomes. Note that you can use the propertySet parameter to request additional properties to be returned from the server. Requesting only the IDs is a very efficient operation; in contrast, requesting one or more properties is very inefficient. A better approach is to combine the SyncFolderItems method to request item IDs with GetItem calls to return the additional properties. For more information about this approach, see the Exchange Server 2010 Development (Part 6 of 6): Best Practices for Building Scalable Exchange Server Applications webcast.

A simple version of a method to process changes in a folder is shown in the following example.

public static void ProcessChanges(FolderId folderId){
   bool moreChangesAvailable;
   do{
      // Get all changes since the last call. The synchronization cookie is stored in the _SynchronizationState field.
      // Just get the IDs of the items.
      // For performance reasons, do not use the PropertySet.FirstClassProperties.
      var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly,
         null, 512, SyncFolderItemsScope.NormalItems, _SynchronizationState);
      // Update the synchronization 
      cookie._SynchronizationState = changes.SyncState;
      // Process all changes. If required, add a GetItem call here to request additional properties.
      foreach (var itemChange in changes){
         // This example just prints the ChangeType and ItemId to the console.
         // A LOB application would apply business rules to each 
         item.Console.Out.WriteLine("ChangeType = {0}", itemChange.ChangeType);
         Console.Out.WriteLine("ChangeType = {0}", itemChange.ItemId.ToString());
      }
      // If more changes are available, issue additional SyncFolderItems requests.
      moreChangesAvailable = changes.MoreChangesAvailable;
   } 
   while (moreChangesAvailable);
}

The preceding example uses the static field _SynchronizationState to store the current synchronization state. This field is updated after each call to the SyncFolderItems method. Each change is processed by printing the type of change and the item ID of the affected item to the console. This process is repeated until no more changes are available on the server. You should call this method once after the application has started and then every now and then to keep the _SynchronizationState field up to date. How often you call this method will vary according to the specifics of the application. If you expect very few events, you could update the field every time you get an OnDisconnect event.

The OnNotification handler is shown in the following example.

private static void OnNotificationEvent(object sender, NotificationEventArgs args){
   // Extract the item IDs for all NewMail events in the list.
   var newMails = from e in args.Events.OfType<ItemEvent>()
                  where e.EventType == EventType.NewMail        
                  select e.ItemId;
   // Note: For the sake of simplicity, error handling is ommited here. 
   // Just assume everything went fine.
   var response = _ExchangeService.BindToItems(newMails, 
      new PropertySet(BasePropertySet.IdOnly, ItemSchema.DateTimeReceived, ItemSchema.Subject));
   var items = response.Select(itemResponse => itemResponse.Item); 
   foreach (var item in items){
      Console.Out.WriteLine("A new mail has been created. Received on {0}",
      item.DateTimeReceived);
      Console.Out.WriteLine("Subject: {0}", item.Subject);
   }
}

Other Things to Consider

Because the application that is consuming notification events does not need to run on the Exchange server and the subscription is performed by using the standard Exchange Service binding, no further action on the Exchange server itself is required. Note that subscriptions are available out-of-the-box and do not need any additional configuration on the server.

And that’s it. The EWS Managed API makes it really easy to use streaming notifications.

Sample Application

This article includes a sample application that enables you to see all the pieces put together. You can download the StreamingNotificationsSample.msi from the Microsoft Download Center. After you install the sample on your local computer, you will have access to the source code, but before you can run it you need to make the following changes:

  • Update lines 82 and 83 in the Program.cs file to include your mailbox information.

  • Add the email address and password for the mailbox you will monitor to line 82.

  • Add the URL of your EWS server to line 83.

After you have added this information, you can run the application.

About the Author

Henning Krause is a software architect who has specialized in server applications for many years. Henning regularly publishes articles about programming with Exchange, and software development in general, on his blog at http://www.infinitec.de.