Search code examples
web-servicesasynchronoussalesforceapex-codeapex

SFDC Apex Code: Access class level static variable from "Future" method


I need to do a callout to webservice from my ApexController class. To do this, I have an asycn method with attribute @future (callout=true). The webservice call needs to refeence an object that gets populated in save call from VF page.

Since, static (future) calls does not all objects to be passed in as method argument, I was planning to add the data in a static Map and access that in my static method to do a webservice call out. However, the static Map object is getting re-initalized and is null in the static method.

I will really appreciate if anyone can give me some pointeres on how to address this issue. Thanks!

Here is the code snipped:

      private static Map<String, WidgetModels.LeadInformation> leadsMap;

       ....
       ......
        public PageReference save() {

       if(leadsMap == null){
           leadsMap = new Map<String, WidgetModels.LeadInformation>();
        }
        leadsMap.put(guid,widgetLead);

       }
        //make async call to Widegt Webservice
        saveWidgetCallInformation(guid)

       //async call to widge webserivce  
      @future (callout=true)
      public static void saveWidgetCallInformation(String guid) {
        WidgetModels.LeadInformation cachedLeadInfo =   
        (WidgetModels.LeadInformation)leadsMap.get(guid);
      .....
      //call websevice

      }

Solution

  • @future is totally separate execution context. It won't have access to any history of how it was called (meaning all static variables are reset, you start with fresh governor limits etc. Like a new action initiated by the user).

    The only thing it will "know" is the method parameters that were passed to it. And you can't pass whole objects, you need to pass primitives (Integer, String, DateTime etc) or collections of primitives (List, Set, Map).

    If you can access all the info you need from the database - just pass a List<Id> for example and query it.

    If you can't - you can cheat by serializing your objects and passing them as List<String>. Check the documentation around JSON class or these 2 handy posts:


    Side note - can you rethink your flow? If the starting point is Visualforce you can skip the @future step. Do the callout first and then the DML (if needed). That way the usual "you have uncommitted work pending" error won't be triggered. This thing is there not only to annoy developers ;) It's there to make you rethink your design. You're asking the application to have open transaction & lock on the table(s) for up to 2 minutes. And you're giving yourself extra work - will you rollback your changes correctly when the insert went OK but callout failed?

    By reversing the order of operations (callout first, then the DML) you're making it simpler - there was no save attempt to DB so there's nothing to roll back if the save fails.