Knowing if a future call is allowed

I’m having an issue where we have some code that can be called anywhere and then it makes an @future call to log a message to an external system.

The problem is if this method is called (either directly or indirectly) in a Controller Extension a System.LimitException is thrown saying @future call currently not allowed

I know you can check to see the number of allowed future calls with Limits.getLimitFutureCalls() and I know you can see how many you’ve made with Limits.getFutureCalls() and see if it’s batch with with System.isBatch() or is in future call with System.isFuture(). Is there any other way to know if you cannot make a future call?

My goal would be to check to see if the @future call can be made, and if not skip the method call.

NOTE: You cannot make @future calls from get/set/constructor in the Controller extension and metioned in the documentation

Example of bad code

Class

public class GenericUtils {
    public static void reportException(Exception e) {
        //Limits.getFutureCalls() returns 0
        //Limits.getLimitFutureCalls() return 10

        //Make @future call to log
        System.debug(e.getMessage());
    }
}

Controller

public with sharing class ControllerExtension {
    public ControllerExtension(ApexPages.StandardController cont) {
        //Do stuff
    }

    public getMyVariable() {
        try {
            //Do more stuff
        } catch (Exception e) {
            GenericUtils.reportException(e); //This throws the LimitException
        }
    }
}

Page

<apex:page standardController="Account" extensions="ControllerExtension">
    <apex:form id="theForm">
        <apex:inputText value="{!myVariable}" />
    </apex:form>
</apex:page>

Answer

Answer and Why. After spending some further time on this, I believe the answer to your question in determining if the future feature is available programatically is unfortunately, no this is not possible. I’ve considered the following in arriving at this answer for you.

  • Catching the LimitException. This is unfortunately not possible. I have had luck in using a ApexPage.getContent callout to catch unhanded exceptions in the past. But this was more relating to the ability to send emails and was not context to the call stack (e.g. in a get method).
  • Parsing the Stack Trace from a freshly created Exception instance. Unfortuantly this is not reliable enough information. Not only is it not generally available (once your code is running in a subscriber org), it would require some maintenance of the detection logic.
  • Utilising the Limit Methods. These are request scope in their responses, it could be argued that Limits.getLimitFutureCalls() should return 0 if called from a call stack that containts a VF get/set or constructor, but sadly they don’t presently. Nor do I see Salesforce changing this anytime soon.

Alternative thoughts to your requirement…

There is plenty of architecture best practice and guidelines that tell the developer about when this feature is not allowed. However this does not help the developer providing a utility function aimed at being called from various contexts. This does present difficulties in implementing a generic logging solution such as yours. There is also the fact that, as you’ve discovered some exceptions cannot be caught by Apex code and thus will bypass your logging utility function completely.

  • Apex Exception User for Packages. . For those unhandled exceptions (or indeed exceptions you don’t care to catch deliberately) the platform will automatically email you the provider of the package with the details, including stack dumps as described here. You might also find this cookbook article useful, in conjunction with this approach you could route such emails to your external service perhaps?

  • Scheduled Job and Staging Errors in a Custom Object. You can of course catch exceptions as you’ve discovered. It might be a better route to consider then a Schedule Apex job that collects unsent errors and sends them to your external web service. This will resolve issues where you cannot make further future calls for example once already in a future call or batch execute. Unfortunatly you still cannot make DML calls in get/set methods however, so any persistant type of logging is not going to be possible in these contexts. If you go down this route consider this.

Other Recommendations

I do recommend that you consider refactoring some of your complex logic from get/set methods and into action methods where you logging will be more manageable (as per this answer). You may have to accept that 100% of exceptions cannot be caught, in which case these will surface via the Apex Exception email back to you from your subscriber orgs.

Attribution
Source : Link , Question Author : Patrick Connelly , Answer Author : Community

Leave a Comment