~/blogs/IvanDDimitrov/posts.aspx
Categories
Bloggers
Blogs RSS feed

Extend Sitefinity SmtpSender

by Ivan D.Dimitrov

Sitefinity offers very easy extensibility of our default SmtpSender class. Since our Email Campaigns module uses the Sitefinity's NotificationService to send out the campaign issues, the SmtpSender gets resolved dynamically and can be replaced from the Notification profile.

This article shows how you can inherit from the default SmtpSender and add logic that in this case detects whether a message has failed delivery due to  System.Net.Mail.SmtpStatusCode.MailboxBusy status code (450) and re-sends it if necessary.

If the message falls within this case the custom sender attempts to re-send it for a predefined number of retries and a predefined period between the retries. To ease configuring this logic we have exposed the numberOfRetriesPerMessage and waitBeforeRetry parameters to be read form your site's web.config <appSettings> section. In other words, here's the full implementation of the overridden SendMessage method of the custom SmtpSender:

public override SendResult SendMessage(IMessageInfo messageInfo, ISubscriberRequest subscriber)
        {
            var result = base.SendMessage(messageInfo, subscriber);
            var numberOfRetriesPerMessage = 3;
            if (!(ConfigurationManager.AppSettings["numberOfRetriesPerMessage"].IsNullOrEmpty()))
                int.TryParse(ConfigurationManager.AppSettings["numberOfRetriesPerMessage"],out numberOfRetriesPerMessage);
            var waitBeforeRetry = 1000;
            if (!(ConfigurationManager.AppSettings["waitBeforeRetry"].IsNullOrEmpty()))
                int.TryParse(ConfigurationManager.AppSettings["waitBeforeRetry"],out waitBeforeRetry);
   
            for (int i = 0; i < numberOfRetriesPerMessage; i++)
            {
                if (result.HandledException != null)
                {
                    if(result.HandledException.GetType() ==typeof(System.Net.Mail.SmtpException)
                    && ((System.Net.Mail.SmtpException)result.HandledException).StatusCode == System.Net.Mail.SmtpStatusCode.MailboxBusy)
                    {
                        Thread.Sleep(waitBeforeRetry);
                        result =base.SendMessage(messageInfo, subscriber);
                    }
                    else
                        break;
                }
                else
                    break;
            }
   
            return result;
        }

As you can see above, we have explicitly restricted the logic for retrying the sending only to messages with status  System.Net.Mail.SmtpStatusCode.MailboxBusy. You are free to extend it as per your specific requirements.

Here is the full source of the CustomSenderSMTP class. In order to use it you need to include it in your project's solution, and build it. When done, you need to go to Adminsitration>Settings->Advanced->Notifications>Profiles->Default and change the SenderType property to the CLR type of the custom SmtpSender class (in our case that is SitefinityWebApp.CustomSenderSMTP). From this point on Sitefinity will use the custom SmtpSender instead of the default one, and the sending logic will go through the custom SendMessage implementation.

 In addition to the sample, there are cases where you need to catch exceptions related to the custom sender at runtime. This can be done by replacing the base SendMessage method in a try-catch statement, so that when an issue occurs, the error generated can be caught and dealt with appropriately by either logging it or handling it with additional logic. Here is the modification to the sample:

public override SendResult SendMessage(IMessageInfo messageInfo, ISubscriberRequest subscriber)
        {
            //the result will be a SendResult object which will either be success or contain an exception
            //you can use this for logging/debugging purposes
           SendResult result;
            try
            {
                result = base.SendMessage(messageInfo, subscriber);
            }
            catch (Exception)
            {
                  
                //implement additional logging
                //can be used for debugging purposes as well
            }
             
            var numberOfRetriesPerMessage = 3;
            if (!(ConfigurationManager.AppSettings["numberOfRetriesPerMessage"].IsNullOrEmpty()))
                int.TryParse(ConfigurationManager.AppSettings["numberOfRetriesPerMessage"], out numberOfRetriesPerMessage);
            //In millliseconds
            var waitBeforeRetry = 1000;
            if (!(ConfigurationManager.AppSettings["waitBeforeRetry"].IsNullOrEmpty()))
                int.TryParse(ConfigurationManager.AppSettings["waitBeforeRetry"], out waitBeforeRetry);
  
            for (int i = 0; i < numberOfRetriesPerMessage; i++)
            {
                if (result.HandledException != null)
                {
                    if (result.HandledException.GetType() == typeof(System.Net.Mail.SmtpException)
                    && ((System.Net.Mail.SmtpException)result.HandledException).StatusCode == System.Net.Mail.SmtpStatusCode.MailboxBusy)
                    {
                        Thread.Sleep(waitBeforeRetry);
                        result = base.SendMessage(messageInfo, subscriber);
                    }
                    else
                        break;
                }
                else
                    break;
            }
  
            return result;
        }
  
 
    }
}


By default we have preset the number of times Sitefinity retries to send a message to 3, and the default period between retries is 1000ms (1 second). If you want to control these you can add the following entries in your web.config <appSettings> section:

<appSettings>
    <!--Indicates how many times Sitefintiy SMTP Sender will retry sending messages with status code 450-->
    <add key="numberOfRetriesPerMessage" value="5" />
    <!--Indicates how long Sitefintiy will wait before retying the send (in milliseconds)-->
    <add key="waitBeforeRetry" value="2000" />
 Changing the values will change the logic behavior  accordingly. Here is a short  video of the sender in action.

Additionally. our default SmtpClient.Timeout is set to 100 000 miliseconds (100 seconds). If you wish to increase this value, you can override the System.Net.Mail.SmtpClient to get a value you have set in the web.config:


protected override System.Net.Mail.SmtpClient CreateSmtpClient(Telerik.Sitefinity.Services.Notifications.Configuration.SmtpSenderProfileElement profile)
        {
            var client = base.CreateSmtpClient(profile);
            //In millliseconds. The default value is 100,000 (100 seconds).
            var smtpClientTimeout = 100000;
            if (!(ConfigurationManager.AppSettings["smtpClientTimeout"].IsNullOrEmpty()))
                int.TryParse(ConfigurationManager.AppSettings["smtpClientTimeout"], out smtpClientTimeout);
            client.Timeout = smtpClientTimeout;
            return client;
        }

This allows you to add an additional key to the appSettings node of the web.config named and smtpClientTimeout. Adding a value to it will alter the default one. 

 

Leave a comment