[SalesForce] How to capture a system.limitexception stopping the emails being sent

In Salseforce governor limits, it states that every org is allowed to send single emails to 1000 external addresses every 24 hours. On very rare occasions, an org I work with has exceeded this limit through Apex emails on a particular busy day. Exceeding the limit prevents them from sending emails until more became available.

I appreciate that Salesforce is not a mass emailing program. However on the rare occasions when the limit is exceeded in this way, I would like there to be a way to capture exceeding this limit and redirect the emails back to the current user with an internal note instructing them to direct the messages manually (there is no limit on internal email).

Salesforce themselves suggest using the Messaging.reserveSingleEmailCapacity(count) method to determine to see if the org has the capacity to send the emails. However, this method is next to useless for this because if the emails are not available, an uncatchable system.limit exception is fired. So instead of allowing the email process to run which would cause an un-catchable exception, we can simply have this happen earlier, doesn't really help!

Does anyone have an idea about how to capture this limit break? I have heard one potential solution that involves a scheduled job updating a global setting with the latest time the job successfully ran, then using another job to read this value and react accordingly. I could do this, but given the rare occurrence of the limit exception, I would prefer not to use regular scheduled jobs in this manner.

Best Answer

Update 26th Oct: See below for a second version following comments below

I have just promoted this idea as well, I would never have imagined it to be a good design for this method to have it throw an unhandled exception, IMHO that is!

Anyway, since we are being inventive here, the following also seems to do the trick. But be aware much like the custom setting counting trick, it does not actually reserve anything for the subsequent sending of emails, as the method call is made in another VF context.

<apex:page controller="TestMessagingLimitController" action="{!check}"/>

public with sharing class TestMessagingLimitController 
{
    public PageReference check()
    {
        Integer amount = Integer.valueOf(ApexPages.currentPage().getParameters().get('amount'));
        Messaging.reserveSingleEmailCapacity(amount);       
        return null;
    }
}

You can then do this...

try
{
    PageReference checkMessages = Page.testmessaginglimit;
    checkMessages.getParameters().put('amount', '1000');
    checkMessages.getContent();
    // Success (note messages will not be reserved however)
}
catch (Exception e)
{
    // Failure
    System.debug(e.getMessage());
}

Implementation Note: If you wrap the above in your own helper method, e.g.

public static boolean checkSingleEmailCapacity(Integer amount)

You can then easily switch out this implementation with a try/catch once Salesforce allows us to catch these exceptions or provides an alternative as per the idea exchange posting.

Hope this helps!

Update: Apex REST Approach

Here is a further approach that is using a Http callout. I've left the above approach in my answer, as it has the benefit of not needing a remote site enabled, global class etc. So please make your choice! In the end if you follow the abstraction I recommended above and only call the helper method you can change your mind swiftly in the future.

@RestResource(urlMapping='/MessageLimit')
global with sharing class MessageLimit 
{
    @HttpGet
    global static void doGet() 
    {
        RestRequest req = RestContext.request;
        Integer amount = Integer.valueOf(req.params.get('amount'));
        Messaging.reserveSingleEmailCapacity(amount);               
    }

    public static boolean checkSingleEmailCapacity(Integer amount)
    {   
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm() + '/services/apexrest/MessageLimit?amount=' + amount);
        req.setMethod('GET');
        req.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());
        HttpResponse res = h.send(req);
        if(res.getStatusCode() == 500) // May want to actually check the body message to be 100% sure
            return false;
        return true;
    }
}    

Thus you can now do this!

if(MessageLimit.checkSingleEmailCapacity(1001))
    System.debug('Good to go!');
else 
    System.debug('No go for launch!');              

Enjoy!

Related Topic