3

I am trying to get PushSharp (2.0.4.0?) to run as a Windows Service. Each time the service runs and performs a push.QueueNotification(), the applet hangs on the call to stopAPNS(push). When I run similar code as a Console applications, it runs fine each time (i.e, I am receiving my push notifications). The console app is based upon the PushSharp.Sample project.

Interestingly, if I start/stop my applet without executing any push.QueueNotification() calls, my service exits properly. In others, the call to stopAPNS(push) in OnStop() does not hang. I guess this makes sense...the queue is empty

I have compiled the PushSharp project under .NET 4.0 and 4.5. In each case, I get the same behavior.

I have provided a sanitized version of my code below.

Any thoughts on why the push.StopAllServices(true) call hangs when run as a Windows Service?

Thanks.

public class PushNoteService : System.ServiceProcess.ServiceBase
{
// Initialize global instance of PushBroker service
    private PushBroker push = new PushBroker();

    #region Constructor
    public PushNoteService()
    {
        // This call is required by the Windows.Forms Component Designer.
        InitializeComponent();

        // TODO: Add any initialization after the InitComponent call
    }
    #endregion

    #region Component Designer generated code

    // The main entry point for the process
    static void Main()
    {
        System.ServiceProcess.ServiceBase[] ServicesToRun;

        // More than one user Service may run within the same process. To add
        // another service to this process, change the following line to
        // create a second service object. For example,
        //
        //   ServicesToRun = new System.ServiceProcess.ServiceBase[] {new PushNoteService(), new MySecondUserService()};
        //
        ServicesToRun = new System.ServiceProcess.ServiceBase[] { new PushNoteService() };

        System.ServiceProcess.ServiceBase.Run(ServicesToRun);
    }

    #endregion

    #region OnStart
    /// <summary>
    /// Set things in motion so your service can do its work.
    /// </summary>
    protected override void OnStart(string[] args)
    {
        YYLog.Log.Instance.Info("Starting service.");

        timer2.Enabled = true;

        YYLog.Log.Instance.Info("Starting APNS.");
        startAPNS(push);
    }
    #endregion

    #region OnStop
    /// <summary>
    /// Stop this service.
    /// </summary>
    protected override void OnStop()
    {
        YYLog.Log.Instance.Info("Stopping service.");

        // Add code here to perform any tear-down necessary to stop your service.
        timer2.Enabled = false;

        YYLog.Log.Instance.Info("Stopping APNS.");
        stopAPNS(push);

        // some clean up.
        push = null;

        YYLog.Log.Instance.Info("Service stopped.");
    }
    #endregion


    #region Acess Methods


