none
Multiple PushNotification Subscriptions some work properly and some don't. RRS feed

  • Question

  • Ok, here is the scoop. I have a windows services that fires every fifteen minutes to see if there is any subscriptions that need to be created or updated. I am using the Managed API v1.1 against Exchange 2007 SP1. I have a table that stores all the users that want there mailbox monitored. So that when a notifcation comes in to the "Listening Service" I am able to look up the user and access the message to log it into the application we are building. In the table I have the following columns that store the subscription information:

    1. SubscriptionId - VARCHAR(MAX)
    2. Watermark - VARCHAR(MAX)
    3. LastStatusUpdate - DATETIME

    My services calls a function that queries the data needed (based on which function it is doing). If the user doesn't have a subscription already the service will go and create one. I am using impersonation to access the mailboxes. Here is my "ActiveSubscription" method that is fired when a user needs the subscription either created or updated.

    private void ActivateSubscription(User user)
    {
      if (user.ADGUID.HasValue)
      {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Settings.ActiveDirectoryServerName, Settings.ActiveDirectoryRootContainer);
    
        using (UserPrincipal up = UserPrincipal.FindByIdentity(ctx, IdentityType.Guid, user.ADGUID.Value.ToString()))
        {
          ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SID, up.Sid.Value);
        }
      }
      else
      {
        ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
      }
          
      PushSubscription pushSubscription = ewService.SubscribeToPushNotifications(
        new FolderId[] { WellKnownFolderName.Inbox, WellKnownFolderName.SentItems },
        Settings.ListenerService, 30, user.Watermark,
        EventType.NewMail, EventType.Created);
    
      user.Watermark = pushSubscription.Watermark;
      user.SubscriptionID = pushSubscription.Id;
      user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();
    
      _users.Update(user);
    }
    

    We have also ran the following cmdlet to give the user we are accessing the EWS with the ability to impersonate on the Exchange Server.

    Get-ExchangeServer | where {$_.IsClientAccessServer -eq $TRUE} | ForEach-Object {Add-ADPermission -Identity $_.distinguishedname -User (Get-User -Identity mailmonitor | select-object).identity -extendedRight ms-Exch-EPI-Impersonation}
    
    

    The "ActivateSubscription" code above works as expected. Or so I thought. When I was testing it I had it monitoring my mailbox and it worked great. The only problem I had to work around was that the subscription was firing twice when the item was a new mail in the inbox, I got a notification for the NewMail event and Created event. I implemented a work around that checks to make sure the message hasn't already been logged on my Listening service. It all worked great.

    Today, we started testing two mailboxes being monitor at the same time. The two mailboxes were mine and another developers mailbox. We found the strangest behavior. My subscription worked as expected. But his didn't, the incoming part of his subscription work properly but any email he sent out the listening service never was sent a notification. Looking at the mailbox properties on Exchange I don't see any difference between his mailbox and mine. We even compared options/settings in Outlook. I can see no reasons why it works on my mailbox and not on his.

    Is there something that I am missing when creating the subscription. I didn't think there was since my subscription works as expected.

    My listening service code works perfectly well. I have placed the code below incase someone wants to see it to make sure it is not the issue.

    I am posting this here before trying anywhere else. Hopefully one of you has some insight.

    Thanks, Terry

    Listening Service Code:

    /// <summary>
    /// Summary description for PushNotificationClient
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class PushNotificationClient : System.Web.Services.WebService, INotificationServiceBinding
    {
      ExchangeService ewService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
    
      public PushNotificationClient()
      {
        //todo: init the service.
        SetupExchangeWebService();
      }
    
      private void SetupExchangeWebService()
      {
        ewService.Credentials = Settings.ServiceCreds;
        try
        {
          ewService.AutodiscoverUrl(Settings.AutoDiscoverThisEmailAddress);
        }
        catch (AutodiscoverRemoteException e)
        {
          //log auto discovery failed
          ewService.Url = Settings.ExchangeService;
        }
      }
    
      public SendNotificationResultType SendNotification(SendNotificationResponseType SendNotification1)
      {
        using (var _users = new ExchangeUser(Settings.SqlConnectionString))
        {
          var result = new SendNotificationResultType();
    
          var responseMessages = SendNotification1.ResponseMessages.Items;
          foreach (var responseMessage in responseMessages)
          {
            if (responseMessage.ResponseCode != ResponseCodeType.NoError)
            {
              //log error and unsubscribe.
              result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
              return result;
            }
    
            var sendNoficationResponse = responseMessage as SendNotificationResponseMessageType;
            if (sendNoficationResponse == null)
            {
              result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
              return result;
            }
    
            var notificationType = sendNoficationResponse.Notification;
            var subscriptionId = notificationType.SubscriptionId;
            var previousWatermark = notificationType.PreviousWatermark;
    
            User user = _users.GetById(subscriptionId);
            if (user != null)
            {
              if (user.MonitorEmailYN == true)
              {
                BaseNotificationEventType[] baseNotifications = notificationType.Items;
    
                for (int i = 0; i < notificationType.Items.Length; i++)
                {
                  if (baseNotifications[i] is BaseObjectChangedEventType)
                  {
                    var bocet = baseNotifications[i] as BaseObjectChangedEventType;
                    AccessCreateDeleteNewMailEvent(bocet, ref user);
                  }
                }
    
                _PreviousItemId = null;
              }
              else
              {
                user.SubscriptionID = String.Empty;
                user.SubscriptionStatusDateTime = null;
                user.Watermark = String.Empty;
                _users.Update(user);
    
                result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
                return result;
              }
    
              user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();
              _users.Update(user);
            }
            else
            {
              result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
              return result;
            }
          }
    
          result.SubscriptionStatus = SubscriptionStatusType.OK;
          return result;
        }
      }
    
      private string _PreviousItemId;
      private void AccessCreateDeleteNewMailEvent(BaseObjectChangedEventType bocet, ref User user)
      {
        var watermark = bocet.Watermark;
        var timestamp = bocet.TimeStamp.ToLocalTime();
        var parentFolderId = bocet.ParentFolderId;
    
        if (bocet.Item is ItemIdType)
        {
          var itemId = bocet.Item as ItemIdType;
          if (itemId != null)
          {
            if (string.IsNullOrEmpty(_PreviousItemId) || (!string.IsNullOrEmpty(_PreviousItemId) && _PreviousItemId != itemId.Id))
            {
              ProcessItem(itemId, ref user);
              _PreviousItemId = itemId.Id;
            }
          }
        }
    
        user.SubscriptionStatusDateTime = timestamp;
        user.Watermark = watermark;
        using (var _users = new ExchangeUser(Settings.SqlConnectionString))
        {
          _users.Update(user);
        }
    
      }
    
      private void ProcessItem(ItemIdType itemId, ref User user)
      {
        try
        {
          ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
          EmailMessage email = EmailMessage.Bind(ewService, itemId.Id);
          using (var _entity = new SalesAssistantEntityDataContext(Settings.SqlConnectionString))
          {
            var direction = EmailDirection.Incoming;
            if (email.From.Address == user.EmailAddress)
            {
              direction = EmailDirection.Outgoing;
            }
              
              
            int? bodyType = (int)email.Body.BodyType;
    
            var _HtmlToRtf = new HtmlToRtf();
            var message = _HtmlToRtf.ConvertHtmlToText(email.Body.Text);
    
            bool? IsIncoming = Convert.ToBoolean((int)direction);
    
            if (IsIncoming.HasValue && IsIncoming.Value == false)
            {
              foreach (var emailTo in email.ToRecipients)
              {
                _entity.InsertMailMessage(email.From.Address, emailTo.Address, email.Subject, message, bodyType, IsIncoming);
              }
            }
            else
            {
              if (email.ReceivedBy != null)
              {
                _entity.InsertMailMessage(email.From.Address, email.ReceivedBy.Address, email.Subject, message, bodyType, IsIncoming);
              }
              else
              {
                var emailToFind = user.EmailAddress;
                if (email.ToRecipients.Any(x => x.Address == emailToFind))
                {
                  _entity.InsertMailMessage(email.From.Address, emailToFind, email.Subject, message, bodyType, IsIncoming);
                }
              }
            }
          }
        }
        catch(Exception e)
        {
          //Log exception 
          using (var errorHandler = new ErrorHandler(Settings.SqlConnectionString))
          {
            errorHandler.LogException(e, user.UserID, user.SubscriptionID, user.Watermark, user.SubscriptionStatusDateTime);
          }
          throw e;
        }
      }
    
    }
    

     


    tnederveld
    Wednesday, March 23, 2011 9:43 PM

All replies

  • Ok, we have found the issue but we don't know how to get around it. The machines that the SentItems notification was never being sent for it turned out that they had Outlook set to "Use Cached Exchange Mode". Once I had one of the guys turn that off, we started getting SentItem notifications from him. Why is this and how can we get around it?

    Thanks, I need an answer ASAP.

    Terry 


    tnederveld
    Thursday, March 24, 2011 1:55 PM
  • I have encountered the same behaviour with "Cached Exchange Mode". And I have to say that it is working properly. I don't know how exactly is the logic of this mode implemented, but no notifications are lost. They just go to some queue and they are sent in a batch after few minutes. You can suppress this behaviour by pressing SHIFT+F9 in Outlook when developing.
    Friday, March 9, 2012 12:45 PM