    /// <summary>
    /// On Timer_Elasped events, websites are accessed to check status.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">E.</param>
    private void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
            int count = checkForPushRequest();
            YYLog.Log.Instance.Info("   Count: " + count.ToString());
            if (count > 0)
            {
                stopAPNS(push);
            }

    }

    private int checkForPushRequest()
    {
        YYLog.Log.Instance.Info("Processing push notifications...");

        int count = 0;

        // Get the ConnectionStrings collection.
        ConnectionStringSettings connections = ConfigurationManager.ConnectionStrings["MyDB"];

        using (SqlConnection conn = new SqlConnection(connections.ConnectionString))
        {
            conn.Open();

            SqlCommand cmd = new SqlCommand("MySP", conn);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            SqlDataReader dr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);

            while (dr.Read())
            {
                // increment counter
                count++;

                int badgeNumber = 0;

                string deviceToken = Convert.ToString(dr[dr.GetOrdinal("DeviceToken")]);
                string alertMessage = Convert.ToString(dr[dr.GetOrdinal("AlertMessage")]);
                if (!dr.IsDBNull(dr.GetOrdinal("BadgeNumber")))
                {
                    badgeNumber = Convert.ToInt16(dr[dr.GetOrdinal("BadgeNumber")]);
                }
                string soundFile = Convert.ToString(dr[dr.GetOrdinal("SoundFile")]);

                // Send out the notification to APNS
                YYLog.Log.Instance.Trace("  Sending notification to " + deviceToken);
                sendPush(deviceToken, alertMessage, badgeNumber, soundFile);
            }

            dr.Close();
        }

        return count;
    }

    private void sendPush(string DeviceToken, string AlertMessage, int BadgeNumber, string SoundFile)
    {
        push.QueueNotification(new AppleNotification()
                                   .ForDeviceToken(DeviceToken)
                                   .WithAlert(AlertMessage)
                                   .WithBadge(BadgeNumber)
                                   .WithSound(SoundFile));

    }

    private void startAPNS(PushBroker push)
    {
        //Wire up the events for all the services that the broker registers
        push.OnNotificationSent += NotificationSent;
        push.OnChannelException += ChannelException;
        push.OnServiceException += ServiceException;
        push.OnNotificationFailed += NotificationFailed;
        push.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired;
        push.OnDeviceSubscriptionChanged += DeviceSubscriptionChanged;
        push.OnChannelCreated += ChannelCreated;
        push.OnChannelDestroyed += ChannelDestroyed;

        string appleCertFileName = System.Configuration.ConfigurationManager.AppSettings["APNS_Certificate"];
        var appleCert = File.ReadAllBytes(appleCertFileName);

        string appleCertPassword = System.Configuration.ConfigurationManager.AppSettings["APNS_Certificate_Password"];
        bool productionMode = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["APNS_Production_Mode"]);
        push.RegisterAppleService(new ApplePushChannelSettings(productionMode, appleCert, appleCertPassword)); //Extension method
    }

    private void stopAPNS(PushBroker push)
    {
        YYLog.Log.Instance.Info("Waiting for Queue to Finish...");

        //Stop and wait for the queues to drains
        push.StopAllServices(true);

        YYLog.Log.Instance.Info("Queue Finished");
    }

    #region Events
    private void DeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, INotification notification)
    {
        //Currently this event will only ever happen for Android GCM
        YYLog.Log.Instance.Info("Device Registration Changed:  Old-> " + oldSubscriptionId + "  New-> " + newSubscriptionId + " -> " + notification);
    }

    private void NotificationSent(object sender, INotification notification)
    {
        YYLog.Log.Instance.Info("Sent: " + sender + " -> " + notification);
    }

    private void NotificationFailed(object sender, INotification notification, Exception notificationFailureException)
    {
        YYLog.Log.Instance.Error("Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification);
    }

    private void ChannelException(object sender, IPushChannel channel, Exception exception)
    {
        YYLog.Log.Instance.Error("Channel Exception: " + sender + " -> " + exception);
    }

    private void ServiceException(object sender, Exception exception)
    {
        YYLog.Log.Instance.Error("Channel Exception: " + sender + " -> " + exception);
    }

    private void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification)
    {
        YYLog.Log.Instance.Info("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId);
    }

    private void ChannelDestroyed(object sender)
    {
        YYLog.Log.Instance.Info("Channel Destroyed for: " + sender);
    }

    private void ChannelCreated(object sender, IPushChannel pushChannel)
    {
        YYLog.Log.Instance.Info("Channel Created for: " + sender);
    }
    #endregion

    #endregion

}
Steve
  • 927
  • 3
  • 10
  • 23
  • Try wiring up some events for the PushBroker instance and seeing if you are actually getting messages sent or errors, etc. – Redth May 23 '13 at 12:29
  • @Redth, in startAPNS(), the events are wired up there. You can see events at the bottom of the code snippet. The code in applet and console app are the same for wiring up events. The console app returns results, i.e, NotificationSent() is called. In the applet, it is not called. Did I understand your comment correctly? Thx. – Steve May 23 '13 at 13:27
  • So no errors are happening? I really need more info to help diagnose this. Could it be that it's 'hanging' is really the fact that it's waiting for the queues to be drained as messages are being sent? – Redth May 23 '13 at 15:02
  • @Redth, no errors. That's what makes it a mystery. The queue has no more than 5 messages (I am using a small subset to test). So, it should not take less than a minute. When I run the same test via a console app, it flushes in under 10 seconds. Just trying to figure out the difference. Do you have an example of PushSharp running as a windows service? Thx. – Steve May 23 '13 at 15:21
  • I Don't have an example as it should really be no different. I'm going to recommend that you compile from source and set some breakpoints and do some debugging to try and see what's going on :) – Redth May 24 '13 at 18:06
  • So the same thing was happening to me and after debugging my code I found out that **PushSharp.StopAllServices** would only hang after calling **AppleNotification.ForDeviceToken** passing null for the **deviceToken** parameter. – Thomas C. G. de Vilhena Sep 06 '14 at 02:49

1 Answers1

1

We have a Web application but I think the solution can be the same. We spent a whole day trying to guess the problem! In the end it was in the wrong Newtonsoft.Json version Some of the projects in our solution was dependant on the older version of this library as a result we had bad luck to get wrong version in the /bin folder of the Web project.

Andrew
  • 73
  • 1
  • 7